spree_cm_commissioner 1.18.1 → 2.0.0.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 (202) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +0 -3
  3. data/.github/workflows/test_and_build_gem.yml +16 -2
  4. data/.gitignore +2 -1
  5. data/Gemfile.lock +28 -1
  6. data/Rakefile +33 -4
  7. data/app/assets/javascripts/spree_cm_commissioner/backend.js +0 -1
  8. data/app/assets/stylesheets/spree_cm_commissioner/backend/calendar.scss +8 -11
  9. data/app/assets/stylesheets/spree_cm_commissioner/backend/commissioner_admin.css.scss +0 -1
  10. data/app/controllers/concerns/spree_cm_commissioner/content_cachable.rb +1 -2
  11. data/app/controllers/spree/admin/inventory_items_controller.rb +83 -0
  12. data/app/controllers/spree/admin/stock_managements_controller.rb +63 -1
  13. data/app/controllers/spree/admin/tenants_controller.rb +0 -8
  14. data/app/controllers/spree/api/v2/storefront/accommodations/variants_controller.rb +42 -0
  15. data/app/controllers/spree/api/v2/storefront/accommodations_controller.rb +14 -31
  16. data/app/controllers/spree/api/v2/storefront/guests_controller.rb +5 -31
  17. data/app/controllers/spree/api/v2/storefront/queue_cart/line_items_controller.rb +2 -2
  18. data/app/controllers/spree_cm_commissioner/admin/variants_controller_decorator.rb +1 -1
  19. data/app/controllers/spree_cm_commissioner/well_known_controller.rb +18 -20
  20. data/app/finders/spree_cm_commissioner/accommodations/find.rb +37 -0
  21. data/app/finders/spree_cm_commissioner/accommodations/find_variant.rb +32 -0
  22. data/app/interactors/spree_cm_commissioner/ensure_correct_product_type.rb +40 -0
  23. data/app/interactors/spree_cm_commissioner/inventory_item_syncer.rb +25 -0
  24. data/app/interactors/spree_cm_commissioner/stock/inventory_item_resetter.rb +44 -0
  25. data/app/interactors/spree_cm_commissioner/stock/inventory_items_adjuster.rb +13 -0
  26. data/app/interactors/spree_cm_commissioner/stock/inventory_items_generator.rb +15 -0
  27. data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +75 -0
  28. data/app/interactors/spree_cm_commissioner/stock/stock_movement_creator.rb +32 -0
  29. data/app/interactors/spree_cm_commissioner/vattanac_bank_initiator.rb +1 -5
  30. data/app/jobs/spree_cm_commissioner/application_job.rb +20 -0
  31. data/app/jobs/spree_cm_commissioner/application_unique_job.rb +20 -0
  32. data/app/jobs/spree_cm_commissioner/ensure_correct_product_type_job.rb +7 -0
  33. data/app/jobs/spree_cm_commissioner/inventory_item_syncer_job.rb +7 -0
  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 -13
  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/concerns/spree_cm_commissioner/tenant_preference.rb +0 -4
  45. data/app/models/spree_cm_commissioner/block.rb +23 -0
  46. data/app/models/spree_cm_commissioner/dynamic_field.rb +3 -15
  47. data/app/models/spree_cm_commissioner/dynamic_field_option.rb +1 -5
  48. data/app/models/spree_cm_commissioner/guest.rb +19 -1
  49. data/app/models/spree_cm_commissioner/guest_dynamic_field.rb +3 -21
  50. data/app/models/spree_cm_commissioner/inventory.rb +11 -0
  51. data/app/models/spree_cm_commissioner/inventory_item.rb +69 -0
  52. data/app/models/spree_cm_commissioner/invite_team.rb +1 -3
  53. data/app/models/spree_cm_commissioner/line_item_decorator.rb +46 -78
  54. data/app/models/spree_cm_commissioner/notification_taxon.rb +1 -1
  55. data/app/models/spree_cm_commissioner/option_type_decorator.rb +1 -11
  56. data/app/models/spree_cm_commissioner/order_decorator.rb +30 -1
  57. data/app/models/spree_cm_commissioner/place.rb +1 -3
  58. data/app/models/spree_cm_commissioner/price_decorator.rb +9 -0
  59. data/app/models/spree_cm_commissioner/product_decorator.rb +10 -4
  60. data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +41 -0
  61. data/app/models/spree_cm_commissioner/redis_stock/inventory_updater.rb +126 -0
  62. data/app/models/spree_cm_commissioner/redis_stock/line_items_cached_inventory_items_builder.rb +36 -0
  63. data/app/models/spree_cm_commissioner/redis_stock/variant_cached_inventory_items_builder.rb +25 -0
  64. data/app/models/spree_cm_commissioner/reserved_block.rb +30 -0
  65. data/app/models/spree_cm_commissioner/seat_layout.rb +20 -0
  66. data/app/models/spree_cm_commissioner/seat_section.rb +16 -0
  67. data/app/models/spree_cm_commissioner/seats/blocks_canceler.rb +30 -0
  68. data/app/models/spree_cm_commissioner/seats/blocks_holder.rb +53 -0
  69. data/app/models/spree_cm_commissioner/seats/blocks_reserver.rb +49 -0
  70. data/app/models/spree_cm_commissioner/seats/errors/blocks_are_on_hold_by_other_guest.rb +4 -0
  71. data/app/models/spree_cm_commissioner/seats/errors/blocks_are_reserved_by_other_guest.rb +4 -0
  72. data/app/models/spree_cm_commissioner/seats/errors/blocks_are_reserved_by_same_guest.rb +4 -0
  73. data/app/models/spree_cm_commissioner/seats/errors/unable_to_save_reserved_block_record.rb +4 -0
  74. data/app/models/spree_cm_commissioner/service_calendar.rb +0 -2
  75. data/app/models/spree_cm_commissioner/state_decorator.rb +0 -1
  76. data/app/models/spree_cm_commissioner/stock/availability_checker.rb +26 -25
  77. data/app/models/spree_cm_commissioner/stock/availability_validator_decorator.rb +2 -1
  78. data/app/models/spree_cm_commissioner/stock/line_item_availability_checker.rb +3 -3
  79. data/app/models/spree_cm_commissioner/stock/order_availability_checker.rb +44 -0
  80. data/app/models/spree_cm_commissioner/stock_item_decorator.rb +17 -0
  81. data/app/models/spree_cm_commissioner/taxon_decorator.rb +0 -2
  82. data/app/models/spree_cm_commissioner/taxonomy_decorator.rb +0 -6
  83. data/app/models/spree_cm_commissioner/trip.rb +20 -6
  84. data/app/models/spree_cm_commissioner/trip_connection.rb +5 -5
  85. data/app/models/spree_cm_commissioner/trip_stop.rb +2 -3
  86. data/app/models/spree_cm_commissioner/user_taxon.rb +0 -1
  87. data/app/models/spree_cm_commissioner/variant_block.rb +9 -0
  88. data/app/models/spree_cm_commissioner/variant_decorator.rb +36 -47
  89. data/app/models/spree_cm_commissioner/variant_options.rb +0 -23
  90. data/app/models/spree_cm_commissioner/vehicle.rb +9 -14
  91. data/app/models/spree_cm_commissioner/vendor_decorator.rb +7 -9
  92. data/app/models/spree_cm_commissioner/vendor_place.rb +6 -5
  93. data/app/models/spree_cm_commissioner/vendor_stop.rb +1 -2
  94. data/app/overrides/spree/admin/variants/_form/kyc_field.html.erb.deface +2 -2
  95. data/app/queries/spree_cm_commissioner/guest_searcher_query.rb +3 -45
  96. data/app/queries/spree_cm_commissioner/trip_query.rb +23 -26
  97. data/app/request_schemas/spree_cm_commissioner/accommodation_request_schema.rb +3 -0
  98. data/app/request_schemas/spree_cm_commissioner/application_request_schema.rb +1 -1
  99. data/app/request_schemas/spree_cm_commissioner/variant_request_schema.rb +19 -0
  100. data/app/serializers/spree/v2/storefront/accommodation_serializer.rb +2 -0
  101. data/app/serializers/spree/v2/storefront/line_item_serializer_decorator.rb +1 -0
  102. data/app/serializers/spree_cm_commissioner/v2/operator/dashboard_crew_event_serializer.rb +1 -4
  103. data/app/serializers/spree_cm_commissioner/v2/storefront/dynamic_field_option_serializer.rb +3 -1
  104. data/app/serializers/spree_cm_commissioner/v2/storefront/dynamic_field_serializer.rb +3 -1
  105. data/app/serializers/spree_cm_commissioner/v2/storefront/guest_dynamic_field_serializer.rb +1 -2
  106. data/app/services/spree_cm_commissioner/organizer/export_guest_csv_service.rb +15 -23
  107. data/app/views/spree/admin/inventory_items/show.html.erb +72 -0
  108. data/app/views/spree/admin/stock_managements/_variant_stock_items.html.erb +7 -2
  109. data/app/views/spree/admin/stock_managements/calendar.html.erb +56 -0
  110. data/app/views/spree/admin/stock_managements/index.html.erb +55 -6
  111. data/app/views/spree/admin/tenants/_form.html.erb +42 -109
  112. data/config/initializers/paper_trail.rb +1 -0
  113. data/config/initializers/spree_permitted_attributes.rb +5 -2
  114. data/config/locales/en.yml +0 -4
  115. data/config/routes.rb +22 -4
  116. data/db/migrate/20240202080634_update_counter_cache_of_vehicle_type.rb +3 -1
  117. data/db/migrate/20250304293518_create_cm_inventory_items.rb +21 -0
  118. data/db/migrate/20250429094228_add_lock_version_to_cm_inventory_items.rb +5 -0
  119. data/db/migrate/20250502025848_add_index_to_spree_products.rb +5 -0
  120. data/db/migrate/20250502030001_add_product_type_to_spree_variants.rb +5 -0
  121. data/db/migrate/20250502030002_add_product_type_to_spree_line_items.rb +5 -0
  122. data/db/migrate/20250603035256_add_inventory_item_to_spree_prices.rb +7 -0
  123. data/db/migrate/20250619073724_drop_table_cm_line_item_seats.rb +5 -0
  124. data/db/migrate/20250619073812_drop_table_cm_vehicle_seats.rb +5 -0
  125. data/db/migrate/20250619073844_drop_table_cm_vehicle_types.rb +9 -0
  126. data/db/migrate/20250619073957_drop_table_cm_option_value_vehicle_types.rb +5 -0
  127. data/db/migrate/20250619082354_remove_unnecessary_fields_from_cm_places.rb +9 -0
  128. data/db/migrate/20250619082736_remove_route_type_from_spree_products.rb +5 -0
  129. data/db/migrate/20250619083055_remove_unnecessary_fields_from_spree_taxons.rb +5 -0
  130. data/db/migrate/20250620083055_remove_variant_id_from_cm_trips.rb +5 -0
  131. data/db/migrate/20250620090000_update_cm_trip_connections_to_use_cm_trips.rb +6 -0
  132. data/db/migrate/20250620090001_create_cm_seat_layouts.rb +17 -0
  133. data/db/migrate/20250620090002_create_cm_seat_sections.rb +18 -0
  134. data/db/migrate/20250620090003_create_cm_blocks.rb +18 -0
  135. data/db/migrate/20250624091005_create_cm_reserved_blocks.rb +29 -0
  136. data/db/migrate/20250626083642_create_cm_variant_blocks.rb +24 -0
  137. data/db/migrate/20250627023314_add_block_id_to_cm_guests.rb +13 -0
  138. data/docker-compose.yml +1 -1
  139. data/lib/cm_app_logger.rb +11 -4
  140. data/lib/generators/spree_cm_commissioner/install/install_generator.rb +14 -11
  141. data/lib/generators/spree_cm_commissioner/install/templates/app/javascript/{spree_cm_commissioner → spree_dashboard/spree_cm_commissioner}/utilities.js +4 -0
  142. data/lib/spree_cm_commissioner/cached_inventory_item.rb +23 -0
  143. data/lib/spree_cm_commissioner/calendar_event.rb +11 -1
  144. data/lib/spree_cm_commissioner/test_helper/factories/block_factory.rb +9 -0
  145. data/lib/spree_cm_commissioner/test_helper/factories/guest_factory.rb +10 -0
  146. data/lib/spree_cm_commissioner/test_helper/factories/homepage_section_relatable_factory.rb +1 -1
  147. data/lib/spree_cm_commissioner/test_helper/factories/inventory_item_factory.rb +9 -0
  148. data/lib/spree_cm_commissioner/test_helper/factories/line_item_factory.rb +1 -1
  149. data/lib/spree_cm_commissioner/test_helper/factories/option_type_factory.rb +6 -30
  150. data/lib/spree_cm_commissioner/test_helper/factories/order_factory.rb +0 -36
  151. data/lib/spree_cm_commissioner/test_helper/factories/product_factory.rb +18 -34
  152. data/lib/spree_cm_commissioner/test_helper/factories/reserved_block_factory.rb +27 -0
  153. data/lib/spree_cm_commissioner/test_helper/factories/seat_layout_factory.rb +8 -0
  154. data/lib/spree_cm_commissioner/test_helper/factories/seat_section_factory.rb +8 -0
  155. data/lib/spree_cm_commissioner/test_helper/factories/stock_location_factory.rb +2 -2
  156. data/lib/spree_cm_commissioner/test_helper/factories/trip_connection_factory.rb +6 -0
  157. data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +10 -3
  158. data/lib/spree_cm_commissioner/test_helper/factories/variant_block_factory.rb +7 -0
  159. data/lib/spree_cm_commissioner/test_helper/factories/variant_factory.rb +41 -19
  160. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_factory.rb +1 -1
  161. data/lib/spree_cm_commissioner/test_helper/factories/vendor_factory.rb +1 -1
  162. data/lib/spree_cm_commissioner/version.rb +1 -1
  163. data/lib/spree_cm_commissioner.rb +35 -1
  164. data/lib/tasks/create_default_non_permanent_inventory_items.rake +16 -0
  165. data/lib/tasks/ensure_correct_product_type.rake +7 -0
  166. data/lib/tasks/generate_inventory_items.rake +7 -0
  167. data/spree_cm_commissioner.gemspec +7 -0
  168. metadata +134 -39
  169. data/app/assets/images/cm-hangmeas-checkout_image.svg +0 -63
  170. data/app/assets/images/cm-hangmeas-failed.svg +0 -56
  171. data/app/assets/images/cm-hangmeas-loader.svg +0 -50
  172. data/app/assets/images/cm-hangmeas-success.svg +0 -51
  173. data/app/assets/javascripts/spree_cm_commissioner/tenant_payment_icon_fields.js +0 -65
  174. data/app/assets/stylesheets/spree_cm_commissioner/backend/tenant_payment_icon_fields.scss +0 -60
  175. data/app/controllers/concerns/spree_cm_commissioner/transit/taxon_bitwise.rb +0 -44
  176. data/app/finders/spree_cm_commissioner/line_items/find_by_variant_decorator.rb +0 -20
  177. data/app/interactors/spree_cm_commissioner/universal_link_fetcher.rb +0 -54
  178. data/app/models/concerns/spree_cm_commissioner/event_check_in_flowable.rb +0 -30
  179. data/app/models/spree_cm_commissioner/branch.rb +0 -12
  180. data/app/models/spree_cm_commissioner/line_item_seat.rb +0 -10
  181. data/app/models/spree_cm_commissioner/option_value_vehicle_type.rb +0 -8
  182. data/app/models/spree_cm_commissioner/stop.rb +0 -23
  183. data/app/models/spree_cm_commissioner/vehicle_seat.rb +0 -11
  184. data/app/models/spree_cm_commissioner/vehicle_type.rb +0 -76
  185. data/app/overrides/spree/admin/taxons/_form/check_in_flows.html.erb.deface +0 -18
  186. data/app/queries/spree_cm_commissioner/trip_search_query.rb +0 -76
  187. data/app/queries/spree_cm_commissioner/variant_availability/non_permanent_stock_query.rb +0 -45
  188. data/app/queries/spree_cm_commissioner/variant_availability/permanent_stock_query.rb +0 -55
  189. data/app/services/spree_cm_commissioner/vehicle_option_value_creator.rb +0 -11
  190. data/db/migrate/20250616084219_add_description_to_cm_vendor_place.rb +0 -5
  191. data/db/migrate/20250701093203_add_configurations_to_cm_dynamic_field.rb +0 -6
  192. data/db/migrate/20250702091305_add_dynamic_field_option_to_guest_dynamic_field.rb +0 -5
  193. data/db/migrate/20250702091935_add_status_to_dynamic_field_option.rb +0 -5
  194. data/lib/spree_cm_commissioner/test_helper/factories/branch_factory.rb +0 -12
  195. data/lib/spree_cm_commissioner/test_helper/factories/departure_time_option_type_factory.rb +0 -8
  196. data/lib/spree_cm_commissioner/test_helper/factories/duration_option_type_factory.rb +0 -8
  197. data/lib/spree_cm_commissioner/test_helper/factories/line_item_seat_factory.rb +0 -7
  198. data/lib/spree_cm_commissioner/test_helper/factories/stop_factory.rb +0 -14
  199. data/lib/spree_cm_commissioner/test_helper/factories/transit_place_factory.rb +0 -8
  200. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_option_type_factory.rb +0 -8
  201. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_type_factory.rb +0 -96
  202. data/lib/spree_cm_commissioner/trip_seat_layout_result.rb +0 -11
@@ -3,31 +3,19 @@ module SpreeCmCommissioner
3
3
  before_action :identify_request_source
4
4
  rescue_from StandardError, with: :handle_error
5
5
 
6
- def assetlinks_json
7
- render_universal_link('assetlinks.json')
8
- end
9
-
10
- def apple_app_site_association
11
- render_universal_link('apple-app-site-association')
12
- end
13
-
14
- private
15
-
16
- def render_universal_link(universal_link)
17
- result = SpreeCmCommissioner::UniversalLinkFetcher.new(
18
- universal_link: universal_link,
19
- store: @store,
20
- tenant: @tenant,
21
- refresh: params[:_refresh].to_s == 'true'
22
- ).call
23
-
24
- if result[:success]
25
- render json: result[:data]
6
+ def show
7
+ case params[:id]
8
+ when 'assetlinks.json'
9
+ render_preference(:preferred_assetlinks)
10
+ when 'apple-app-site-association'
11
+ render_preference(:preferred_apple_app_site_association)
26
12
  else
27
13
  render_not_found
28
14
  end
29
15
  end
30
16
 
17
+ private
18
+
31
19
  def identify_request_source
32
20
  host = params[:original_host].presence || request.host.downcase
33
21
 
@@ -37,6 +25,16 @@ module SpreeCmCommissioner
37
25
  @tenant = result.tenant
38
26
  end
39
27
 
28
+ def render_preference(preference_key)
29
+ if @store&.send(preference_key).present?
30
+ render json: @store.send(preference_key)
31
+ elsif @tenant&.send(preference_key).present?
32
+ render json: @tenant.send(preference_key)
33
+ else
34
+ render_not_found
35
+ end
36
+ end
37
+
40
38
  def render_not_found
41
39
  render json: { error: 'Not found' }, status: :not_found
42
40
  end
@@ -0,0 +1,37 @@
1
+ module SpreeCmCommissioner
2
+ module Accommodations
3
+ class Find
4
+ attr_reader :from_date, :to_date, :state_id, :number_of_guests
5
+
6
+ def initialize(from_date:, to_date:, state_id:, number_of_adults:, number_of_kids:)
7
+ @from_date = from_date
8
+ @to_date = to_date
9
+ @state_id = state_id
10
+ @number_of_guests = number_of_adults.to_i + number_of_kids.to_i
11
+ end
12
+
13
+ def execute
14
+ scope
15
+ .where(default_state_id: state_id)
16
+ .where(inventory_items: { inventory_date: stay_dates })
17
+ .where('CAST(spree_variants.public_metadata->\'cm_options\'->>\'number-of-adults\' AS INTEGER) +
18
+ CAST(spree_variants.public_metadata->\'cm_options\'->>\'number-of-kids\' AS INTEGER) >= ?', number_of_guests
19
+ )
20
+ .where('inventory_items.quantity_available > 0')
21
+ .distinct
22
+ end
23
+
24
+ private
25
+
26
+ def scope
27
+ Spree::Vendor
28
+ .joins(variants: :inventory_items)
29
+ .where(primary_product_type: :accommodation, state: :active)
30
+ end
31
+
32
+ def stay_dates
33
+ from_date..to_date
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,32 @@
1
+ module SpreeCmCommissioner
2
+ module Accommodations
3
+ class FindVariant
4
+ attr_reader :vendor_id, :from_date, :to_date, :number_of_guests
5
+
6
+ def initialize(vendor_id:, from_date:, to_date:, number_of_adults:, number_of_kids:)
7
+ @vendor_id = vendor_id
8
+ @from_date = from_date
9
+ @to_date = to_date
10
+ @number_of_guests = number_of_adults.to_i + number_of_kids.to_i
11
+ end
12
+
13
+ def execute
14
+ Spree::Variant
15
+ .joins(:inventory_items)
16
+ .where(vendor_id: vendor_id)
17
+ .where(inventory_items: { inventory_date: stay_dates })
18
+ .where('CAST(public_metadata->\'cm_options\'->>\'number-of-adults\' AS INTEGER) +
19
+ CAST(public_metadata->\'cm_options\'->>\'number-of-kids\' AS INTEGER) >= ?', number_of_guests
20
+ )
21
+ .where('inventory_items.quantity_available > 0')
22
+ .distinct
23
+ end
24
+
25
+ private
26
+
27
+ def stay_dates
28
+ from_date..to_date
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,40 @@
1
+ module SpreeCmCommissioner
2
+ class EnsureCorrectProductType < BaseInteractor
3
+ def call
4
+ Spree::Product
5
+ .left_joins(variants_including_master: %i[inventory_items line_items])
6
+ .where(
7
+ 'spree_variants.product_type IS NULL OR
8
+ spree_variants.product_type != spree_products.product_type OR
9
+
10
+ cm_inventory_items.product_type IS NULL OR
11
+ cm_inventory_items.product_type != spree_products.product_type OR
12
+
13
+ spree_line_items.product_type IS NULL OR
14
+ spree_line_items.product_type != spree_products.product_type OR
15
+
16
+ spree_products.product_type IS NOT NULL
17
+ '
18
+ )
19
+ .distinct.find_each do |product|
20
+ sync_product_type_for(product)
21
+ end
22
+ end
23
+
24
+ def sync_product_type_for(product)
25
+ product_type = Spree::Variant.product_types[product.product_type]
26
+
27
+ product.variants_including_master
28
+ .where('spree_variants.product_type IS NULL OR spree_variants.product_type != ?', product_type)
29
+ .update_all(product_type: product_type) # rubocop:disable Rails/SkipsModelValidations
30
+
31
+ product.line_items
32
+ .where('spree_line_items.product_type IS NULL OR spree_line_items.product_type != ?', product_type)
33
+ .update_all(product_type: product_type) # rubocop:disable Rails/SkipsModelValidations
34
+
35
+ product.inventory_items
36
+ .where('cm_inventory_items.product_type IS NULL OR cm_inventory_items.product_type != ?', product_type)
37
+ .update_all(product_type: product_type) # rubocop:disable Rails/SkipsModelValidations
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,25 @@
1
+ module SpreeCmCommissioner
2
+ class InventoryItemSyncer < BaseInteractor
3
+ # inventory_id_and_quantities = [{ inventory_id: inventory_item1.id, quantity: 5 } ]
4
+ delegate :inventory_id_and_quantities, to: :context
5
+
6
+ def call
7
+ ActiveRecord::Base.transaction do
8
+ inventory_items.each do |inventory_item|
9
+ quantity = inventory_id_and_quantities.find { |item| item[:inventory_id] == inventory_item.id }&.dig(:quantity) || 0
10
+ adjust_quantity_available(inventory_item, quantity)
11
+ end
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def adjust_quantity_available(inventory_item, quantity)
18
+ inventory_item.update!(quantity_available: inventory_item.quantity_available + quantity)
19
+ end
20
+
21
+ def inventory_items
22
+ @inventory_items ||= InventoryItem.where(id: inventory_id_and_quantities.pluck(:inventory_id))
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,44 @@
1
+ module SpreeCmCommissioner
2
+ module Stock
3
+ class InventoryItemResetter < BaseInteractor
4
+ delegate :inventory_item, to: :context
5
+
6
+ def call
7
+ max_capacity = variant_total_inventory
8
+ total_purchases = variant_total_purchases
9
+ quantity_available = [max_capacity - total_purchases, 0].max
10
+
11
+ updated = inventory_item.update(max_capacity: max_capacity, quantity_available: quantity_available)
12
+ return context.fail!(message: 'Failed to update inventory item', errors: inventory_item.errors.full_messages) unless updated
13
+
14
+ clear_inventory_cache
15
+ end
16
+
17
+ def variant_total_inventory
18
+ # for shipment, total_on_hand is not orignal stock. shipment does subtract the stock.
19
+ # to get desire result, we need to add to total_purchase.
20
+ if inventory_item.variant.delivery_required?
21
+ inventory_item.variant.total_on_hand.to_i + variant_total_purchases
22
+ else
23
+ inventory_item.variant.total_on_hand.to_i
24
+ end
25
+ end
26
+
27
+ def variant_total_purchases
28
+ scope = inventory_item.variant.complete_line_items
29
+
30
+ if inventory_item.permanent_stock?
31
+ scope.where('? BETWEEN from_date AND to_date', inventory_item.inventory_date).sum(:quantity).to_i
32
+ else
33
+ scope.sum(:quantity).to_i
34
+ end
35
+ end
36
+
37
+ def clear_inventory_cache
38
+ SpreeCmCommissioner.redis_pool.with do |redis|
39
+ redis.del(inventory_item.redis_key)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,13 @@
1
+ module SpreeCmCommissioner
2
+ module Stock
3
+ class InventoryItemsAdjuster < BaseInteractor
4
+ delegate :variant, :quantity, to: :context
5
+
6
+ def call
7
+ variant.inventory_items.active.find_each do |inventory_item|
8
+ inventory_item.adjust_quantity!(quantity)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module SpreeCmCommissioner
2
+ module Stock
3
+ class InventoryItemsGenerator < BaseInteractor
4
+ delegate :variant, to: :context
5
+
6
+ def call
7
+ if variant.permanent_stock?
8
+ SpreeCmCommissioner::Stock::PermanentInventoryItemsGenerator.call(variant_ids: [variant.id])
9
+ else
10
+ variant.create_default_non_permanent_inventory_item! unless variant.default_inventory_item_exist?
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,75 @@
1
+ module SpreeCmCommissioner
2
+ module Stock
3
+ class PermanentInventoryItemsGenerator < BaseInteractor
4
+ delegate :variant_ids, to: :context
5
+
6
+ def variants_per_batch = 1000
7
+
8
+ def pre_inventory_days_for(variant)
9
+ context.pre_inventory_days || variant.pre_inventory_days
10
+ end
11
+
12
+ def call
13
+ variants.in_batches(of: variants_per_batch) do |batch|
14
+ generate_inventory_items_for_batch(batch)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def generate_inventory_items_for_batch(batch)
21
+ total_on_hand_by_variant = total_on_hand_for(batch)
22
+ batch.each do |variant|
23
+ count_on_hand = total_on_hand_by_variant[variant.id] || 0
24
+ generate_inventory_items_for_variant(variant, count_on_hand)
25
+ end
26
+ end
27
+
28
+ def generate_inventory_items_for_variant(variant, count_on_hand)
29
+ inventory_dates_for(variant).each do |inventory_date|
30
+ next if inventory_exist?(variant, inventory_date)
31
+
32
+ create_inventory_item(variant, inventory_date, count_on_hand)
33
+ end
34
+ end
35
+
36
+ def inventory_dates_for(variant)
37
+ start_date = Time.zone.tomorrow
38
+ end_date = Time.zone.today + pre_inventory_days_for(variant)
39
+
40
+ (start_date..end_date)
41
+ end
42
+
43
+ def inventory_exist?(variant, inventory_date)
44
+ variant.inventory_items.exists?(inventory_date: inventory_date)
45
+ end
46
+
47
+ def create_inventory_item(variant, inventory_date, count_on_hand)
48
+ variant.inventory_items.create!(
49
+ inventory_date: inventory_date,
50
+ quantity_available: count_on_hand,
51
+ max_capacity: count_on_hand,
52
+ product_type: variant.product_type
53
+ )
54
+ end
55
+
56
+ # Returns a hash: { variant_id => total_on_hand, ... }
57
+ def total_on_hand_for(variants)
58
+ variant_ids = variants.pluck(:id)
59
+
60
+ Spree::StockItem
61
+ .joins(:stock_location)
62
+ .where(deleted_at: nil, variant_id: variant_ids)
63
+ .where(spree_stock_locations: { active: true })
64
+ .group(:variant_id)
65
+ .sum(:count_on_hand)
66
+ end
67
+
68
+ def variants
69
+ scope = Spree::Variant.active.with_permanent_stock.where(is_master: false)
70
+ scope = scope.where(id: variant_ids) if variant_ids.present?
71
+ scope
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,32 @@
1
+ module SpreeCmCommissioner
2
+ module Stock
3
+ class StockMovementCreator < BaseInteractor
4
+ delegate :variant_id, :stock_location_id, :current_store, :stock_movement_params, to: :context
5
+
6
+ def call
7
+ variant = current_store.variants.find(variant_id)
8
+
9
+ return context.fail!(message: Spree.t(:doesnt_track_inventory)) unless variant.track_inventory?
10
+
11
+ stock_location = Spree::StockLocation.find(stock_location_id)
12
+ stock_movement = stock_location.stock_movements.build(stock_movement_params)
13
+ stock_movement.stock_item = stock_location.set_up_stock_item(variant)
14
+
15
+ if stock_movement.save
16
+ adjust_inventory_items_async(variant_id, stock_movement.quantity)
17
+ context.stock_movement = stock_movement
18
+ else
19
+ context.fail!(message: stock_movement.errors.full_messages.join(', '))
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def adjust_inventory_items_async(variant_id, quantity)
26
+ CmAppLogger.log(label: "#{self.class.name}#adjust_inventory_items_async", data: { variant_id: variant_id, quantity: quantity }) do
27
+ SpreeCmCommissioner::Stock::InventoryItemsAdjusterJob.perform_later(variant_id: variant_id, quantity: quantity)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -93,10 +93,6 @@ module SpreeCmCommissioner
93
93
  context.fail!(message: "User creation failed: #{context.user.errors.full_messages.join(', ')}")
94
94
  end
95
95
 
96
- def disable_services
97
- 'bus,ferry'
98
- end
99
-
100
96
  def construct_data
101
97
  user = context.user
102
98
 
@@ -105,7 +101,7 @@ module SpreeCmCommissioner
105
101
  name: user.full_name,
106
102
  phone: user.phone_number,
107
103
  email: user.email,
108
- webUrl: "#{Spree::Store.default.formatted_url}/vattanac_bank_web_app?session_id=#{session_id}&ds=#{CGI.escape(disable_services)}"
104
+ webUrl: "#{Spree::Store.default.formatted_url}/vattanac_bank_web_app?session_id=#{session_id}"
109
105
  }
110
106
 
111
107
  json_data = raw_data.to_json
@@ -1,5 +1,25 @@
1
1
  module SpreeCmCommissioner
2
2
  class ApplicationJob < ::ApplicationJob
3
3
  queue_as :default
4
+
5
+ around_perform :log_exceptions
6
+
7
+ private
8
+
9
+ def log_exceptions
10
+ yield
11
+ rescue StandardError => e
12
+ CmAppLogger.log(
13
+ label: "#{self.class.name} failed",
14
+ data: {
15
+ error_class: e.class.name,
16
+ error_message: e.message,
17
+ arguments: arguments.inspect,
18
+ backtrace: e.backtrace&.first(5)&.join("\n")
19
+ }
20
+ )
21
+
22
+ raise # Re-raise the exception to let Sidekiq handle retries or failure
23
+ end
4
24
  end
5
25
  end
@@ -2,5 +2,25 @@
2
2
  module SpreeCmCommissioner
3
3
  class ApplicationUniqueJob < ::ApplicationUniqueJob
4
4
  queue_as :default
5
+
6
+ around_perform :log_exceptions
7
+
8
+ private
9
+
10
+ def log_exceptions
11
+ yield
12
+ rescue StandardError => e
13
+ CmAppLogger.log(
14
+ label: "#{self.class.name} failed",
15
+ data: {
16
+ error_class: e.class.name,
17
+ error_message: e.message,
18
+ arguments: arguments.inspect,
19
+ backtrace: e.backtrace&.first(5)&.join("\n")
20
+ }
21
+ )
22
+
23
+ raise # Re-raise the exception to let Sidekiq handle retries or failure
24
+ end
5
25
  end
6
26
  end
@@ -0,0 +1,7 @@
1
+ module SpreeCmCommissioner
2
+ class EnsureCorrectProductTypeJob < ApplicationJob
3
+ def perform
4
+ SpreeCmCommissioner::EnsureCorrectProductType.call
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module SpreeCmCommissioner
2
+ class InventoryItemSyncerJob < ApplicationUniqueJob
3
+ def perform(inventory_id_and_quantities:)
4
+ InventoryItemSyncer.call(inventory_id_and_quantities:)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ module SpreeCmCommissioner
2
+ module Stock
3
+ class InventoryItemsAdjusterJob < ApplicationUniqueJob
4
+ def perform(variant_id:, quantity:)
5
+ variant = Spree::Variant.find(variant_id)
6
+
7
+ SpreeCmCommissioner::Stock::InventoryItemsAdjuster.call(variant: variant, quantity: quantity)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module SpreeCmCommissioner
2
+ module Stock
3
+ class InventoryItemsGeneratorJob < ApplicationUniqueJob
4
+ def perform(variant_id:)
5
+ variant = Spree::Variant.find(variant_id)
6
+
7
+ SpreeCmCommissioner::Stock::InventoryItemsGenerator.call(variant: variant)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ module SpreeCmCommissioner
2
+ module Stock
3
+ class PermanentInventoryItemsGeneratorJob < ApplicationUniqueJob
4
+ def perform
5
+ SpreeCmCommissioner::Stock::PermanentInventoryItemsGenerator.call
6
+ end
7
+ end
8
+ end
9
+ end
@@ -18,29 +18,23 @@ module SpreeCmCommissioner
18
18
  def date_unit
19
19
  return nil unless permanent_stock?
20
20
 
21
- date_range_excluding_checkout.size if accommodation?
21
+ date_range.size
22
22
  end
23
23
 
24
- def date_range_excluding_checkout
24
+ def date_range
25
25
  return [] unless date_present?
26
26
 
27
- date_range = (from_date.to_date..to_date.to_date).to_a
28
- date_range.pop if date_range.size > 1
29
- date_range
27
+ (from_date.to_date..to_date.to_date).to_a
30
28
  end
31
29
 
32
- def date_range_including_checkout
33
- return [] unless date_present?
34
-
35
- (from_date.to_date..to_date.to_date).to_a
30
+ def checkin_date
31
+ from_date&.to_date
36
32
  end
37
33
 
38
- def date_range
39
- if accommodation?
40
- date_range_excluding_checkout
41
- else
42
- date_range_including_checkout
43
- end
34
+ def checkout_date
35
+ return to_date ? to_date.to_date - 1.day : nil if accommodation?
36
+
37
+ to_date&.to_date
44
38
  end
45
39
 
46
40
  private
@@ -14,13 +14,6 @@ module SpreeCmCommissioner
14
14
  state_selection
15
15
  payment_option
16
16
  delivery_option
17
- amenity
18
- departure_time
19
- duration
20
- vehicle_id
21
- origin
22
- destination
23
- place_id
24
17
  ].freeze
25
18
 
26
19
  RESERVED_OPTIONS = {
@@ -47,12 +40,7 @@ module SpreeCmCommissioner
47
40
  'bib-zerofill' => 'integer',
48
41
  'bib-display-prefix' => 'boolean',
49
42
  'bib-pre-generation-on-create' => 'boolean',
50
- 'seat-number-positions' => 'array',
51
- 'origin' => 'place_id',
52
- 'destination' => 'place_id',
53
- 'departure-time' => 'time',
54
- 'vehicle' => 'vehicle_id',
55
- 'allow-seat-selection' => 'boolean'
43
+ 'seat-number-positions' => 'array'
56
44
  }.freeze
57
45
 
58
46
  included do
@@ -0,0 +1,44 @@
1
+ module SpreeCmCommissioner
2
+ module OrderSeatable
3
+ def should_manage_blocks?
4
+ preload_block_ids.any?
5
+ end
6
+
7
+ def hold_blocks!
8
+ return unless should_manage_blocks?
9
+
10
+ CmAppLogger.log(label: "#{self.class.name}#hold_blocks!", data: { order_id: id }) do
11
+ SpreeCmCommissioner::Seats::BlocksHolder.new(line_item_ids: line_item_ids, hold_by: user).hold_blocks!
12
+ end
13
+ end
14
+
15
+ def cancel_blocks!
16
+ return unless should_manage_blocks?
17
+
18
+ CmAppLogger.log(label: "#{self.class.name}#cancel_blocks!", data: { order_id: id }) do
19
+ SpreeCmCommissioner::Seats::BlocksCanceler.new(order_id: id, cancel_by: user).cancel_blocks!
20
+ end
21
+ end
22
+
23
+ def reserve_blocks!
24
+ return unless should_manage_blocks?
25
+
26
+ CmAppLogger.log(label: "#{self.class.name}#reserve_blocks!", data: { order_id: id }) do
27
+ SpreeCmCommissioner::Seats::BlocksReserver.new(line_item_ids: line_item_ids, reserve_by: user).reserve_blocks!
28
+ end
29
+ end
30
+
31
+ # Calling `.block_ids` directly can cause many slow database queries (N+1 problem)
32
+ # every time `.should_manage_blocks?` or `.preload_block_ids` runs.
33
+ # To avoid this, we store a precomputed list of block IDs in `private_metadata`.
34
+ # This list is updated whenever a guest’s block is saved or destroy.
35
+ def preload_block_ids=(preload_block_ids = [])
36
+ self.private_metadata ||= {}
37
+ self.private_metadata['preload_block_ids'] = preload_block_ids
38
+ end
39
+
40
+ def preload_block_ids
41
+ self.private_metadata&.fetch('preload_block_ids', []) || []
42
+ end
43
+ end
44
+ end