spree_cm_commissioner 1.21.0 → 2.0.1.pre.pre

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 (190) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test_and_build_gem.yml +16 -2
  3. data/.gitignore +2 -1
  4. data/Gemfile.lock +28 -1
  5. data/Rakefile +33 -4
  6. data/app/assets/stylesheets/spree_cm_commissioner/backend/calendar.scss +8 -11
  7. data/app/controllers/spree/admin/inventory_items_controller.rb +83 -0
  8. data/app/controllers/spree/admin/stock_managements_controller.rb +63 -1
  9. data/app/controllers/spree/api/v2/organizer/invite_guests_controller.rb +1 -1
  10. data/app/controllers/spree/api/v2/storefront/accommodations/variants_controller.rb +42 -0
  11. data/app/controllers/spree/api/v2/storefront/accommodations_controller.rb +14 -31
  12. data/app/controllers/spree/api/v2/storefront/queue_cart/line_items_controller.rb +2 -2
  13. data/app/factory/spree_cm_commissioner/invite_guest_claimed_telegram_message_factory.rb +84 -37
  14. data/app/finders/spree_cm_commissioner/accommodations/find.rb +37 -0
  15. data/app/finders/spree_cm_commissioner/accommodations/find_variant.rb +32 -0
  16. data/app/interactors/spree_cm_commissioner/ensure_correct_product_type.rb +40 -0
  17. data/app/interactors/spree_cm_commissioner/inventory_item_syncer.rb +25 -0
  18. data/app/interactors/spree_cm_commissioner/pin_code_sender.rb +1 -0
  19. data/app/interactors/spree_cm_commissioner/sms.rb +1 -1
  20. data/app/interactors/spree_cm_commissioner/stock/inventory_item_resetter.rb +44 -0
  21. data/app/interactors/spree_cm_commissioner/stock/inventory_items_adjuster.rb +13 -0
  22. data/app/interactors/spree_cm_commissioner/stock/inventory_items_generator.rb +15 -0
  23. data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +75 -0
  24. data/app/interactors/spree_cm_commissioner/stock/stock_movement_creator.rb +32 -0
  25. data/app/interactors/spree_cm_commissioner/user_id_token_authenticator.rb +3 -7
  26. data/app/interactors/spree_cm_commissioner/user_id_token_checker.rb +3 -11
  27. data/app/interactors/spree_cm_commissioner/user_identity_checker.rb +6 -12
  28. data/app/interactors/spree_cm_commissioner/user_registration_with_id_token.rb +1 -7
  29. data/app/jobs/spree_cm_commissioner/application_job.rb +20 -0
  30. data/app/jobs/spree_cm_commissioner/application_unique_job.rb +20 -0
  31. data/app/jobs/spree_cm_commissioner/ensure_correct_product_type_job.rb +7 -0
  32. data/app/jobs/spree_cm_commissioner/inventory_item_syncer_job.rb +7 -0
  33. data/app/jobs/spree_cm_commissioner/sms_pin_code_job.rb +1 -1
  34. data/app/jobs/spree_cm_commissioner/stock/inventory_items_adjuster_job.rb +11 -0
  35. data/app/jobs/spree_cm_commissioner/stock/inventory_items_generator_job.rb +11 -0
  36. data/app/jobs/spree_cm_commissioner/stock/permanent_inventory_items_generator_job.rb +9 -0
  37. data/app/models/concerns/spree_cm_commissioner/line_item_durationable.rb +9 -15
  38. data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +1 -12
  39. data/app/models/concerns/spree_cm_commissioner/order_seatable.rb +44 -0
  40. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +39 -0
  41. data/app/models/concerns/spree_cm_commissioner/product_delegation.rb +1 -3
  42. data/app/models/concerns/spree_cm_commissioner/product_type.rb +10 -0
  43. data/app/models/concerns/spree_cm_commissioner/taxon_kind.rb +1 -1
  44. data/app/models/spree_cm_commissioner/block.rb +23 -0
  45. data/app/models/spree_cm_commissioner/guest.rb +18 -0
  46. data/app/models/spree_cm_commissioner/inventory.rb +11 -0
  47. data/app/models/spree_cm_commissioner/inventory_item.rb +69 -0
  48. data/app/models/spree_cm_commissioner/line_item_decorator.rb +46 -78
  49. data/app/models/spree_cm_commissioner/notification_taxon.rb +1 -1
  50. data/app/models/spree_cm_commissioner/option_type_decorator.rb +1 -11
  51. data/app/models/spree_cm_commissioner/order_decorator.rb +30 -1
  52. data/app/models/spree_cm_commissioner/place.rb +1 -4
  53. data/app/models/spree_cm_commissioner/price_decorator.rb +9 -0
  54. data/app/models/spree_cm_commissioner/product_decorator.rb +10 -6
  55. data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +41 -0
  56. data/app/models/spree_cm_commissioner/redis_stock/inventory_updater.rb +126 -0
  57. data/app/models/spree_cm_commissioner/redis_stock/line_items_cached_inventory_items_builder.rb +36 -0
  58. data/app/models/spree_cm_commissioner/redis_stock/variant_cached_inventory_items_builder.rb +25 -0
  59. data/app/models/spree_cm_commissioner/reserved_block.rb +30 -0
  60. data/app/models/spree_cm_commissioner/seat_layout.rb +20 -0
  61. data/app/models/spree_cm_commissioner/seat_section.rb +16 -0
  62. data/app/models/spree_cm_commissioner/seats/blocks_canceler.rb +30 -0
  63. data/app/models/spree_cm_commissioner/seats/blocks_holder.rb +53 -0
  64. data/app/models/spree_cm_commissioner/seats/blocks_reserver.rb +49 -0
  65. data/app/models/spree_cm_commissioner/seats/errors/blocks_are_on_hold_by_other_guest.rb +4 -0
  66. data/app/models/spree_cm_commissioner/seats/errors/blocks_are_reserved_by_other_guest.rb +4 -0
  67. data/app/models/spree_cm_commissioner/seats/errors/blocks_are_reserved_by_same_guest.rb +4 -0
  68. data/app/models/spree_cm_commissioner/seats/errors/unable_to_save_reserved_block_record.rb +4 -0
  69. data/app/models/spree_cm_commissioner/service_calendar.rb +0 -2
  70. data/app/models/spree_cm_commissioner/state_decorator.rb +0 -1
  71. data/app/models/spree_cm_commissioner/stock/availability_checker.rb +26 -25
  72. data/app/models/spree_cm_commissioner/stock/availability_validator_decorator.rb +2 -1
  73. data/app/models/spree_cm_commissioner/stock/line_item_availability_checker.rb +3 -3
  74. data/app/models/spree_cm_commissioner/stock/order_availability_checker.rb +44 -0
  75. data/app/models/spree_cm_commissioner/stock_item_decorator.rb +17 -0
  76. data/app/models/spree_cm_commissioner/taxon_decorator.rb +0 -1
  77. data/app/models/spree_cm_commissioner/taxonomy_decorator.rb +0 -6
  78. data/app/models/spree_cm_commissioner/trip.rb +8 -10
  79. data/app/models/spree_cm_commissioner/trip_connection.rb +5 -5
  80. data/app/models/spree_cm_commissioner/trip_stop.rb +6 -25
  81. data/app/models/spree_cm_commissioner/user_identity_provider.rb +4 -26
  82. data/app/models/spree_cm_commissioner/variant_block.rb +9 -0
  83. data/app/models/spree_cm_commissioner/variant_decorator.rb +37 -47
  84. data/app/models/spree_cm_commissioner/variant_options.rb +0 -23
  85. data/app/models/spree_cm_commissioner/vehicle.rb +9 -14
  86. data/app/models/spree_cm_commissioner/vendor_decorator.rb +11 -14
  87. data/app/models/spree_cm_commissioner/vendor_place.rb +9 -3
  88. data/app/queries/spree_cm_commissioner/trip_query.rb +70 -44
  89. data/app/request_schemas/spree_cm_commissioner/accommodation_request_schema.rb +3 -0
  90. data/app/request_schemas/spree_cm_commissioner/application_request_schema.rb +1 -1
  91. data/app/request_schemas/spree_cm_commissioner/variant_request_schema.rb +19 -0
  92. data/app/serializers/spree/v2/storefront/accommodation_serializer.rb +2 -0
  93. data/app/serializers/spree/v2/storefront/line_item_serializer_decorator.rb +1 -0
  94. data/app/serializers/spree/v2/tenant/user_serializer.rb +0 -1
  95. data/app/services/spree_cm_commissioner/user_authenticator.rb +1 -1
  96. data/app/views/spree/admin/inventory_items/show.html.erb +72 -0
  97. data/app/views/spree/admin/stock_managements/_variant_stock_items.html.erb +7 -2
  98. data/app/views/spree/admin/stock_managements/calendar.html.erb +56 -0
  99. data/app/views/spree/admin/stock_managements/index.html.erb +55 -6
  100. data/config/initializers/paper_trail.rb +1 -0
  101. data/config/initializers/spree_permitted_attributes.rb +5 -0
  102. data/config/routes.rb +21 -2
  103. data/db/migrate/20240202080634_update_counter_cache_of_vehicle_type.rb +3 -1
  104. data/db/migrate/20250304293518_create_cm_inventory_items.rb +21 -0
  105. data/db/migrate/20250429094228_add_lock_version_to_cm_inventory_items.rb +5 -0
  106. data/db/migrate/20250502025848_add_index_to_spree_products.rb +5 -0
  107. data/db/migrate/20250502030001_add_product_type_to_spree_variants.rb +5 -0
  108. data/db/migrate/20250502030002_add_product_type_to_spree_line_items.rb +5 -0
  109. data/db/migrate/20250603035256_add_inventory_item_to_spree_prices.rb +7 -0
  110. data/db/migrate/20250619073724_drop_table_cm_line_item_seats.rb +5 -0
  111. data/db/migrate/20250619073812_drop_table_cm_vehicle_seats.rb +5 -0
  112. data/db/migrate/20250619073844_drop_table_cm_vehicle_types.rb +9 -0
  113. data/db/migrate/20250619073957_drop_table_cm_option_value_vehicle_types.rb +5 -0
  114. data/db/migrate/20250619082354_remove_unnecessary_fields_from_cm_places.rb +9 -0
  115. data/db/migrate/20250619082736_remove_route_type_from_spree_products.rb +5 -0
  116. data/db/migrate/20250619083055_remove_unnecessary_fields_from_spree_taxons.rb +5 -0
  117. data/db/migrate/20250620083055_remove_variant_id_from_cm_trips.rb +5 -0
  118. data/db/migrate/20250620090000_update_cm_trip_connections_to_use_cm_trips.rb +6 -0
  119. data/db/migrate/20250620090001_create_cm_seat_layouts.rb +17 -0
  120. data/db/migrate/20250620090002_create_cm_seat_sections.rb +18 -0
  121. data/db/migrate/20250620090003_create_cm_blocks.rb +18 -0
  122. data/db/migrate/20250624091005_create_cm_reserved_blocks.rb +29 -0
  123. data/db/migrate/20250626083642_create_cm_variant_blocks.rb +24 -0
  124. data/db/migrate/20250627023314_add_block_id_to_cm_guests.rb +13 -0
  125. data/db/migrate/20250716022821_add_location_reference_to_cm_vendor_places.rb +5 -0
  126. data/db/migrate/20250716031743_drop_table_cm_vendor_stops.rb +5 -0
  127. data/db/migrate/20250717023824_add_vendor_reference_to_cm_trips.rb +5 -0
  128. data/db/migrate/20250717041414_add_location_place_reference_to_cm_trip_stops.rb +5 -0
  129. data/db/migrate/20250717042539_rename_cm_trip_stops_stop_id_column_to_stop_place_id.rb +7 -0
  130. data/db/migrate/20250717042707_rename_cm_trips_origin_and_destination_to_origin_place_and_destination_place.rb +11 -0
  131. data/docker-compose.yml +1 -1
  132. data/lib/cm_app_logger.rb +11 -4
  133. data/lib/generators/spree_cm_commissioner/install/install_generator.rb +14 -11
  134. data/lib/generators/spree_cm_commissioner/install/templates/app/javascript/{spree_cm_commissioner → spree_dashboard/spree_cm_commissioner}/utilities.js +4 -0
  135. data/lib/spree_cm_commissioner/cached_inventory_item.rb +23 -0
  136. data/lib/spree_cm_commissioner/calendar_event.rb +11 -1
  137. data/lib/spree_cm_commissioner/test_helper/factories/block_factory.rb +15 -0
  138. data/lib/spree_cm_commissioner/test_helper/factories/guest_factory.rb +10 -0
  139. data/lib/spree_cm_commissioner/test_helper/factories/homepage_section_relatable_factory.rb +1 -1
  140. data/lib/spree_cm_commissioner/test_helper/factories/inventory_item_factory.rb +9 -0
  141. data/lib/spree_cm_commissioner/test_helper/factories/line_item_factory.rb +1 -1
  142. data/lib/spree_cm_commissioner/test_helper/factories/option_type_factory.rb +6 -30
  143. data/lib/spree_cm_commissioner/test_helper/factories/order_factory.rb +0 -36
  144. data/lib/spree_cm_commissioner/test_helper/factories/product_factory.rb +18 -34
  145. data/lib/spree_cm_commissioner/test_helper/factories/reserved_block_factory.rb +27 -0
  146. data/lib/spree_cm_commissioner/test_helper/factories/seat_layout_factory.rb +11 -0
  147. data/lib/spree_cm_commissioner/test_helper/factories/seat_section_factory.rb +16 -0
  148. data/lib/spree_cm_commissioner/test_helper/factories/stock_location_factory.rb +2 -2
  149. data/lib/spree_cm_commissioner/test_helper/factories/trip_connection_factory.rb +6 -0
  150. data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +11 -3
  151. data/lib/spree_cm_commissioner/test_helper/factories/trip_stop_factory.rb +10 -0
  152. data/lib/spree_cm_commissioner/test_helper/factories/user_identity_provider_factory.rb +1 -1
  153. data/lib/spree_cm_commissioner/test_helper/factories/variant_block_factory.rb +7 -0
  154. data/lib/spree_cm_commissioner/test_helper/factories/variant_factory.rb +41 -19
  155. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_factory.rb +1 -1
  156. data/lib/spree_cm_commissioner/test_helper/factories/vendor_factory.rb +6 -1
  157. data/lib/spree_cm_commissioner/test_helper/factories/vendor_place_factory.rb +13 -1
  158. data/lib/spree_cm_commissioner/trip_query_result.rb +8 -0
  159. data/lib/spree_cm_commissioner/trip_result.rb +5 -3
  160. data/lib/spree_cm_commissioner/version.rb +1 -1
  161. data/lib/spree_cm_commissioner.rb +35 -1
  162. data/lib/tasks/create_default_non_permanent_inventory_items.rake +16 -0
  163. data/lib/tasks/ensure_correct_product_type.rake +7 -0
  164. data/lib/tasks/generate_inventory_items.rake +7 -0
  165. data/spree_cm_commissioner.gemspec +7 -0
  166. metadata +140 -28
  167. data/app/controllers/concerns/spree_cm_commissioner/transit/taxon_bitwise.rb +0 -44
  168. data/app/finders/spree_cm_commissioner/line_items/find_by_variant_decorator.rb +0 -20
  169. data/app/models/spree_cm_commissioner/branch.rb +0 -12
  170. data/app/models/spree_cm_commissioner/line_item_seat.rb +0 -10
  171. data/app/models/spree_cm_commissioner/option_value_vehicle_type.rb +0 -8
  172. data/app/models/spree_cm_commissioner/stop.rb +0 -23
  173. data/app/models/spree_cm_commissioner/vehicle_seat.rb +0 -11
  174. data/app/models/spree_cm_commissioner/vehicle_type.rb +0 -76
  175. data/app/models/spree_cm_commissioner/vendor_stop.rb +0 -10
  176. data/app/queries/spree_cm_commissioner/trip_search_query.rb +0 -76
  177. data/app/queries/spree_cm_commissioner/variant_availability/non_permanent_stock_query.rb +0 -45
  178. data/app/queries/spree_cm_commissioner/variant_availability/permanent_stock_query.rb +0 -55
  179. data/app/queries/spree_cm_commissioner/vendor_stop_place_query.rb +0 -54
  180. data/app/services/spree_cm_commissioner/vehicle_option_value_creator.rb +0 -11
  181. data/db/migrate/20250715103333_remove_indexes_from_cm_user_identity_providers.rb +0 -13
  182. data/lib/spree_cm_commissioner/test_helper/factories/branch_factory.rb +0 -12
  183. data/lib/spree_cm_commissioner/test_helper/factories/departure_time_option_type_factory.rb +0 -8
  184. data/lib/spree_cm_commissioner/test_helper/factories/duration_option_type_factory.rb +0 -8
  185. data/lib/spree_cm_commissioner/test_helper/factories/line_item_seat_factory.rb +0 -7
  186. data/lib/spree_cm_commissioner/test_helper/factories/stop_factory.rb +0 -14
  187. data/lib/spree_cm_commissioner/test_helper/factories/transit_place_factory.rb +0 -8
  188. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_option_type_factory.rb +0 -8
  189. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_type_factory.rb +0 -96
  190. data/lib/spree_cm_commissioner/trip_seat_layout_result.rb +0 -11
@@ -1,47 +1,48 @@
1
1
  module SpreeCmCommissioner
2
2
  module Stock
3
3
  class AvailabilityChecker
4
- attr_reader :variant, :error_message
4
+ attr_reader :variant, :options, :error_message
5
5
 
6
- def initialize(variant)
6
+ def initialize(variant, options = {})
7
7
  @variant = variant
8
+ @options = options
8
9
  @error_message = nil
9
10
  end
10
11
 
11
- def can_supply?(quantity = 1, options = {})
12
+ def can_supply?(quantity = 1)
12
13
  return false unless variant.available?
13
14
  return true unless variant.should_track_inventory?
14
15
  return true if variant.backorderable?
15
16
  return true if variant.need_confirmation?
16
17
 
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?
18
+ variant_available?(quantity)
19
+ end
20
20
 
21
- if variant.permanent_stock?
22
- permanent_stock_variant_available?(quantity, options)
23
- else
24
- variant_available?(quantity, options)
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
25
26
  end
26
27
  end
27
28
 
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
29
+ def cached_inventory_items
30
+ return @cached_inventory_items if defined?(@cached_inventory_items)
31
+
32
+ if variant.permanent_stock?
33
+ return [] if options[:from_date].blank? || options[:to_date].blank?
34
+
35
+ dates = options[:from_date].to_date..options[:to_date].to_date
36
+ @cached_inventory_items = builder_klass.new(variant_id: variant.id, dates: dates).call
37
+ @cached_inventory_items = [] if @cached_inventory_items.size != dates.count
38
+ @cached_inventory_items
39
+ else
40
+ @cached_inventory_items = builder_klass.new(variant_id: variant.id).call
41
+ end
36
42
  end
37
43
 
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)
44
+ def builder_klass
45
+ ::SpreeCmCommissioner::RedisStock::VariantCachedInventoryItemsBuilder
45
46
  end
46
47
  end
47
48
  end
@@ -3,7 +3,8 @@ module SpreeCmCommissioner
3
3
  module AvailabilityValidatorDecorator
4
4
  # override
5
5
  def item_available?(line_item, quantity)
6
- SpreeCmCommissioner::Stock::LineItemAvailabilityChecker.new(line_item).can_supply?(quantity)
6
+ SpreeCmCommissioner::Stock::LineItemAvailabilityChecker.new(line_item)
7
+ .can_supply?(quantity)
7
8
  end
8
9
  end
9
10
  end
@@ -8,14 +8,14 @@ module SpreeCmCommissioner
8
8
  end
9
9
 
10
10
  def can_supply?(quantity)
11
- AvailabilityChecker.new(line_item.variant).can_supply?(quantity, options)
11
+ ::SpreeCmCommissioner::Stock::AvailabilityChecker.new(line_item.variant, options)
12
+ .can_supply?(quantity)
12
13
  end
13
14
 
14
15
  def options
15
16
  {
16
17
  from_date: line_item.from_date,
17
- to_date: line_item.to_date,
18
- except_line_item_id: line_item.id
18
+ to_date: line_item.to_date
19
19
  }
20
20
  end
21
21
  end
@@ -0,0 +1,44 @@
1
+ # On top of SpreeCmCommissioner::Stock::AvailabilityChecker, this class does the same thing but in bulk check with redis.
2
+ # Which mean, it check basic condition with db first, once pass, it check with redis.
3
+ module SpreeCmCommissioner
4
+ module Stock
5
+ class OrderAvailabilityChecker
6
+ attr_reader :order
7
+
8
+ def initialize(order)
9
+ @order = order
10
+ end
11
+
12
+ def can_supply_all?
13
+ insufficient_stock_lines.empty?
14
+ end
15
+
16
+ def insufficient_stock_lines
17
+ cached_inventory_items_group_by_line_item_id.map do |line_item_id, cached_inventory_items|
18
+ line_item = order.line_items.find { |item| item.id == line_item_id }
19
+ line_item unless sufficient_stock_for?(line_item, cached_inventory_items)
20
+ end.compact
21
+ end
22
+
23
+ # {
24
+ # 1: [ {inventory_key: "inventory:1", active: true, quantity_available: 5, inventory_item_id: 1, variant_id: 1}],
25
+ # 2: [ {inventory_key: "inventory:2", active: true, quantity_available: 5, inventory_item_id: 2, variant_id: 2}],
26
+ # }
27
+ def cached_inventory_items_group_by_line_item_id
28
+ @cached_inventory_items_group_by_line_item_id ||=
29
+ ::SpreeCmCommissioner::RedisStock::LineItemsCachedInventoryItemsBuilder.new(
30
+ line_item_ids: order.line_items.pluck(:id)
31
+ ).call
32
+ end
33
+
34
+ def sufficient_stock_for?(line_item, cached_inventory_items)
35
+ return false unless line_item.variant.available?
36
+ return true unless line_item.variant.should_track_inventory?
37
+ return true if line_item.variant.backorderable?
38
+ return true if line_item.variant.need_confirmation?
39
+
40
+ cached_inventory_items.all? { |item| item.active? && item.quantity_available >= line_item.quantity }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -3,11 +3,28 @@ module SpreeCmCommissioner
3
3
  def self.prepended(base)
4
4
  base.has_one :vendor, through: :variant
5
5
  base.after_save :update_vendor_total_inventory, if: :saved_change_to_count_on_hand?
6
+
7
+ base.after_commit :create_inventory_items, on: :create
8
+ base.after_commit :adjust_inventory_items_async, on: :destroy
6
9
  end
7
10
 
8
11
  def update_vendor_total_inventory
9
12
  SpreeCmCommissioner::VendorJob.perform_later(vendor.id) if vendor.present?
10
13
  end
14
+
15
+ private
16
+
17
+ def create_inventory_items
18
+ SpreeCmCommissioner::Stock::InventoryItemsGeneratorJob.perform_later(variant_id: variant.id)
19
+ end
20
+
21
+ # When admin delete stock item, it will deduct stock from inventory item
22
+ def adjust_inventory_items_async
23
+ params = { variant_id: variant.id, quantity: -count_on_hand }
24
+ CmAppLogger.log(label: "#{self.class.name}#adjust_inventory_items_async", data: params) do
25
+ SpreeCmCommissioner::Stock::InventoryItemsAdjusterJob.perform_later(**params)
26
+ end
27
+ end
11
28
  end
12
29
  end
13
30
 
@@ -2,7 +2,6 @@ module SpreeCmCommissioner
2
2
  module TaxonDecorator
3
3
  def self.prepended(base) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
4
4
  base.include SpreeCmCommissioner::TaxonKind
5
- base.include SpreeCmCommissioner::Transit::TaxonBitwise
6
5
  base.include SpreeCmCommissioner::ParticipationTypeBitwise
7
6
  base.include SpreeCmCommissioner::EventCheckInFlowable
8
7
 
@@ -8,12 +8,6 @@ module SpreeCmCommissioner
8
8
  Spree::Taxonomy.find_or_create_by(name: 'Businesses', kind: 'category', store: Spree::Store.default)
9
9
  end
10
10
  end
11
-
12
- def base.place
13
- ActiveRecord::Base.connected_to(role: :writing) do
14
- Spree::Taxonomy.find_or_create_by(name: 'Place', kind: 'transit', store: Spree::Store.default)
15
- end
16
- end
17
11
  end
18
12
  end
19
13
  end
@@ -1,14 +1,15 @@
1
- require_dependency 'spree_cm_commissioner'
2
1
  module SpreeCmCommissioner
3
- class Trip < SpreeCmCommissioner::Base
2
+ class Trip < Base
4
3
  attr_accessor :hours, :minutes, :seconds
5
4
 
6
5
  before_validation :convert_duration_to_seconds
7
6
 
7
+ belongs_to :vendor, class_name: 'Spree::Vendor', inverse_of: :trips, optional: false
8
8
  belongs_to :product, class_name: 'Spree::Product', inverse_of: :trip, optional: false
9
9
  belongs_to :vehicle, class_name: 'SpreeCmCommissioner::Vehicle', optional: false
10
- belongs_to :origin, class_name: 'SpreeCmCommissioner::Place', optional: true
11
- belongs_to :destination, class_name: 'SpreeCmCommissioner::Place', optional: true
10
+
11
+ belongs_to :origin_place, class_name: 'SpreeCmCommissioner::Place', optional: false
12
+ belongs_to :destination_place, class_name: 'SpreeCmCommissioner::Place', optional: false
12
13
 
13
14
  has_many :trip_blazer_queries, as: :queryable, class_name: 'SpreeCmCommissioner::BlazerQueryable'
14
15
  has_many :blazer_queries, through: :trip_blazer_queries, source: :blazer_query, class_name: 'Blazer::Query'
@@ -16,16 +17,13 @@ module SpreeCmCommissioner
16
17
  has_many :trip_stops, class_name: 'SpreeCmCommissioner::TripStop', dependent: :destroy
17
18
  has_many :variants, through: :product
18
19
  has_many :inventory_items, through: :variants
19
- belongs_to :variant, class_name: 'Spree::Variant'
20
-
21
- belongs_to :route, class_name: 'Spree::Product'
22
- belongs_to :vehicle, class_name: 'SpreeCmCommissioner::Vehicle'
23
- validates :departure_time, presence: true
24
20
 
25
21
  validates :departure_time, presence: true
26
22
  validates :duration, numericality: { greater_than: 0 }
27
23
  validate :origin_and_destination_cannot_be_the_same
28
24
 
25
+ accepts_nested_attributes_for :trip_stops, :product, allow_destroy: true
26
+
29
27
  def convert_duration_to_seconds
30
28
  return if hours.blank? && minutes.blank? && seconds.blank?
31
29
 
@@ -50,7 +48,7 @@ module SpreeCmCommissioner
50
48
  private
51
49
 
52
50
  def origin_and_destination_cannot_be_the_same
53
- return unless origin_id == destination_id
51
+ return unless origin_place_id == destination_place_id
54
52
 
55
53
  errors.add(:base, 'Origin and destination cannot be the same')
56
54
  end
@@ -1,7 +1,7 @@
1
1
  module SpreeCmCommissioner
2
- class TripConnection < ApplicationRecord
3
- belongs_to :from_trip, class_name: 'Spree::Variant'
4
- belongs_to :to_trip, class_name: 'Spree::Variant'
2
+ class TripConnection < Base
3
+ belongs_to :from_trip, class_name: 'SpreeCmCommissioner::Trip'
4
+ belongs_to :to_trip, class_name: 'SpreeCmCommissioner::Trip'
5
5
 
6
6
  validate :both_trip_cannot_be_the_same
7
7
  before_validation :calculate_connection_time_minutes
@@ -12,8 +12,8 @@ module SpreeCmCommissioner
12
12
  def calculate_connection_time_minutes
13
13
  return if from_trip.nil? || to_trip.nil?
14
14
 
15
- arrival_seconds = from_trip.trip.arrival_time.seconds_since_midnight
16
- departure_seconds = to_trip.trip.departure_time.seconds_since_midnight
15
+ arrival_seconds = from_trip.arrival_time.seconds_since_midnight
16
+ departure_seconds = to_trip.departure_time.seconds_since_midnight
17
17
 
18
18
  layover_seconds = departure_seconds - arrival_seconds
19
19
  layover_seconds += 86_400 if layover_seconds.negative?
@@ -1,36 +1,17 @@
1
- require_dependency 'spree_cm_commissioner'
2
1
  module SpreeCmCommissioner
3
- class TripStop < SpreeCmCommissioner::Base
2
+ class TripStop < Base
4
3
  acts_as_list column: :sequence, scope: :trip_id
5
4
  enum :stop_type, { boarding: 0, drop_off: 1 }
6
5
 
7
- belongs_to :trip, class_name: 'Spree::Variant'
8
- belongs_to :stop, class_name: 'SpreeCmCommissioner::Place'
6
+ belongs_to :trip, class_name: 'SpreeCmCommissioner::Trip', optional: false
7
+ belongs_to :stop_place, class_name: 'SpreeCmCommissioner::Place', optional: false
8
+ belongs_to :location_place, class_name: 'SpreeCmCommissioner::Place', optional: false
9
9
 
10
10
  before_validation :set_stop_name
11
- after_destroy :decrement_trip_count
12
- after_commit :create_vendor_stop
13
- validates :stop_id, uniqueness: { scope: :trip_id }
11
+ validates :stop_place_id, uniqueness: { scope: :trip_id }
14
12
 
15
13
  def set_stop_name
16
- self.stop_name = stop.name if stop.present?
17
- end
18
-
19
- def create_vendor_stop
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)
22
- end
23
-
24
- private
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
-
32
- def vendor
33
- Spree::Product.find(trip&.product_id).vendor
14
+ self.stop_name = stop_place.name if stop_place.present?
34
15
  end
35
16
  end
36
17
  end
@@ -3,17 +3,15 @@ module SpreeCmCommissioner
3
3
  enum identity_type: { :google => 0, :apple => 1, :facebook => 2, :telegram => 3, :vattanac_bank => 4 }
4
4
 
5
5
  belongs_to :user, class_name: Spree.user_class.to_s, optional: false
6
- has_one :tenant, through: :user
7
-
8
- has_many :user_identity_provider_telegram_bots
9
- has_many :telegram_bots, through: :user_identity_provider_telegram_bots
10
6
 
11
7
  validates :sub, presence: true
8
+ validates :sub, uniqueness: { scope: :identity_type }
9
+
12
10
  validates :identity_type, presence: true
13
11
  validates :identity_type, uniqueness: { scope: :user_id }
14
12
 
15
- # custom validation to ensure that the sub is unique per identity_type and spree_users.tenant
16
- validate :unique_sub_per_identity_type_and_tenant
13
+ has_many :user_identity_provider_telegram_bots
14
+ has_many :telegram_bots, through: :user_identity_provider_telegram_bots
17
15
 
18
16
  # sub is a telegram uid, which telegram considered a chatID if
19
17
  # user have /started with bot.
@@ -22,25 +20,5 @@ module SpreeCmCommissioner
22
20
 
23
21
  nil
24
22
  end
25
-
26
- private
27
-
28
- def unique_sub_per_identity_type_and_tenant
29
- return if user.nil? || sub.blank? || identity_type.blank?
30
-
31
- tenant_id = user.tenant_id
32
-
33
- # Check for duplicates in the same tenant
34
- scope = self.class
35
- .joins(:user)
36
- .where(sub: sub, identity_type: identity_type)
37
- .where(spree_users: { tenant_id: tenant_id })
38
-
39
- scope = scope.where.not(id: id) if persisted?
40
-
41
- return unless scope.exists?
42
-
43
- errors.add(:sub, 'must be unique per identity_type and tenant')
44
- end
45
23
  end
46
24
  end
@@ -0,0 +1,9 @@
1
+ module SpreeCmCommissioner
2
+ class VariantBlock < Base
3
+ belongs_to :variant, class_name: 'Spree::Variant', optional: false
4
+ belongs_to :trip, class_name: 'SpreeCmCommissioner::Trip', optional: false
5
+ belongs_to :block, class_name: 'SpreeCmCommissioner::Block', optional: false
6
+
7
+ validates :block_id, uniqueness: { scope: %i[variant_id trip_id] }
8
+ end
9
+ end
@@ -1,6 +1,7 @@
1
1
  module SpreeCmCommissioner
2
2
  module VariantDecorator
3
- def self.prepended(base)
3
+ def self.prepended(base) # rubocop:disable Metrics/AbcSize
4
+ base.include SpreeCmCommissioner::ProductType
4
5
  base.include SpreeCmCommissioner::ProductDelegation
5
6
  base.include SpreeCmCommissioner::VariantOptionsConcern
6
7
  base.include SpreeCmCommissioner::KycBitwise
@@ -20,24 +21,21 @@ module SpreeCmCommissioner
20
21
  base.has_many :video_on_demands, class_name: 'SpreeCmCommissioner::VideoOnDemand', dependent: :destroy
21
22
  base.has_many :complete_line_items, -> { complete }, class_name: 'Spree::LineItem'
22
23
 
24
+ base.has_many :variant_blocks, class_name: 'SpreeCmCommissioner::VariantBlock', dependent: :destroy
25
+ base.has_many :blocks, class_name: 'SpreeCmCommissioner::Block', through: :variant_blocks
26
+
23
27
  base.has_many :variant_guest_card_class, class_name: 'SpreeCmCommissioner::VariantGuestCardClass'
24
28
  base.has_many :guest_card_classes, class_name: 'SpreeCmCommissioner::GuestCardClass', through: :variant_guest_card_class
25
29
 
30
+ base.has_many :inventory_items, class_name: 'SpreeCmCommissioner::InventoryItem'
31
+
26
32
  base.scope :subscribable, -> { active.joins(:product).where(product: { subscribable: true, status: :active }) }
27
- base.has_one :trip,
28
- class_name: 'SpreeCmCommissioner::Trip'
29
- base.has_many :trip_stops, class_name: 'SpreeCmCommissioner::TripStop', dependent: :destroy, foreign_key: :trip_id
30
- base.accepts_nested_attributes_for :option_values
31
- base.after_commit :sync_trip, if: :transit?
32
- base.accepts_nested_attributes_for :trip_stops, allow_destroy: true
33
- base.after_commit :create_trip_stops, if: :transit?
34
- end
33
+ base.scope :with_permanent_stock, -> { where(product_type: base::PERMANENT_STOCK_PRODUCT_TYPES) }
34
+ base.scope :with_non_permanent_stock, -> { where.not(product_type: base::PERMANENT_STOCK_PRODUCT_TYPES) }
35
35
 
36
- def create_trip_stops
37
- return if is_master?
36
+ base.accepts_nested_attributes_for :option_values
38
37
 
39
- trip_stops.find_or_create_by(stop_type: :boarding, stop_id: options.origin)
40
- trip_stops.find_or_create_by(stop_type: :drop_off, stop_id: options.destination)
38
+ base.before_save -> { self.product_type = product.product_type }, if: -> { product_type.nil? }
41
39
  end
42
40
 
43
41
  def delivery_required?
@@ -53,8 +51,20 @@ module SpreeCmCommissioner
53
51
  super || product.discontinued?
54
52
  end
55
53
 
56
- def permanent_stock?
57
- accommodation?
54
+ def default_inventory_item_exist?
55
+ inventory_items.exists?(inventory_date: nil)
56
+ end
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
68
  end
59
69
 
60
70
  # override
@@ -72,30 +82,26 @@ module SpreeCmCommissioner
72
82
  "#{display_sku} - #{display_price}"
73
83
  end
74
84
 
75
- def transit?
76
- product.product_type == 'transit'
77
- end
78
-
79
85
  # override
80
- def in_stock?
81
- available_quantity.positive?
86
+ def in_stock?(options = {})
87
+ SpreeCmCommissioner::Stock::AvailabilityChecker.new(self, options).can_supply?
82
88
  end
83
89
 
84
- private
90
+ def price_for_date(currency:, date:)
91
+ currency = currency.upcase
85
92
 
86
- def total_purchases
87
- Spree::LineItem.complete.where(variant_id: id).sum(:quantity).to_i
93
+ prices.joins(:inventory_item)
94
+ .find_by(
95
+ currency: currency,
96
+ inventory_item: { inventory_date: date }
97
+ )
88
98
  end
89
99
 
90
- def available_quantity
91
- stock_count = stock_items.sum(&:count_on_hand)
92
- return stock_count if delivery_required?
93
-
94
- stock_count - total_purchases
95
- end
100
+ private
96
101
 
97
102
  def update_vendor_price
98
- return unless vendor.present? && product&.product_type == vendor&.primary_product_type
103
+ return unless vendor.present? && product_type == vendor&.primary_product_type
104
+ return if price.blank?
99
105
 
100
106
  vendor.update(min_price: price) if vendor.min_price.nil? || price < vendor.min_price
101
107
  vendor.update(max_price: price) if vendor.max_price.nil? || price > vendor.max_price
@@ -119,22 +125,6 @@ module SpreeCmCommissioner
119
125
  end
120
126
  end
121
127
  end
122
-
123
- def sync_trip
124
- return unless product.product_type == 'transit'
125
-
126
- trip = SpreeCmCommissioner::Trip.find_or_initialize_by(variant_id: id)
127
-
128
- trip.assign_attributes(
129
- product_id: product_id,
130
- origin_id: options.origin,
131
- destination_id: options.destination,
132
- departure_time: options.departure_time,
133
- duration: options.total_duration_in_seconds,
134
- vehicle_id: options.vehicle
135
- )
136
- trip.save
137
- end
138
128
  end
139
129
  end
140
130
 
@@ -143,28 +143,5 @@ 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
169
146
  end
170
147
  end
@@ -1,29 +1,24 @@
1
- require_dependency 'spree_cm_commissioner'
2
-
3
1
  module SpreeCmCommissioner
4
- class Vehicle < SpreeCmCommissioner::Base
2
+ class Vehicle < Base
5
3
  include SpreeCmCommissioner::RouteType
6
4
 
7
- belongs_to :vehicle_type, class_name: 'SpreeCmCommissioner::VehicleType'
8
- has_one :primary_photo, -> { order(position: :asc) }, class_name: 'SpreeCmCommissioner::VehiclePhoto', as: :viewable, dependent: :destroy
9
5
  belongs_to :vendor, class_name: 'Spree::Vendor'
10
6
 
11
- after_commit :create_vehicle_option_value
7
+ has_one :primary_photo, -> { order(position: :asc) }, class_name: 'SpreeCmCommissioner::VehiclePhoto', as: :viewable, dependent: :destroy
8
+ has_one :seat_layout, as: :layoutable, class_name: 'SpreeCmCommissioner::SeatLayout'
12
9
 
13
- has_many :vehicle_photo, class_name: 'SpreeCmCommissioner::VehiclePhoto', as: :viewable, dependent: :destroy
14
- has_many :vehicle_seats, class_name: 'SpreeCmCommissioner::VehicleSeat', through: :vehicle_type
10
+ has_many :vehicle_photos, class_name: 'SpreeCmCommissioner::VehiclePhoto', as: :viewable, dependent: :destroy
15
11
 
16
12
  has_many :vehicle_option_types, class_name: 'SpreeCmCommissioner::VehicleOptionType', dependent: :destroy
17
13
  has_many :option_value_vehicles, class_name: 'SpreeCmCommissioner::OptionValueVehicle', dependent: :destroy
18
14
 
19
15
  validates :code, uniqueness: { scope: :vendor_id }, presence: true
20
- validates :license_plate, uniqueness: {}, allow_blank: true
21
-
22
- def create_vehicle_option_value
23
- SpreeCmCommissioner::VehicleOptionValueCreator.call(self)
24
- end
16
+ validates :license_plate, uniqueness: true, allow_blank: true
25
17
 
26
18
  self.whitelisted_ransackable_attributes = %w[license_plate code]
27
- self.whitelisted_ransackable_associations = %w[vehicle_type]
19
+
20
+ def display_name
21
+ "#{code} (#{license_plate})"
22
+ end
28
23
  end
29
24
  end
@@ -36,21 +36,17 @@ module SpreeCmCommissioner
36
36
  base.has_many :vendor_kind_option_values,
37
37
  through: :option_value_vendors, source: :option_value
38
38
 
39
- base.has_many :branches, lambda {
40
- where(cm_vendor_places: { place_type: 0 })
41
- }, through: :vendor_places, class_name: 'SpreeCmCommissioner::VendorPlace'
42
- base.has_many :stops, -> { where(cm_vendor_places: { place_type: 1 }) }, through: :vendor_places, class_name: 'SpreeCmCommissioner::VendorPlace'
43
- base.has_many :locations, -> { where(cm_vendor_places: { place_type: 2 }) },
44
- through: :vendor_places, class_name: 'SpreeCmCommissioner::VendorPlace'
39
+ base.has_many :branches, -> { branch }, class_name: 'SpreeCmCommissioner::VendorPlace'
40
+ base.has_many :stops, -> { stop }, class_name: 'SpreeCmCommissioner::VendorPlace'
41
+ base.has_many :locations, -> { location }, class_name: 'SpreeCmCommissioner::VendorPlace'
42
+
43
+ base.has_many :branch_places, through: :branches, class_name: 'SpreeCmCommissioner::Place', source: :place
44
+ base.has_many :stop_places, through: :stops, class_name: 'SpreeCmCommissioner::Place', source: :place
45
+ base.has_many :location_places, through: :locations, class_name: 'SpreeCmCommissioner::Place', source: :place
45
46
 
46
47
  base.has_many :places,
47
48
  through: :nearby_places, source: :place, class_name: 'SpreeCmCommissioner::Place'
48
49
 
49
- base.has_many :vendor_stops, class_name: 'SpreeCmCommissioner::VendorStop', dependent: :destroy
50
- base.has_many :boarding_points, -> { where(cm_vendor_stops: { stop_type: 0 }) },
51
- through: :vendor_stops, source: :stop, class_name: 'SpreeCmCommissioner::Place'
52
- base.has_many :drop_off_points, -> { where(cm_vendor_stops: { stop_type: 1 }) },
53
- through: :vendor_stops, source: :stop, class_name: 'SpreeCmCommissioner::Place'
54
50
  base.has_many :dynamic_fields, class_name: 'SpreeCmCommissioner::DynamicField', foreign_key: :vendor_id, dependent: :destroy
55
51
 
56
52
  base.has_one :logo, as: :viewable, dependent: :destroy, class_name: 'SpreeCmCommissioner::VendorLogo'
@@ -81,12 +77,13 @@ module SpreeCmCommissioner
81
77
  base.has_many :promotion_categories, class_name: 'Spree::PromotionCategory', foreign_key: :vendor_id, dependent: :destroy
82
78
 
83
79
  base.has_many :homepage_section_relatables,
80
+ as: :relatable,
84
81
  class_name: 'SpreeCmCommissioner::HomepageSectionRelatable',
85
- dependent: :destroy, inverse_of: :relatable
82
+ dependent: :destroy,
83
+ inverse_of: :relatable
86
84
 
87
- base.has_many :vehicle_types, class_name: 'SpreeCmCommissioner::VehicleType', dependent: :destroy
88
85
  base.has_many :vehicles, class_name: 'SpreeCmCommissioner::Vehicle', dependent: :destroy
89
- # base.has_many :vehicles, through: :vehicle_types, class_name: 'SpreeCmCommissioner::Vehicle', dependent: :destroy
86
+ base.has_many :trips, class_name: 'SpreeCmCommissioner::Trip', dependent: :destroy
90
87
 
91
88
  base.validates :account_name, :account_number, presence: true, if: lambda {
92
89
  payment_qrcode.present? && Spree::Store.default.code.include?('billing')
@@ -5,15 +5,21 @@ module SpreeCmCommissioner
5
5
 
6
6
  belongs_to :vendor, class_name: 'Spree::Vendor', optional: false
7
7
  belongs_to :place, class_name: 'SpreeCmCommissioner::Place', optional: false
8
+ belongs_to :location, class_name: 'SpreeCmCommissioner::VendorPlace', optional: true
9
+
10
+ has_many :branches, -> { branch }, class_name: 'SpreeCmCommissioner::VendorPlace'
11
+ has_many :stops, -> { stop }, class_name: 'SpreeCmCommissioner::VendorPlace'
8
12
 
9
13
  validates :place_type, presence: true
10
14
  validates :place_id, uniqueness: { scope: %i[vendor_id place_type] }
15
+ validates :location, presence: true, if: -> { branch? || stop? }
11
16
 
12
17
  accepts_nested_attributes_for :place, allow_destroy: true
13
18
 
14
- scope :branches, -> { where(place_type: :branch) }
15
- scope :stops, -> { where(place_type: :stop) }
16
- scope :locations, -> { where(place_type: :location) }
19
+ # include place when using these delegate to avoid N+1
20
+ def display_name
21
+ place&.name
22
+ end
17
23
 
18
24
  def selected
19
25
  vendor.selected_place_references.include?(place&.reference)