spree_cm_commissioner 1.21.0 → 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 (241) 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/event_blazer_queries_controller.rb +4 -11
  12. data/app/controllers/spree/admin/inventory_items_controller.rb +83 -0
  13. data/app/controllers/spree/admin/stock_managements_controller.rb +63 -1
  14. data/app/controllers/spree/admin/tenants_controller.rb +0 -8
  15. data/app/controllers/spree/api/v2/organizer/invite_guests_controller.rb +1 -1
  16. data/app/controllers/spree/api/v2/storefront/accommodations/variants_controller.rb +42 -0
  17. data/app/controllers/spree/api/v2/storefront/accommodations_controller.rb +14 -31
  18. data/app/controllers/spree/api/v2/storefront/dynamic_field_options_controller.rb +1 -5
  19. data/app/controllers/spree/api/v2/storefront/guests_controller.rb +5 -31
  20. data/app/controllers/spree/api/v2/storefront/queue_cart/line_items_controller.rb +2 -2
  21. data/app/controllers/spree/api/v2/tenant/checkout_controller.rb +0 -2
  22. data/app/controllers/spree_cm_commissioner/admin/variants_controller_decorator.rb +1 -1
  23. data/app/controllers/spree_cm_commissioner/api/v2/storefront/checkout_controller_decorator.rb +0 -11
  24. data/app/controllers/spree_cm_commissioner/well_known_controller.rb +17 -31
  25. data/app/factory/spree_cm_commissioner/invite_guest_claimed_telegram_message_factory.rb +84 -37
  26. data/app/finders/spree_cm_commissioner/accommodations/find.rb +37 -0
  27. data/app/finders/spree_cm_commissioner/accommodations/find_variant.rb +32 -0
  28. data/app/interactors/spree_cm_commissioner/ensure_correct_product_type.rb +40 -0
  29. data/app/interactors/spree_cm_commissioner/inventory_item_syncer.rb +25 -0
  30. data/app/interactors/spree_cm_commissioner/pin_code_sender.rb +1 -0
  31. data/app/interactors/spree_cm_commissioner/sms.rb +1 -1
  32. data/app/interactors/spree_cm_commissioner/stock/inventory_item_resetter.rb +44 -0
  33. data/app/interactors/spree_cm_commissioner/stock/inventory_items_adjuster.rb +13 -0
  34. data/app/interactors/spree_cm_commissioner/stock/inventory_items_generator.rb +15 -0
  35. data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +75 -0
  36. data/app/interactors/spree_cm_commissioner/stock/stock_movement_creator.rb +32 -0
  37. data/app/interactors/spree_cm_commissioner/user_id_token_authenticator.rb +3 -7
  38. data/app/interactors/spree_cm_commissioner/user_id_token_checker.rb +3 -11
  39. data/app/interactors/spree_cm_commissioner/user_identity_checker.rb +6 -12
  40. data/app/interactors/spree_cm_commissioner/user_registration_with_id_token.rb +1 -7
  41. data/app/interactors/spree_cm_commissioner/vattanac_bank_initiator.rb +1 -5
  42. data/app/jobs/spree_cm_commissioner/application_job.rb +20 -0
  43. data/app/jobs/spree_cm_commissioner/application_unique_job.rb +20 -0
  44. data/app/jobs/spree_cm_commissioner/ensure_correct_product_type_job.rb +7 -0
  45. data/app/jobs/spree_cm_commissioner/inventory_item_syncer_job.rb +7 -0
  46. data/app/jobs/spree_cm_commissioner/sms_pin_code_job.rb +1 -1
  47. data/app/jobs/spree_cm_commissioner/stock/inventory_items_adjuster_job.rb +11 -0
  48. data/app/jobs/spree_cm_commissioner/stock/inventory_items_generator_job.rb +11 -0
  49. data/app/jobs/spree_cm_commissioner/stock/permanent_inventory_items_generator_job.rb +9 -0
  50. data/app/models/concerns/spree_cm_commissioner/line_item_durationable.rb +9 -15
  51. data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +1 -13
  52. data/app/models/concerns/spree_cm_commissioner/order_seatable.rb +44 -0
  53. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +39 -0
  54. data/app/models/concerns/spree_cm_commissioner/product_delegation.rb +1 -3
  55. data/app/models/concerns/spree_cm_commissioner/product_type.rb +10 -0
  56. data/app/models/concerns/spree_cm_commissioner/taxon_kind.rb +1 -1
  57. data/app/models/concerns/spree_cm_commissioner/tenant_preference.rb +0 -4
  58. data/app/models/spree_cm_commissioner/block.rb +23 -0
  59. data/app/models/spree_cm_commissioner/dynamic_field.rb +3 -17
  60. data/app/models/spree_cm_commissioner/dynamic_field_option.rb +1 -7
  61. data/app/models/spree_cm_commissioner/guest.rb +19 -1
  62. data/app/models/spree_cm_commissioner/guest_dynamic_field.rb +3 -21
  63. data/app/models/spree_cm_commissioner/inventory.rb +11 -0
  64. data/app/models/spree_cm_commissioner/inventory_item.rb +69 -0
  65. data/app/models/spree_cm_commissioner/invite.rb +6 -4
  66. data/app/models/spree_cm_commissioner/invite_team.rb +1 -3
  67. data/app/models/spree_cm_commissioner/line_item_decorator.rb +46 -78
  68. data/app/models/spree_cm_commissioner/notification_taxon.rb +1 -1
  69. data/app/models/spree_cm_commissioner/option_type_decorator.rb +1 -11
  70. data/app/models/spree_cm_commissioner/order_decorator.rb +30 -1
  71. data/app/models/spree_cm_commissioner/pin_code.rb +2 -3
  72. data/app/models/spree_cm_commissioner/place.rb +1 -3
  73. data/app/models/spree_cm_commissioner/price_decorator.rb +9 -0
  74. data/app/models/spree_cm_commissioner/product_decorator.rb +10 -4
  75. data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +41 -0
  76. data/app/models/spree_cm_commissioner/redis_stock/inventory_updater.rb +126 -0
  77. data/app/models/spree_cm_commissioner/redis_stock/line_items_cached_inventory_items_builder.rb +36 -0
  78. data/app/models/spree_cm_commissioner/redis_stock/variant_cached_inventory_items_builder.rb +25 -0
  79. data/app/models/spree_cm_commissioner/reserved_block.rb +30 -0
  80. data/app/models/spree_cm_commissioner/seat_layout.rb +20 -0
  81. data/app/models/spree_cm_commissioner/seat_section.rb +16 -0
  82. data/app/models/spree_cm_commissioner/seats/blocks_canceler.rb +30 -0
  83. data/app/models/spree_cm_commissioner/seats/blocks_holder.rb +53 -0
  84. data/app/models/spree_cm_commissioner/seats/blocks_reserver.rb +49 -0
  85. data/app/models/spree_cm_commissioner/seats/errors/blocks_are_on_hold_by_other_guest.rb +4 -0
  86. data/app/models/spree_cm_commissioner/seats/errors/blocks_are_reserved_by_other_guest.rb +4 -0
  87. data/app/models/spree_cm_commissioner/seats/errors/blocks_are_reserved_by_same_guest.rb +4 -0
  88. data/app/models/spree_cm_commissioner/seats/errors/unable_to_save_reserved_block_record.rb +4 -0
  89. data/app/models/spree_cm_commissioner/service_calendar.rb +0 -2
  90. data/app/models/spree_cm_commissioner/state_decorator.rb +0 -1
  91. data/app/models/spree_cm_commissioner/stock/availability_checker.rb +26 -25
  92. data/app/models/spree_cm_commissioner/stock/availability_validator_decorator.rb +2 -1
  93. data/app/models/spree_cm_commissioner/stock/line_item_availability_checker.rb +3 -3
  94. data/app/models/spree_cm_commissioner/stock/order_availability_checker.rb +44 -0
  95. data/app/models/spree_cm_commissioner/stock_item_decorator.rb +17 -0
  96. data/app/models/spree_cm_commissioner/taxon_blazer_query.rb +8 -0
  97. data/app/models/spree_cm_commissioner/taxon_decorator.rb +2 -6
  98. data/app/models/spree_cm_commissioner/taxonomy_decorator.rb +0 -6
  99. data/app/models/spree_cm_commissioner/trip.rb +12 -10
  100. data/app/models/spree_cm_commissioner/trip_connection.rb +5 -5
  101. data/app/models/spree_cm_commissioner/trip_stop.rb +2 -3
  102. data/app/models/spree_cm_commissioner/user_identity_provider.rb +4 -26
  103. data/app/models/spree_cm_commissioner/user_taxon.rb +0 -1
  104. data/app/models/spree_cm_commissioner/variant_block.rb +9 -0
  105. data/app/models/spree_cm_commissioner/variant_decorator.rb +36 -47
  106. data/app/models/spree_cm_commissioner/variant_options.rb +0 -23
  107. data/app/models/spree_cm_commissioner/vehicle.rb +9 -14
  108. data/app/models/spree_cm_commissioner/vendor_decorator.rb +7 -10
  109. data/app/models/spree_cm_commissioner/vendor_place.rb +6 -5
  110. data/app/models/spree_cm_commissioner/vendor_stop.rb +1 -2
  111. data/app/overrides/spree/admin/variants/_form/kyc_field.html.erb.deface +2 -2
  112. data/app/queries/spree_cm_commissioner/guest_searcher_query.rb +3 -45
  113. data/app/queries/spree_cm_commissioner/trip_query.rb +23 -26
  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/variant_request_schema.rb +19 -0
  117. data/app/serializers/spree/v2/storefront/accommodation_serializer.rb +2 -0
  118. data/app/serializers/spree/v2/storefront/line_item_serializer_decorator.rb +1 -0
  119. data/app/serializers/spree/v2/tenant/user_serializer.rb +0 -1
  120. data/app/serializers/spree_cm_commissioner/v2/operator/dashboard_crew_event_serializer.rb +1 -4
  121. data/app/serializers/spree_cm_commissioner/v2/storefront/dynamic_field_option_serializer.rb +3 -1
  122. data/app/serializers/spree_cm_commissioner/v2/storefront/dynamic_field_serializer.rb +3 -1
  123. data/app/serializers/spree_cm_commissioner/v2/storefront/guest_dynamic_field_serializer.rb +1 -2
  124. data/app/services/spree_cm_commissioner/organizer/export_guest_csv_service.rb +15 -23
  125. data/app/services/spree_cm_commissioner/user_authenticator.rb +1 -1
  126. data/app/views/blazer/queries/_content.html.erb +0 -8
  127. data/app/views/spree/admin/inventory_items/show.html.erb +72 -0
  128. data/app/views/spree/admin/stock_managements/_variant_stock_items.html.erb +7 -2
  129. data/app/views/spree/admin/stock_managements/calendar.html.erb +56 -0
  130. data/app/views/spree/admin/stock_managements/index.html.erb +55 -6
  131. data/app/views/spree/admin/tenants/_form.html.erb +42 -109
  132. data/config/initializers/paper_trail.rb +1 -0
  133. data/config/initializers/spree_permitted_attributes.rb +5 -2
  134. data/config/locales/en.yml +0 -5
  135. data/config/routes.rb +22 -6
  136. data/db/migrate/20240202080634_update_counter_cache_of_vehicle_type.rb +3 -1
  137. data/db/migrate/20250304293518_create_cm_inventory_items.rb +21 -0
  138. data/db/migrate/20250429094228_add_lock_version_to_cm_inventory_items.rb +5 -0
  139. data/db/migrate/20250502025848_add_index_to_spree_products.rb +5 -0
  140. data/db/migrate/20250502030001_add_product_type_to_spree_variants.rb +5 -0
  141. data/db/migrate/20250502030002_add_product_type_to_spree_line_items.rb +5 -0
  142. data/db/migrate/20250603035256_add_inventory_item_to_spree_prices.rb +7 -0
  143. data/db/migrate/20250619073724_drop_table_cm_line_item_seats.rb +5 -0
  144. data/db/migrate/20250619073812_drop_table_cm_vehicle_seats.rb +5 -0
  145. data/db/migrate/20250619073844_drop_table_cm_vehicle_types.rb +9 -0
  146. data/db/migrate/20250619073957_drop_table_cm_option_value_vehicle_types.rb +5 -0
  147. data/db/migrate/20250619082354_remove_unnecessary_fields_from_cm_places.rb +9 -0
  148. data/db/migrate/20250619082736_remove_route_type_from_spree_products.rb +5 -0
  149. data/db/migrate/20250619083055_remove_unnecessary_fields_from_spree_taxons.rb +5 -0
  150. data/db/migrate/20250620083055_remove_variant_id_from_cm_trips.rb +5 -0
  151. data/db/migrate/20250620090000_update_cm_trip_connections_to_use_cm_trips.rb +6 -0
  152. data/db/migrate/20250620090001_create_cm_seat_layouts.rb +17 -0
  153. data/db/migrate/20250620090002_create_cm_seat_sections.rb +18 -0
  154. data/db/migrate/20250620090003_create_cm_blocks.rb +18 -0
  155. data/db/migrate/20250624091005_create_cm_reserved_blocks.rb +29 -0
  156. data/db/migrate/20250626083642_create_cm_variant_blocks.rb +24 -0
  157. data/db/migrate/20250627023314_add_block_id_to_cm_guests.rb +13 -0
  158. data/docker-compose.yml +1 -1
  159. data/lib/cm_app_logger.rb +11 -4
  160. data/lib/generators/spree_cm_commissioner/install/install_generator.rb +14 -11
  161. data/lib/generators/spree_cm_commissioner/install/templates/app/javascript/{spree_cm_commissioner → spree_dashboard/spree_cm_commissioner}/utilities.js +4 -0
  162. data/lib/spree_cm_commissioner/cached_inventory_item.rb +23 -0
  163. data/lib/spree_cm_commissioner/calendar_event.rb +11 -1
  164. data/lib/spree_cm_commissioner/test_helper/factories/block_factory.rb +9 -0
  165. data/lib/spree_cm_commissioner/test_helper/factories/guest_factory.rb +10 -0
  166. data/lib/spree_cm_commissioner/test_helper/factories/homepage_section_relatable_factory.rb +1 -1
  167. data/lib/spree_cm_commissioner/test_helper/factories/inventory_item_factory.rb +9 -0
  168. data/lib/spree_cm_commissioner/test_helper/factories/line_item_factory.rb +1 -1
  169. data/lib/spree_cm_commissioner/test_helper/factories/option_type_factory.rb +6 -30
  170. data/lib/spree_cm_commissioner/test_helper/factories/order_factory.rb +0 -36
  171. data/lib/spree_cm_commissioner/test_helper/factories/product_factory.rb +18 -34
  172. data/lib/spree_cm_commissioner/test_helper/factories/reserved_block_factory.rb +27 -0
  173. data/lib/spree_cm_commissioner/test_helper/factories/seat_layout_factory.rb +8 -0
  174. data/lib/spree_cm_commissioner/test_helper/factories/seat_section_factory.rb +8 -0
  175. data/lib/spree_cm_commissioner/test_helper/factories/stock_location_factory.rb +2 -2
  176. data/lib/spree_cm_commissioner/test_helper/factories/trip_connection_factory.rb +6 -0
  177. data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +10 -3
  178. data/lib/spree_cm_commissioner/test_helper/factories/user_identity_provider_factory.rb +1 -1
  179. data/lib/spree_cm_commissioner/test_helper/factories/variant_block_factory.rb +7 -0
  180. data/lib/spree_cm_commissioner/test_helper/factories/variant_factory.rb +41 -19
  181. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_factory.rb +1 -1
  182. data/lib/spree_cm_commissioner/test_helper/factories/vendor_factory.rb +1 -1
  183. data/lib/spree_cm_commissioner/version.rb +1 -1
  184. data/lib/spree_cm_commissioner.rb +35 -1
  185. data/lib/tasks/create_default_non_permanent_inventory_items.rake +16 -0
  186. data/lib/tasks/ensure_correct_product_type.rake +7 -0
  187. data/lib/tasks/generate_inventory_items.rake +7 -0
  188. data/spree_cm_commissioner.gemspec +7 -0
  189. metadata +135 -57
  190. data/app/assets/images/cm-hangmeas-checkout_image.svg +0 -63
  191. data/app/assets/images/cm-hangmeas-failed.svg +0 -56
  192. data/app/assets/images/cm-hangmeas-loader.svg +0 -50
  193. data/app/assets/images/cm-hangmeas-success.svg +0 -51
  194. data/app/assets/javascripts/spree_cm_commissioner/tenant_payment_icon_fields.js +0 -65
  195. data/app/assets/stylesheets/spree_cm_commissioner/backend/tenant_payment_icon_fields.scss +0 -60
  196. data/app/controllers/concerns/spree_cm_commissioner/transit/taxon_bitwise.rb +0 -44
  197. data/app/controllers/spree/admin/trip_blazer_queries_controller.rb +0 -35
  198. data/app/controllers/spree/api/v2/organizer/invite_crews_controller.rb +0 -33
  199. data/app/finders/spree_cm_commissioner/line_items/find_by_variant_decorator.rb +0 -20
  200. data/app/interactors/spree_cm_commissioner/apple_app_site_association_fetcher.rb +0 -27
  201. data/app/interactors/spree_cm_commissioner/asset_links_fetcher.rb +0 -27
  202. data/app/interactors/spree_cm_commissioner/crew_invite_link_handler.rb +0 -48
  203. data/app/interactors/spree_cm_commissioner/telegram_chats_auto_finder.rb +0 -144
  204. data/app/models/concerns/spree_cm_commissioner/event_check_in_flowable.rb +0 -30
  205. data/app/models/spree_cm_commissioner/blazer_queryable.rb +0 -8
  206. data/app/models/spree_cm_commissioner/branch.rb +0 -12
  207. data/app/models/spree_cm_commissioner/crew_invite_link.rb +0 -4
  208. data/app/models/spree_cm_commissioner/line_item_seat.rb +0 -10
  209. data/app/models/spree_cm_commissioner/option_value_vehicle_type.rb +0 -8
  210. data/app/models/spree_cm_commissioner/pin_code_telegram.rb +0 -28
  211. data/app/models/spree_cm_commissioner/promotion_category_decorator.rb +0 -11
  212. data/app/models/spree_cm_commissioner/stop.rb +0 -23
  213. data/app/models/spree_cm_commissioner/telegram_chat.rb +0 -6
  214. data/app/models/spree_cm_commissioner/vehicle_seat.rb +0 -11
  215. data/app/models/spree_cm_commissioner/vehicle_type.rb +0 -76
  216. data/app/overrides/spree/admin/taxons/_form/check_in_flows.html.erb.deface +0 -18
  217. data/app/queries/spree_cm_commissioner/trip_search_query.rb +0 -76
  218. data/app/queries/spree_cm_commissioner/variant_availability/non_permanent_stock_query.rb +0 -45
  219. data/app/queries/spree_cm_commissioner/variant_availability/permanent_stock_query.rb +0 -55
  220. data/app/serializers/spree/v2/organizer/invite_crews_serializer.rb +0 -11
  221. data/app/services/spree_cm_commissioner/vehicle_option_value_creator.rb +0 -11
  222. data/db/migrate/20250616084219_add_description_to_cm_vendor_place.rb +0 -5
  223. data/db/migrate/20250630103536_create_cm_telegram_chats.rb +0 -13
  224. data/db/migrate/20250701093203_add_configurations_to_cm_dynamic_field.rb +0 -6
  225. data/db/migrate/20250702091305_add_dynamic_field_option_to_guest_dynamic_field.rb +0 -5
  226. data/db/migrate/20250702091935_add_status_to_dynamic_field_option.rb +0 -5
  227. data/db/migrate/20250707032008_add_vendor_id_to_spree_category.rb +0 -7
  228. data/db/migrate/20250711092937_add_position_to_cm_dynamic_fields.rb +0 -11
  229. data/db/migrate/20250711093045_add_position_to_cm_dynamic_field_options.rb +0 -11
  230. data/db/migrate/20250714121508_rename_cm_taxon_blazer_query_to_blazer_queryables.rb +0 -7
  231. data/db/migrate/20250714124323_make_cm_blazer_queryables_polymorphic.rb +0 -19
  232. data/db/migrate/20250715103333_remove_indexes_from_cm_user_identity_providers.rb +0 -13
  233. data/lib/spree_cm_commissioner/test_helper/factories/branch_factory.rb +0 -12
  234. data/lib/spree_cm_commissioner/test_helper/factories/departure_time_option_type_factory.rb +0 -8
  235. data/lib/spree_cm_commissioner/test_helper/factories/duration_option_type_factory.rb +0 -8
  236. data/lib/spree_cm_commissioner/test_helper/factories/line_item_seat_factory.rb +0 -7
  237. data/lib/spree_cm_commissioner/test_helper/factories/stop_factory.rb +0 -14
  238. data/lib/spree_cm_commissioner/test_helper/factories/transit_place_factory.rb +0 -8
  239. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_option_type_factory.rb +0 -8
  240. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_type_factory.rb +0 -96
  241. data/lib/spree_cm_commissioner/trip_seat_layout_result.rb +0 -11
@@ -38,25 +38,10 @@ module Spree
38
38
  def update
39
39
  spree_authorize! :update, spree_current_order, order_token
40
40
 
41
- begin
42
- if guest_params[:guest_dynamic_fields_attributes]
43
- resource.guest_dynamic_fields.destroy_all
44
-
45
- guest_params[:guest_dynamic_fields_attributes].each do |attr|
46
- field = build_dynamic_field(attr)
47
- field.save!
48
- end
49
- end
50
-
51
- resource.assign_attributes(guest_params.except(:guest_dynamic_fields_attributes))
52
-
53
- if resource.save
54
- render_serialized_payload { serialize_resource(resource) }
55
- else
56
- render_error_payload(resource.errors, 400)
57
- end
58
- rescue ActiveRecord::RecordInvalid => e
59
- render_error_payload(e.record.errors, 400)
41
+ if resource.update(guest_params)
42
+ render_serialized_payload { serialize_resource(resource) }
43
+ else
44
+ render_error_payload(resource, 400)
60
45
  end
61
46
  end
62
47
 
@@ -91,20 +76,9 @@ module Spree
91
76
  :upload_later,
92
77
  :country_code,
93
78
  :contact,
94
- guest_dynamic_fields_attributes: %i[id dynamic_field_id dynamic_field_option_id value _destroy]
79
+ guest_dynamic_fields_attributes: %i[id dynamic_field_id value _destroy]
95
80
  )
96
81
  end
97
-
98
- def build_dynamic_field(attr)
99
- field = resource.guest_dynamic_fields.build(attr.except(:id))
100
-
101
- if field.dynamic_field.requires_dynamic_field_options?
102
- option = field.dynamic_field.dynamic_field_options.find_by(id: field.value)
103
- field.dynamic_field_option = option if option
104
- end
105
-
106
- field
107
- end
108
82
  end
109
83
  end
110
84
  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
 
@@ -27,8 +27,6 @@ module Spree
27
27
  end
28
28
 
29
29
  def complete
30
- return render_serialized_payload { serialized_current_order } if spree_current_order.confirm? || spree_current_order.complete?
31
-
32
30
  spree_authorize! :update, spree_current_order, order_token
33
31
 
34
32
  result = complete_service.call(order: spree_current_order)
@@ -37,7 +37,7 @@ module SpreeCmCommissioner
37
37
  bit_fields = SpreeCmCommissioner::KycBitwise::BIT_FIELDS.keys
38
38
 
39
39
  if permitted_resource_params[:use_product_kyc] == '1'
40
- permitted_resource_params[:kyc] = nil
40
+ permitted_resource_params[:kyc] = @product.kyc
41
41
  else
42
42
  @kyc_result = calculate_kyc_value(params[:variant])
43
43
  permitted_resource_params[:kyc] = @kyc_result
@@ -6,17 +6,6 @@ module SpreeCmCommissioner
6
6
  def self.prepended(base)
7
7
  base.include SpreeCmCommissioner::OrderConcern
8
8
  end
9
-
10
- # Override Spree's default behavior:
11
- # By default, Spree raises an error if `complete` is called on a completed or confirmed order.
12
- # We want to allow this call to succeed, so the API can be used both to:
13
- # - complete the order (if it's not yet complete), or
14
- # - return the latest state of a confirmed or completed order without raising a forbidden error.
15
- def complete
16
- return render_serialized_payload { serialized_current_order } if spree_current_order.confirm? || spree_current_order.complete?
17
-
18
- super
19
- end
20
9
  end
21
10
  end
22
11
  end
@@ -3,42 +3,18 @@ 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_platform_link(:android)
8
- end
9
-
10
- def apple_app_site_association
11
- render_platform_link(:ios)
12
- end
13
-
14
- private
15
-
16
- def render_platform_link(platform)
17
- fetcher_class = platform_link_finder(platform)
18
-
19
- result = fetcher_class.call(
20
- store: @store,
21
- tenant: @tenant,
22
- refreshed: params[:_refreshed].present?
23
- )
24
-
25
- if result.success? && result.contents.present?
26
- render json: result.contents
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)
27
12
  else
28
13
  render_not_found
29
14
  end
30
15
  end
31
16
 
32
- def platform_link_finder(platform)
33
- case platform
34
- when :android
35
- SpreeCmCommissioner::AssetLinksFetcher
36
- when :ios
37
- SpreeCmCommissioner::AppleAppSiteAssociationFetcher
38
- else
39
- render_not_found
40
- end
41
- end
17
+ private
42
18
 
43
19
  def identify_request_source
44
20
  host = params[:original_host].presence || request.host.downcase
@@ -49,6 +25,16 @@ module SpreeCmCommissioner
49
25
  @tenant = result.tenant
50
26
  end
51
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
+
52
38
  def render_not_found
53
39
  render json: { error: 'Not found' }, status: :not_found
54
40
  end
@@ -1,17 +1,26 @@
1
- # <p>📣 --- <b>[NEW GUEST CLAIMED INVITATION]</b> ---</p>
2
-
3
- # <p>✨ <b>Ticket Successfully Claimed!</b> ✨</p>
4
-
5
- # <p><b>🙍 Claimed By</b><br>
6
- # Name: Orng SreyMoch<br>
7
- # Tel: <code>0966827830</code></p>
8
-
9
- # <p><b>Run Ticket + 2 way bus ticket + Tent</b><br>
10
- # <i>👉 Distance: 5km, Tent: tent (1 people), and T-shirt: S</i><br>
11
- # <i>🗓️ May 30, 2025 -> Jun 30, 2025</i></p>
12
-
13
- # <p><b>📑 Order Number:</b><br>
14
- # <code>R172627517</code></p>
1
+ # 🎟️ --- [TICKET CLAIMED] ---
2
+ #
3
+ # Order Number:
4
+ # <code>R621016753</code>
5
+ #
6
+ # <b>Ticket Successfully Claimed!</b>
7
+ #
8
+ # [ x1 ]
9
+ # <b>5km Running Ticket</b>
10
+ # <i>👉 Distance: 5km, T-shirt: M</i>
11
+ # <i>🗓️ Nov 15, 2023</i>
12
+ # <i>🏪 Temple Run with Sai</i>
13
+ #
14
+ # [ x2 ]
15
+ # <b>5km Running Ticket</b>
16
+ # <i>👉 Distance: 5km, T-shirt: S</i>
17
+ # <i>🗓️ Nov 15, 2023 -> Nov 17, 2023</i>
18
+ # <i>🏪 Temple Run with Sai</i>
19
+ #
20
+ # <b>🙍 Claimed By</b>
21
+ # Name: Vaneath Awesome
22
+ # Tel: <code>+85570760548</code>
23
+ # Email: <code>admin_dev@cm.com</code>
15
24
 
16
25
  module SpreeCmCommissioner
17
26
  class InviteGuestClaimedTelegramMessageFactory < TelegramMessageFactory
@@ -35,39 +44,77 @@ module SpreeCmCommissioner
35
44
  # override
36
45
  def body
37
46
  text = []
38
- text << '<p>📣 --- <b>[NEW GUEST CLAIMED INVITATION]</b> ---</p>'
39
- text << '<p>✨ <b>Ticket Successfully Claimed!</b> ✨</p>'
40
- text << '<p><b>🙍 Claimed By</b><br>'
41
- text << "Name: #{guest.first_name} #{guest.last_name}<br>"
42
- text << "Tel: <code>#{guest.phone_number}</code></p>"
43
-
44
- selected_line_items.each do |item|
45
- text << line_item_block(item)
46
- end
47
-
48
- text << "<p><b>📑 Order Number:</b><br><code>#{order.number}</code></p>"
49
-
50
- text.join("\n")
47
+ text << "Order Number:\n<code>#{order.number}</code>\n"
48
+ text << "\n✅ <b>Ticket Successfully Claimed!</b>\n"
49
+ text << selected_line_items.map { |item| line_item_content(item) }.compact.join("\n\n")
50
+ text.compact.join("\n")
51
51
  end
52
52
 
53
- def line_item_block(item)
54
- parts = []
55
- parts << "<p><b>#{item.product.name}</b><br>"
53
+ def line_item_content(line_item)
54
+ text = []
56
55
 
57
- details = []
58
- details << "👉 #{item.options_text}" if item.options_text.present?
59
- details << pretty_date_for(item) if item.date_present?
56
+ text << bold(line_item.product.name.to_s)
57
+ text << "Quantity: #{line_item.quantity}"
58
+ text << italic("👉 #{line_item.options_text}") if line_item.options_text.present?
59
+ text << italic(pretty_date_for(line_item)) if pretty_date_for(line_item).present?
60
+ text << italic("🏪 #{line_item.vendor.name}") if line_item.vendor&.name.present? && vendor.blank?
60
61
 
61
- parts << details.map { |d| "<i>#{d}</i><br>" }.join
62
- parts << '</p>'
63
- parts.join("\n")
62
+ text.compact.join("\n")
64
63
  end
65
64
 
66
65
  def pretty_date_for(line_item)
66
+ return nil unless line_item.date_present?
67
+
67
68
  from_date = pretty_date(line_item.from_date)
68
69
  to_date = pretty_date(line_item.to_date)
69
70
 
70
- from_date == to_date ? "🗓️ #{from_date}" : "🗓️ #{from_date} -> #{to_date}"
71
+ if from_date == to_date
72
+ "🗓️ #{from_date}"
73
+ else
74
+ "🗓️ #{from_date} -> #{to_date}"
75
+ end
76
+ end
77
+
78
+ # override
79
+ def footer
80
+ text = []
81
+
82
+ text << bold('🙍 Claimed By')
83
+ text << "Name: #{guest.first_name} #{guest.last_name}" if guest.first_name.present? || guest.last_name.present?
84
+ text << "Tel: #{inline_code(guest.intel_phone_number)}" if guest.intel_phone_number.present?
85
+
86
+ if show_details_link && order.guests.any?
87
+ text << ''
88
+ text << 'View Tickets:'
89
+ text += generate_guests_links(order.guests)
90
+ end
91
+
92
+ text.compact.join("\n")
93
+ end
94
+
95
+ # Result:
96
+ # | No. A24 | No. A24 |
97
+ # | No. A24 | No. A24 |
98
+ # | No. A24 | No. A24 |
99
+ # | No. A24 | No. A24 |
100
+ # | No. A24 | No. A24 |
101
+ # | No. A24 |
102
+ def generate_guests_links(guests, guests_per_row = 2)
103
+ rows = (guests.size.to_f / guests_per_row).ceil
104
+ formatted_rows = []
105
+
106
+ rows.times do |i|
107
+ row_guests = guests.slice(i * guests_per_row, guests_per_row)
108
+ formatted_row = row_guests.map do |guest|
109
+ button_label = guest.seat_number.present? ? "No. #{guest.seat_number}" : "No. #{guest.formatted_bib_number || 'N/A'}"
110
+ link = Rails.application.routes.url_helpers.guest_cards_url(guest.token)
111
+ "| <a href='#{link}'>#{button_label}</a>"
112
+ end.join(' ')
113
+
114
+ formatted_rows << ("#{formatted_row} |")
115
+ end
116
+
117
+ formatted_rows
71
118
  end
72
119
  end
73
120
  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
@@ -18,6 +18,7 @@ module SpreeCmCommissioner
18
18
 
19
19
  def send_sms
20
20
  options = {
21
+ from: sender_name(context.tenant),
21
22
  to: context.pin_code.contact,
22
23
  body: I18n.t('pincode_sender.sms.body', code: context.pin_code.code, readable_type: context.pin_code.readable_type)
23
24
  }
@@ -42,7 +42,7 @@ module SpreeCmCommissioner
42
42
  end
43
43
 
44
44
  def sms_options
45
- opts = { to: context.to, body: context.body }
45
+ opts = { from: context.from, to: context.to, body: context.body }
46
46
  opts[:to] = sanitize(opts[:to]) if opts[:to].present?
47
47
  opts[:from] = from_number
48
48
  opts
@@ -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