spree_cm_commissioner 2.13.4 → 2.13.7

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 (404) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +5 -0
  3. data/Gemfile.lock +1 -1
  4. data/app/controllers/spree/admin/base_controller_decorator.rb +14 -0
  5. data/app/controllers/spree/admin/customer_notifications_controller.rb +16 -1
  6. data/app/controllers/spree/admin/inventory_items_controller.rb +11 -0
  7. data/app/controllers/spree/admin/payment_fee_rules_controller.rb +76 -0
  8. data/app/controllers/spree/admin/prepaid_transactions_controller.rb +69 -0
  9. data/app/controllers/spree/admin/private_sales_controller.rb +6 -0
  10. data/app/controllers/spree/admin/product_commissions_controller.rb +2 -1
  11. data/app/controllers/spree/admin/reschedule_policies_controller.rb +148 -0
  12. data/app/controllers/spree/admin/reschedule_requests_controller.rb +82 -0
  13. data/app/controllers/spree/admin/service_fee_rules_controller.rb +1 -0
  14. data/app/controllers/spree/admin/store_credit_admin_controller.rb +122 -0
  15. data/app/controllers/spree/admin/ticket_transfer_rules_controller.rb +7 -0
  16. data/app/controllers/spree/api/v2/platform/store_credits_controller_decorator.rb +35 -0
  17. data/app/controllers/spree/api/v2/storefront/account/prepaid_balances_controller.rb +52 -0
  18. data/app/controllers/spree/api/v2/storefront/account/prepaid_transactions_controller.rb +56 -0
  19. data/app/controllers/spree/api/v2/storefront/cart_payment_fees_controller.rb +48 -0
  20. data/app/controllers/spree/api/v2/storefront/draft_orders_controller.rb +37 -0
  21. data/app/controllers/spree/api/v2/storefront/prepaid_config_controller.rb +22 -0
  22. data/app/controllers/spree/api/v2/storefront/reschedule_requests_controller.rb +156 -0
  23. data/app/controllers/spree/api/v2/storefront/seat_layouts_controller.rb +3 -1
  24. data/app/controllers/spree/api/v2/storefront/transit/item_availabilities_controller.rb +66 -0
  25. data/app/controllers/spree/api/v2/tenant/account/prepaid_balances_controller.rb +51 -0
  26. data/app/controllers/spree/api/v2/tenant/account/prepaid_transactions_controller.rb +53 -0
  27. data/app/controllers/spree/api/v2/tenant/base_controller.rb +20 -0
  28. data/app/controllers/spree/api/v2/tenant/cart_payment_fees_controller.rb +43 -0
  29. data/app/controllers/spree/api/v2/tenant/draft_orders_controller.rb +42 -0
  30. data/app/controllers/spree/api/v2/tenant/prepaid_config_controller.rb +29 -0
  31. data/app/controllers/spree_cm_commissioner/admin/store_credits_controller_decorator.rb +217 -0
  32. data/app/errors/spree_cm_commissioner/reschedule_policies/invalid_policyable_error.rb +8 -0
  33. data/app/errors/spree_cm_commissioner/reschedule_policies/not_reschedulable_error.rb +11 -0
  34. data/app/errors/spree_cm_commissioner/reschedule_requests/cancel_not_allowed_error.rb +6 -0
  35. data/app/errors/spree_cm_commissioner/reschedule_requests/complete_cash_payment_error.rb +6 -0
  36. data/app/errors/spree_cm_commissioner/reschedule_requests/eligibility_denied_error.rb +7 -0
  37. data/app/factory/spree_cm_commissioner/order_telegram_message_factory.rb +6 -2
  38. data/app/factory/spree_cm_commissioner/reschedule_telegram_message_factory.rb +141 -0
  39. data/app/finders/spree_cm_commissioner/inventory_items/recently_changed_finder.rb +1 -1
  40. data/app/helpers/spree/admin/base_helper_decorator.rb +12 -0
  41. data/app/helpers/spree/admin/reschedule_policies_helper.rb +35 -0
  42. data/app/helpers/spree/admin/reschedule_requests_helper.rb +79 -0
  43. data/app/helpers/spree_cm_commissioner/admin/fee_rules_helper.rb +23 -0
  44. data/app/interactors/spree_cm_commissioner/create_ticket.rb +1 -0
  45. data/app/jobs/spree_cm_commissioner/audit_event_job.rb +29 -0
  46. data/app/jobs/spree_cm_commissioner/prepaid_reconciliation_cron_job.rb +9 -0
  47. data/app/jobs/spree_cm_commissioner/prepaid_reverse_top_up_job.rb +16 -0
  48. data/app/jobs/spree_cm_commissioner/prepaid_setup_types_job.rb +24 -0
  49. data/app/jobs/spree_cm_commissioner/prepaid_top_up_credit_job.rb +31 -0
  50. data/app/jobs/spree_cm_commissioner/reschedule_requests/approval_reminder_job.rb +17 -0
  51. data/app/jobs/spree_cm_commissioner/reschedule_requests/execute_job.rb +20 -0
  52. data/app/jobs/spree_cm_commissioner/reschedule_requests/expire_overdue_requests_job.rb +12 -0
  53. data/app/jobs/spree_cm_commissioner/reschedule_requests/expire_request_job.rb +15 -0
  54. data/app/jobs/spree_cm_commissioner/reschedule_requests/payment_reminder_job.rb +17 -0
  55. data/app/models/concerns/spree_cm_commissioner/audit_wrapper.rb +139 -0
  56. data/app/models/concerns/spree_cm_commissioner/integrations/inventory_sync_cachable.rb +9 -0
  57. data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +2 -1
  58. data/app/models/concerns/spree_cm_commissioner/order_prepaid_payment.rb +15 -0
  59. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +58 -8
  60. data/app/models/concerns/spree_cm_commissioner/percent_fee_calculable.rb +36 -0
  61. data/app/models/concerns/spree_cm_commissioner/product_type.rb +6 -2
  62. data/app/models/concerns/spree_cm_commissioner/reschedule_request_state_machine.rb +332 -0
  63. data/app/models/concerns/spree_cm_commissioner/scoped_fee_rule.rb +129 -0
  64. data/app/models/concerns/spree_cm_commissioner/service_type.rb +1 -1
  65. data/app/models/concerns/spree_cm_commissioner/store_preference.rb +1 -0
  66. data/app/models/concerns/spree_cm_commissioner/tenant_preference.rb +6 -0
  67. data/app/models/concerns/spree_cm_commissioner/variant_metadata.rb +24 -0
  68. data/app/models/concerns/spree_cm_commissioner/vehicle_kind.rb +1 -1
  69. data/app/models/spree/core/controller_helpers/currency_decorator.rb +70 -0
  70. data/app/models/spree_cm_commissioner/adjustment_decorator.rb +1 -0
  71. data/app/models/spree_cm_commissioner/audit_event.rb +11 -0
  72. data/app/models/spree_cm_commissioner/calculators/payment_fee_flat_amount.rb +21 -0
  73. data/app/models/spree_cm_commissioner/calculators/payment_fee_percent.rb +17 -0
  74. data/app/models/spree_cm_commissioner/calculators/service_fee_flat_amount.rb +4 -0
  75. data/app/models/spree_cm_commissioner/calculators/service_fee_percent.rb +5 -2
  76. data/app/models/spree_cm_commissioner/configuration.rb +17 -9
  77. data/app/models/spree_cm_commissioner/currency_rate.rb +15 -0
  78. data/app/models/spree_cm_commissioner/current.rb +8 -0
  79. data/app/models/spree_cm_commissioner/customer_notification.rb +4 -1
  80. data/app/models/spree_cm_commissioner/exports/import_order_guests_csv.rb +153 -0
  81. data/app/models/spree_cm_commissioner/guest.rb +21 -0
  82. data/app/models/spree_cm_commissioner/integration.rb +31 -0
  83. data/app/models/spree_cm_commissioner/integrations/book_me_bus_v1.rb +64 -0
  84. data/app/models/spree_cm_commissioner/integrations/larryta.rb +81 -0
  85. data/app/models/spree_cm_commissioner/integrations/stadium_x_v1.rb +4 -10
  86. data/app/models/spree_cm_commissioner/integrations/vireak_buntham.rb +82 -0
  87. data/app/models/spree_cm_commissioner/inventory_item.rb +22 -5
  88. data/app/models/spree_cm_commissioner/line_item_decorator.rb +6 -2
  89. data/app/models/spree_cm_commissioner/notification.rb +8 -0
  90. data/app/models/spree_cm_commissioner/order_decorator.rb +31 -3
  91. data/app/models/spree_cm_commissioner/order_updater_decorator.rb +6 -1
  92. data/app/models/spree_cm_commissioner/payment_decorator.rb +31 -0
  93. data/app/models/spree_cm_commissioner/payment_fee_rule.rb +97 -0
  94. data/app/models/spree_cm_commissioner/payment_method_decorator.rb +5 -0
  95. data/app/models/spree_cm_commissioner/payment_method_store_credit_decorator.rb +81 -0
  96. data/app/models/spree_cm_commissioner/place.rb +2 -0
  97. data/app/models/spree_cm_commissioner/prepaid_config.rb +59 -0
  98. data/app/models/spree_cm_commissioner/prepaid_transaction.rb +102 -0
  99. data/app/models/spree_cm_commissioner/product_decorator.rb +6 -0
  100. data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +4 -2
  101. data/app/models/spree_cm_commissioner/reschedule_policy.rb +94 -0
  102. data/app/models/spree_cm_commissioner/reschedule_policy_assignment.rb +19 -0
  103. data/app/models/spree_cm_commissioner/reschedule_request.rb +227 -0
  104. data/app/models/spree_cm_commissioner/reschedule_requests/guest_selection.rb +80 -0
  105. data/app/models/spree_cm_commissioner/reschedule_snapshot.rb +65 -0
  106. data/app/models/spree_cm_commissioner/route.rb +1 -0
  107. data/app/models/spree_cm_commissioner/seat_layout.rb +8 -0
  108. data/app/models/spree_cm_commissioner/service_calendar.rb +4 -0
  109. data/app/models/spree_cm_commissioner/service_fee_rule.rb +17 -114
  110. data/app/models/spree_cm_commissioner/stock/availability_checker.rb +1 -1
  111. data/app/models/spree_cm_commissioner/stock/order_availability_checker.rb +1 -1
  112. data/app/models/spree_cm_commissioner/stock_item_decorator.rb +4 -0
  113. data/app/models/spree_cm_commissioner/store_credit_category_decorator.rb +23 -0
  114. data/app/models/spree_cm_commissioner/store_credit_decorator.rb +142 -0
  115. data/app/models/spree_cm_commissioner/store_credit_event_decorator.rb +94 -0
  116. data/app/models/spree_cm_commissioner/store_credit_type_decorator.rb +43 -0
  117. data/app/models/spree_cm_commissioner/store_decorator.rb +8 -0
  118. data/app/models/spree_cm_commissioner/tenant.rb +37 -0
  119. data/app/models/spree_cm_commissioner/trip.rb +2 -1
  120. data/app/models/spree_cm_commissioner/trip_stop.rb +2 -0
  121. data/app/models/spree_cm_commissioner/user_decorator.rb +79 -0
  122. data/app/models/spree_cm_commissioner/variant_decorator.rb +9 -1
  123. data/app/models/spree_cm_commissioner/vehicle.rb +3 -0
  124. data/app/models/spree_cm_commissioner/vehicle_type.rb +1 -0
  125. data/app/models/spree_cm_commissioner/vendor_decorator.rb +53 -0
  126. data/app/models/spree_cm_commissioner/vendor_place.rb +2 -0
  127. data/app/notifications/spree_cm_commissioner/reschedule_approved_notification.rb +7 -0
  128. data/app/notifications/spree_cm_commissioner/reschedule_cancelled_notification.rb +7 -0
  129. data/app/notifications/spree_cm_commissioner/reschedule_completed_notification.rb +7 -0
  130. data/app/notifications/spree_cm_commissioner/reschedule_expired_notification.rb +7 -0
  131. data/app/notifications/spree_cm_commissioner/reschedule_failed_notification.rb +7 -0
  132. data/app/notifications/spree_cm_commissioner/reschedule_notification_base.rb +41 -0
  133. data/app/notifications/spree_cm_commissioner/reschedule_payment_reminder_notification.rb +15 -0
  134. data/app/notifications/spree_cm_commissioner/reschedule_rejected_notification.rb +7 -0
  135. data/app/notifications/spree_cm_commissioner/reschedule_requested_notification.rb +7 -0
  136. data/app/overrides/spree/admin/products/_form/offline.html.erb.deface +9 -0
  137. data/app/overrides/spree/admin/shared/_main_menu/sidebar_reschedule.html.erb.deface +9 -0
  138. data/app/overrides/spree/admin/shared/_main_menu/sidebar_store_credit.html.erb.deface +8 -0
  139. data/app/overrides/spree/admin/shared/_order_tabs/reschedule_requests.html.erb.deface +10 -0
  140. data/app/overrides/spree/admin/shared/_product_tabs/stock_managements.html.erb.deface +1 -1
  141. data/app/overrides/spree/admin/shared/sub_menu/_configuration/menu_items.html.erb.deface +5 -0
  142. data/app/presenters/spree_cm_commissioner/prepaid_balance_presenter.rb +16 -0
  143. data/app/queries/spree_cm_commissioner/store_credit_overview_query.rb +75 -0
  144. data/app/queries/spree_cm_commissioner/user_store_credit_summary_query.rb +53 -0
  145. data/app/serializables/spree_cm_commissioner/item_availability_sync.rb +10 -0
  146. data/app/serializers/spree/v2/storefront/prepaid_balance_serializer.rb +15 -0
  147. data/app/serializers/spree/v2/storefront/prepaid_config_serializer.rb +11 -0
  148. data/app/serializers/spree/v2/storefront/prepaid_transaction_serializer.rb +17 -0
  149. data/app/serializers/spree/v2/storefront/user_serializer_decorator.rb +6 -0
  150. data/app/serializers/spree/v2/tenant/cart_serializer.rb +2 -1
  151. data/app/serializers/spree/v2/tenant/payment_method_serializer.rb +3 -0
  152. data/app/serializers/spree/v2/tenant/prepaid_balance_serializer.rb +15 -0
  153. data/app/serializers/spree/v2/tenant/prepaid_config_serializer.rb +16 -0
  154. data/app/serializers/spree/v2/tenant/prepaid_transaction_serializer.rb +16 -0
  155. data/app/serializers/spree_cm_commissioner/api/v2/platform/homepage_section_relatable_options_serializer.rb +5 -0
  156. data/app/serializers/spree_cm_commissioner/v2/storefront/cart_serializer_decorator.rb +3 -0
  157. data/app/serializers/spree_cm_commissioner/v2/storefront/inventory_item_serializer.rb +1 -0
  158. data/app/serializers/spree_cm_commissioner/v2/storefront/item_availability_sync_serializer.rb +10 -0
  159. data/app/serializers/spree_cm_commissioner/v2/storefront/payment_fee_preview_serializer.rb +19 -0
  160. data/app/serializers/spree_cm_commissioner/v2/storefront/payment_method_serializer_decorator.rb +16 -0
  161. data/app/serializers/spree_cm_commissioner/v2/storefront/reschedule_request_serializer.rb +49 -0
  162. data/app/serializers/spree_cm_commissioner/v2/storefront/reschedule_snapshot_serializer.rb +24 -0
  163. data/app/serializers/spree_cm_commissioner/v2/storefront/seat_layout_serializer.rb +8 -0
  164. data/app/services/spree_cm_commissioner/audit_logger.rb +59 -0
  165. data/app/services/spree_cm_commissioner/checkout/add_store_credit_decorator.rb +17 -0
  166. data/app/services/spree_cm_commissioner/checkout/add_store_credit_payments.rb +96 -0
  167. data/app/services/spree_cm_commissioner/checkout/remove_store_credit_decorator.rb +17 -0
  168. data/app/services/spree_cm_commissioner/currency_converter/create.rb +27 -0
  169. data/app/services/spree_cm_commissioner/currency_rates/manage.rb +99 -0
  170. data/app/services/spree_cm_commissioner/customer_notifications/clone.rb +39 -0
  171. data/app/services/spree_cm_commissioner/google_maps_geocoder.rb +41 -0
  172. data/app/services/spree_cm_commissioner/imports/orders/create.rb +24 -11
  173. data/app/services/spree_cm_commissioner/integrations/base/sync_manager.rb +1 -2
  174. data/app/services/spree_cm_commissioner/integrations/base/sync_result.rb +11 -0
  175. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/external_client/client.rb +230 -0
  176. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/external_client/connection.rb +147 -0
  177. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/inventory/external_inventory_item_status.rb +52 -0
  178. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/inventory/external_inventory_items.rb +91 -0
  179. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/inventory/unstock_inventory.rb +188 -0
  180. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_inventory.rb +79 -0
  181. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_item_availability.rb +354 -0
  182. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_locations.rb +103 -0
  183. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_nationality_pricing.rb +93 -0
  184. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_product.rb +70 -0
  185. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_route_stops.rb +121 -0
  186. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_routes.rb +128 -0
  187. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_seat_layout.rb +412 -0
  188. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_service_calendars.rb +59 -0
  189. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_trip_stop.rb +224 -0
  190. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_trips.rb +769 -0
  191. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_variant.rb +69 -0
  192. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_vehicle_types.rb +92 -0
  193. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/time_parser.rb +19 -0
  194. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/base.rb +40 -0
  195. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/inventory_item_status.rb +148 -0
  196. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/location.rb +55 -0
  197. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/reservation_cart.rb +85 -0
  198. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/seat.rb +128 -0
  199. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/seat_layout.rb +386 -0
  200. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/stop.rb +84 -0
  201. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/trip.rb +237 -0
  202. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/wallet_payment.rb +48 -0
  203. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/sync_manager.rb +22 -0
  204. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/sync_strategies/full_sync_strategy.rb +116 -0
  205. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/sync_strategies/incremental_sync_strategy.rb +63 -0
  206. data/app/services/spree_cm_commissioner/integrations/larryta/external_client/client.rb +314 -0
  207. data/app/services/spree_cm_commissioner/integrations/larryta/external_client/connection.rb +74 -0
  208. data/app/services/spree_cm_commissioner/integrations/larryta/inventory/external_inventory_item_status.rb +53 -0
  209. data/app/services/spree_cm_commissioner/integrations/larryta/inventory/external_inventory_items.rb +106 -0
  210. data/app/services/spree_cm_commissioner/integrations/larryta/inventory/hold_seats.rb +143 -0
  211. data/app/services/spree_cm_commissioner/integrations/larryta/inventory/release_seats.rb +90 -0
  212. data/app/services/spree_cm_commissioner/integrations/larryta/inventory/unstock_inventory.rb +143 -0
  213. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_item_availability.rb +325 -0
  214. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_product.rb +72 -0
  215. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_routes.rb +142 -0
  216. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_seat_layout.rb +255 -0
  217. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_stock_item.rb +55 -0
  218. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_trip_stop.rb +171 -0
  219. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_trips.rb +397 -0
  220. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_variant.rb +63 -0
  221. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_vehicle_type.rb +79 -0
  222. data/app/services/spree_cm_commissioner/integrations/larryta/resources/base.rb +39 -0
  223. data/app/services/spree_cm_commissioner/integrations/larryta/resources/booking.rb +113 -0
  224. data/app/services/spree_cm_commissioner/integrations/larryta/resources/booking_session.rb +43 -0
  225. data/app/services/spree_cm_commissioner/integrations/larryta/resources/booking_transaction.rb +236 -0
  226. data/app/services/spree_cm_commissioner/integrations/larryta/resources/direction.rb +71 -0
  227. data/app/services/spree_cm_commissioner/integrations/larryta/resources/passenger.rb +48 -0
  228. data/app/services/spree_cm_commissioner/integrations/larryta/resources/schedule.rb +150 -0
  229. data/app/services/spree_cm_commissioner/integrations/larryta/resources/seat.rb +31 -0
  230. data/app/services/spree_cm_commissioner/integrations/larryta/resources/seat_availability.rb +49 -0
  231. data/app/services/spree_cm_commissioner/integrations/larryta/resources/seat_layout.rb +211 -0
  232. data/app/services/spree_cm_commissioner/integrations/larryta/sync_manager.rb +17 -0
  233. data/app/services/spree_cm_commissioner/integrations/larryta/sync_strategies/full_sync_strategy.rb +79 -0
  234. data/app/services/spree_cm_commissioner/integrations/larryta/sync_strategies/incremental_sync_strategy.rb +58 -0
  235. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/external_client/client.rb +14 -15
  236. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/polling/sync_matches.rb +7 -4
  237. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/polling/sync_zones.rb +6 -3
  238. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_manager.rb +0 -3
  239. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_strategies/full_sync_strategy.rb +1 -3
  240. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_strategies/incremental_sync_strategy.rb +1 -3
  241. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_strategies/webhook_sync_strategy.rb +1 -2
  242. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/external_client/client.rb +170 -0
  243. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/external_client/connection.rb +59 -0
  244. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/inventory/external_inventory_item_status.rb +48 -0
  245. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/inventory/external_inventory_items.rb +92 -0
  246. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/inventory/unstock_inventory.rb +230 -0
  247. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_item_availability.rb +270 -0
  248. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_locations.rb +96 -0
  249. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_nationality_pricing.rb +76 -0
  250. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_product.rb +69 -0
  251. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_routes.rb +110 -0
  252. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_seat_layout.rb +207 -0
  253. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_stock_item.rb +43 -0
  254. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_trip_stop.rb +192 -0
  255. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_trips.rb +377 -0
  256. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_variant.rb +56 -0
  257. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_vehicle_type.rb +51 -0
  258. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/base.rb +32 -0
  259. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/booking.rb +48 -0
  260. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/booking_transaction.rb +46 -0
  261. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/cell.rb +53 -0
  262. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/confirm_booking_request.rb +43 -0
  263. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/location.rb +37 -0
  264. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/schedule.rb +138 -0
  265. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/seat.rb +48 -0
  266. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/seat_layout.rb +79 -0
  267. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/sync_manager.rb +17 -0
  268. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/sync_strategies/full_sync_strategy.rb +87 -0
  269. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/sync_strategies/incremental_sync_strategy.rb +53 -0
  270. data/app/services/spree_cm_commissioner/inventory_holds/release.rb +13 -0
  271. data/app/services/spree_cm_commissioner/inventory_items/generate_permanent_item.rb +48 -0
  272. data/app/services/spree_cm_commissioner/order_holds/hold.rb +17 -0
  273. data/app/services/spree_cm_commissioner/order_holds/release.rb +31 -0
  274. data/app/services/spree_cm_commissioner/payment_fees/apply.rb +30 -0
  275. data/app/services/spree_cm_commissioner/payment_fees/clear.rb +15 -0
  276. data/app/services/spree_cm_commissioner/payment_fees/preview.rb +70 -0
  277. data/app/services/spree_cm_commissioner/payments/find_or_create_decorator.rb +18 -0
  278. data/app/services/spree_cm_commissioner/place_resolver.rb +95 -0
  279. data/app/services/spree_cm_commissioner/prepaid/admin_credit.rb +44 -0
  280. data/app/services/spree_cm_commissioner/prepaid/check_top_up_limits.rb +47 -0
  281. data/app/services/spree_cm_commissioner/prepaid/freeze.rb +58 -0
  282. data/app/services/spree_cm_commissioner/prepaid/mutation.rb +78 -0
  283. data/app/services/spree_cm_commissioner/prepaid/reverse_order_top_up.rb +23 -0
  284. data/app/services/spree_cm_commissioner/prepaid/reverse_top_up.rb +53 -0
  285. data/app/services/spree_cm_commissioner/prepaid/top_up.rb +68 -0
  286. data/app/services/spree_cm_commissioner/prepaid/unfreeze.rb +47 -0
  287. data/app/services/spree_cm_commissioner/prepaid_metrics.rb +37 -0
  288. data/app/services/spree_cm_commissioner/prepaid_reconciliation_service.rb +71 -0
  289. data/app/services/spree_cm_commissioner/reschedule_policies/assign.rb +52 -0
  290. data/app/services/spree_cm_commissioner/reschedule_policies/create.rb +71 -0
  291. data/app/services/spree_cm_commissioner/reschedule_policies/exclude.rb +40 -0
  292. data/app/services/spree_cm_commissioner/reschedule_policies/resolve.rb +109 -0
  293. data/app/services/spree_cm_commissioner/reschedule_policies/unassign.rb +23 -0
  294. data/app/services/spree_cm_commissioner/reschedule_policies/update.rb +73 -0
  295. data/app/services/spree_cm_commissioner/reschedule_requests/authorize_initiator.rb +79 -0
  296. data/app/services/spree_cm_commissioner/reschedule_requests/build_request.rb +317 -0
  297. data/app/services/spree_cm_commissioner/reschedule_requests/calculate_fee.rb +71 -0
  298. data/app/services/spree_cm_commissioner/reschedule_requests/cancel.rb +55 -0
  299. data/app/services/spree_cm_commissioner/reschedule_requests/check_eligibility.rb +243 -0
  300. data/app/services/spree_cm_commissioner/reschedule_requests/collect_payment.rb +204 -0
  301. data/app/services/spree_cm_commissioner/reschedule_requests/execute.rb +252 -0
  302. data/app/services/spree_cm_commissioner/reschedule_requests/expire_overdue_requests.rb +28 -0
  303. data/app/services/spree_cm_commissioner/reschedule_requests/expire_request.rb +24 -0
  304. data/app/services/spree_cm_commissioner/reschedule_requests/guests/update_seats.rb +210 -0
  305. data/app/services/spree_cm_commissioner/reschedule_requests/initiate.rb +255 -0
  306. data/app/services/spree_cm_commissioner/reschedule_requests/issue_compensation.rb +67 -0
  307. data/app/services/spree_cm_commissioner/reschedule_requests/issue_coupon.rb +90 -0
  308. data/app/services/spree_cm_commissioner/reschedule_requests/issue_store_credit.rb +16 -0
  309. data/app/services/spree_cm_commissioner/reschedule_requests/notifications/send_approval_reminder.rb +43 -0
  310. data/app/services/spree_cm_commissioner/reschedule_requests/notifications/send_payment_reminder.rb +46 -0
  311. data/app/services/spree_cm_commissioner/reschedule_requests/notifications/send_status_update.rb +42 -0
  312. data/app/services/spree_cm_commissioner/reschedule_requests/seats/hold.rb +137 -0
  313. data/app/services/spree_cm_commissioner/reschedule_requests/split_line_item.rb +86 -0
  314. data/app/services/spree_cm_commissioner/reschedule_requests/telegram/notify_operations.rb +37 -0
  315. data/app/services/spree_cm_commissioner/reschedule_requests/telegram/notify_vendor.rb +51 -0
  316. data/app/services/spree_cm_commissioner/reschedule_requests/vendors/approve.rb +61 -0
  317. data/app/services/spree_cm_commissioner/reschedule_requests/vendors/reject.rb +58 -0
  318. data/app/services/spree_cm_commissioner/topup_draft_orders/create.rb +234 -0
  319. data/app/services/spree_cm_commissioner/trips/service_calendars/create_or_update.rb +25 -14
  320. data/app/services/spree_cm_commissioner/trips/update_single_leg.rb +43 -6
  321. data/app/views/spree/admin/classifications/edit.html.erb +17 -2
  322. data/app/views/spree/admin/classifications/new.html.erb +18 -2
  323. data/app/views/spree/admin/customer_notifications/index.html.erb +45 -3
  324. data/app/views/spree/admin/integrations/_book_me_bus_v1_fields.html.erb +47 -0
  325. data/app/views/spree/admin/integrations/_form.html.erb +12 -0
  326. data/app/views/spree/admin/integrations/_larryta_fields.html.erb +58 -0
  327. data/app/views/spree/admin/integrations/_vireak_buntham_fields.html.erb +75 -0
  328. data/app/views/spree/admin/integrations/index.html.erb +1 -1
  329. data/app/views/spree/admin/payment_fee_rules/_form.html.erb +135 -0
  330. data/app/views/spree/admin/payment_fee_rules/edit.html.erb +11 -0
  331. data/app/views/spree/admin/payment_fee_rules/index.html.erb +100 -0
  332. data/app/views/spree/admin/payment_fee_rules/new.html.erb +11 -0
  333. data/app/views/spree/admin/prepaid_transactions/index.html.erb +102 -0
  334. data/app/views/spree/admin/prepaid_transactions/show.html.erb +146 -0
  335. data/app/views/spree/admin/private_sales/_form.html.erb +24 -1
  336. data/app/views/spree/admin/private_sales/_stock.html.erb +6 -1
  337. data/app/views/spree/admin/product_commissions/index.html.erb +7 -2
  338. data/app/views/spree/admin/reschedule_policies/_form.html.erb +65 -0
  339. data/app/views/spree/admin/reschedule_policies/edit.html.erb +11 -0
  340. data/app/views/spree/admin/reschedule_policies/index.html.erb +84 -0
  341. data/app/views/spree/admin/reschedule_policies/new.html.erb +11 -0
  342. data/app/views/spree/admin/reschedule_requests/_cancel_modal.html.erb +34 -0
  343. data/app/views/spree/admin/reschedule_requests/_search_form.html.erb +57 -0
  344. data/app/views/spree/admin/reschedule_requests/_tabs.html.erb +14 -0
  345. data/app/views/spree/admin/reschedule_requests/index.html.erb +64 -0
  346. data/app/views/spree/admin/reschedule_requests/show.html.erb +212 -0
  347. data/app/views/spree/admin/reschedule_requests/state_changes.html.erb +42 -0
  348. data/app/views/spree/admin/service_fee_rules/_form.html.erb +15 -9
  349. data/app/views/spree/admin/service_fee_rules/index.html.erb +1 -1
  350. data/app/views/spree/admin/shared/_products.html.erb +3 -0
  351. data/app/views/spree/admin/shared/_reschedule_policies_tabs.html.erb +22 -0
  352. data/app/views/spree/admin/shared/_ticket_transfer_details.html.erb +3 -0
  353. data/app/views/spree/admin/shared/sub_menu/_reschedule.html.erb +4 -0
  354. data/app/views/spree/admin/stock_managements/_header.html.erb +26 -0
  355. data/app/views/spree/admin/stock_managements/_variant_stock_items.html.erb +1 -1
  356. data/app/views/spree/admin/stock_managements/calendar.html.erb +285 -171
  357. data/app/views/spree/admin/stock_managements/index.html.erb +1 -18
  358. data/app/views/spree/admin/store_credit_admin/_tabs.html.erb +31 -0
  359. data/app/views/spree/admin/store_credit_admin/_tenant_selector.html.erb +17 -0
  360. data/app/views/spree/admin/store_credit_admin/show.html.erb +200 -0
  361. data/app/views/spree/admin/store_credits/index.html.erb +224 -0
  362. data/app/views/spree/admin/tenants/_form.html.erb +33 -0
  363. data/app/views/spree/admin/ticket_transfer_rules/_form.html.erb +28 -5
  364. data/config/initializers/spree_dependencies.rb +3 -0
  365. data/config/initializers/spree_permitted_attributes.rb +1 -0
  366. data/config/locales/en.yml +363 -1
  367. data/config/locales/km.yml +282 -1
  368. data/config/routes.rb +67 -2
  369. data/db/migrate/20260327143200_create_cm_audit_events.rb +21 -0
  370. data/db/migrate/20260410045815_create_cm_currency_rates.rb +23 -0
  371. data/db/migrate/20260411102139_add_metadata_to_cm_inventory_items.rb +6 -0
  372. data/db/migrate/20260506120000_create_cm_reschedule_policies.rb +38 -0
  373. data/db/migrate/20260506120001_create_cm_reschedule_requests.rb +67 -0
  374. data/db/migrate/20260506120002_add_reschedule_count_to_cm_guests.rb +5 -0
  375. data/db/migrate/20260521140000_create_cm_reschedule_snapshots.rb +38 -0
  376. data/db/migrate/20260522160000_create_cm_reschedule_policy_assignments.rb +17 -0
  377. data/db/migrate/20260722000001_create_cm_prepaid_transactions.rb +31 -0
  378. data/db/migrate/20260722000002_add_constraints_to_store_credits.rb +46 -0
  379. data/db/migrate/20260722000003_add_expires_at_to_store_credits.rb +6 -0
  380. data/db/migrate/20260722000004_add_issuer_tenant_id_to_spree_store_credits.rb +7 -0
  381. data/db/migrate/20260722000005_add_immutability_trigger_to_cm_prepaid_transactions.rb +36 -0
  382. data/db/migrate/20260918045558_add_prepaid_currency_columns_to_cm_tenants.rb +6 -0
  383. data/db/migrate/20260923090000_add_public_private_metadata_to_cm_prepaid_transactions.rb +36 -0
  384. data/db/migrate/20260928000001_add_closed_to_cm_inventory_items.rb +5 -0
  385. data/db/migrate/20261001100000_create_cm_payment_fee_rules.rb +17 -0
  386. data/db/migrate/20261001100001_add_payment_fee_total_to_spree_orders.rb +5 -0
  387. data/db/migrate/20261006073317_add_offline_to_spree_products.rb +6 -0
  388. data/lib/spree_cm_commissioner/cached_inventory_item.rb +7 -1
  389. data/lib/spree_cm_commissioner/payment_fees/payment_fee_preview.rb +24 -0
  390. data/lib/spree_cm_commissioner/reschedule_policy_rules.rb +75 -0
  391. data/lib/spree_cm_commissioner/serializable_transaction.rb +38 -0
  392. data/lib/spree_cm_commissioner/store_credit_currency_summary.rb +11 -0
  393. data/lib/spree_cm_commissioner/store_credit_summary.rb +11 -0
  394. data/lib/spree_cm_commissioner/test_helper/factories/integration_factory.rb +11 -0
  395. data/lib/spree_cm_commissioner/test_helper/factories/prepaid_transaction_factory.rb +12 -0
  396. data/lib/spree_cm_commissioner/test_helper/factories/reschedule_policy_assignment_factory.rb +7 -0
  397. data/lib/spree_cm_commissioner/test_helper/factories/reschedule_policy_factory.rb +59 -0
  398. data/lib/spree_cm_commissioner/test_helper/factories/reschedule_request_factory.rb +51 -0
  399. data/lib/spree_cm_commissioner/test_helper/factories/reschedule_snapshot_factory.rb +27 -0
  400. data/lib/spree_cm_commissioner/version.rb +1 -1
  401. data/lib/spree_cm_commissioner.rb +5 -0
  402. data/lib/tasks/prepaid_reconciliation_cron_scheduler.rake +7 -0
  403. data/lib/tasks/prepaid_setup_types.rake +7 -0
  404. metadata +295 -2
@@ -0,0 +1,35 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Platform
5
+ # Platform::ResourceController#authorize_spree_user only runs CanCan checks when the
6
+ # request carries a user OAuth token (`spree_current_user.present?`); a bare app
7
+ # (client_credentials) token skips it entirely by design, and the base permitted-params
8
+ # list falls back to every column on the model (`json_api_permitted_attributes`). That
9
+ # would let any app token holding the generic `:admin` doorkeeper scope rewrite
10
+ # `amount`/`amount_used`/`user_id`/`issuer_tenant_id` etc. directly -- bypassing the
11
+ # prepaid ledger (cm_prepaid_transactions) and the AdminCredit/ReverseTopUp/Freeze
12
+ # services that exist to keep it consistent, with no record of who changed what.
13
+ #
14
+ # Nothing in this monorepo (docs, internal services, cm-market-app/client) calls this
15
+ # endpoint -- it's just Spree's default auto-generated CRUD from `resources :store_credits`
16
+ # in spree_api's routes, not something built for a known consumer. Rather than maintain a
17
+ # safe-fields allowlist forever (every future Spree::StoreCredit column is a fresh chance
18
+ # to leak through it), the endpoint is disabled outright: every action 404s, same as an
19
+ # unrouted path would. The route itself still exists (routes.rb lives in gems/spree_api,
20
+ # which this repo's decorator-only rule forbids editing directly), so this is done from
21
+ # our own extension gem instead.
22
+ module StoreCreditsControllerDecorator
23
+ def self.prepended(base)
24
+ # prepend_before_action, not before_action -- runs ahead of the base controller's own
25
+ # doorkeeper_authorize!/authorize_spree_user checks, so this 404s unconditionally
26
+ # (same as an unrouted path would) rather than only after an auth check passes.
27
+ base.prepend_before_action { head :not_found }
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ Spree::Api::V2::Platform::StoreCreditsController.prepend Spree::Api::V2::Platform::StoreCreditsControllerDecorator
@@ -0,0 +1,52 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ module Account
6
+ # Endpoint: GET /api/v2/storefront/account/prepaid_balances
7
+ # Single endpoint for both "my one balance" and "all my balances" -- an optional
8
+ # ?currency= filters down to one (still an array, just 0-or-1 items), omitted returns
9
+ # every currency the platform has issued the user. Replaces the old separate
10
+ # GET account/prepaid (show) + GET account/prepaid_balances (index) pair: #show
11
+ # couldn't express "no currency given" without an :id segment, and this app's other
12
+ # index/show split forced the response shape (object vs array) to vary by param, which
13
+ # is worse than one endpoint with one fixed (array) shape and an optional filter.
14
+ class PrepaidBalancesController < Spree::Api::V2::ResourceController
15
+ before_action :require_spree_current_user
16
+
17
+ def index
18
+ if params[:currency].present?
19
+ currency = params[:currency].to_s.upcase
20
+ unless Spree::Store.default.supported_currency_codes.include?(currency)
21
+ return render_error_payload("unsupported currency: #{currency}")
22
+ end
23
+ end
24
+
25
+ render_serialized_payload { collection_serializer.new(presented_collection(currency)).serializable_hash }
26
+ end
27
+
28
+ private
29
+
30
+ # Platform-issued prepaid credit only (Spree::StoreCredit#platform_prepaid scope) --
31
+ # storefront is the platform namespace; tenant-issued wallet credit isn't
32
+ # spendable/visible here. Preferred currency sorted first (matches PrepaidConfig's
33
+ # supported_currencies order) rather than left in arbitrary DB order -- the client
34
+ # shouldn't have to inspect is_preferred on every item just to know which one to
35
+ # show first.
36
+ def presented_collection(currency)
37
+ preferred_currency = Spree::Store.default.default_currency
38
+ credits = spree_current_user.store_credits.platform_prepaid(currency).to_a
39
+ credits.sort_by! { |credit| credit.currency == preferred_currency ? 0 : 1 }
40
+
41
+ credits.map { |credit| SpreeCmCommissioner::PrepaidBalancePresenter.new(credit, preferred_currency) }
42
+ end
43
+
44
+ def collection_serializer
45
+ Spree::V2::Storefront::PrepaidBalanceSerializer
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,56 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ module Account
6
+ class PrepaidTransactionsController < Spree::Api::V2::ResourceController
7
+ before_action :require_spree_current_user
8
+
9
+ # See PrepaidTransaction::CUSTOMER_FACING_TYPES for which types these are and why.
10
+ # top_up/admin_credit are the credit-increasing rows; authorize is the negative (hold)
11
+ # row at spend time, already stored with a negative amount (see
12
+ # StoreCreditEventDecorator#signed_amount) so no sign flip is needed here. void is the
13
+ # reversal of an authorize that never captured (order failed/abandoned) -- its amount
14
+ # is positive (money back), pairing with the authorize row it reverses so a "Purchase"
15
+ # doesn't look permanent when it wasn't.
16
+ DISPLAYED_TRANSACTION_TYPES = SpreeCmCommissioner::PrepaidTransaction::CUSTOMER_FACING_TYPES
17
+
18
+ def index
19
+ if params[:currency].present?
20
+ currency = params[:currency].to_s.upcase
21
+ unless Spree::Store.default.supported_currency_codes.include?(currency)
22
+ return render_error_payload("unsupported currency: #{currency}")
23
+ end
24
+ end
25
+
26
+ super
27
+ end
28
+
29
+ def collection
30
+ @collection ||= SpreeCmCommissioner::PrepaidTransaction
31
+ .where(store_credit_id: prepaid_credit_ids, transaction_type: DISPLAYED_TRANSACTION_TYPES)
32
+ .includes(order: { payments: :payment_method })
33
+ .order(created_at: :desc)
34
+ end
35
+
36
+ private
37
+
38
+ # Mirrors PrepaidBalancesController#presented_collection -- an optional ?currency=
39
+ # narrows to one wallet, omitted returns transactions across every currency the user
40
+ # holds. Previously pinned to current_currency (session/store default), which had no
41
+ # way to request a non-default wallet's history, so a KHR top-up never showed up here
42
+ # even though its cm_prepaid_transactions row was written correctly.
43
+ def prepaid_credit_ids
44
+ currency = params[:currency].presence&.upcase
45
+ spree_current_user.store_credits.platform_prepaid(currency).pluck(:id)
46
+ end
47
+
48
+ def collection_serializer
49
+ Spree::V2::Storefront::PrepaidTransactionSerializer
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,48 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ # GET /api/v2/storefront/cart_payment_fees?payment_method_ids[]=1&payment_method_ids[]=2
6
+ #
7
+ # Payment fee each requested payment method would add to the current cart. Order-scoped
8
+ # and uncached on purpose: the answer depends on line items and applied store credit, both
9
+ # of which change on the payment screen. The client skips methods whose catalog entry says
10
+ # `fee_configured: false`, so carts with no fees configured never hit this.
11
+ class CartPaymentFeesController < Spree::Api::V2::ResourceController
12
+ include OrderConcern
13
+
14
+ before_action :ensure_order
15
+
16
+ # override
17
+ def index
18
+ render_serialized_payload do
19
+ collection_serializer.new(collection).serializable_hash
20
+ end
21
+ end
22
+
23
+ # override
24
+ def collection
25
+ @collection ||= requested_payment_methods.map do |payment_method|
26
+ SpreeCmCommissioner::PaymentFees::Preview.call(order: spree_current_order, payment_method: payment_method)
27
+ .value[:payment_fee_preview]
28
+ end
29
+ end
30
+
31
+ # override
32
+ def collection_serializer
33
+ SpreeCmCommissioner::V2::Storefront::PaymentFeePreviewSerializer
34
+ end
35
+
36
+ private
37
+
38
+ # Ids the order can't actually pay with are dropped rather than rejected, so a stale client
39
+ # list (e.g. a method disabled since the catalog was cached) still gets the rest.
40
+ def requested_payment_methods
41
+ ids = Array(params[:payment_method_ids]).map(&:to_s)
42
+ spree_current_order.available_payment_methods.select { |pm| ids.include?(pm.id.to_s) }
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -9,6 +9,10 @@ module Spree
9
9
 
10
10
  before_action -> { ensure_anonymous_booking_allowed!(draft_order_products) }, only: :create
11
11
 
12
+ # A top-up credits a wallet, which must belong to a real user -- unlike #create,
13
+ # this action has no guest-checkout path.
14
+ before_action :require_spree_current_user, only: [:topup]
15
+
12
16
  # Creates a generic draft order from a locally-assembled cart in a single request.
13
17
  # Endpoint: POST /api/v2/storefront/draft_orders
14
18
  # Params:
@@ -36,6 +40,39 @@ module Spree
36
40
  end
37
41
  end
38
42
 
43
+ # Builds a prepaid top-up order from just an amount and a payment method -- the client
44
+ # never fetches/knows the prepaid_top_up product or variant.
45
+ # Endpoint: POST /api/v2/storefront/draft_orders/topup
46
+ # Params:
47
+ # - amount: the custom top-up amount (required)
48
+ # - payment_method_id: id of the payment method to attach (required)
49
+ # - currency: the wallet currency the client's own currency picker selected (optional --
50
+ # TopupDraftOrders::Create falls back to store.default_currency when nil). Deliberately
51
+ # NOT current_currency: that helper resolves general storefront browsing currency
52
+ # (session -> ?currency= -> store default) -- a stale session[:currency] would
53
+ # silently override what the client explicitly picked here, leading to the amount
54
+ # being validated against the wrong currency's min/max (e.g. a KHR amount checked
55
+ # against the USD cap).
56
+ def topup
57
+ result = SpreeCmCommissioner::TopupDraftOrders::Create.call(
58
+ amount: params[:amount],
59
+ payment_method_id: params[:payment_method_id],
60
+ user: spree_current_user,
61
+ store: current_store,
62
+ currency: params[:currency].presence&.upcase,
63
+ options: {
64
+ device_id: device_id,
65
+ last_ip_address: request.remote_ip
66
+ }
67
+ )
68
+
69
+ if result.success?
70
+ render_serialized_payload(201) { serialize_resource(serializable_order(result.value[:order])) }
71
+ else
72
+ render_error_payload(result.error)
73
+ end
74
+ end
75
+
39
76
  private
40
77
 
41
78
  # Re-fetch the created order with everything the enriched payload touches eager-loaded, so
@@ -0,0 +1,22 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ # Endpoint: GET /api/v2/storefront/prepaid_config
6
+ # Public (no sign-in required): a guest should be able to discover currencies/limits
7
+ # before a top-up is submitted.
8
+ class PrepaidConfigController < Spree::Api::V2::ResourceController
9
+ def show
10
+ render_serialized_payload { serialize_resource(SpreeCmCommissioner::PrepaidConfig.resolve) }
11
+ end
12
+
13
+ private
14
+
15
+ def resource_serializer
16
+ Spree::V2::Storefront::PrepaidConfigSerializer
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,156 @@
1
+ # Customer-initiated reschedule lifecycle, scoped to the signed-in customer's
2
+ # own bookings. Every action delegates to the same RescheduleRequests::*
3
+ # services the vendor console drives.
4
+ #
5
+ # Actions:
6
+ # - index/show - the customer's own requests only, scoped by `load_request!`
7
+ # - preview - quote only, nothing persisted, terms not required yet
8
+ # - create - persists and auto-advances per policy
9
+ # - pay - online balance-due payment
10
+ # - cancel - withdraws a request already in flight
11
+ #
12
+
13
+ module Spree
14
+ module Api
15
+ module V2
16
+ module Storefront
17
+ class RescheduleRequestsController < ::Spree::Api::V2::ResourceController
18
+ before_action :require_spree_current_user
19
+
20
+ def index
21
+ render_serialized_payload { serialize_collection(paginated_collection) }
22
+ end
23
+
24
+ def show
25
+ render_serialized_payload { serialize_resource(load_request!) }
26
+ end
27
+
28
+ def preview
29
+ result = run_initiate(preview: true)
30
+ return render_error_payload(result.error) if result.failure?
31
+
32
+ render_serialized_payload { { data: preview_payload(result.value) } }
33
+ end
34
+
35
+ def create
36
+ return render_error_payload(I18n.t('reschedule_requests.initiate.terms_not_accepted')) unless terms_accepted?
37
+
38
+ result = run_initiate(
39
+ preview: false,
40
+ terms_accepted_at: Time.current,
41
+ reason: reschedule_params[:reason]
42
+ )
43
+ return render_error_payload(result.error) if result.failure?
44
+
45
+ render_serialized_payload(201) { serialize_resource(result.value[:reschedule_request]) }
46
+ end
47
+
48
+ def cancel
49
+ result = SpreeCmCommissioner::RescheduleRequests::Cancel.call(
50
+ reschedule_request: load_request!,
51
+ cancelled_by: spree_current_user,
52
+ reason: params[:reason]
53
+ )
54
+ return render_error_payload(result.error) if result.failure?
55
+
56
+ render_serialized_payload { serialize_resource(result.value[:reschedule_request]) }
57
+ end
58
+
59
+ def pay
60
+ result = SpreeCmCommissioner::RescheduleRequests::CollectPayment.call(
61
+ reschedule_request: load_request!,
62
+ payment_params: payment_params,
63
+ acting_user: spree_current_user
64
+ )
65
+ return render_error_payload(result.error) if result.failure?
66
+
67
+ render_serialized_payload do
68
+ Spree::V2::Storefront::PaymentSerializer.new(result.value[:payment], params: serializer_params).serializable_hash
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def terms_accepted?
75
+ ActiveModel::Type::Boolean.new.cast(reschedule_params[:terms_accepted])
76
+ end
77
+
78
+ def run_initiate(preview:, terms_accepted_at: nil, reason: nil)
79
+ SpreeCmCommissioner::RescheduleRequests::Initiate.call(
80
+ order_id: reschedule_params[:order_number],
81
+ line_item_ids: reschedule_params[:line_item_ids],
82
+ new_from_date: reschedule_params[:new_from_date],
83
+ new_to_date: reschedule_params[:new_to_date],
84
+ targets: reschedule_params[:targets],
85
+ initiated_by: spree_current_user,
86
+ initiated_as: 'user',
87
+ options: {
88
+ preview: preview,
89
+ terms_accepted_at: terms_accepted_at,
90
+ reason: reason,
91
+ request_ip: request.remote_ip
92
+ }.compact
93
+ )
94
+ end
95
+
96
+ def load_request!
97
+ SpreeCmCommissioner::RescheduleRequest
98
+ .for_user(spree_current_user.id)
99
+ .find_by!(number: params[:id])
100
+ end
101
+
102
+ def collection
103
+ SpreeCmCommissioner::RescheduleRequest
104
+ .for_user(spree_current_user.id)
105
+ .includes(:order, :previous_request_snapshots, :new_request_snapshots)
106
+ .order(id: :desc)
107
+ end
108
+
109
+ def default_resource_includes
110
+ %w[before_snapshots after_snapshots]
111
+ end
112
+
113
+ def resource_serializer
114
+ SpreeCmCommissioner::V2::Storefront::RescheduleRequestSerializer
115
+ end
116
+
117
+ def collection_serializer
118
+ SpreeCmCommissioner::V2::Storefront::RescheduleRequestSerializer
119
+ end
120
+
121
+ def preview_payload(value)
122
+ request = value[:reschedule_request]
123
+ {
124
+ fee_amount: request.fee_amount,
125
+ amount_due_total: request.amount_due_total,
126
+ credit_amount_total: request.credit_amount_total,
127
+ currency: request.currency,
128
+ payment_required: request.payment_required?,
129
+ requires_vendor_approval: value[:requires_vendor_approval],
130
+ fee_breakdown: value[:fee_breakdown]
131
+ }
132
+ end
133
+
134
+ def reschedule_params
135
+ params.require(:reschedule_request).permit(
136
+ :order_number, :new_from_date, :new_to_date, :reason, :terms_accepted,
137
+ line_item_ids: [],
138
+ targets: [
139
+ :line_item_id, :new_from_date, :new_to_date, :trip_id,
140
+ :boarding_trip_stop_id, :drop_off_trip_stop_id, :variant_id,
141
+ { guest_ids: [], block_ids: [] }
142
+ ]
143
+ )
144
+ end
145
+
146
+ def payment_params
147
+ params.require(:payment).permit(
148
+ :payment_method_id, :payment_option, :gateway_payment_profile_id,
149
+ :gateway_customer_profile_id, :last_digits, :month, :year, :name
150
+ )
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -10,6 +10,8 @@ module Spree
10
10
  class SeatLayoutsController < Spree::Api::V2::ResourceController
11
11
  # GET /api/v2/storefront/seat_layouts/:id
12
12
  # - include_details=true|false (optional, default: true)
13
+ #
14
+ # Always returns `product_ids` (see SeatLayout#product_ids).
13
15
  def show
14
16
  render_serialized_payload { serialize_resource(resource) }
15
17
  end
@@ -70,7 +72,7 @@ module Spree
70
72
 
71
73
  # override
72
74
  def serializer_params
73
- super.merge(include_details: include_details?)
75
+ super.merge(include_details: include_details?, include_product_ids: action_name == 'show')
74
76
  end
75
77
 
76
78
  # default return true
@@ -0,0 +1,66 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ module Transit
6
+ class ItemAvailabilitiesController < ::Spree::Api::V2::ResourceController
7
+ include SpreeCmCommissioner::Integrations::InventorySyncCachable
8
+
9
+ def create
10
+ sync_result = sync_item_availability!
11
+
12
+ unless sync_result.success?
13
+ error_message = sync_result.to_h.dig(:error, :message) || 'Sync failed'
14
+ return render_error_payload(error_message, 422)
15
+ end
16
+
17
+ item_availability = SpreeCmCommissioner::ItemAvailabilitySync.new(
18
+ id: trip.id,
19
+ last_sync_at: sync_result.last_synced_at
20
+ )
21
+ render_serialized_payload { serialize_resource(item_availability) }
22
+ rescue ActiveRecord::RecordNotFound => e
23
+ render_error_payload(e.message, 404)
24
+ end
25
+
26
+ private
27
+
28
+ def trip
29
+ @trip ||= SpreeCmCommissioner::Trip.find(params[:trip_id])
30
+ end
31
+
32
+ def integration
33
+ @integration ||= trip.vendor&.integration
34
+ end
35
+
36
+ def sync_item_availability!
37
+ # Return success result if vendor has no integration
38
+ return SpreeCmCommissioner::Integrations::Base::SyncResult.new if integration.nil?
39
+
40
+ # Check cache first for successful results only
41
+ cached_result = Rails.cache.read(sync_cache_key)
42
+ return cached_result if cached_result
43
+
44
+ # Perform sync
45
+ result = integration.sync_item_availability!(trip_id: trip.id, on_date: params[:on_date])
46
+
47
+ # Only cache successful results to avoid sticky failures
48
+ Rails.cache.write(sync_cache_key, result, expires_in: sync_cache_ttl) if result.success?
49
+
50
+ result
51
+ end
52
+
53
+ def sync_cache_key
54
+ # Include trip.updated_at to auto-bust cache when trip is updated by background syncs
55
+ "item_availability_syncs:#{trip.id}:#{params[:on_date]}:#{trip.updated_at.to_i}"
56
+ end
57
+
58
+ def resource_serializer
59
+ SpreeCmCommissioner::V2::Storefront::ItemAvailabilitySyncSerializer
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,51 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ module Account
6
+ # Endpoint: GET /api/v2/tenant/account/prepaid_balances
7
+ # Single endpoint for both "my one balance" and "all my balances" -- an optional
8
+ # ?currency= filters down to one (still an array, just 0-or-1 items; hard-rejected if
9
+ # outside the tenant's supported_currencies_list, via BaseController#
10
+ # resolve_requested_currency), omitted returns every currency this tenant has issued the
11
+ # user (ignoring supported_currencies_list -- a balance in a since-unsupported currency
12
+ # stays visible/spendable). Replaces the old separate GET account/prepaid (show) +
13
+ # GET account/prepaid_balances (index) pair -- see Storefront::Account::
14
+ # PrepaidController's own comment for why one endpoint with a fixed array shape beats
15
+ # two endpoints whose response type differs by param.
16
+ class PrepaidBalancesController < BaseController
17
+ before_action :require_spree_current_user
18
+
19
+ def index
20
+ currency = nil
21
+ if params[:currency].present?
22
+ currency, error = resolve_requested_currency
23
+ return render_error_payload(error) if error
24
+ end
25
+
26
+ render_serialized_payload { collection_serializer.new(presented_collection(currency)).serializable_hash }
27
+ end
28
+
29
+ private
30
+
31
+ # Tenant-issued prepaid credit only (Spree::StoreCredit#tenant_prepaid) -- unlike the
32
+ # storefront version, this is never platform-issued credit: a tenant's own wallet
33
+ # screen shows only what that tenant itself issued this user. Preferred currency
34
+ # sorted first -- see Storefront::Account::PrepaidBalancesController's own comment.
35
+ def presented_collection(currency)
36
+ preferred_currency = @tenant.preferred_currency_or_default
37
+ credits = spree_current_user.store_credits.tenant_prepaid(@tenant.id, currency).to_a
38
+ credits.sort_by! { |credit| credit.currency == preferred_currency ? 0 : 1 }
39
+
40
+ credits.map { |credit| SpreeCmCommissioner::PrepaidBalancePresenter.new(credit, preferred_currency) }
41
+ end
42
+
43
+ def collection_serializer
44
+ Spree::V2::Tenant::PrepaidBalanceSerializer
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,53 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ module Account
6
+ # Endpoint: GET /api/v2/tenant/account/prepaid_transactions
7
+ # Mirrors Spree::Api::V2::Storefront::Account::PrepaidTransactionsController, scoped to
8
+ # this tenant's own issued credit only (Spree::StoreCredit#tenant_prepaid) -- same
9
+ # scoping split as Tenant::Account::PrepaidController vs its storefront counterpart.
10
+ class PrepaidTransactionsController < BaseController
11
+ before_action :require_spree_current_user
12
+
13
+ # See PrepaidTransaction::CUSTOMER_FACING_TYPES for which types these are and why.
14
+ DISPLAYED_TRANSACTION_TYPES = SpreeCmCommissioner::PrepaidTransaction::CUSTOMER_FACING_TYPES
15
+
16
+ # Overridden (rather than left to ResourceController#index) so an out-of-list
17
+ # ?currency= is a hard rejection here too, same as Tenant::Account::PrepaidController
18
+ # #show -- without this check, collection's `prepaid_credit&.id` would just silently
19
+ # render an empty list for a bad currency instead of telling the client why.
20
+ def index
21
+ _currency, error = resolve_requested_currency
22
+ return render_error_payload(error) if error
23
+
24
+ super
25
+ end
26
+
27
+ def collection
28
+ @collection ||= SpreeCmCommissioner::PrepaidTransaction
29
+ .where(store_credit_id: prepaid_credit&.id, transaction_type: DISPLAYED_TRANSACTION_TYPES)
30
+ .includes(order: { payments: :payment_method })
31
+ .order(created_at: :desc)
32
+ end
33
+
34
+ private
35
+
36
+ # Same currency resolution as Tenant::Account::PrepaidController#show -- the currency
37
+ # this tenant's wallet screen is currently viewing, not every currency at once (that's
38
+ # what prepaid_balances is for). #index already rejected a bad currency above, so
39
+ # resolve_requested_currency's error here is always nil by the time collection runs.
40
+ def prepaid_credit
41
+ currency, = resolve_requested_currency
42
+ @prepaid_credit ||= spree_current_user.store_credits.tenant_prepaid(@tenant.id, currency).take
43
+ end
44
+
45
+ def collection_serializer
46
+ Spree::V2::Tenant::PrepaidTransactionSerializer
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -20,6 +20,26 @@ module Spree
20
20
  @current_vendor ||= @tenant.active_vendor
21
21
  end
22
22
 
23
+ # current_currency/supported_currency?/supported_currencies are tenant-aware via
24
+ # Spree::Core::ControllerHelpers::CurrencyDecorator (keys off @tenant, set above) --
25
+ # see that file for the tenant-vs-store resolution logic.
26
+
27
+ # Resolves the wallet currency for a tenant-scoped prepaid request: an explicit
28
+ # ?currency= param must be inside this tenant's own supported_currencies_list -- a hard
29
+ # rejection, not a silent fallback, per the design doc (a currency outside the
30
+ # tenant's configured set would otherwise make the restriction cosmetic). Omitted,
31
+ # falls back to Tenant#preferred_currency_or_default. Returns [currency, error_message]
32
+ # -- error_message is nil on success.
33
+ def resolve_requested_currency
34
+ requested = params[:currency].presence
35
+ return [@tenant.preferred_currency_or_default, nil] if requested.blank?
36
+
37
+ currency = requested.to_s.upcase
38
+ return [nil, "unsupported currency: #{currency}"] unless @tenant.supports_currency?(currency)
39
+
40
+ [currency, nil]
41
+ end
42
+
23
43
  def render_serialized_payload(status = 200)
24
44
  render json: yield, status: status, content_type: content_type
25
45
  end