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
@@ -5,6 +5,8 @@ module SpreeCmCommissioner
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
+ state_machine.before_transition to: :payment, do: :hold_blocks!
9
+
8
10
  state_machine.before_transition to: :complete, do: :request, if: :need_confirmation?
9
11
  state_machine.before_transition to: :complete, do: :generate_bib_number
10
12
 
@@ -14,10 +16,14 @@ module SpreeCmCommissioner
14
16
  state_machine.after_transition to: :complete, do: :send_order_complete_telegram_alert_to_vendors, unless: :need_confirmation?
15
17
  state_machine.after_transition to: :complete, do: :send_order_complete_telegram_alert_to_store, unless: :need_confirmation?
16
18
 
19
+ state_machine.around_transition to: :complete, do: :unstock_inventory!
20
+
17
21
  state_machine.after_transition to: :complete, do: :send_transaction_email_to_user, if: :user_has_email?
18
22
  state_machine.after_transition to: :resumed, do: :precalculate_conversion
23
+ state_machine.around_transition to: :resumed, do: :unstock_inventory!
19
24
 
20
25
  state_machine.after_transition to: :canceled, do: :precalculate_conversion
26
+ state_machine.after_transition to: :canceled, do: :restock_inventory!
21
27
 
22
28
  scope :accepted, -> { where(request_state: 'accepted') }
23
29
 
@@ -67,6 +73,39 @@ module SpreeCmCommissioner
67
73
  end
68
74
  end
69
75
 
76
+ def unstock_inventory!
77
+ ActiveRecord::Base.transaction do
78
+ yield # Equal to block.call
79
+
80
+ # After the transition is complete, the following code will execute first before proceeding to other `after_transition` callbacks.
81
+ # This ensures that if `reserve_blocks!` or `unstock_inventory_in_redis!` fails, the state will be rolled back,
82
+ # and neither the `finalize!` method nor any notifications will be triggered.
83
+ # The payment will be reversed in vPago gem, and `Spree::Checkout::Complete` will be called, which checks `order.reload.complete?`.
84
+ # This is critical because if the order state is complete, the payment will be marked as paid.
85
+
86
+ reserve_blocks!
87
+ unstock_inventory_in_redis!
88
+
89
+ # We rollback only order state, and we keep payment state as it is.
90
+ # We implement payment in vPago gem, and it will be reversed in the gem.
91
+ # Some bank has api for refund, but some don't have the api to refund yet. So we keep the payment state as it is and refund manually.
92
+ end
93
+ rescue StandardError => e
94
+ CmAppLogger.log(
95
+ label: 'SpreeCmCommissioner::OrderStateMachine#reserve_inventory failed',
96
+ data: { order_id: id, error: e.message, type: e.class.name, backtrace: e.backtrace&.first(5)&.join("\n") }
97
+ )
98
+
99
+ raise e
100
+ end
101
+
102
+ def restock_inventory!
103
+ ActiveRecord::Base.transaction do
104
+ cancel_blocks!
105
+ restock_inventory_in_redis!
106
+ end
107
+ end
108
+
70
109
  def generate_bib_number
71
110
  line_items.find_each(&:generate_remaining_guests)
72
111
 
@@ -3,12 +3,10 @@ module SpreeCmCommissioner
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- delegate :product_type,
7
- :subscribable?,
6
+ delegate :subscribable?,
8
7
  :allowed_upload_later?,
9
8
  :need_confirmation?, :need_confirmation,
10
9
  :allow_anonymous_booking,
11
- :accommodation?, :service?, :ecommerce?,
12
10
  :allow_self_check_in,
13
11
  :allow_self_check_in?,
14
12
  :required_self_check_in_location,
@@ -6,10 +6,20 @@ module SpreeCmCommissioner
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  PRODUCT_TYPES = %i[accommodation service ecommerce transit].freeze
9
+ PERMANENT_STOCK_PRODUCT_TYPES = %w[accommodation service transit].freeze
10
+ PRE_INVENTORY_DAYS = { 'transit' => 90, 'accommodation' => 365, 'service' => 30 }.freeze
9
11
 
10
12
  included do
11
13
  enum product_type: PRODUCT_TYPES if table_exists? && column_names.include?('product_type')
12
14
  enum primary_product_type: PRODUCT_TYPES if table_exists? && column_names.include?('primary_product_type')
13
15
  end
16
+
17
+ def permanent_stock?
18
+ PERMANENT_STOCK_PRODUCT_TYPES.include?(product_type)
19
+ end
20
+
21
+ def pre_inventory_days
22
+ PRE_INVENTORY_DAYS[product_type]
23
+ end
14
24
  end
15
25
  end
@@ -2,7 +2,7 @@ module SpreeCmCommissioner
2
2
  module RouteType
3
3
  extend ActiveSupport::Concern
4
4
 
5
- ROUTE_TYPES = %i[automobile subway rails ferry].freeze
5
+ ROUTE_TYPES = %i[bus intercity_taxi airport_shuttle rails ferry].freeze
6
6
 
7
7
  included do
8
8
  enum route_type: ROUTE_TYPES if table_exists? && column_names.include?('route_type')
@@ -7,19 +7,27 @@ module SpreeCmCommissioner
7
7
 
8
8
  has_many :user_identity_providers, dependent: :destroy, class_name: 'SpreeCmCommissioner::UserIdentityProvider'
9
9
 
10
- validates :user_identity_providers, presence: true, if: -> (u) { u.require_login_identity_all_blank_for(:user_identity_providers) }
10
+ # Attribute to flag social network registration to skip presence validation
11
+ # for email, login, phone_number, and user_identity_providers
12
+ attr_accessor :registering_via_social_network
11
13
 
12
- validates :phone_number, presence: true, if: -> (u) { u.require_login_identity_all_blank_for(:phone_number) }
14
+ validates :user_identity_providers, presence: true, if: lambda { |u|
15
+ u.require_login_identity_all_blank_for(:user_identity_providers) && !u.registering_via_social_network
16
+ }
17
+
18
+ validates :phone_number, presence: true, if: lambda { |u|
19
+ u.require_login_identity_all_blank_for(:phone_number) && !u.registering_via_social_network
20
+ }
13
21
  validates :phone_number, uniqueness: { scope: :tenant_id, allow_blank: true }
14
22
  validates :phone_number, format: { with: /^\d{8,12}$/, :multiline => true, allow_blank: true }
15
23
 
16
- validates :login, presence: true, if: -> (u) { u.require_login_identity_all_blank_for(:login) }
24
+ validates :login, presence: true, if: -> (u) { u.require_login_identity_all_blank_for(:login) && !u.registering_via_social_network }
17
25
  validates :login, uniqueness: { scope: :tenant_id, case_sensitive: false, allow_blank: true }
18
26
 
19
27
  _validators.reject! { |key, _| key == :email }
20
28
  _validate_callbacks.each { |c| c.filter.attributes.delete(:email) if c.filter.respond_to?(:attributes) }
21
29
 
22
- validates :email, presence: true, if: -> (u) { u.require_login_identity_all_blank_for(:email) }
30
+ validates :email, presence: true, if: -> (u) { u.require_login_identity_all_blank_for(:email) && !u.registering_via_social_network }
23
31
  validates :email, uniqueness: { scope: :tenant_id, case_sensitive: false, allow_blank: true }
24
32
  validates :email, format: { with: URI::MailTo::EMAIL_REGEXP, allow_blank: true }
25
33
  end
@@ -29,6 +29,7 @@ module SpreeCmCommissioner
29
29
  :bib_pre_generation_on_create?,
30
30
  :seat_number_positions,
31
31
  :seat_number_layouts,
32
+ :seat_type,
32
33
  to: :options
33
34
  end
34
35
 
@@ -0,0 +1,11 @@
1
+ module SpreeCmCommissioner
2
+ module VehicleType
3
+ extend ActiveSupport::Concern
4
+
5
+ VEHICLE_TYPES = %i[suv sedan minivan van sleeping_bus luxury_van air_bus bus].freeze
6
+
7
+ included do
8
+ enum vehicle_type: VEHICLE_TYPES if table_exists? && column_names.include?('vehicle_type')
9
+ end
10
+ end
11
+ end
@@ -1,10 +1,44 @@
1
1
  module SpreeCmCommissioner
2
2
  module AddressDecorator
3
3
  def self.prepended(base)
4
- # Field is not mandatory
5
4
  base.enum gender: { :not_selected => 0, :male => 1, :female => 2, :other => 3 }
5
+
6
+ base.validates :address1, presence: true, if: :require_address1?
7
+ base.validates :city, presence: true, if: :require_city?
8
+ base.validates :country, presence: true, if: :require_country?
9
+ end
10
+
11
+ # override to return false instead of true
12
+ # in case no country
13
+ def require_zipcode?
14
+ country ? country.zipcode_required? : false
15
+ end
16
+
17
+ def require_address1?
18
+ false
19
+ end
20
+
21
+ def require_city?
22
+ false
23
+ end
24
+
25
+ def require_country?
26
+ false
6
27
  end
7
28
  end
8
29
  end
9
30
 
10
- Spree::Address.prepend(SpreeCmCommissioner::AddressDecorator) unless Spree::Address.included_modules.include?(SpreeCmCommissioner::AddressDecorator)
31
+ if Spree::Address.included_modules.exclude?(SpreeCmCommissioner::AddressDecorator)
32
+ keys = %i[address1 city country]
33
+
34
+ Spree::Address._validators.reject! { |key, _| keys.include?(key) }
35
+ Spree::Address._validate_callbacks.each do |c|
36
+ next unless c.filter.respond_to?(:attributes)
37
+
38
+ c.filter.attributes.delete(:address1)
39
+ c.filter.attributes.delete(:city)
40
+ c.filter.attributes.delete(:country)
41
+ end
42
+
43
+ Spree::Address.prepend(SpreeCmCommissioner::AddressDecorator)
44
+ end
@@ -0,0 +1,43 @@
1
+ module SpreeCmCommissioner
2
+ class Block < Base
3
+ enum block_type: {
4
+ seat: 1,
5
+ sleeping_seat: 2,
6
+ driver: 3,
7
+ bathroom: 4,
8
+ other: 0
9
+ }
10
+
11
+ belongs_to :seat_section, class_name: 'SpreeCmCommissioner::SeatSection', optional: true
12
+ belongs_to :seat_layout, class_name: 'SpreeCmCommissioner::SeatLayout', optional: false
13
+
14
+ validates :label, presence: true, if: :label_required?
15
+
16
+ has_many :variant_blocks, class_name: 'SpreeCmCommissioner::VariantBlock', dependent: :destroy
17
+ has_many :variants, class_name: 'Spree::Variant', through: :variant_blocks
18
+
19
+ validates :width, presence: true, numericality: { greater_than: 0 }
20
+ validates :height, presence: true, numericality: { greater_than: 0 }
21
+ validates :x, presence: true, numericality: true
22
+ validates :y, presence: true, numericality: true
23
+ validates :rotation, presence: true, numericality: { greater_than_or_equal_to: -360, less_than_or_equal_to: 360 }
24
+
25
+ before_validation :assign_layout_from_section, if: -> { seat_layout.nil? && seat_section.present? }
26
+
27
+ def label_required?
28
+ seatable? || block_type.to_s == 'other'
29
+ end
30
+
31
+ def self.seatable?(block_type)
32
+ block_type.to_s.in?(%w[sleeping_seat seat])
33
+ end
34
+
35
+ def seatable?
36
+ self.class.seatable?(block_type)
37
+ end
38
+
39
+ def assign_layout_from_section
40
+ self.seat_layout = seat_section.seat_layout
41
+ end
42
+ end
43
+ end
@@ -6,14 +6,19 @@ module SpreeCmCommissioner
6
6
  has_one :feature_image, as: :viewable, dependent: :destroy, class_name: 'SpreeCmCommissioner::FeatureImage'
7
7
 
8
8
  has_many :feature_images, as: :viewable, dependent: :destroy, class_name: 'SpreeCmCommissioner::FeatureImage'
9
- has_many :notification_taxons, class_name: 'SpreeCmCommissioner::NotificationTaxon'
9
+ has_many :notification_taxons, dependent: :destroy, class_name: 'SpreeCmCommissioner::NotificationTaxon'
10
10
  has_many :taxons, through: :notification_taxons, class_name: 'Spree::Taxon'
11
11
 
12
+ belongs_to :vendor, class_name: 'Spree::Vendor'
13
+
12
14
  validates :title, presence: true
13
15
  validates :url, presence: true
14
16
 
15
17
  validates :notification_type, presence: true
16
18
 
19
+ self.whitelisted_ransackable_attributes = %w[title excerpt notification_type]
20
+ self.whitelisted_ransackable_associations = %w[notification_taxons taxons]
21
+
17
22
  enum notification_type: { :promotion => 0, :announcement => 1 }
18
23
 
19
24
  def push_notification_image_url
@@ -18,6 +18,8 @@ module SpreeCmCommissioner
18
18
  validates :configurations, presence: true, allow_blank: false
19
19
  validate :validate_data_fill_stage_phase
20
20
 
21
+ scope :with_dynamic_field_options, -> { includes(dynamic_field_options: :dynamic_field) }
22
+
21
23
  def requires_dynamic_field_options?
22
24
  checkbox? || radio? || selection?
23
25
  end
@@ -24,10 +24,17 @@ module SpreeCmCommissioner
24
24
  scope :none_bib, -> { where(bib_prefix: [nil, '']) }
25
25
 
26
26
  belongs_to :line_item, class_name: 'Spree::LineItem'
27
+
27
28
  has_one :variant, class_name: 'Spree::Variant', through: :line_item
29
+
28
30
  belongs_to :user, class_name: 'Spree::User'
29
31
  belongs_to :occupation, class_name: 'Spree::Taxon'
30
32
  belongs_to :nationality, class_name: 'Spree::Taxon'
33
+ belongs_to :block, class_name: 'SpreeCmCommissioner::Block', optional: true
34
+ belongs_to :saved_guest, class_name: 'SpreeCmCommissioner::SavedGuest', optional: true
35
+
36
+ has_one :reserved_block, class_name: 'SpreeCmCommissioner::ReservedBlock'
37
+
31
38
  scope :checked_ins, -> { joins(:check_in) }
32
39
  scope :no_show, -> { left_outer_joins(:check_in).where(cm_check_ins: { id: nil }) }
33
40
 
@@ -48,12 +55,22 @@ module SpreeCmCommissioner
48
55
 
49
56
  before_create :generate_bib, if: -> { line_item.reload && variant.bib_pre_generation_on_create? }
50
57
 
58
+ after_create :preload_order_block_ids, if: -> { block_id.present? }
59
+ after_update :preload_order_block_ids, if: :saved_change_to_block_id?
60
+ after_destroy :preload_order_block_ids
61
+
51
62
  validates :seat_number, uniqueness: { scope: :event_id }, allow_nil: true, if: -> { event_id.present? }
52
63
  validates :bib_index, uniqueness: true, allow_nil: true
53
64
 
65
+ validate :validate_block, if: :should_validate_block?
66
+
54
67
  self.whitelisted_ransackable_associations = %w[id_card event line_item]
55
68
  self.whitelisted_ransackable_attributes = %w[first_name last_name phone_number gender contact occupation_id card_type created_at check_in_status]
56
69
 
70
+ scope :with_dynamic_fields_and_options, lambda {
71
+ includes(guest_dynamic_fields: { dynamic_field: :dynamic_field_options })
72
+ }
73
+
57
74
  def self.csv_importable_columns
58
75
  %i[
59
76
  first_name last_name phone_number age dob gender address other_occupation
@@ -228,6 +245,28 @@ module SpreeCmCommissioner
228
245
  end
229
246
  end
230
247
 
248
+ # validate only if block_id is present AND (new record OR block_id is changing)
249
+ # goal: don't bother re-validating when block_id stays the same
250
+ def should_validate_block?
251
+ block_id.present? && (new_record? || will_save_change_to_block_id?)
252
+ end
253
+
254
+ def validate_block
255
+ return if block_id.blank?
256
+ return if line_item.blank? || line_item.variant.blank?
257
+ return if line_item.variant.variant_blocks.exists?(block_id: block_id)
258
+
259
+ errors.add(:block, "does not exist in variant #{line_item.variant_id}")
260
+ end
261
+
262
+ def preload_order_block_ids
263
+ return if line_item.blank?
264
+ return if line_item.order.blank?
265
+
266
+ block_ids = line_item.order.blocks.pluck(:id)
267
+ line_item.order.update(preload_block_ids: block_ids)
268
+ end
269
+
231
270
  def all_required_dynamic_fields_completed?(stage)
232
271
  missing_dynamic_fields_for_stage(stage).blank?
233
272
  end
@@ -12,6 +12,10 @@ module SpreeCmCommissioner
12
12
 
13
13
  after_commit :sync_guest_data_fill_stage_phase
14
14
 
15
+ scope :with_dynamic_field_and_options, lambda {
16
+ includes(:dynamic_field, :dynamic_field_option)
17
+ }
18
+
15
19
  private
16
20
 
17
21
  def validate_value_format
@@ -0,0 +1,11 @@
1
+ module SpreeCmCommissioner
2
+ class Inventory
3
+ include ActiveModel::Model
4
+
5
+ attr_accessor :variant_id, :inventory_date, :quantity_available, :max_capacity, :product_type
6
+
7
+ validates :variant_id, presence: true
8
+ validates :quantity_available, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, allow_nil: true
9
+ validates :max_capacity, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, allow_nil: true
10
+ end
11
+ end
@@ -0,0 +1,82 @@
1
+ module SpreeCmCommissioner
2
+ class InventoryItem < Base
3
+ include SpreeCmCommissioner::ProductType
4
+
5
+ MAX_DISPLAY_STOCK = 20
6
+
7
+ # Association
8
+ belongs_to :variant, class_name: 'Spree::Variant', optional: false
9
+
10
+ has_many :prices, class_name: 'Spree::Price', inverse_of: :inventory_item, dependent: :destroy
11
+
12
+ has_many :reserved_blocks, -> { reserved_or_on_hold }, class_name: 'SpreeCmCommissioner::ReservedBlock', inverse_of: :inventory_item
13
+
14
+ # Validation
15
+ validates :quantity_available, numericality: { greater_than_or_equal_to: 0 }
16
+ validates :max_capacity, numericality: { greater_than_or_equal_to: 0 } # Originally inventory of each variant.
17
+ validates :inventory_date, presence: true, if: :permanent_stock?
18
+ validates :variant_id, uniqueness: { scope: :inventory_date, message: -> (object, _data) { "The variant is taken on #{object.inventory_date}" } }
19
+
20
+ # Scope
21
+ scope :active, -> { where(inventory_date: nil).or(where('inventory_date >= ?', Time.zone.today)) }
22
+
23
+ before_save -> { self.product_type = variant.product_type }, if: -> { product_type.nil? }
24
+
25
+ def public_quantity_available
26
+ [quantity_available, MAX_DISPLAY_STOCK].min
27
+ end
28
+
29
+ def price_in(currency)
30
+ prices.detect { |price| price.currency == currency } || prices.build(currency: currency)
31
+ end
32
+
33
+ # This method is only used when admin update stock
34
+ def adjust_quantity!(quantity)
35
+ with_lock do
36
+ self.max_capacity = [max_capacity + quantity, 0].max
37
+ self.quantity_available = [quantity_available + quantity, 0].max
38
+ save!
39
+
40
+ # When user has been searched or booked a product, it has cached the quantity in redis,
41
+ # So we need to update redis cache if inventory key has been created in redis
42
+ adjust_quantity_in_redis(quantity)
43
+ end
44
+ end
45
+
46
+ def redis_key
47
+ "inventory:#{id}"
48
+ end
49
+
50
+ def adjust_quantity_in_redis(quantity)
51
+ SpreeCmCommissioner.redis_pool.with do |redis|
52
+ cached_quantity_available = redis.get(redis_key)
53
+ # ignore if redis doesn't exist
54
+ return if cached_quantity_available.nil? # rubocop:disable Lint/NonLocalExitFromIterator
55
+
56
+ script = <<~LUA
57
+ local key = KEYS[1]
58
+ local increment = tonumber(ARGV[1])
59
+ local current = tonumber(redis.call('GET', key) or 0)
60
+ local new_value = current + increment
61
+ if new_value < 0 then
62
+ new_value = 0
63
+ end
64
+ redis.call('SET', key, new_value)
65
+ return new_value
66
+ LUA
67
+
68
+ redis.eval(script, keys: [redis_key], argv: [quantity])
69
+ end
70
+ end
71
+
72
+ def active?
73
+ inventory_date.nil? || inventory_date >= Time.zone.today
74
+ end
75
+
76
+ def redis_expired_in
77
+ expired_in = 31_536_000 # 1 year for normal stock
78
+ expired_in = Time.parse(inventory_date.to_s).end_of_day.to_i - Time.zone.now.to_i if inventory_date.present?
79
+ [expired_in, 0].max
80
+ end
81
+ end
82
+ end
@@ -1,6 +1,6 @@
1
1
  module SpreeCmCommissioner
2
2
  module LineItemDecorator
3
- def self.prepended(base) # rubocop:disable Metrics/MethodLength
3
+ def self.prepended(base) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
4
4
  include_modules(base)
5
5
 
6
6
  base.belongs_to :accepter, class_name: 'Spree::User', optional: true
@@ -10,31 +10,32 @@ module SpreeCmCommissioner
10
10
  base.has_one :google_wallet, class_name: 'SpreeCmCommissioner::GoogleWallet', through: :product
11
11
 
12
12
  base.has_many :option_types, through: :product
13
+
14
+ base.has_many :inventory_items, lambda { |line_item|
15
+ where(inventory_date: nil).or(where(inventory_date: line_item.date_range))
16
+ }, through: :variant
17
+
13
18
  base.has_many :taxons, class_name: 'Spree::Taxon', through: :product
14
19
  base.has_many :guests, class_name: 'SpreeCmCommissioner::Guest', dependent: :destroy
20
+ base.has_many :guests_with_blocks, -> { where.not(block_id: nil) }, class_name: 'SpreeCmCommissioner::Guest'
21
+ base.has_many :reserved_blocks, through: :guests, class_name: 'SpreeCmCommissioner::ReservedBlock'
15
22
  base.has_many :pending_guests, pending_guests_query, class_name: 'SpreeCmCommissioner::Guest', dependent: :destroy
16
23
  base.has_many :product_completion_steps, class_name: 'SpreeCmCommissioner::ProductCompletionStep', through: :product
17
- base.has_many :line_item_seats, class_name: 'SpreeCmCommissioner::LineItemSeat', dependent: :destroy
18
24
 
19
25
  base.before_save :update_vendor_id
20
- base.before_save :update_quantity, if: :transit?
21
-
22
- base.validate :validate_seats_reservation, if: :transit?
23
26
 
24
27
  base.before_create :add_due_date, if: :subscription?
28
+ base.before_save -> { self.product_type = variant.product_type }, if: -> { product_type.nil? }
25
29
 
26
30
  base.validate :ensure_not_exceed_max_quantity_per_order, if: -> { variant&.max_quantity_per_order.present? }
27
31
 
28
32
  base.whitelisted_ransackable_associations |= %w[guests order]
29
33
  base.whitelisted_ransackable_attributes |= %w[number to_date from_date vendor_id]
30
34
 
31
- base.delegate :delivery_required?, :permanent_stock?, :high_demand, :transit?,
35
+ base.delegate :delivery_required?, :high_demand,
32
36
  to: :variant
33
- base.delegate :discontinue_on, :product_type, :accommodation?, :service?, :ecommerce?, :need_confirmation,
34
- to: :product
35
37
 
36
38
  base.accepts_nested_attributes_for :guests, allow_destroy: true
37
- base.accepts_nested_attributes_for :line_item_seats, allow_destroy: true
38
39
 
39
40
  def base.json_api_columns
40
41
  json_api_columns = column_names.reject { |c| c.match(/_id$|id|preferences|(.*)password|(.*)token|(.*)api_key/) }
@@ -42,6 +43,10 @@ module SpreeCmCommissioner
42
43
  json_api_columns << :vendor_id
43
44
  end
44
45
 
46
+ def discontinue_on
47
+ variant.discontinue_on || product.discontinue_on
48
+ end
49
+
45
50
  def base.search_by_qr_data!(data)
46
51
  matches = data.match(/(R\d+)-([A-Za-z0-9_\-]+)-(L\d+)/)&.captures
47
52
 
@@ -60,6 +65,8 @@ module SpreeCmCommissioner
60
65
  base.include SpreeCmCommissioner::LineItemDurationable
61
66
  base.include SpreeCmCommissioner::LineItemsFilterScope
62
67
  base.include SpreeCmCommissioner::LineItemGuestsConcern
68
+ base.include SpreeCmCommissioner::LineItemTransitable
69
+ base.include SpreeCmCommissioner::ProductType
63
70
  base.include SpreeCmCommissioner::ProductDelegation
64
71
  base.include SpreeCmCommissioner::KycBitwise
65
72
  end
@@ -75,24 +82,39 @@ module SpreeCmCommissioner
75
82
  }
76
83
  end
77
84
 
78
- def reservation?
79
- date_present? && !subscription?
80
- end
85
+ # override to calculate price per date for permanent stock
86
+ def update_price_from_modifier(currency, opts)
87
+ self.currency = currency
81
88
 
82
- # date_unit could be number of nights, or days or hours depending on usecases
83
- # For example:
84
- # - accomodation uses number of nights.
85
- # - appointment uses number of hours, etc.
86
- #
87
- # override
88
- def amount
89
- base_price = price * quantity
89
+ # these fields can be null during this process below.
90
+ # it is needed for permanent_stock?, date_range to work.
91
+ self.product_type = variant.product_type
92
+ self.from_date = opts.delete(:from_date)&.to_datetime
93
+ self.to_date = opts.delete(:to_date)&.to_datetime
90
94
 
91
- if permanent_stock? && date_unit.present?
92
- base_price * date_unit
93
- else
94
- base_price
95
- end
95
+ base_price = variant.price_in(currency)
96
+
97
+ self.price = if permanent_stock?
98
+ date_range.map do |date|
99
+ selected_price = variant.price_for_date(date: date, currency: currency) || base_price
100
+ (selected_price.amount || 0) + variant.price_modifier_amount_in(currency, opts.merge(date: date))
101
+ end.sum
102
+ else
103
+ (base_price.amount || 0) + variant.price_modifier_amount_in(currency, opts)
104
+ end
105
+ end
106
+
107
+ # override to calculate price per date for permanent stock
108
+ def update_price
109
+ base_price = variant.price_in(order.currency)
110
+ self.price = if permanent_stock?
111
+ date_range.map do |date|
112
+ selected_price = variant.price_for_date(date: date, currency: order.currency) || base_price
113
+ selected_price.amount || 0
114
+ end.sum
115
+ else
116
+ base_price.amount || 0
117
+ end
96
118
  end
97
119
 
98
120
  def allowed_self_check_in?
@@ -179,8 +201,6 @@ module SpreeCmCommissioner
179
201
 
180
202
  # override
181
203
  def sufficient_stock?
182
- return transit_sufficient_stock? if variant.product.product_type == 'transit'
183
-
184
204
  SpreeCmCommissioner::Stock::LineItemAvailabilityChecker.new(self).can_supply?(quantity)
185
205
  end
186
206
 
@@ -199,18 +219,6 @@ module SpreeCmCommissioner
199
219
  errors.add(:quantity, 'exceeded_max_quantity_per_order') if quantity > variant.max_quantity_per_order
200
220
  end
201
221
 
202
- def transit_sufficient_stock?
203
- return selected_seats_available? if reservation_trip.allow_seat_selection
204
-
205
- seat_quantity_available?(reservation_trip)
206
- end
207
-
208
- def update_quantity
209
- return if line_item_seats.blank?
210
-
211
- self.quantity = line_item_seats.size
212
- end
213
-
214
222
  def update_vendor_id
215
223
  self.vendor_id = variant.vendor_id
216
224
  end
@@ -233,45 +241,6 @@ module SpreeCmCommissioner
233
241
 
234
242
  from_date + variant.month.month + day.days
235
243
  end
236
-
237
- def validate_seats_reservation
238
- if reservation_trip.allow_seat_selection && !selected_seats_available?
239
- errors.add(:base, :some_seats_are_booked, message: 'Some seats are already booked')
240
- elsif !reservation_trip.allow_seat_selection && !seat_quantity_available?(reservation_trip)
241
- errors.add(:quantity, :exceeded_available_quantity, message: 'exceeded available quantity')
242
- end
243
- end
244
-
245
- def selected_seats_available?
246
- selected_seat_ids = line_item_seats.map(&:seat_id)
247
- !selected_seat_ids_occupied?(selected_seat_ids)
248
- end
249
-
250
- def seat_quantity_available?(trip)
251
- booked_quantity = Spree::LineItem.joins(:order)
252
- .where(variant_id: variant_id, date: date, spree_orders: { state: 'complete' })
253
- .where.not(spree_line_items: { id: id })
254
- .sum(:quantity)
255
- remaining_quantity = trip.vehicle.number_of_seats - booked_quantity
256
- remaining_quantity >= quantity
257
- end
258
-
259
- def reservation_trip
260
- return @trip if defined? @trip
261
-
262
- route = Spree::Variant.find_by(id: variant_id).product
263
- @trip = route.trip
264
- end
265
-
266
- def selected_seat_ids_occupied?(selected_seat_ids)
267
- # check to see if there are any selected_ids exist in the line_item_seats and belongs to completed order
268
- SpreeCmCommissioner::LineItemSeat.joins(line_item: :order)
269
- .where(seat_id: selected_seat_ids, date: date, variant_id: variant_id, spree_orders: { state: 'complete',
270
- canceled_at: nil
271
- }
272
- )
273
- .present?
274
- end
275
244
  end
276
245
  end
277
246
 
@@ -1,5 +1,5 @@
1
1
  module SpreeCmCommissioner
2
- class NotificationTaxon < SpreeCmCommissioner::Base
2
+ class NotificationTaxon < Base
3
3
  belongs_to :customer_notification, class_name: 'SpreeCmCommissioner::CustomerNotification'
4
4
  belongs_to :taxon, class_name: 'Spree::Taxon'
5
5
  end