spree_cm_commissioner 2.0.3.pre.pre.pre7 → 2.1.0.pre.pre1

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 (253) 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 +39 -4
  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/locations_controller.rb +62 -0
  9. data/app/controllers/spree/admin/stock_managements_controller.rb +63 -1
  10. data/app/controllers/spree/api/v2/platform/places_controller.rb +4 -1
  11. data/app/controllers/spree/api/v2/storefront/accommodations/variants_controller.rb +42 -0
  12. data/app/controllers/spree/api/v2/storefront/accommodations_controller.rb +14 -31
  13. data/app/controllers/spree/api/v2/storefront/dynamic_fields_controller.rb +1 -1
  14. data/app/controllers/spree/api/v2/storefront/guests_controller.rb +26 -18
  15. data/app/controllers/spree/api/v2/storefront/inventory_items_controller.rb +57 -0
  16. data/app/controllers/spree/api/v2/storefront/queue_cart/line_items_controller.rb +2 -2
  17. data/app/controllers/spree/api/v2/storefront/transit/draft_orders_controller.rb +46 -0
  18. data/app/controllers/spree/api/v2/storefront/trip_places_controller.rb +29 -0
  19. data/app/controllers/spree/api/v2/storefront/trip_search_controller.rb +62 -0
  20. data/app/controllers/spree/api/v2/storefront/trips_controller.rb +24 -0
  21. data/app/finders/spree_cm_commissioner/accommodations/find.rb +37 -0
  22. data/app/finders/spree_cm_commissioner/accommodations/find_variant.rb +32 -0
  23. data/app/interactors/spree_cm_commissioner/billing_address_creator.rb +33 -0
  24. data/app/interactors/spree_cm_commissioner/customer_notification_sender.rb +7 -2
  25. data/app/interactors/spree_cm_commissioner/ensure_correct_product_type.rb +40 -0
  26. data/app/interactors/spree_cm_commissioner/fetch_facebook_user_data.rb +45 -0
  27. data/app/interactors/spree_cm_commissioner/google_places_fetcher.rb +1 -0
  28. data/app/interactors/spree_cm_commissioner/intercity_taxi_order_creator.rb +106 -0
  29. data/app/interactors/spree_cm_commissioner/inventory_item_syncer.rb +25 -0
  30. data/app/interactors/spree_cm_commissioner/stock/inventory_item_resetter.rb +44 -0
  31. data/app/interactors/spree_cm_commissioner/stock/inventory_items_adjuster.rb +13 -0
  32. data/app/interactors/spree_cm_commissioner/stock/inventory_items_generator.rb +15 -0
  33. data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +75 -0
  34. data/app/interactors/spree_cm_commissioner/stock/stock_movement_creator.rb +32 -0
  35. data/app/interactors/spree_cm_commissioner/taxi_trip_creator.rb +66 -0
  36. data/app/interactors/spree_cm_commissioner/transit/draft_order_creator.rb +136 -0
  37. data/app/interactors/spree_cm_commissioner/trip_clone_creator.rb +155 -0
  38. data/app/interactors/spree_cm_commissioner/user_fb_token_authenticator.rb +37 -0
  39. data/app/interactors/spree_cm_commissioner/user_fb_token_checker.rb +20 -0
  40. data/app/interactors/spree_cm_commissioner/user_identity_checker.rb +8 -8
  41. data/app/interactors/spree_cm_commissioner/user_registration_with_fb_token.rb +62 -0
  42. data/app/interactors/spree_cm_commissioner/variant_block_updater.rb +62 -0
  43. data/app/interactors/spree_cm_commissioner/vehicle_updater.rb +41 -0
  44. data/app/jobs/spree_cm_commissioner/application_job.rb +20 -0
  45. data/app/jobs/spree_cm_commissioner/application_unique_job.rb +20 -0
  46. data/app/jobs/spree_cm_commissioner/ensure_correct_product_type_job.rb +7 -0
  47. data/app/jobs/spree_cm_commissioner/inventory_item_syncer_job.rb +7 -0
  48. data/app/jobs/spree_cm_commissioner/stock/inventory_items_adjuster_job.rb +11 -0
  49. data/app/jobs/spree_cm_commissioner/stock/inventory_items_generator_job.rb +11 -0
  50. data/app/jobs/spree_cm_commissioner/stock/permanent_inventory_items_generator_job.rb +9 -0
  51. data/app/models/concerns/spree_cm_commissioner/line_item_durationable.rb +18 -15
  52. data/app/models/concerns/spree_cm_commissioner/line_item_guests_concern.rb +29 -15
  53. data/app/models/concerns/spree_cm_commissioner/line_item_transitable.rb +144 -0
  54. data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +3 -12
  55. data/app/models/concerns/spree_cm_commissioner/order_seatable.rb +44 -0
  56. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +39 -0
  57. data/app/models/concerns/spree_cm_commissioner/product_delegation.rb +1 -3
  58. data/app/models/concerns/spree_cm_commissioner/product_type.rb +10 -0
  59. data/app/models/concerns/spree_cm_commissioner/route_type.rb +1 -1
  60. data/app/models/concerns/spree_cm_commissioner/user_identity.rb +12 -4
  61. data/app/models/concerns/spree_cm_commissioner/variant_options_concern.rb +1 -0
  62. data/app/models/concerns/spree_cm_commissioner/vehicle_type.rb +11 -0
  63. data/app/models/spree_cm_commissioner/address_decorator.rb +36 -2
  64. data/app/models/spree_cm_commissioner/block.rb +43 -0
  65. data/app/models/spree_cm_commissioner/customer_notification.rb +6 -1
  66. data/app/models/spree_cm_commissioner/dynamic_field.rb +2 -0
  67. data/app/models/spree_cm_commissioner/guest.rb +39 -0
  68. data/app/models/spree_cm_commissioner/guest_dynamic_field.rb +4 -0
  69. data/app/models/spree_cm_commissioner/inventory.rb +11 -0
  70. data/app/models/spree_cm_commissioner/inventory_item.rb +82 -0
  71. data/app/models/spree_cm_commissioner/line_item_decorator.rb +47 -78
  72. data/app/models/spree_cm_commissioner/notification_taxon.rb +1 -1
  73. data/app/models/spree_cm_commissioner/option_type_decorator.rb +1 -11
  74. data/app/models/spree_cm_commissioner/order/address_book_decorator.rb +30 -12
  75. data/app/models/spree_cm_commissioner/order_decorator.rb +38 -1
  76. data/app/models/spree_cm_commissioner/place.rb +3 -4
  77. data/app/models/spree_cm_commissioner/price_decorator.rb +9 -0
  78. data/app/models/spree_cm_commissioner/product_decorator.rb +13 -10
  79. data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +41 -0
  80. data/app/models/spree_cm_commissioner/redis_stock/inventory_updater.rb +126 -0
  81. data/app/models/spree_cm_commissioner/redis_stock/line_items_cached_inventory_items_builder.rb +36 -0
  82. data/app/models/spree_cm_commissioner/redis_stock/variant_cached_inventory_items_builder.rb +25 -0
  83. data/app/models/spree_cm_commissioner/reserved_block.rb +31 -0
  84. data/app/models/spree_cm_commissioner/saved_guest.rb +21 -0
  85. data/app/models/spree_cm_commissioner/seat_layout.rb +20 -0
  86. data/app/models/spree_cm_commissioner/seat_section.rb +16 -0
  87. data/app/models/spree_cm_commissioner/seats/blocks_canceler.rb +30 -0
  88. data/app/models/spree_cm_commissioner/seats/blocks_holder.rb +53 -0
  89. data/app/models/spree_cm_commissioner/seats/blocks_reserver.rb +49 -0
  90. data/app/models/spree_cm_commissioner/seats/errors/blocks_are_on_hold_by_other_guest.rb +4 -0
  91. data/app/models/spree_cm_commissioner/seats/errors/blocks_are_reserved_by_other_guest.rb +4 -0
  92. data/app/models/spree_cm_commissioner/seats/errors/blocks_are_reserved_by_same_guest.rb +4 -0
  93. data/app/models/spree_cm_commissioner/seats/errors/unable_to_save_reserved_block_record.rb +4 -0
  94. data/app/models/spree_cm_commissioner/service_calendar.rb +0 -2
  95. data/app/models/spree_cm_commissioner/state_decorator.rb +0 -1
  96. data/app/models/spree_cm_commissioner/stock/availability_checker.rb +26 -25
  97. data/app/models/spree_cm_commissioner/stock/availability_validator_decorator.rb +2 -1
  98. data/app/models/spree_cm_commissioner/stock/line_item_availability_checker.rb +3 -3
  99. data/app/models/spree_cm_commissioner/stock/order_availability_checker.rb +44 -0
  100. data/app/models/spree_cm_commissioner/stock_item_decorator.rb +17 -0
  101. data/app/models/spree_cm_commissioner/taxon_decorator.rb +7 -1
  102. data/app/models/spree_cm_commissioner/taxonomy_decorator.rb +0 -6
  103. data/app/models/spree_cm_commissioner/trip.rb +19 -10
  104. data/app/models/spree_cm_commissioner/trip_connection.rb +5 -5
  105. data/app/models/spree_cm_commissioner/trip_stop.rb +10 -21
  106. data/app/models/spree_cm_commissioner/variant_block.rb +17 -0
  107. data/app/models/spree_cm_commissioner/variant_decorator.rb +37 -47
  108. data/app/models/spree_cm_commissioner/variant_options.rb +4 -23
  109. data/app/models/spree_cm_commissioner/vehicle.rb +12 -15
  110. data/app/models/spree_cm_commissioner/vendor_decorator.rb +14 -14
  111. data/app/models/spree_cm_commissioner/vendor_place.rb +13 -3
  112. data/app/overrides/spree/admin/shared/sub_menu/_configuration/locations_tab.html.erb.deface +5 -0
  113. data/app/queries/spree_cm_commissioner/trip_query.rb +110 -131
  114. data/app/request_schemas/spree_cm_commissioner/accommodation_request_schema.rb +3 -0
  115. data/app/request_schemas/spree_cm_commissioner/application_request_schema.rb +1 -1
  116. data/app/request_schemas/spree_cm_commissioner/inventory_item_schema.rb +8 -0
  117. data/app/request_schemas/spree_cm_commissioner/trip_search_request_schema.rb +20 -0
  118. data/app/request_schemas/spree_cm_commissioner/variant_request_schema.rb +19 -0
  119. data/app/serializers/spree/v2/storefront/accommodation_serializer.rb +2 -0
  120. data/app/serializers/spree/v2/storefront/cart_serializer_decorator.rb +1 -0
  121. data/app/serializers/spree/v2/storefront/line_item_serializer_decorator.rb +3 -1
  122. data/app/serializers/spree/v2/storefront/user_serializer_decorator.rb +1 -0
  123. data/app/serializers/spree_cm_commissioner/v2/storefront/amenity_serializer.rb +15 -0
  124. data/app/serializers/spree_cm_commissioner/v2/storefront/block_serializer.rb +11 -0
  125. data/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb +5 -1
  126. data/app/serializers/spree_cm_commissioner/v2/storefront/inventory_item_serializer.rb +42 -0
  127. data/app/serializers/spree_cm_commissioner/v2/storefront/reserved_block_serializer.rb +9 -0
  128. data/app/serializers/spree_cm_commissioner/v2/storefront/saved_guest_serializer.rb +21 -0
  129. data/app/serializers/spree_cm_commissioner/v2/storefront/seat_layout_serializer.rb +12 -0
  130. data/app/serializers/spree_cm_commissioner/v2/storefront/seat_section_serializer.rb +11 -0
  131. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_place_serializer.rb +10 -0
  132. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_query_result_serializer.rb +14 -0
  133. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_result_serializer.rb +17 -0
  134. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_serializer.rb +20 -0
  135. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_stop_serializer.rb +11 -0
  136. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_variant_serializer.rb +12 -0
  137. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_vehicle_serializer.rb +16 -0
  138. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_vendor_serializer.rb +16 -0
  139. data/app/serializers/spree_cm_commissioner/v2/storefront/variant_block_serializer.rb +9 -0
  140. data/app/services/spree_cm_commissioner/transit/legs_builder_service.rb +46 -0
  141. data/app/services/spree_cm_commissioner/user_authenticator.rb +10 -1
  142. data/app/views/shared/_map.html.erb +5 -4
  143. data/app/views/spree/admin/customer_notifications/_form.html.erb +3 -3
  144. data/app/views/spree/admin/inventory_items/show.html.erb +72 -0
  145. data/app/views/spree/admin/locations/_form.html.erb +133 -0
  146. data/app/views/spree/admin/locations/edit.html.erb +11 -0
  147. data/app/views/spree/admin/locations/index.html.erb +67 -0
  148. data/app/views/spree/admin/locations/new.html.erb +11 -0
  149. data/app/views/spree/admin/stock_managements/_variant_stock_items.html.erb +7 -2
  150. data/app/views/spree/admin/stock_managements/calendar.html.erb +56 -0
  151. data/app/views/spree/admin/stock_managements/index.html.erb +55 -6
  152. data/config/initializers/paper_trail.rb +1 -0
  153. data/config/initializers/spree_permitted_attributes.rb +24 -4
  154. data/config/locales/en.yml +2 -0
  155. data/config/routes.rb +32 -2
  156. data/db/migrate/20240202080634_update_counter_cache_of_vehicle_type.rb +3 -1
  157. data/db/migrate/20250122000000_move_route_type_from_vehicles_to_trips.rb +7 -0
  158. data/db/migrate/20250304293518_create_cm_inventory_items.rb +21 -0
  159. data/db/migrate/20250428092706_add_active_to_cm_customer_notifications.rb +7 -0
  160. data/db/migrate/20250428103550_remove_tenant_id_and_vendor_id_from_cm_customer_notifications.rb +9 -0
  161. data/db/migrate/20250429094228_add_lock_version_to_cm_inventory_items.rb +5 -0
  162. data/db/migrate/20250502025848_add_index_to_spree_products.rb +5 -0
  163. data/db/migrate/20250502030001_add_product_type_to_spree_variants.rb +5 -0
  164. data/db/migrate/20250502030002_add_product_type_to_spree_line_items.rb +5 -0
  165. data/db/migrate/20250603035256_add_inventory_item_to_spree_prices.rb +7 -0
  166. data/db/migrate/20250619073724_drop_table_cm_line_item_seats.rb +5 -0
  167. data/db/migrate/20250619073812_drop_table_cm_vehicle_seats.rb +5 -0
  168. data/db/migrate/20250619073844_drop_table_cm_vehicle_types.rb +9 -0
  169. data/db/migrate/20250619073957_drop_table_cm_option_value_vehicle_types.rb +5 -0
  170. data/db/migrate/20250619082354_remove_unnecessary_fields_from_cm_places.rb +9 -0
  171. data/db/migrate/20250619082736_remove_route_type_from_spree_products.rb +5 -0
  172. data/db/migrate/20250619083055_remove_unnecessary_fields_from_spree_taxons.rb +5 -0
  173. data/db/migrate/20250620083055_remove_variant_id_from_cm_trips.rb +5 -0
  174. data/db/migrate/20250620090000_update_cm_trip_connections_to_use_cm_trips.rb +6 -0
  175. data/db/migrate/20250620090001_create_cm_seat_layouts.rb +17 -0
  176. data/db/migrate/20250620090002_create_cm_seat_sections.rb +18 -0
  177. data/db/migrate/20250620090003_create_cm_blocks.rb +18 -0
  178. data/db/migrate/20250624091005_create_cm_reserved_blocks.rb +29 -0
  179. data/db/migrate/20250626083642_create_cm_variant_blocks.rb +24 -0
  180. data/db/migrate/20250627023314_add_block_id_to_cm_guests.rb +13 -0
  181. data/db/migrate/20250716022821_add_location_reference_to_cm_vendor_places.rb +5 -0
  182. data/db/migrate/20250716031743_drop_table_cm_vendor_stops.rb +5 -0
  183. data/db/migrate/20250717023824_add_vendor_reference_to_cm_trips.rb +5 -0
  184. data/db/migrate/20250717041414_add_location_place_reference_to_cm_trip_stops.rb +5 -0
  185. data/db/migrate/20250717042539_rename_cm_trip_stops_stop_id_column_to_stop_place_id.rb +7 -0
  186. data/db/migrate/20250717042707_rename_cm_trips_origin_and_destination_to_origin_place_and_destination_place.rb +11 -0
  187. data/db/migrate/20250721080738_add_vehicle_type_to_cm_vehicles.rb +7 -0
  188. data/db/migrate/20250725092713_add_block_type_to_cm_blocks.rb +5 -0
  189. data/db/migrate/20250731062816_add_departure_time_and_arrival_time_to_trip_stop.rb +6 -0
  190. data/db/migrate/20250807033035_create_spree_cm_commissioner_saved_guests.rb +23 -0
  191. data/db/migrate/20250807040000_generate_pickup_time_integer_option_values.rb +71 -0
  192. data/db/migrate/20250808054835_add_saved_guest_reference_to_cm_blocks.rb +5 -0
  193. data/docker-compose.yml +1 -1
  194. data/lib/cm_app_logger.rb +17 -1
  195. data/lib/generators/spree_cm_commissioner/install/install_generator.rb +14 -11
  196. data/lib/generators/spree_cm_commissioner/install/templates/app/javascript/{spree_cm_commissioner → spree_dashboard/spree_cm_commissioner}/utilities.js +4 -0
  197. data/lib/spree_cm_commissioner/cached_inventory_item.rb +23 -0
  198. data/lib/spree_cm_commissioner/calendar_event.rb +11 -1
  199. data/lib/spree_cm_commissioner/test_helper/factories/block_factory.rb +16 -0
  200. data/lib/spree_cm_commissioner/test_helper/factories/guest_factory.rb +10 -0
  201. data/lib/spree_cm_commissioner/test_helper/factories/homepage_section_relatable_factory.rb +1 -1
  202. data/lib/spree_cm_commissioner/test_helper/factories/inventory_item_factory.rb +20 -0
  203. data/lib/spree_cm_commissioner/test_helper/factories/line_item_factory.rb +1 -1
  204. data/lib/spree_cm_commissioner/test_helper/factories/option_type_factory.rb +6 -30
  205. data/lib/spree_cm_commissioner/test_helper/factories/order_factory.rb +0 -36
  206. data/lib/spree_cm_commissioner/test_helper/factories/product_factory.rb +18 -34
  207. data/lib/spree_cm_commissioner/test_helper/factories/reserved_block_factory.rb +27 -0
  208. data/lib/spree_cm_commissioner/test_helper/factories/saved_guest_factory.rb +8 -0
  209. data/lib/spree_cm_commissioner/test_helper/factories/seat_layout_factory.rb +11 -0
  210. data/lib/spree_cm_commissioner/test_helper/factories/seat_section_factory.rb +16 -0
  211. data/lib/spree_cm_commissioner/test_helper/factories/stock_location_factory.rb +2 -2
  212. data/lib/spree_cm_commissioner/test_helper/factories/trip_connection_factory.rb +6 -0
  213. data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +11 -3
  214. data/lib/spree_cm_commissioner/test_helper/factories/trip_stop_factory.rb +13 -0
  215. data/lib/spree_cm_commissioner/test_helper/factories/variant_block_factory.rb +7 -0
  216. data/lib/spree_cm_commissioner/test_helper/factories/variant_factory.rb +41 -19
  217. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_factory.rb +1 -1
  218. data/lib/spree_cm_commissioner/test_helper/factories/vendor_factory.rb +6 -1
  219. data/lib/spree_cm_commissioner/test_helper/factories/vendor_place_factory.rb +13 -1
  220. data/lib/spree_cm_commissioner/transit/leg.rb +37 -0
  221. data/lib/spree_cm_commissioner/transit/seat_selection.rb +27 -0
  222. data/lib/spree_cm_commissioner/trip_query_result.rb +50 -0
  223. data/lib/spree_cm_commissioner/trip_result.rb +19 -10
  224. data/lib/spree_cm_commissioner/version.rb +1 -1
  225. data/lib/spree_cm_commissioner.rb +37 -1
  226. data/lib/tasks/create_default_non_permanent_inventory_items.rake +16 -0
  227. data/lib/tasks/ensure_correct_product_type.rake +7 -0
  228. data/lib/tasks/generate_inventory_items.rake +7 -0
  229. data/spree_cm_commissioner.gemspec +7 -0
  230. metadata +195 -26
  231. data/app/controllers/concerns/spree_cm_commissioner/transit/taxon_bitwise.rb +0 -44
  232. data/app/finders/spree_cm_commissioner/line_items/find_by_variant_decorator.rb +0 -20
  233. data/app/models/spree_cm_commissioner/branch.rb +0 -12
  234. data/app/models/spree_cm_commissioner/line_item_seat.rb +0 -10
  235. data/app/models/spree_cm_commissioner/option_value_vehicle_type.rb +0 -8
  236. data/app/models/spree_cm_commissioner/stop.rb +0 -23
  237. data/app/models/spree_cm_commissioner/vehicle_seat.rb +0 -11
  238. data/app/models/spree_cm_commissioner/vehicle_type.rb +0 -76
  239. data/app/models/spree_cm_commissioner/vendor_stop.rb +0 -10
  240. data/app/queries/spree_cm_commissioner/trip_search_query.rb +0 -76
  241. data/app/queries/spree_cm_commissioner/variant_availability/non_permanent_stock_query.rb +0 -45
  242. data/app/queries/spree_cm_commissioner/variant_availability/permanent_stock_query.rb +0 -55
  243. data/app/queries/spree_cm_commissioner/vendor_stop_place_query.rb +0 -54
  244. data/app/services/spree_cm_commissioner/vehicle_option_value_creator.rb +0 -11
  245. data/lib/spree_cm_commissioner/test_helper/factories/branch_factory.rb +0 -12
  246. data/lib/spree_cm_commissioner/test_helper/factories/departure_time_option_type_factory.rb +0 -8
  247. data/lib/spree_cm_commissioner/test_helper/factories/duration_option_type_factory.rb +0 -8
  248. data/lib/spree_cm_commissioner/test_helper/factories/line_item_seat_factory.rb +0 -7
  249. data/lib/spree_cm_commissioner/test_helper/factories/stop_factory.rb +0 -14
  250. data/lib/spree_cm_commissioner/test_helper/factories/transit_place_factory.rb +0 -8
  251. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_option_type_factory.rb +0 -8
  252. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_type_factory.rb +0 -96
  253. data/lib/spree_cm_commissioner/trip_seat_layout_result.rb +0 -11
@@ -32,7 +32,7 @@ module Spree
32
32
  resource.save_and_move_to_next_stage
33
33
  render_serialized_payload(201) { serialize_resource(resource) }
34
34
  else
35
- render_error_payload(resource.errors, 400)
35
+ render_error_payload(resource.errors, 422)
36
36
  end
37
37
  end
38
38
 
@@ -41,15 +41,7 @@ module Spree
41
41
 
42
42
  begin
43
43
  ActiveRecord::Base.transaction do
44
- if guest_params[:guest_dynamic_fields_attributes]
45
- dynamic_field_ids = guest_params[:guest_dynamic_fields_attributes].pluck(:dynamic_field_id)
46
- resource.guest_dynamic_fields.where(dynamic_field_id: dynamic_field_ids).destroy_all
47
-
48
- guest_params[:guest_dynamic_fields_attributes].each do |attr|
49
- field = build_dynamic_field(attr)
50
- field.save!
51
- end
52
- end
44
+ process_dynamic_fields(guest_params[:guest_dynamic_fields_attributes]) if guest_params[:guest_dynamic_fields_attributes]
53
45
 
54
46
  resource.assign_attributes(guest_params.except(:guest_dynamic_fields_attributes))
55
47
  resource.save!
@@ -58,7 +50,7 @@ module Spree
58
50
  resource.save_and_move_to_next_stage
59
51
  render_serialized_payload { serialize_resource(resource) }
60
52
  rescue ActiveRecord::RecordInvalid => e
61
- render_error_payload(e.record.errors, 400)
53
+ render_error_payload(e.record.errors, 422)
62
54
  end
63
55
  end
64
56
 
@@ -97,15 +89,31 @@ module Spree
97
89
  )
98
90
  end
99
91
 
100
- def build_dynamic_field(attr)
101
- field = resource.guest_dynamic_fields.build(attr.except(:id))
102
-
103
- if field.dynamic_field.requires_dynamic_field_options?
104
- option = field.dynamic_field.dynamic_field_options.find_by(id: field.value)
105
- field.dynamic_field_option = option if option
92
+ def process_dynamic_fields(dynamic_fields_attributes)
93
+ dynamic_fields_attributes.each do |attr|
94
+ if attr[:_destroy] == '1' && attr[:id].present?
95
+ field = resource.guest_dynamic_fields.find_by(id: attr[:id])
96
+ field&.destroy!
97
+ elsif attr[:id].present?
98
+ field = resource.guest_dynamic_fields.find_by(id: attr[:id])
99
+ if field
100
+ field.assign_attributes(attr.except(:id, :_destroy))
101
+ associate_dynamic_field_option(field) if field.dynamic_field.present?
102
+ field.save!
103
+ end
104
+ else
105
+ field = resource.guest_dynamic_fields.build(attr.except(:id, :_destroy))
106
+ associate_dynamic_field_option(field) if field.dynamic_field.present?
107
+ field.save!
108
+ end
106
109
  end
110
+ end
111
+
112
+ def associate_dynamic_field_option(field)
113
+ return unless field.dynamic_field&.requires_dynamic_field_options?
107
114
 
108
- field
115
+ option = field.dynamic_field.dynamic_field_options.find_by(id: field.value)
116
+ field.dynamic_field_option = option if option
109
117
  end
110
118
  end
111
119
  end
@@ -0,0 +1,57 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ class InventoryItemsController < ::Spree::Api::V2::ResourceController
6
+ # GET /api/v2/storefront/inventory_items?
7
+ # - variant_ids[]=[1,2]
8
+ # - inventory_dates[]=[2025-03-06] (optional)
9
+ # - includes[]=reserved_blocks
10
+ def index
11
+ render_serialized_payload do
12
+ collection_serializer.new(
13
+ collection,
14
+ { include: resource_includes, params: serializer_params }
15
+ ).serializable_hash
16
+ end
17
+ end
18
+
19
+ # override
20
+ def collection
21
+ return @collection if defined?(@collection)
22
+
23
+ scope = if include_reserved_blocks?
24
+ SpreeCmCommissioner::InventoryItem.includes(:prices, :reserved_blocks)
25
+ else
26
+ SpreeCmCommissioner::InventoryItem.includes(:prices)
27
+ end
28
+
29
+ @collection = scope.where(variant_id: params[:variant_ids], inventory_date: params[:inventory_dates])
30
+ @collection
31
+ end
32
+
33
+ # override
34
+ def collection_serializer
35
+ SpreeCmCommissioner::V2::Storefront::InventoryItemSerializer
36
+ end
37
+
38
+ # override
39
+ def required_schema
40
+ SpreeCmCommissioner::InventoryItemSchema
41
+ end
42
+
43
+ # override
44
+ def serializer_params
45
+ super.merge(include_reserved_blocks: include_reserved_blocks?)
46
+ end
47
+
48
+ private
49
+
50
+ def include_reserved_blocks?
51
+ resource_includes.include?(:reserved_blocks)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -12,9 +12,9 @@ module Spree
12
12
  spree_authorize! :update, spree_current_order, order_token
13
13
  spree_authorize! :show, @variant
14
14
 
15
- availability_checker = SpreeCmCommissioner::Stock::AvailabilityChecker.new(@variant)
15
+ availability_checker = SpreeCmCommissioner::Stock::AvailabilityChecker.new(@variant, add_item_params[:options])
16
16
 
17
- unless availability_checker.can_supply?(add_item_params[:quantity].to_i, add_item_params[:options])
17
+ unless availability_checker.can_supply?(add_item_params[:quantity].to_i)
18
18
  return render_error_payload(availability_checker.error_message || I18n.t('variant_availability.items_out_of_stock'))
19
19
  end
20
20
 
@@ -0,0 +1,46 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ module Transit
6
+ class DraftOrdersController < ::Spree::Api::V2::ResourceController
7
+ # Creates a draft transit order.
8
+ # Endpoint: POST /api/v2/storefront/transit/draft_orders
9
+ # Params:
10
+ # - outbound_date: Date of outbound transit (optional)
11
+ # - inbound_date: Date of inbound transit (optional)
12
+ # - outbound_legs: Array of outbound leg details (required)
13
+ # - inbound_legs: Array of inbound leg details (optional)
14
+ def create
15
+ @outbound_legs = params[:outbound_legs].is_a?(Array) && params[:outbound_legs].any? ? build_legs(:outbound, params[:outbound_legs]) : []
16
+ @inbound_legs = params[:inbound_legs].is_a?(Array) && params[:inbound_legs].any? ? build_legs(:inbound, params[:inbound_legs]) : []
17
+
18
+ context = SpreeCmCommissioner::Transit::DraftOrderCreator.call(
19
+ outbound_date: params[:outbound_date]&.to_date,
20
+ inbound_date: params[:inbound_date]&.to_date,
21
+ outbound_legs: @outbound_legs,
22
+ inbound_legs: @inbound_legs,
23
+ user: spree_current_user
24
+ )
25
+
26
+ if context.success?
27
+ render_serialized_payload { serialize_resource(context.order) }
28
+ else
29
+ render_error_payload(context.message)
30
+ end
31
+ end
32
+
33
+ def build_legs(direction, legs_params)
34
+ SpreeCmCommissioner::Transit::LegsBuilderService.new(direction: direction, legs_params: legs_params).call
35
+ end
36
+
37
+ # override
38
+ def resource_serializer
39
+ Spree::Api::Dependencies.storefront_cart_serializer.constantize
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,29 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ class TripPlacesController < ::Spree::Api::V2::ResourceController
6
+ # override
7
+ def collection
8
+ base_scope = model_class.joins(:location_vendor_places).distinct
9
+
10
+ if params[:q].present?
11
+ @search = base_scope.ransack(params[:q])
12
+ @search.result
13
+ else
14
+ base_scope
15
+ end
16
+ end
17
+
18
+ def model_class
19
+ SpreeCmCommissioner::Place
20
+ end
21
+
22
+ def collection_serializer
23
+ SpreeCmCommissioner::V2::Storefront::TripPlaceSerializer
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,62 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ class TripSearchController < ::Spree::Api::V2::ResourceController
6
+ def index
7
+ trips = SpreeCmCommissioner::TripQuery.new(
8
+ origin_id: params[:origin_id],
9
+ destination_id: params[:destination_id],
10
+ date: params[:date],
11
+ vendor_id: params[:vendor_id],
12
+ number_of_guests: params[:number_of_guests],
13
+ params: params
14
+ ).call
15
+
16
+ render_serialized_payload { serialize_collection(trips) }
17
+ end
18
+
19
+ private
20
+
21
+ # override
22
+ def default_resource_includes
23
+ [
24
+ 'trips.vendor',
25
+ 'trips.vendor.logo',
26
+ 'trips.vehicle',
27
+ 'trips.amenities'
28
+ ]
29
+ end
30
+
31
+ def serialize_collection(collection)
32
+ serialized_data = SpreeCmCommissioner::V2::Storefront::TripQueryResultSerializer.new(
33
+ collection,
34
+ include: default_resource_includes,
35
+ params: serializer_params
36
+ ).serializable_hash
37
+ serialized_data[:meta] = {
38
+ count: collection.size,
39
+ total_count: collection.respond_to?(:total_count) ? collection.total_count : collection.size,
40
+ current_page: collection.respond_to?(:current_page) ? collection.current_page : 1,
41
+ per_page: collection.respond_to?(:limit_value) ? collection.limit_value : collection.size,
42
+ pages: collection.respond_to?(:total_pages) ? collection.total_pages : 1,
43
+ next_page: collection.respond_to?(:next_page) ? collection.next_page : nil,
44
+ prev_page: collection.respond_to?(:prev_page) ? collection.prev_page : nil
45
+ }
46
+ serialized_data
47
+ end
48
+
49
+ # override
50
+ def serializer_params
51
+ params.permit(:include).to_hash
52
+ end
53
+
54
+ # override
55
+ def required_schema
56
+ SpreeCmCommissioner::TripSearchRequestSchema
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,24 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ class TripsController < ::Spree::Api::V2::ResourceController
6
+ # override
7
+ def resource
8
+ model_class.find(params[:id])
9
+ end
10
+
11
+ # override
12
+ def model_class
13
+ SpreeCmCommissioner::Trip
14
+ end
15
+
16
+ # override
17
+ def resource_serializer
18
+ SpreeCmCommissioner::V2::Storefront::TripSerializer
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ 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,33 @@
1
+ module SpreeCmCommissioner
2
+ class BillingAddressCreator < BaseInteractor
3
+ delegate :order, to: :context
4
+
5
+ def call
6
+ validate_context!
7
+ create_address
8
+ end
9
+
10
+ private
11
+
12
+ def validate_context!
13
+ context.fail!(message: 'Order is missing') unless order
14
+ end
15
+
16
+ def create_address
17
+ line_item = order.line_items.first
18
+ guest = line_item&.guests&.first
19
+
20
+ billing_address = order.build_bill_address(
21
+ firstname: guest&.first_name,
22
+ lastname: guest&.last_name,
23
+ phone: order.intel_phone_number || order.phone_number
24
+ )
25
+
26
+ context.fail!(message: billing_address.errors.full_messages.to_sentence) unless billing_address.save!
27
+ # Persist association on the order so subsequent reloads reflect the new billing address
28
+ order.update!(bill_address: billing_address)
29
+
30
+ context.billing_address = billing_address
31
+ end
32
+ end
33
+ end
@@ -55,7 +55,8 @@ module SpreeCmCommissioner
55
55
  end
56
56
 
57
57
  def end_users
58
- users_scope = Spree.user_class.where(tenant_id: customer_notification.tenant_id)
58
+ users_scope = Spree.user_class
59
+ .where(tenant_id: notification_tenant_id)
59
60
 
60
61
  if customer_notification.notification_taxons.exists?
61
62
  taxon_ids = customer_notification.notification_taxons.pluck(:taxon_id)
@@ -63,12 +64,16 @@ module SpreeCmCommissioner
63
64
  .new(taxon_ids)
64
65
  .call
65
66
  .end_users_push_notificable
66
- .where(tenant_id: customer_notification.tenant_id)
67
+ .where(tenant_id: notification_tenant_id)
67
68
  else
68
69
  users_scope = users_scope.end_users_push_notificable
69
70
  end
70
71
 
71
72
  users_scope
72
73
  end
74
+
75
+ def notification_tenant_id
76
+ @notification_tenant_id ||= customer_notification.vendor&.tenant_id
77
+ end
73
78
  end
74
79
  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,45 @@
1
+ module SpreeCmCommissioner
2
+ class FetchFacebookUserData < BaseInteractor
3
+ delegate :fb_access_token, to: :context
4
+
5
+ def call
6
+ context.fail!(message: 'fb_access_token_missing') if fb_access_token.blank?
7
+
8
+ graph_response = fetch_user_info
9
+
10
+ context.fail!(message: 'invalid_facebook_response') unless graph_response.is_a?(Hash)
11
+ context.fail!(message: graph_response['error']['message'] || 'facebook_error') if graph_response['error']
12
+
13
+ context.provider = {
14
+ identity_type: SpreeCmCommissioner::UserIdentityProvider.identity_types[:facebook],
15
+ sub: graph_response['id'],
16
+ name: graph_response['name'],
17
+ email: graph_response['email'], # can be nil
18
+ first_name: graph_response['first_name'],
19
+ last_name: graph_response['last_name'],
20
+ picture: graph_response.dig('picture', 'data', 'url')
21
+ }
22
+ end
23
+
24
+ private
25
+
26
+ def fetch_user_info
27
+ uri = URI('https://graph.facebook.com/v23.0/me')
28
+ uri.query = URI.encode_www_form(
29
+ fields: 'id,email,first_name,last_name,name,picture',
30
+ access_token: fb_access_token
31
+ )
32
+
33
+ http = Net::HTTP.new(uri.host, uri.port)
34
+ http.use_ssl = true
35
+ http.open_timeout = 5
36
+ http.read_timeout = 10
37
+
38
+ response = http.get(uri.request_uri)
39
+ JSON.parse(response.body)
40
+ rescue StandardError => e
41
+ CmAppLogger.error("Facebook API error: #{e.message}")
42
+ context.fail!(message: 'facebook_api_error', error: e.message)
43
+ end
44
+ end
45
+ end
@@ -11,6 +11,7 @@ module SpreeCmCommissioner
11
11
  types: json['types'].blank? ? '' : json['types'][0],
12
12
  name: json['name'],
13
13
  vicinity: json['vicinity'],
14
+ formatted_address: json['formatted_address'],
14
15
  lat: json['geometry']['location']['lat'],
15
16
  lon: json['geometry']['location']['lng'],
16
17
  icon: json['icon'],
@@ -0,0 +1,106 @@
1
+ module SpreeCmCommissioner
2
+ class IntercityTaxiOrderCreator < BaseInteractor
3
+ delegate :trip, :raw_params, :current_vendor, to: :context
4
+
5
+ def call
6
+ validate_context!
7
+
8
+ ActiveRecord::Base.transaction do
9
+ build_order_params
10
+ create_order
11
+ create_billing_address
12
+ advance_order
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def validate_context!
19
+ context.fail!(message: 'Trip is missing') unless trip
20
+ context.fail!(message: 'Raw parameters are missing') unless raw_params
21
+ context.fail!(message: 'Current vendor is missing') unless current_vendor
22
+ end
23
+
24
+ def build_order_params
25
+ context.order_params = build_taxi_booking_params
26
+ end
27
+
28
+ def build_taxi_booking_params
29
+ permitted_params = raw_params.require(:order).permit(
30
+ :intel_phone_number,
31
+ :email,
32
+ :store_id,
33
+ line_items_attributes: [
34
+ :remark,
35
+ :variant_id,
36
+ :quantity,
37
+ :vendor_id,
38
+ :from_date,
39
+ :to_date,
40
+ :currency,
41
+ :direction,
42
+ :trip_id,
43
+ :pickup_place_name,
44
+ :drop_off_place_name,
45
+ :pickup_lat,
46
+ :pickup_lng,
47
+ :drop_off_lat,
48
+ :drop_off_lng,
49
+ :passenger_count,
50
+ :pickup_oob_confirmed,
51
+ :drop_off_oob_confirmed,
52
+ { guests_attributes: %i[
53
+ first_name last_name gender age nationality_id
54
+ ]
55
+ }
56
+ ]
57
+ )
58
+
59
+ process_line_items_attributes(permitted_params)
60
+ permitted_params
61
+ end
62
+
63
+ def process_line_items_attributes(params)
64
+ date_param = raw_params[:date] || Date.current.to_s
65
+
66
+ params[:line_items_attributes].each_value do |item|
67
+ pickup_time_minutes = item[:from_date]
68
+ combined_datetime = "#{date_param} #{pickup_time_minutes}"
69
+
70
+ item[:from_date] = combined_datetime
71
+ item[:to_date] = combined_datetime
72
+ item[:variant_id] = load_variant
73
+ item[:vendor_id] = current_vendor.id
74
+ item[:quantity] = 1
75
+ item[:currency] = 'USD'
76
+ item[:direction] = 'outbound'
77
+ item[:trip_id] = trip.id
78
+ end
79
+ end
80
+
81
+ def create_order
82
+ context.order = Spree::Order.create(context.order_params)
83
+
84
+ context.fail!(message: context.order.errors.full_messages.to_sentence) unless context.order.persisted?
85
+ end
86
+
87
+ def create_billing_address
88
+ return if context.order.bill_address.present?
89
+
90
+ SpreeCmCommissioner::BillingAddressCreator.call(
91
+ order: context.order
92
+ )
93
+ end
94
+
95
+ def advance_order
96
+ context.order.update_with_updater!
97
+
98
+ advance_service = Spree::Api::Dependencies.storefront_checkout_advance_service.constantize
99
+ advance_service.call(order: context.order)
100
+ end
101
+
102
+ def load_variant
103
+ trip.product.master.id
104
+ end
105
+ end
106
+ 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