spree_cm_commissioner 1.10.0.pre.pre → 1.10.0.pre.hotfix.pre.sms

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 (105) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test_and_build_gem.yml +48 -26
  3. data/.gitignore +1 -2
  4. data/.vscode/settings.json +1 -1
  5. data/Gemfile.lock +1 -22
  6. data/app/controllers/spree/admin/cms_pages_controller_decorator.rb +32 -0
  7. data/app/controllers/spree/admin/stock_managements_controller.rb +1 -17
  8. data/app/controllers/spree/api/v2/storefront/accommodations_controller.rb +31 -14
  9. data/app/controllers/spree/api/v2/tenant/account_checker_controller.rb +1 -1
  10. data/app/controllers/spree/api/v2/tenant/account_recovers_controller.rb +2 -2
  11. data/app/controllers/spree/api/v2/tenant/cms_pages_controller.rb +41 -0
  12. data/app/controllers/spree/api/v2/tenant/reset_passwords_controller.rb +1 -1
  13. data/app/controllers/spree_cm_commissioner/api/v2/storefront/cms_pages_controller_decorator.rb +18 -0
  14. data/app/interactors/spree_cm_commissioner/account_recover.rb +2 -2
  15. data/app/interactors/spree_cm_commissioner/create_event.rb +23 -0
  16. data/app/interactors/spree_cm_commissioner/existing_account_checker.rb +1 -1
  17. data/app/interactors/spree_cm_commissioner/pin_code_sender.rb +3 -3
  18. data/app/interactors/spree_cm_commissioner/sms.rb +14 -0
  19. data/app/interactors/spree_cm_commissioner/telegram_debug_pin_code_sender.rb +2 -1
  20. data/app/interactors/spree_cm_commissioner/user_forgotten_password_updater.rb +2 -1
  21. data/app/interactors/spree_cm_commissioner/user_password_authenticator.rb +4 -2
  22. data/app/interactors/spree_cm_commissioner/vattanac_bank_initiator.rb +2 -2
  23. data/app/jobs/spree_cm_commissioner/telegram_debug_pin_code_sender_job.rb +3 -1
  24. data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +3 -2
  25. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +0 -26
  26. data/app/models/concerns/spree_cm_commissioner/product_type.rb +0 -10
  27. data/app/models/concerns/spree_cm_commissioner/user_identity.rb +7 -4
  28. data/app/models/spree_cm_commissioner/cms_page_decorator.rb +9 -0
  29. data/app/models/spree_cm_commissioner/line_item_decorator.rb +0 -1
  30. data/app/models/spree_cm_commissioner/order_decorator.rb +0 -15
  31. data/app/models/spree_cm_commissioner/place.rb +11 -2
  32. data/app/models/spree_cm_commissioner/product_decorator.rb +0 -1
  33. data/app/models/spree_cm_commissioner/stock/availability_checker.rb +25 -27
  34. data/app/models/spree_cm_commissioner/stock/availability_validator_decorator.rb +1 -2
  35. data/app/models/spree_cm_commissioner/stock/line_item_availability_checker.rb +3 -3
  36. data/app/models/spree_cm_commissioner/taxon_decorator.rb +11 -0
  37. data/app/models/spree_cm_commissioner/taxon_option_type.rb +8 -0
  38. data/app/models/spree_cm_commissioner/taxon_option_value.rb +8 -0
  39. data/app/models/spree_cm_commissioner/trip.rb +0 -11
  40. data/app/models/spree_cm_commissioner/trip_connection.rb +1 -0
  41. data/app/models/spree_cm_commissioner/trip_stop.rb +12 -5
  42. data/app/models/spree_cm_commissioner/variant_decorator.rb +28 -25
  43. data/app/models/spree_cm_commissioner/variant_options.rb +0 -14
  44. data/app/models/spree_cm_commissioner/vehicle.rb +0 -7
  45. data/app/models/spree_cm_commissioner/vendor_decorator.rb +2 -1
  46. data/app/models/spree_cm_commissioner/vendor_stop.rb +2 -1
  47. data/app/overrides/spree/admin/cms_pages/_form/tenant_fields.html.erb.deface +7 -0
  48. data/app/overrides/spree/admin/cms_pages/index/cms_pages_tabs.html.erb.deface +21 -0
  49. data/app/overrides/spree/admin/users/index/body.html.erb.deface +3 -0
  50. data/app/overrides/spree/admin/users/index/headers.html.erb.deface +3 -0
  51. data/app/queries/spree_cm_commissioner/trip_query.rb +11 -11
  52. data/app/queries/spree_cm_commissioner/variant_availability/non_permanent_stock_query.rb +45 -0
  53. data/app/queries/spree_cm_commissioner/variant_availability/permanent_stock_query.rb +55 -0
  54. data/app/queries/spree_cm_commissioner/vendor_stop_place_query.rb +54 -0
  55. data/app/request_schemas/spree_cm_commissioner/accommodation_request_schema.rb +0 -3
  56. data/app/request_schemas/spree_cm_commissioner/application_request_schema.rb +1 -1
  57. data/app/serializers/spree/v2/storefront/accommodation_serializer.rb +0 -2
  58. data/app/serializers/spree/v2/tenant/guest_serializer.rb +1 -0
  59. data/app/services/spree_cm_commissioner/aes_encryption_service.rb +6 -4
  60. data/app/services/spree_cm_commissioner/organizer/export_guest_csv_service.rb +2 -0
  61. data/app/services/spree_cm_commissioner/user_authenticator.rb +3 -1
  62. data/app/views/spree/admin/shared/_cms_pages_tabs.html.erb +20 -0
  63. data/app/views/spree/admin/stock_managements/index.html.erb +5 -31
  64. data/config/routes.rb +4 -11
  65. data/db/migrate/20250418072528_add_nested_set_columns_to_places.rb +10 -0
  66. data/db/migrate/20250430091742_create_cm_taxon_option_types.rb +9 -0
  67. data/db/migrate/20250430092928_create_cm_taxon_option_values.rb +9 -0
  68. data/db/migrate/20250506092929_add_trip_count_to_cm_vendor_stops.rb +5 -0
  69. data/db/migrate/20250512075319_add_tenant_id_to_spree_cms_pages.rb +6 -0
  70. data/docker-compose.yml +1 -1
  71. data/lib/generators/spree_cm_commissioner/install/install_generator.rb +2 -2
  72. data/lib/generators/spree_cm_commissioner/install/templates/app/javascript/{spree_dashboard/spree_cm_commissioner → spree_cm_commissioner}/utilities.js +0 -4
  73. data/lib/spree_cm_commissioner/calendar_event.rb +1 -11
  74. data/lib/spree_cm_commissioner/test_helper/factories/place_factory.rb +11 -1
  75. data/lib/spree_cm_commissioner/test_helper/factories/variant_factory.rb +6 -28
  76. data/lib/spree_cm_commissioner/version.rb +1 -1
  77. data/lib/spree_cm_commissioner.rb +0 -34
  78. data/lib/tasks/migrate_and_rebuild_place_hierarchy.rake +9 -0
  79. data/lib/tasks/update_orphan_root_places.rake +7 -0
  80. data/spree_cm_commissioner.gemspec +0 -5
  81. metadata +22 -69
  82. data/app/controllers/spree/api/v2/storefront/accommodations/variants_controller.rb +0 -42
  83. data/app/finders/spree_cm_commissioner/accommodations/find.rb +0 -40
  84. data/app/finders/spree_cm_commissioner/accommodations/find_variant.rb +0 -35
  85. data/app/interactors/spree_cm_commissioner/inventory_item_syncer.rb +0 -25
  86. data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +0 -71
  87. data/app/jobs/spree_cm_commissioner/inventory_item_syncer_job.rb +0 -7
  88. data/app/jobs/spree_cm_commissioner/stock/permanent_inventory_items_generator_job.rb +0 -9
  89. data/app/models/spree_cm_commissioner/inventory.rb +0 -11
  90. data/app/models/spree_cm_commissioner/inventory_item.rb +0 -37
  91. data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +0 -40
  92. data/app/models/spree_cm_commissioner/redis_stock/inventory_updater.rb +0 -114
  93. data/app/models/spree_cm_commissioner/redis_stock/line_items_cached_inventory_items_builder.rb +0 -42
  94. data/app/models/spree_cm_commissioner/redis_stock/variant_cached_inventory_items_builder.rb +0 -27
  95. data/app/models/spree_cm_commissioner/stock/order_availability_checker.rb +0 -44
  96. data/app/models/spree_cm_commissioner/stock_movement_decorator.rb +0 -34
  97. data/app/request_schemas/spree_cm_commissioner/variant_request_schema.rb +0 -19
  98. data/app/views/spree/admin/stock_managements/_events_popover.html.erb +0 -17
  99. data/app/views/spree/admin/stock_managements/calendar.html.erb +0 -32
  100. data/db/migrate/20250304293518_create_cm_inventory_items.rb +0 -21
  101. data/db/migrate/20250429094228_add_lock_version_to_cm_inventory_items.rb +0 -5
  102. data/lib/spree_cm_commissioner/cached_inventory_item.rb +0 -23
  103. data/lib/spree_cm_commissioner/test_helper/factories/inventory_item_factory.rb +0 -9
  104. data/lib/tasks/create_default_non_permanent_inventory_items.rake +0 -16
  105. data/lib/tasks/generate_inventory_items.rake +0 -7
@@ -13,13 +13,10 @@ module SpreeCmCommissioner
13
13
  state_machine.after_transition to: :complete, do: :notify_order_complete_telegram_notification_to_user, unless: :subscription?
14
14
  state_machine.after_transition to: :complete, do: :send_order_complete_telegram_alert_to_vendors, unless: :need_confirmation?
15
15
  state_machine.after_transition to: :complete, do: :send_order_complete_telegram_alert_to_store, unless: :need_confirmation?
16
- state_machine.around_transition to: :complete, do: :handle_unstock_in_redis
17
16
 
18
17
  state_machine.after_transition to: :resumed, do: :precalculate_conversion
19
- state_machine.around_transition to: :resumed, do: :handle_unstock_in_redis
20
18
 
21
19
  state_machine.after_transition to: :canceled, do: :precalculate_conversion
22
- state_machine.after_transition to: :canceled, do: :restock_inventory_in_redis!
23
20
 
24
21
  scope :accepted, -> { where(request_state: 'accepted') }
25
22
 
@@ -69,29 +66,6 @@ module SpreeCmCommissioner
69
66
  end
70
67
  end
71
68
 
72
- def handle_unstock_in_redis
73
- ActiveRecord::Base.transaction do
74
- yield # Equal to block.call
75
-
76
- # After the transition is complete, the following code will execute first before proceeding to other `after_transition` callbacks.
77
- # This ensures that if `unstock_inventory_in_redis!` fails, the state will be rolled back,
78
- # and neither the `finalize!` method nor any notifications will be triggered.
79
- # The payment will be reversed in vPago gem, and `Spree::Checkout::Complete` will be called, which checks `order.reload.complete?`.
80
- # This is critical because if the order state is complete, the payment will be marked as paid.
81
- CmAppLogger.log(label: 'order_state_machine_before_unstock', data: { order_id: id, state: state })
82
- unstock_inventory_in_redis!
83
- # We rollback only order state, and we keep payment state as it is.
84
- # We implement payment in vPago gem, and it will be reversed in the gem.
85
- # Some bank has api for refund, but some don't have the api to refund yet. So we keep the payment state as it is and refund manually.
86
- CmAppLogger.log(label: 'order_state_machine_after_unstock', data: { order_id: id, state: state })
87
- end
88
- rescue StandardError => e
89
- CmAppLogger.log(label: 'order_state_machine',
90
- data: { order_id: id, error: e.message, type: e.class.name, backtrace: e.backtrace.first(5).join("\n") }
91
- )
92
- raise e
93
- end
94
-
95
69
  def generate_bib_number
96
70
  line_items.find_each(&:generate_remaining_guests)
97
71
 
@@ -6,20 +6,10 @@ module SpreeCmCommissioner
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  PRODUCT_TYPES = %i[accommodation service ecommerce transit].freeze
9
- PERMANENT_STOCK_PRODUCT_TYPES = %w[accommodation service transit].freeze
10
- PRE_INVENTORY_DAYS = { 'transit' => 90, 'accommodation' => 365, 'service' => 30 }.freeze
11
9
 
12
10
  included do
13
11
  enum product_type: PRODUCT_TYPES if table_exists? && column_names.include?('product_type')
14
12
  enum primary_product_type: PRODUCT_TYPES if table_exists? && column_names.include?('primary_product_type')
15
13
  end
16
-
17
- def permanent_stock?
18
- PERMANENT_STOCK_PRODUCT_TYPES.include?(product_type)
19
- end
20
-
21
- def pre_inventory_days
22
- PRE_INVENTORY_DAYS[product_type]
23
- end
24
14
  end
25
15
  end
@@ -25,18 +25,21 @@ module SpreeCmCommissioner
25
25
  end
26
26
 
27
27
  class_methods do
28
- def find_user_by_login(login)
28
+ def find_user_by_login(login, tenant_id)
29
29
  return nil if login.blank?
30
30
 
31
31
  login = login.downcase
32
32
  parser = PhoneNumberParser.call(phone_number: login)
33
33
 
34
+ scope = Spree.user_class.all
35
+ scope = scope.where(tenant_id: tenant_id) if tenant_id.present?
36
+
34
37
  if parser.intel_phone_number.present?
35
- find_by(intel_phone_number: parser.intel_phone_number)
38
+ scope.find_by(intel_phone_number: parser.intel_phone_number)
36
39
  elsif login =~ URI::MailTo::EMAIL_REGEXP
37
- find_by(email: login)
40
+ scope.find_by(email: login)
38
41
  else
39
- find_by(login: login)
42
+ scope.find_by(login: login)
40
43
  end
41
44
  end
42
45
  end
@@ -0,0 +1,9 @@
1
+ module SpreeCmCommissioner
2
+ module CmsPageDecorator
3
+ def self.prepended(base)
4
+ base.multi_tenant :tenant, class_name: 'SpreeCmCommissioner::Tenant'
5
+ end
6
+ end
7
+ end
8
+
9
+ Spree::CmsPage.prepend(SpreeCmCommissioner::CmsPageDecorator) unless Spree::CmsPage.included_modules.include?(SpreeCmCommissioner::CmsPageDecorator)
@@ -9,7 +9,6 @@ module SpreeCmCommissioner
9
9
  base.has_one :google_wallet, class_name: 'SpreeCmCommissioner::GoogleWallet', through: :product
10
10
 
11
11
  base.has_many :option_types, through: :product
12
- base.has_many :inventory_items, through: :variant
13
12
  base.has_many :taxons, class_name: 'Spree::Taxon', through: :product
14
13
  base.has_many :guests, class_name: 'SpreeCmCommissioner::Guest', dependent: :destroy
15
14
  base.has_many :pending_guests, pending_guests_query, class_name: 'SpreeCmCommissioner::Guest', dependent: :destroy
@@ -58,13 +58,6 @@ module SpreeCmCommissioner
58
58
  end
59
59
  end
60
60
 
61
- # override
62
- # spree use this method to check stock availability & consider whether :order can continue to next state.
63
- def insufficient_stock_lines
64
- checker = SpreeCmCommissioner::Stock::OrderAvailabilityChecker.new(self)
65
- checker.insufficient_stock_lines
66
- end
67
-
68
61
  def ticket_seller_user?
69
62
  return false if user.nil?
70
63
 
@@ -201,14 +194,6 @@ module SpreeCmCommissioner
201
194
 
202
195
  private
203
196
 
204
- def unstock_inventory_in_redis!
205
- SpreeCmCommissioner::RedisStock::InventoryUpdater.new(line_item_ids).unstock!
206
- end
207
-
208
- def restock_inventory_in_redis!
209
- SpreeCmCommissioner::RedisStock::InventoryUpdater.new(line_item_ids).restock!
210
- end
211
-
212
197
  # override :spree_api
213
198
  def webhook_payload_body
214
199
  resource_serializer.new(
@@ -2,6 +2,8 @@ require_dependency 'spree_cm_commissioner'
2
2
 
3
3
  module SpreeCmCommissioner
4
4
  class Place < ApplicationRecord
5
+ acts_as_nested_set
6
+
5
7
  validates :reference, presence: true, if: :validate_reference?
6
8
  validates :lat, presence: true, if: :validate_lat?
7
9
  validates :lon, presence: true, if: :validate_lon?
@@ -13,15 +15,22 @@ module SpreeCmCommissioner
13
15
 
14
16
  has_many :product_places, class_name: 'SpreeCmCommissioner::ProductPlace', dependent: :destroy
15
17
  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
18
18
 
19
+ has_many :children, -> { order(:lft) }, class_name: 'SpreeCmCommissioner::Place', foreign_key: :parent_id, dependent: :destroy
19
20
  has_many :vendor_stops, class_name: 'SpreeCmCommissioner::VendorStop', dependent: :destroy
20
21
 
21
22
  def self.ransackable_attributes(auth_object = nil)
22
23
  super & %w[name code]
23
24
  end
24
25
 
26
+ def full_path_name
27
+ self_and_ancestors.map(&:name).reverse.join(', ')
28
+ end
29
+
30
+ def path_ids
31
+ self_and_ancestors.map(&:id)
32
+ end
33
+
25
34
  def validate_reference?
26
35
  Spree::Store.default.code.exclude?('billing')
27
36
  end
@@ -33,7 +33,6 @@ module SpreeCmCommissioner
33
33
  base.has_one :google_wallet, class_name: 'SpreeCmCommissioner::GoogleWallet', dependent: :destroy
34
34
 
35
35
  base.has_many :complete_line_items, through: :classifications, source: :line_items
36
- base.has_many :inventory_items, through: :variants
37
36
 
38
37
  base.has_many :product_places, class_name: 'SpreeCmCommissioner::ProductPlace', dependent: :destroy
39
38
  base.has_many :places, through: :product_places
@@ -1,49 +1,47 @@
1
1
  module SpreeCmCommissioner
2
2
  module Stock
3
3
  class AvailabilityChecker
4
- attr_reader :variant, :options, :error_message
4
+ attr_reader :variant, :error_message
5
5
 
6
- def initialize(variant, options = {})
6
+ def initialize(variant)
7
7
  @variant = variant
8
- @options = options
9
8
  @error_message = nil
10
9
  end
11
10
 
12
- def can_supply?(quantity = 1)
11
+ def can_supply?(quantity = 1, options = {})
13
12
  return false unless variant.available?
14
13
  return true unless variant.should_track_inventory?
15
14
  return true if variant.backorderable?
16
15
  return true if variant.need_confirmation?
17
16
 
18
- variant_available?(quantity)
19
- end
20
-
21
- def variant_available?(quantity = 1)
22
- return false if cached_inventory_items.empty?
23
-
24
- cached_inventory_items.all? do |cached_inventory_item|
25
- cached_inventory_item.active? && cached_inventory_item.quantity_available >= quantity
26
- end
27
- end
28
-
29
- def cached_inventory_items
30
- return @cached_inventory_items if defined?(@cached_inventory_items)
17
+ # when delivery required, shipment will dynamically add / remove unit from stock item.
18
+ # so we can directly check can_supply with stock items directly.
19
+ return variant.stock_items.sum(:count_on_hand) >= quantity if variant.delivery_required?
31
20
 
32
21
  if variant.permanent_stock?
33
- return [] if options[:from_date].blank? || options[:to_date].blank?
34
-
35
- @cached_inventory_items = builder_klass.new(
36
- variant_id: variant.id,
37
- from_date: options[:from_date].to_date,
38
- to_date: options[:to_date].to_date
39
- ).call
22
+ permanent_stock_variant_available?(quantity, options)
40
23
  else
41
- @cached_inventory_items = builder_klass.new(variant_id: variant.id).call
24
+ variant_available?(quantity, options)
42
25
  end
43
26
  end
44
27
 
45
- def builder_klass
46
- ::SpreeCmCommissioner::RedisStock::VariantCachedInventoryItemsBuilder
28
+ def variant_available?(quantity = 1, options = {})
29
+ query = SpreeCmCommissioner::VariantAvailability::NonPermanentStockQuery.new(
30
+ variant: variant,
31
+ except_line_item_id: options[:except_line_item_id]
32
+ )
33
+ result = query.available?(quantity)
34
+ @error_message = query.error_message unless result
35
+ result
36
+ end
37
+
38
+ def permanent_stock_variant_available?(quantity = 1, options = {})
39
+ SpreeCmCommissioner::VariantAvailability::PermanentStockQuery.new(
40
+ variant: variant,
41
+ from_date: options[:from_date].to_date,
42
+ to_date: options[:to_date].to_date,
43
+ except_line_item_id: options[:except_line_item_id]
44
+ ).available?(quantity)
47
45
  end
48
46
  end
49
47
  end
@@ -3,8 +3,7 @@ module SpreeCmCommissioner
3
3
  module AvailabilityValidatorDecorator
4
4
  # override
5
5
  def item_available?(line_item, quantity)
6
- SpreeCmCommissioner::Stock::LineItemAvailabilityChecker.new(line_item)
7
- .can_supply?(quantity)
6
+ SpreeCmCommissioner::Stock::LineItemAvailabilityChecker.new(line_item).can_supply?(quantity)
8
7
  end
9
8
  end
10
9
  end
@@ -8,14 +8,14 @@ module SpreeCmCommissioner
8
8
  end
9
9
 
10
10
  def can_supply?(quantity)
11
- ::SpreeCmCommissioner::Stock::AvailabilityChecker.new(line_item.variant, options)
12
- .can_supply?(quantity)
11
+ AvailabilityChecker.new(line_item.variant).can_supply?(quantity, options)
13
12
  end
14
13
 
15
14
  def options
16
15
  {
17
16
  from_date: line_item.from_date,
18
- to_date: line_item.to_date
17
+ to_date: line_item.to_date,
18
+ except_line_item_id: line_item.id
19
19
  }
20
20
  end
21
21
  end
@@ -57,6 +57,9 @@ module SpreeCmCommissioner
57
57
  base.has_many :event_blazer_queries, class_name: 'SpreeCmCommissioner::TaxonBlazerQuery'
58
58
  base.has_many :blazer_queries, through: :event_blazer_queries, class_name: 'Blazer::Query'
59
59
 
60
+ base.has_many :taxon_option_types, class_name: 'SpreeCmCommissioner::TaxonOptionType'
61
+ base.has_many :taxon_option_values, class_name: 'SpreeCmCommissioner::TaxonOptionValue'
62
+
60
63
  def base.active_homepage_events
61
64
  joins(:homepage_section_relatables)
62
65
  .joins("INNER JOIN spree_taxons taxon ON taxon.id = cm_homepage_section_relatables.relatable_id
@@ -96,6 +99,14 @@ module SpreeCmCommissioner
96
99
  .uniq
97
100
  end
98
101
 
102
+ def selected_option_types
103
+ taxon_option_types.pluck(:option_type_id)
104
+ end
105
+
106
+ def selected_option_values
107
+ taxon_option_values.pluck(:option_value_id)
108
+ end
109
+
99
110
  def event_url
100
111
  "https://#{Spree::Store.default.url}/t/#{permalink}"
101
112
  end
@@ -0,0 +1,8 @@
1
+ require_dependency 'spree_cm_commissioner'
2
+
3
+ module SpreeCmCommissioner
4
+ class TaxonOptionType < ApplicationRecord
5
+ belongs_to :taxon, class_name: 'Spree::Taxon', dependent: :destroy
6
+ belongs_to :option_type, class_name: 'Spree::OptionType', dependent: :destroy
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ require_dependency 'spree_cm_commissioner'
2
+
3
+ module SpreeCmCommissioner
4
+ class TaxonOptionValue < ApplicationRecord
5
+ belongs_to :taxon, class_name: 'Spree::Taxon', dependent: :destroy
6
+ belongs_to :option_value, class_name: 'Spree::OptionValue', dependent: :destroy
7
+ end
8
+ end
@@ -14,23 +14,12 @@ module SpreeCmCommissioner
14
14
  validates :duration, numericality: { greater_than: 0 }
15
15
  validate :origin_and_destination_cannot_be_the_same
16
16
 
17
- has_many :trip_stops, class_name: 'SpreeCmCommissioner::TripStop', dependent: :destroy
18
-
19
- after_commit :create_trip_stops
20
-
21
- accepts_nested_attributes_for :trip_stops, allow_destroy: true
22
-
23
17
  def convert_duration_to_seconds
24
18
  return if hours.blank? && minutes.blank? && seconds.blank?
25
19
 
26
20
  self.duration = (hours.to_i * 3600) + (minutes.to_i * 60) + seconds.to_i
27
21
  end
28
22
 
29
- def create_trip_stops
30
- trip_stops.create(stop_type: :boarding, stop_id: origin_id)
31
- trip_stops.create(stop_type: :drop_off, stop_id: destination_id)
32
- end
33
-
34
23
  def duration_in_hms
35
24
  return { hours: 0, minutes: 0, seconds: 0 } if duration.nil?
36
25
 
@@ -5,6 +5,7 @@ module SpreeCmCommissioner
5
5
 
6
6
  validate :both_trip_cannot_be_the_same
7
7
  before_validation :calculate_connection_time_minutes
8
+ validates :from_trip_id, uniqueness: { scope: :to_trip_id }
8
9
 
9
10
  private
10
11
 
@@ -8,22 +8,29 @@ module SpreeCmCommissioner
8
8
  belongs_to :stop, class_name: 'SpreeCmCommissioner::Place'
9
9
 
10
10
  before_validation :set_stop_name
11
- after_create :create_vendor_stop
12
-
11
+ after_destroy :decrement_trip_count
12
+ after_commit :create_vendor_stop
13
13
  validates :stop_id, uniqueness: { scope: :trip_id }
14
14
 
15
15
  def set_stop_name
16
- self.stop_name = stop.name
16
+ self.stop_name = stop.name if stop.present?
17
17
  end
18
18
 
19
19
  def create_vendor_stop
20
- vendor.vendor_stops.where(stop_id: stop_id, stop_type: stop_type).first_or_create
20
+ vendor_stop = vendor.vendor_stops.where(stop_id: stop_id, stop_type: stop_type).first_or_create
21
+ vendor_stop.update(trip_count: vendor_stop.trip_count.to_i + 1)
21
22
  end
22
23
 
23
24
  private
24
25
 
26
+ def decrement_trip_count
27
+ vendor.vendor_stops.where(stop_id: stop_id, stop_type: stop_type).find_each do |vendor_stop|
28
+ vendor_stop.update(trip_count: [vendor_stop.trip_count - 1, 0].max)
29
+ end
30
+ end
31
+
25
32
  def vendor
26
- Spree::Product.find(trip.product_id).vendor
33
+ Spree::Product.find(trip&.product_id).vendor
27
34
  end
28
35
  end
29
36
  end
@@ -1,7 +1,6 @@
1
1
  module SpreeCmCommissioner
2
2
  module VariantDecorator
3
3
  def self.prepended(base)
4
- base.include SpreeCmCommissioner::ProductType
5
4
  base.include SpreeCmCommissioner::ProductDelegation
6
5
  base.include SpreeCmCommissioner::VariantOptionsConcern
7
6
 
@@ -22,16 +21,21 @@ module SpreeCmCommissioner
22
21
  base.has_many :variant_guest_card_class, class_name: 'SpreeCmCommissioner::VariantGuestCardClass'
23
22
  base.has_many :guest_card_classes, class_name: 'SpreeCmCommissioner::GuestCardClass', through: :variant_guest_card_class
24
23
 
25
- base.has_many :inventory_items, class_name: 'SpreeCmCommissioner::InventoryItem'
26
-
27
24
  base.scope :subscribable, -> { active.joins(:product).where(product: { subscribable: true, status: :active }) }
28
- base.scope :with_permanent_stock, -> { joins(:product).where(product: { product_type: base::PERMANENT_STOCK_PRODUCT_TYPES }) }
29
- base.scope :with_non_permanent_stock, -> { joins(:product).where.not(product: { product_type: base::PERMANENT_STOCK_PRODUCT_TYPES }) }
30
-
31
25
  base.has_one :trip,
32
26
  class_name: 'SpreeCmCommissioner::Trip'
27
+ base.has_many :trip_stops, class_name: 'SpreeCmCommissioner::TripStop', dependent: :destroy, foreign_key: :trip_id
33
28
  base.accepts_nested_attributes_for :option_values
34
- base.after_commit :sync_trip, if: -> { product.product_type == 'transit' }
29
+ base.after_commit :sync_trip, if: :transit?
30
+ base.accepts_nested_attributes_for :trip_stops, allow_destroy: true
31
+ base.after_commit :create_trip_stops, if: :transit?
32
+ end
33
+
34
+ def create_trip_stops
35
+ return if is_master?
36
+
37
+ trip_stops.find_or_create_by(stop_type: :boarding, stop_id: options.origin)
38
+ trip_stops.find_or_create_by(stop_type: :drop_off, stop_id: options.destination)
35
39
  end
36
40
 
37
41
  def delivery_required?
@@ -47,24 +51,12 @@ module SpreeCmCommissioner
47
51
  super || product.discontinued?
48
52
  end
49
53
 
50
- def event
51
- taxons.event.first
52
- end
53
-
54
- def default_inventory_item_exist?
55
- inventory_items.exists?(inventory_date: nil)
54
+ def permanent_stock?
55
+ accommodation?
56
56
  end
57
57
 
58
- def create_default_non_permanent_inventory_item!(quantity_available: nil, max_capacity: nil)
59
- return if product_type.blank? # handle in case product not exist for variant.
60
- return unless should_track_inventory?
61
- return if default_inventory_item_exist?
62
-
63
- inventory_items.create!(
64
- product_type: product_type,
65
- quantity_available: [0, quantity_available || total_on_hand].max,
66
- max_capacity: [0, max_capacity || total_on_hand].max
67
- )
58
+ def event
59
+ taxons.event.first
68
60
  end
69
61
 
70
62
  # override
@@ -87,12 +79,23 @@ module SpreeCmCommissioner
87
79
  end
88
80
 
89
81
  # override
90
- def in_stock?(options = {})
91
- SpreeCmCommissioner::Stock::AvailabilityChecker.new(self, options).can_supply?
82
+ def in_stock?
83
+ available_quantity.positive?
92
84
  end
93
85
 
94
86
  private
95
87
 
88
+ def total_purchases
89
+ Spree::LineItem.complete.where(variant_id: id).sum(:quantity).to_i
90
+ end
91
+
92
+ def available_quantity
93
+ stock_count = stock_items.sum(&:count_on_hand)
94
+ return stock_count if delivery_required?
95
+
96
+ stock_count - total_purchases
97
+ end
98
+
96
99
  def update_vendor_price
97
100
  return unless vendor.present? && product&.product_type == vendor&.primary_product_type
98
101
 
@@ -166,19 +166,5 @@ module SpreeCmCommissioner
166
166
  Time.zone.parse(time) if time.present?
167
167
  end
168
168
  end
169
-
170
- def arrival_time
171
- @arrival_time ||= departure_time + total_duration_in_minutes.minutes
172
- end
173
-
174
- def total_duration_in_minutes
175
- total_duration_in_minutes = 0
176
-
177
- total_duration_in_minutes += duration_in_hours * 60 if duration_in_hours.present?
178
- total_duration_in_minutes += duration_in_minutes if duration_in_minutes.present?
179
- total_duration_in_minutes += duration_in_seconds / 60 if duration_in_seconds.present?
180
-
181
- total_duration_in_minutes
182
- end
183
169
  end
184
170
  end
@@ -8,7 +8,6 @@ module SpreeCmCommissioner
8
8
  has_one :primary_photo, -> { order(position: :asc) }, class_name: 'SpreeCmCommissioner::VehiclePhoto', as: :viewable, dependent: :destroy
9
9
  belongs_to :vendor, class_name: 'Spree::Vendor'
10
10
 
11
- before_save :set_attributes
12
11
  after_commit :create_vehicle_option_value
13
12
 
14
13
  has_many :vehicle_photo, class_name: 'SpreeCmCommissioner::VehiclePhoto', as: :viewable, dependent: :destroy
@@ -17,12 +16,6 @@ module SpreeCmCommissioner
17
16
  validates :code, uniqueness: { scope: :vendor_id }, presence: true
18
17
  validates :license_plate, uniqueness: {}, allow_blank: true
19
18
 
20
- def set_attributes
21
- self.route_type = vehicle_type.route_type
22
- self.number_of_seats = vehicle_type.vehicle_seats_count
23
- self.allow_seat_selection = vehicle_type.allow_seat_selection
24
- end
25
-
26
19
  def create_vehicle_option_value
27
20
  SpreeCmCommissioner::VehicleOptionValueCreator.call(self)
28
21
  end
@@ -79,7 +79,8 @@ module SpreeCmCommissioner
79
79
  dependent: :destroy, inverse_of: :relatable
80
80
 
81
81
  base.has_many :vehicle_types, class_name: 'SpreeCmCommissioner::VehicleType', dependent: :destroy
82
- base.has_many :vehicles, through: :vehicle_types, class_name: 'SpreeCmCommissioner::Vehicle', dependent: :destroy
82
+ base.has_many :vehicles, class_name: 'SpreeCmCommissioner::Vehicle', dependent: :destroy
83
+ # base.has_many :vehicles, through: :vehicle_types, class_name: 'SpreeCmCommissioner::Vehicle', dependent: :destroy
83
84
 
84
85
  base.validates :account_name, :account_number, presence: true, if: lambda {
85
86
  payment_qrcode.present? && Spree::Store.default.code.include?('billing')
@@ -4,6 +4,7 @@ module SpreeCmCommissioner
4
4
  belongs_to :vendor, class_name: 'Spree::Vendor'
5
5
  belongs_to :stop, class_name: 'SpreeCmCommissioner::Place'
6
6
 
7
- enum stop_type: { boarding: 0, drop_off: 1 }
7
+ validates :trip_count, numericality: { greater_than_or_equal_to: 0 }
8
+ enum :stop_type, { boarding: 0, drop_off: 1 }
8
9
  end
9
10
  end
@@ -0,0 +1,7 @@
1
+ <!-- insert_before "erb[loud]:contains('f.field_container :title')" -->
2
+
3
+ <%= f.field_container :tenant_id, class: ['col-12'] do %>
4
+ <%= f.label :tenant_id, raw(Spree.t(:tenant) + ' (optional)') %>
5
+ <%= f.collection_select :tenant_id, SpreeCmCommissioner::Tenant.all, :id, :name, { prompt: Spree.t('select_tenant') }, { class: 'form-control select2' } %>
6
+ <%= f.error_message_on :tenant_id %>
7
+ <% end %>
@@ -0,0 +1,21 @@
1
+ <!-- insert_before "erb[silent]:contains('content_for :page_actions')" -->
2
+
3
+ <%= render partial: 'spree/admin/shared/cms_pages_tabs' %>
4
+
5
+ <% if params[:tab] == 'default' %>
6
+ <div class="alert alert-info mb-3">
7
+ <%= svg_icon name: "info-circle.svg", classes: 'mr-2', width: '16', height: '16' %>
8
+ CMS Pages for the default store. These pages are shared globally and are not tied to any specific tenant.
9
+ </div>
10
+ <% elsif params[:tab] == 'tenants' && params[:tenant_id].present? %>
11
+ <div class="alert alert-info mb-3">
12
+ <%= svg_icon name: "info-circle.svg", classes: 'mr-2', width: '16', height: '16' %>
13
+ CMS Pages for the tenant: <strong><%= SpreeCmCommissioner::Tenant.find(params[:tenant_id]).name %></strong>.
14
+ These pages are displayed only to users associated with this tenant.
15
+ </div>
16
+ <% elsif params[:tab] == 'tenants' %>
17
+ <div class="alert alert-info mb-3">
18
+ <%= svg_icon name: "info-circle.svg", classes: 'mr-2', width: '16', height: '16' %>
19
+ CMS Pages for all tenants. These pages are displayed only to users associated with specific tenants.
20
+ </div>
21
+ <% end %>
@@ -23,6 +23,9 @@
23
23
  <td class='phone_number'>
24
24
  <%= user.phone_number%>
25
25
  </td>
26
+ <td class='tenant'>
27
+ <%= user.tenant&.name || Spree::Store.default.name %>
28
+ </td>
26
29
  <td class='created_at'>
27
30
  <%= user.created_at %>
28
31
  </td>
@@ -19,6 +19,9 @@
19
19
  <th>
20
20
  <%= sort_link @search, :phone_number, I18n.t('activerecord.attributes.spree/order.phone_number'), {}, {title: 'phone_number_title'} %>
21
21
  </th>
22
+ <th>
23
+ <%= Spree.t(:tenant) %>
24
+ </th>
22
25
  <th>
23
26
  <%= sort_link @search, :created_at, Spree.t(:create_at), {}, {title: 'create_at_title'} %>
24
27
  </th>