spree_cm_commissioner 2.8.0 → 2.8.1.pre.3

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 (295) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/Gemfile.lock +1 -1
  4. data/app/controllers/spree/admin/featured_trips_controller.rb +135 -0
  5. data/app/controllers/spree/admin/homepage_section_controller.rb +4 -1
  6. data/app/controllers/spree/admin/oauth_applications_controller.rb +19 -0
  7. data/app/controllers/spree/admin/taxons_controller_decorator.rb +19 -0
  8. data/app/controllers/spree/api/v2/storefront/homepage_sections_controller.rb +1 -0
  9. data/app/controllers/spree/api/v2/storefront/preview_products_controller.rb +48 -0
  10. data/app/controllers/spree/api/v2/storefront/preview_sections_controller.rb +27 -0
  11. data/app/controllers/spree/api/v2/storefront/preview_taxons_controller.rb +18 -0
  12. data/app/controllers/spree/api/v2/storefront/products_controller_decorator.rb +15 -0
  13. data/app/controllers/spree/api/v2/storefront/taxons_controller_decorator.rb +15 -0
  14. data/app/controllers/spree/api/v2/storefront/transit/item_availabilities_controller.rb +66 -0
  15. data/app/controllers/spree/api/v2/storefront/trip_search_controller.rb +1 -0
  16. data/app/controllers/spree/api/v2/tenant/base_controller.rb +4 -0
  17. data/app/controllers/spree/api/v2/tenant/free_vote_claims_controller.rb +37 -0
  18. data/app/controllers/spree/api/v2/tenant/homepage_sections_controller.rb +1 -0
  19. data/app/controllers/spree/api/v2/tenant/preview_products_controller.rb +47 -0
  20. data/app/controllers/spree/api/v2/tenant/preview_sections_controller.rb +26 -0
  21. data/app/controllers/spree/api/v2/tenant/preview_shows_controller.rb +19 -0
  22. data/app/controllers/spree/api/v2/tenant/preview_taxons_controller.rb +19 -0
  23. data/app/controllers/spree/api/v2/tenant/products_controller.rb +1 -1
  24. data/app/controllers/spree/api/v2/tenant/show_contestants_controller.rb +51 -0
  25. data/app/controllers/spree/api/v2/tenant/show_people_controller.rb +49 -0
  26. data/app/controllers/spree/api/v2/tenant/show_person_assignments_controller.rb +36 -0
  27. data/app/controllers/spree/api/v2/tenant/shows_controller.rb +34 -0
  28. data/app/controllers/spree/api/v2/tenant/taxons_controller.rb +1 -1
  29. data/app/controllers/spree/api/v2/tenant/votes_controller.rb +94 -0
  30. data/app/controllers/spree/api/v2/tenant/voting_contestants_controller.rb +40 -0
  31. data/app/controllers/spree/api/v2/tenant/voting_credit_transactions_controller.rb +41 -0
  32. data/app/controllers/spree/api/v2/tenant/voting_credits_controller.rb +31 -0
  33. data/app/controllers/spree_cm_commissioner/admin/products_controller_decorator.rb +19 -0
  34. data/app/finders/spree_cm_commissioner/accommodations/find_variant.rb +2 -2
  35. data/app/finders/spree_cm_commissioner/events/find_matches.rb +1 -0
  36. data/app/helpers/spree_cm_commissioner/admin/homepage_segment_helper.rb +2 -0
  37. data/app/interactors/spree_cm_commissioner/pin_code_sender.rb +11 -1
  38. data/app/jobs/spree_cm_commissioner/audit_event_job.rb +29 -0
  39. data/app/jobs/spree_cm_commissioner/vote_fraud_event_job.rb +9 -0
  40. data/app/jobs/spree_cm_commissioner/voting_credit_allocation_job.rb +10 -0
  41. data/app/jobs/spree_cm_commissioner/voting_credit_de_allocation_job.rb +10 -0
  42. data/app/models/concerns/spree_cm_commissioner/audit_wrapper.rb +139 -0
  43. data/app/models/concerns/spree_cm_commissioner/homepage_section_bitwise.rb +2 -1
  44. data/app/models/concerns/spree_cm_commissioner/integrations/inventory_sync_cachable.rb +9 -0
  45. data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +2 -1
  46. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +10 -0
  47. data/app/models/concerns/spree_cm_commissioner/product_type.rb +1 -1
  48. data/app/models/concerns/spree_cm_commissioner/variant_metadata.rb +24 -0
  49. data/app/models/concerns/spree_cm_commissioner/vehicle_kind.rb +1 -1
  50. data/app/models/spree_cm_commissioner/audit_event.rb +11 -0
  51. data/app/models/spree_cm_commissioner/currency_rate.rb +15 -0
  52. data/app/models/spree_cm_commissioner/integration.rb +31 -0
  53. data/app/models/spree_cm_commissioner/integrations/book_me_bus_v1.rb +56 -0
  54. data/app/models/spree_cm_commissioner/integrations/larryta.rb +81 -0
  55. data/app/models/spree_cm_commissioner/integrations/stadium_x_v1.rb +4 -10
  56. data/app/models/spree_cm_commissioner/integrations/vireak_buntham.rb +82 -0
  57. data/app/models/spree_cm_commissioner/inventory_item.rb +6 -0
  58. data/app/models/spree_cm_commissioner/maintenance_tasks/voting_session.rb +34 -0
  59. data/app/models/spree_cm_commissioner/order_decorator.rb +1 -0
  60. data/app/models/spree_cm_commissioner/place.rb +2 -0
  61. data/app/models/spree_cm_commissioner/preview_role.rb +8 -0
  62. data/app/models/spree_cm_commissioner/product_decorator.rb +39 -0
  63. data/app/models/spree_cm_commissioner/role_decorator.rb +4 -1
  64. data/app/models/spree_cm_commissioner/role_user_decorator.rb +8 -0
  65. data/app/models/spree_cm_commissioner/route.rb +1 -0
  66. data/app/models/spree_cm_commissioner/seat_layout.rb +2 -0
  67. data/app/models/spree_cm_commissioner/show.rb +157 -0
  68. data/app/models/spree_cm_commissioner/show_contestant.rb +34 -0
  69. data/app/models/spree_cm_commissioner/show_contestant_image.rb +11 -0
  70. data/app/models/spree_cm_commissioner/show_contestant_video.rb +15 -0
  71. data/app/models/spree_cm_commissioner/show_episode.rb +135 -0
  72. data/app/models/spree_cm_commissioner/show_person.rb +15 -0
  73. data/app/models/spree_cm_commissioner/show_person_assignment.rb +20 -0
  74. data/app/models/spree_cm_commissioner/show_person_image.rb +11 -0
  75. data/app/models/spree_cm_commissioner/stock_item_decorator.rb +4 -0
  76. data/app/models/spree_cm_commissioner/taxon_decorator.rb +15 -0
  77. data/app/models/spree_cm_commissioner/taxonomy_decorator.rb +10 -1
  78. data/app/models/spree_cm_commissioner/tenant.rb +11 -0
  79. data/app/models/spree_cm_commissioner/trip.rb +9 -1
  80. data/app/models/spree_cm_commissioner/trip_stop.rb +2 -0
  81. data/app/models/spree_cm_commissioner/user_decorator.rb +5 -0
  82. data/app/models/spree_cm_commissioner/variant_decorator.rb +1 -0
  83. data/app/models/spree_cm_commissioner/variant_options.rb +4 -0
  84. data/app/models/spree_cm_commissioner/vehicle.rb +3 -0
  85. data/app/models/spree_cm_commissioner/vehicle_type.rb +1 -0
  86. data/app/models/spree_cm_commissioner/vendor_decorator.rb +21 -0
  87. data/app/models/spree_cm_commissioner/vendor_place.rb +2 -0
  88. data/app/models/spree_cm_commissioner/vote.rb +16 -0
  89. data/app/models/spree_cm_commissioner/vote_fraud_event.rb +19 -0
  90. data/app/models/spree_cm_commissioner/voting_contestant.rb +34 -0
  91. data/app/models/spree_cm_commissioner/voting_credit.rb +72 -0
  92. data/app/models/spree_cm_commissioner/voting_credit_transaction.rb +55 -0
  93. data/app/models/spree_cm_commissioner/voting_session.rb +194 -0
  94. data/app/overrides/spree/admin/oauth_applications/_form/tenant_fields.html.erb.deface +0 -6
  95. data/app/overrides/spree/admin/products/_form/preview_checkbox.html.erb.deface +9 -0
  96. data/app/overrides/spree/admin/shared/sub_menu/_promotion/featured_trips.html.erb.deface +6 -0
  97. data/app/overrides/spree/admin/taxons/_form/preview_checkbox.html.erb.deface +7 -0
  98. data/app/queries/spree_cm_commissioner/multi_leg_trips_query.rb +9 -0
  99. data/app/queries/spree_cm_commissioner/single_leg_trips_query.rb +19 -0
  100. data/app/serializables/spree_cm_commissioner/item_availability_sync.rb +10 -0
  101. data/app/serializers/spree/v2/storefront/homepage_section_serializer.rb +1 -1
  102. data/app/serializers/spree/v2/storefront/product_serializer_decorator.rb +1 -1
  103. data/app/serializers/spree/v2/storefront/role_serializer.rb +1 -1
  104. data/app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb +2 -1
  105. data/app/serializers/spree/v2/tenant/homepage_section_serializer.rb +1 -1
  106. data/app/serializers/spree/v2/tenant/role_serializer.rb +1 -1
  107. data/app/serializers/spree/v2/tenant/show_contestant_serializer.rb +21 -0
  108. data/app/serializers/spree/v2/tenant/show_episode_serializer.rb +17 -0
  109. data/app/serializers/spree/v2/tenant/show_person_assignment_serializer.rb +16 -0
  110. data/app/serializers/spree/v2/tenant/show_person_serializer.rb +13 -0
  111. data/app/serializers/spree/v2/tenant/show_serializer.rb +26 -0
  112. data/app/serializers/spree/v2/tenant/video_serializer.rb +9 -0
  113. data/app/serializers/spree/v2/tenant/vote_serializer.rb +14 -0
  114. data/app/serializers/spree/v2/tenant/voting_contestant_serializer.rb +18 -0
  115. data/app/serializers/spree/v2/tenant/voting_credit_serializer.rb +10 -0
  116. data/app/serializers/spree/v2/tenant/voting_credit_transaction_serializer.rb +14 -0
  117. data/app/serializers/spree/v2/tenant/voting_session_serializer.rb +15 -0
  118. data/app/serializers/spree_cm_commissioner/v2/storefront/item_availability_sync_serializer.rb +10 -0
  119. data/app/services/spree_cm_commissioner/audit_logger.rb +59 -0
  120. data/app/services/spree_cm_commissioner/currency_converter/create.rb +27 -0
  121. data/app/services/spree_cm_commissioner/currency_rates/manage.rb +99 -0
  122. data/app/services/spree_cm_commissioner/fraud_check.rb +279 -0
  123. data/app/services/spree_cm_commissioner/google_maps_geocoder.rb +41 -0
  124. data/app/services/spree_cm_commissioner/integrations/base/sync_manager.rb +1 -2
  125. data/app/services/spree_cm_commissioner/integrations/base/sync_result.rb +11 -0
  126. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/external_client/client.rb +261 -0
  127. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/external_client/connection.rb +147 -0
  128. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/inventory/external_inventory_item_status.rb +52 -0
  129. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/inventory/external_inventory_items.rb +91 -0
  130. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/inventory/unstock_inventory.rb +140 -0
  131. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_inventory.rb +52 -0
  132. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_item_availability.rb +249 -0
  133. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_locations.rb +103 -0
  134. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_product.rb +66 -0
  135. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_route_stops.rb +113 -0
  136. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_routes.rb +128 -0
  137. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_seat_layout.rb +342 -0
  138. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_service_calendars.rb +59 -0
  139. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_trip_stop.rb +179 -0
  140. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_trips.rb +597 -0
  141. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_variant.rb +58 -0
  142. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_vehicle_types.rb +87 -0
  143. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/time_parser.rb +19 -0
  144. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/base.rb +40 -0
  145. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/branch.rb +65 -0
  146. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/direction.rb +51 -0
  147. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/inventory_item_status.rb +148 -0
  148. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/location.rb +55 -0
  149. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/operator.rb +17 -0
  150. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/reservation_cart.rb +85 -0
  151. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/seat.rb +128 -0
  152. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/seat_layout.rb +363 -0
  153. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/stop.rb +84 -0
  154. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/trip.rb +199 -0
  155. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/wallet_payment.rb +40 -0
  156. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/sync_manager.rb +22 -0
  157. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/sync_strategies/full_sync_strategy.rb +117 -0
  158. data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/sync_strategies/incremental_sync_strategy.rb +64 -0
  159. data/app/services/spree_cm_commissioner/integrations/larryta/external_client/client.rb +344 -0
  160. data/app/services/spree_cm_commissioner/integrations/larryta/external_client/connection.rb +74 -0
  161. data/app/services/spree_cm_commissioner/integrations/larryta/inventory/external_inventory_item_status.rb +53 -0
  162. data/app/services/spree_cm_commissioner/integrations/larryta/inventory/external_inventory_items.rb +106 -0
  163. data/app/services/spree_cm_commissioner/integrations/larryta/inventory/hold_seats.rb +143 -0
  164. data/app/services/spree_cm_commissioner/integrations/larryta/inventory/release_seats.rb +90 -0
  165. data/app/services/spree_cm_commissioner/integrations/larryta/inventory/unstock_inventory.rb +113 -0
  166. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_item_availability.rb +261 -0
  167. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_product.rb +72 -0
  168. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_routes.rb +142 -0
  169. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_seat_layout.rb +255 -0
  170. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_stock_item.rb +55 -0
  171. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_trip_stop.rb +167 -0
  172. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_trips.rb +320 -0
  173. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_variant.rb +63 -0
  174. data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_vehicle_type.rb +79 -0
  175. data/app/services/spree_cm_commissioner/integrations/larryta/resources/base.rb +39 -0
  176. data/app/services/spree_cm_commissioner/integrations/larryta/resources/booking.rb +113 -0
  177. data/app/services/spree_cm_commissioner/integrations/larryta/resources/booking_session.rb +43 -0
  178. data/app/services/spree_cm_commissioner/integrations/larryta/resources/booking_transaction.rb +236 -0
  179. data/app/services/spree_cm_commissioner/integrations/larryta/resources/direction.rb +71 -0
  180. data/app/services/spree_cm_commissioner/integrations/larryta/resources/passenger.rb +48 -0
  181. data/app/services/spree_cm_commissioner/integrations/larryta/resources/schedule.rb +150 -0
  182. data/app/services/spree_cm_commissioner/integrations/larryta/resources/seat.rb +31 -0
  183. data/app/services/spree_cm_commissioner/integrations/larryta/resources/seat_availability.rb +49 -0
  184. data/app/services/spree_cm_commissioner/integrations/larryta/resources/seat_layout.rb +211 -0
  185. data/app/services/spree_cm_commissioner/integrations/larryta/sync_manager.rb +17 -0
  186. data/app/services/spree_cm_commissioner/integrations/larryta/sync_strategies/full_sync_strategy.rb +79 -0
  187. data/app/services/spree_cm_commissioner/integrations/larryta/sync_strategies/incremental_sync_strategy.rb +58 -0
  188. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/external_client/client.rb +14 -15
  189. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/polling/sync_matches.rb +7 -4
  190. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/polling/sync_zones.rb +6 -3
  191. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_manager.rb +0 -3
  192. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_strategies/full_sync_strategy.rb +1 -3
  193. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_strategies/incremental_sync_strategy.rb +1 -3
  194. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_strategies/webhook_sync_strategy.rb +1 -2
  195. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/external_client/client.rb +206 -0
  196. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/external_client/connection.rb +59 -0
  197. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/inventory/external_inventory_item_status.rb +48 -0
  198. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/inventory/external_inventory_items.rb +97 -0
  199. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/inventory/unstock_inventory.rb +208 -0
  200. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_item_availability.rb +270 -0
  201. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_locations.rb +96 -0
  202. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_nationality_pricing.rb +76 -0
  203. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_product.rb +69 -0
  204. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_routes.rb +110 -0
  205. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_seat_layout.rb +207 -0
  206. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_stock_item.rb +43 -0
  207. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_trip_stop.rb +185 -0
  208. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_trips.rb +300 -0
  209. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_variant.rb +56 -0
  210. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_vehicle_type.rb +51 -0
  211. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/base.rb +32 -0
  212. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/booking.rb +48 -0
  213. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/booking_transaction.rb +46 -0
  214. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/cell.rb +53 -0
  215. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/confirm_booking_request.rb +43 -0
  216. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/location.rb +37 -0
  217. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/schedule.rb +138 -0
  218. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/seat.rb +48 -0
  219. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/seat_layout.rb +79 -0
  220. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/sync_manager.rb +17 -0
  221. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/sync_strategies/full_sync_strategy.rb +87 -0
  222. data/app/services/spree_cm_commissioner/integrations/vireak_buntham/sync_strategies/incremental_sync_strategy.rb +53 -0
  223. data/app/services/spree_cm_commissioner/inventory_items/generate_permanent_item.rb +48 -0
  224. data/app/services/spree_cm_commissioner/order_holds/hold.rb +17 -0
  225. data/app/services/spree_cm_commissioner/order_holds/release.rb +17 -0
  226. data/app/services/spree_cm_commissioner/place_resolver.rb +95 -0
  227. data/app/services/spree_cm_commissioner/show_contestants/normalize_video_highlights.rb +56 -0
  228. data/app/services/spree_cm_commissioner/url_embed/youtube_embed.rb +49 -0
  229. data/app/services/spree_cm_commissioner/vote_counters/audit_counters.rb +43 -0
  230. data/app/services/spree_cm_commissioner/vote_counters/base.rb +31 -0
  231. data/app/services/spree_cm_commissioner/vote_counters/increment.rb +44 -0
  232. data/app/services/spree_cm_commissioner/vote_counters/per_contestant_counter.rb +66 -0
  233. data/app/services/spree_cm_commissioner/vote_counters/rebuild_from_db.rb +70 -0
  234. data/app/services/spree_cm_commissioner/vote_credit_deductor.rb +68 -0
  235. data/app/services/spree_cm_commissioner/vote_package/create.rb +145 -0
  236. data/app/services/spree_cm_commissioner/vote_package/update.rb +91 -0
  237. data/app/services/spree_cm_commissioner/vote_processor.rb +144 -0
  238. data/app/services/spree_cm_commissioner/voting_contestants/advancer.rb +334 -0
  239. data/app/services/spree_cm_commissioner/voting_contestants/assigner.rb +32 -0
  240. data/app/services/spree_cm_commissioner/voting_contestants/bulk_updater.rb +104 -0
  241. data/app/services/spree_cm_commissioner/voting_credits/allocate.rb +77 -0
  242. data/app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb +119 -0
  243. data/app/services/spree_cm_commissioner/voting_credits/credit_calculator.rb +35 -0
  244. data/app/services/spree_cm_commissioner/voting_credits/de_allocate.rb +87 -0
  245. data/app/views/spree/admin/featured_trips/index.html.erb +130 -0
  246. data/app/views/spree/admin/homepage_section/_form.html.erb +5 -0
  247. data/app/views/spree/admin/integrations/_book_me_bus_v1_fields.html.erb +47 -0
  248. data/app/views/spree/admin/integrations/_form.html.erb +12 -0
  249. data/app/views/spree/admin/integrations/_larryta_fields.html.erb +58 -0
  250. data/app/views/spree/admin/integrations/_vireak_buntham_fields.html.erb +75 -0
  251. data/app/views/spree/admin/integrations/index.html.erb +1 -1
  252. data/app/views/spree/admin/tenants/_form.html.erb +11 -0
  253. data/config/initializers/spree_permitted_attributes.rb +8 -0
  254. data/config/locales/en.yml +13 -0
  255. data/config/locales/km.yml +7 -0
  256. data/config/routes.rb +27 -0
  257. data/db/migrate/20260309230148_create_cm_show_people.rb +14 -0
  258. data/db/migrate/20260309230149_create_cm_show_people_assignments.rb +16 -0
  259. data/db/migrate/20260310082711_create_cm_show_contestants.rb +28 -0
  260. data/db/migrate/20260310082720_create_cm_voting_sessions.rb +21 -0
  261. data/db/migrate/20260310082721_create_cm_voting_contestants.rb +23 -0
  262. data/db/migrate/20260310082734_add_voting_fields_to_spree_taxons.rb +9 -0
  263. data/db/migrate/20260310082735_add_type_to_spree_products.rb +6 -0
  264. data/db/migrate/20260310082749_create_cm_voting_credits.rb +27 -0
  265. data/db/migrate/20260326080200_create_cm_voting_credit_transactions.rb +27 -0
  266. data/db/migrate/20260327143200_create_cm_audit_events.rb +21 -0
  267. data/db/migrate/20260330160000_create_cm_votes.rb +25 -0
  268. data/db/migrate/20260401072500_add_advanced_from_to_cm_voting_contestants.rb +7 -0
  269. data/db/migrate/20260402000001_add_voting_credit_scope_to_spree_taxons.rb +6 -0
  270. data/db/migrate/20260402000002_rename_scopeable_to_votable_in_cm_voting_credits.rb +12 -0
  271. data/db/migrate/20260403070000_add_name_to_cm_voting_sessions.rb +5 -0
  272. data/db/migrate/20260406000001_add_vendor_id_to_voting_tables.rb +6 -0
  273. data/db/migrate/20260406000001_rename_votes_remaining_to_amount_in_cm_voting_credits.rb +11 -0
  274. data/db/migrate/20260408085255_add_show_id_and_vendor_id_to_cm_voting_sessions.rb +9 -0
  275. data/db/migrate/20260410045815_create_cm_currency_rates.rb +23 -0
  276. data/db/migrate/20260411102139_add_metadata_to_cm_inventory_items.rb +6 -0
  277. data/db/migrate/20260420000001_rename_type_to_credit_type_in_cm_voting_credits.rb +25 -0
  278. data/db/migrate/20260422000001_create_cm_vote_fraud_events.rb +23 -0
  279. data/db/migrate/20260423000001_add_preview_to_taxons_products_and_sections.rb +11 -0
  280. data/db/migrate/20260423000002_create_preview_roles.rb +24 -0
  281. data/db/migrate/20260508103605_add_sms_sender_id_to_cm_tenants.rb +6 -0
  282. data/db/migrate/20260511000000_add_featured_until_to_cm_trips.rb +6 -0
  283. data/db/migrate/20260515120000_add_public_metadata_to_cm_voting_sessions.rb +5 -0
  284. data/db/migrate/20260520000001_optimize_cm_votes_indexes.rb +22 -0
  285. data/docs/sql/jsonb_query_guide.md +57 -0
  286. data/lib/spree_cm_commissioner/test_helper/factories/integration_factory.rb +11 -0
  287. data/lib/spree_cm_commissioner/test_helper/factories/show_episode_factory.rb +12 -0
  288. data/lib/spree_cm_commissioner/test_helper/factories/show_factory.rb +96 -0
  289. data/lib/spree_cm_commissioner/test_helper/factories/vote_credit_factory.rb +37 -0
  290. data/lib/spree_cm_commissioner/test_helper/factories/vote_factory.rb +28 -0
  291. data/lib/spree_cm_commissioner/test_helper/factories/voting_credit_transaction_factory.rb +11 -0
  292. data/lib/spree_cm_commissioner/test_helper/factories/voting_session_factory.rb +11 -0
  293. data/lib/spree_cm_commissioner/version.rb +1 -1
  294. data/lib/spree_cm_commissioner.rb +7 -1
  295. metadata +224 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a112e266f3450b307b12ea0ac4a28a4d5ee9a3eff8e2650d6de5c93c1b73df3
4
- data.tar.gz: 3285f85273a6c1d19e2eb4b23f087eccca371af1f6cd48282c22042b97d4d8d8
3
+ metadata.gz: 8250d9a96dcceb058991c416ad4b91380fa42f5d43e301c366e74bfe9eb0e27b
4
+ data.tar.gz: a8c9301047c15f6ef760bdda1cc9087ac4f7a3539cd6aa86b43a99ab97fc6f16
5
5
  SHA512:
6
- metadata.gz: 81c02501bd52647acbc65cc34e6ebcfa309b349a1215a0eadde56101b8d508877cb96e08c044612863ea458be0e70ff018311dc5be59806bc67f97a7cff99061
7
- data.tar.gz: feb4e3ef254c32769245dd1053ab3ec987f73250db800bd36373bf40e5d9e9ea981586b92a57fbe8bbcfc22ab09bf5cf7343b1a9b8d35bf672e1057510be11ad
6
+ metadata.gz: 87bba18fe41a801abf62fc7a83c444452f5b3780b23edaa44bbf86b98271b06877bc8c52bddfc5761b5fdee5953226201c52556cfba30405c1e1f95af2067861
7
+ data.tar.gz: 2a7fd81827d6be81291146d17f46ff54a61a59aa4194a552f89ccc59463dc93a4ccc742c0534fd5c18354136314b2e9db571e97b6e8822fff3bbc6c116f3114d
data/.gitignore CHANGED
@@ -34,3 +34,7 @@ vendor/bundle/
34
34
  # Cursor
35
35
  .cursor
36
36
  dump.rdb
37
+ # Added by code-review-graph
38
+ .code-review-graph/
39
+ .cursorrules
40
+ .claude
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (2.8.0)
37
+ spree_cm_commissioner (2.8.1.pre.3)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -0,0 +1,135 @@
1
+ # rubocop:disable Rails/I18nLocaleTexts
2
+ module Spree
3
+ module Admin
4
+ class FeaturedTripsController < BaseController
5
+ before_action :require_taxon_manager
6
+
7
+ def index
8
+ @status = filter_status
9
+ @query = params[:q].to_s.strip
10
+ @vendor_id = params[:vendor_id].presence
11
+ @origin_place_id = params[:origin_place_id].presence
12
+ @destination_place_id = params[:destination_place_id].presence
13
+ @vendors = Spree::Vendor.order(:name)
14
+ @places = SpreeCmCommissioner::Place.order(:name)
15
+ @active_count = SpreeCmCommissioner::Trip.currently_featured.count
16
+ @expired_count = SpreeCmCommissioner::Trip.where.not(featured_until: nil).where('featured_until < ?', Time.current).count
17
+ @featured_trips = filtered_featured_trips
18
+ @available_trips = available_trips
19
+ end
20
+
21
+ def create
22
+ trip = SpreeCmCommissioner::Trip.find(params[:trip_id])
23
+ featured_until = parse_featured_until(params[:featured_until])
24
+
25
+ if featured_until.present? && featured_until > Time.current && trip.update(featured_until: featured_until)
26
+ flash[:success] = 'Trip was added to featured trips.'
27
+ else
28
+ flash[:error] = 'Featured until must be a future date and time.'
29
+ end
30
+
31
+ redirect_to spree.admin_featured_trips_path
32
+ end
33
+
34
+ def update
35
+ trip = SpreeCmCommissioner::Trip.find(params[:id])
36
+
37
+ if ActiveModel::Type::Boolean.new.cast(params[:remove])
38
+ trip.update(featured_until: nil)
39
+ flash[:success] = 'Trip was removed from featured trips.'
40
+ else
41
+ update_featured_until(trip)
42
+ end
43
+
44
+ redirect_to spree.admin_featured_trips_path(
45
+ status: params[:status],
46
+ vendor_id: params[:vendor_id],
47
+ origin_place_id: params[:origin_place_id],
48
+ destination_place_id: params[:destination_place_id],
49
+ q: params[:q]
50
+ )
51
+ end
52
+
53
+ private
54
+
55
+ def require_taxon_manager
56
+ return if can?(:manage, Spree::Taxon)
57
+
58
+ flash[:error] = 'You are not authorized to access this page.'
59
+ redirect_to spree.admin_promotions_path
60
+ end
61
+
62
+ def update_featured_until(trip)
63
+ featured_until = parse_featured_until(params[:featured_until])
64
+
65
+ if featured_until.present? && featured_until > Time.current && trip.update(featured_until: featured_until)
66
+ flash[:success] = 'Featured trip was updated.'
67
+ else
68
+ flash[:error] = 'Featured until must be a future date and time.'
69
+ end
70
+ end
71
+
72
+ def filtered_featured_trips
73
+ scope = SpreeCmCommissioner::Trip.includes(:vendor, :product, :origin_place, :destination_place, :vehicle_type)
74
+ .where.not(featured_until: nil)
75
+ scope = apply_status_filter(scope)
76
+ scope = apply_place_filter(scope)
77
+ scope = apply_vendor_filter(scope)
78
+ scope = apply_text_filter(scope)
79
+ scope.order(featured_until: :asc, departure_time: :asc).page(params[:page]).per(50)
80
+ end
81
+
82
+ def available_trips
83
+ scope = SpreeCmCommissioner::Trip.includes(:vendor, :product, :origin_place, :destination_place, :vehicle_type)
84
+ .not_currently_featured
85
+ scope = apply_place_filter(scope)
86
+ scope = apply_vendor_filter(scope)
87
+ scope = apply_text_filter(scope)
88
+ scope.order(departure_time: :desc).limit(200)
89
+ end
90
+
91
+ def apply_status_filter(scope)
92
+ case @status
93
+ when 'expired'
94
+ scope.where('cm_trips.featured_until < ?', Time.current)
95
+ when 'all'
96
+ scope
97
+ else
98
+ scope.currently_featured
99
+ end
100
+ end
101
+
102
+ def apply_vendor_filter(scope)
103
+ return scope if @vendor_id.blank?
104
+
105
+ scope.where(vendor_id: @vendor_id)
106
+ end
107
+
108
+ def apply_place_filter(scope)
109
+ scope = scope.where(origin_place_id: @origin_place_id) if @origin_place_id.present?
110
+ scope = scope.where(destination_place_id: @destination_place_id) if @destination_place_id.present?
111
+ scope
112
+ end
113
+
114
+ def apply_text_filter(scope)
115
+ return scope if @query.blank?
116
+
117
+ scope.left_joins(:product, :vendor)
118
+ .where('spree_products.name ILIKE :query OR spree_vendors.name ILIKE :query', query: "%#{@query}%")
119
+ end
120
+
121
+ def filter_status
122
+ %w[active expired all].include?(params[:status]) ? params[:status] : 'active'
123
+ end
124
+
125
+ def parse_featured_until(value)
126
+ return if value.blank?
127
+
128
+ Time.zone.parse(value.to_s)
129
+ rescue ArgumentError, TypeError
130
+ nil
131
+ end
132
+ end
133
+ end
134
+ end
135
+ # rubocop:enable Rails/I18nLocaleTexts
@@ -51,7 +51,10 @@ module Spree
51
51
  def permitted_resource_params
52
52
  segment_value = helpers.calculate_segment_value(params[:spree_cm_commissioner_homepage_section])
53
53
 
54
- params.require(:spree_cm_commissioner_homepage_section).permit(:title, :description, :active, :tenant_id).merge(segment: segment_value)
54
+ params
55
+ .require(:spree_cm_commissioner_homepage_section)
56
+ .permit(:title, :description, :active, :tenant_id, :preview)
57
+ .merge(segment: segment_value)
55
58
  end
56
59
 
57
60
  def load_tenants
@@ -0,0 +1,19 @@
1
+ module Spree
2
+ module Admin
3
+ class OauthApplicationsController < Spree::Admin::ResourceController
4
+ # Override default scopes behavior - only auto-assign 'admin' scope for new applications
5
+ # Edit forms allow users to modify scopes without forced default
6
+ before_action :set_default_scopes, only: [:new]
7
+
8
+ private
9
+
10
+ def create_turbo_stream_enabled?
11
+ true
12
+ end
13
+
14
+ def set_default_scopes
15
+ @object.scopes = 'admin' if @object.scopes.blank?
16
+ end
17
+ end
18
+ end
19
+ end
@@ -3,6 +3,7 @@ module Spree
3
3
  module TaxonsControllerDecorator
4
4
  def self.prepended(base)
5
5
  base.before_action :build_assets, only: %i[create update]
6
+ base.before_action :normalize_sti_taxon_params, only: %i[create update]
6
7
  end
7
8
 
8
9
  # override
@@ -50,6 +51,24 @@ module Spree
50
51
  @taxon.build_home_banner(attachment: permitted_resource_params.delete(:home_banner)) if permitted_resource_params[:home_banner]
51
52
  @taxon.build_video_banner(attachment: permitted_resource_params.delete(:video_banner)) if permitted_resource_params[:video_banner]
52
53
  end
54
+
55
+ # Some admin forms submit STI records under their class key
56
+ # (e.g. :spree_cm_commissioner_show) while Spree update reads :taxon.
57
+ # Merge both into :taxon so all edited fields are persisted.
58
+ def normalize_sti_taxon_params
59
+ return unless @taxon
60
+
61
+ sti_key = @taxon.class.model_name.param_key.to_sym
62
+ sti_params = params[sti_key]
63
+ return unless sti_params.is_a?(::ActionController::Parameters)
64
+
65
+ base_params = params[:taxon]
66
+ base_hash = base_params.is_a?(::ActionController::Parameters) ? base_params.to_unsafe_h : {}
67
+ sti_hash = sti_params.to_unsafe_h
68
+ merged_params = ::ActionController::Parameters.new(base_hash.merge(sti_hash))
69
+
70
+ params[:taxon] = merged_params
71
+ end
53
72
  end
54
73
  end
55
74
  end
@@ -19,6 +19,7 @@ module Spree
19
19
  @collection ||= model_class.filter_by_segment(params[:homepage_id] || :general)
20
20
  .active
21
21
  .where(tenant_id: nil)
22
+ .where(preview: false)
22
23
  .order(position: :asc)
23
24
  end
24
25
  end
@@ -0,0 +1,48 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ class PreviewProductsController < ::Spree::Api::V2::Storefront::ProductsController
6
+ before_action :require_spree_current_user
7
+
8
+ private
9
+
10
+ def scope(**args)
11
+ taxon_type = model_class.reflect_on_association(:taxons).klass.polymorphic_name
12
+
13
+ # IDs of products that have been explicitly assigned their own PreviewRole.
14
+ products_with_own_roles = SpreeCmCommissioner::PreviewRole
15
+ .where(previewable_type: model_class.polymorphic_name)
16
+ .select(:previewable_id)
17
+
18
+ # Base: all active preview products from the parent scope.
19
+ base = super(**args).rewhere(preview: true).where(status: :active)
20
+
21
+ # Path A: product has its own PreviewRole → user must hold that role directly.
22
+ # Taxon membership is irrelevant for these products.
23
+ via_product_role = base
24
+ .where(id: products_with_own_roles)
25
+ .where(id: spree_current_user.preview_roles
26
+ .where(previewable_type: model_class.polymorphic_name)
27
+ .select(:previewable_id)
28
+ )
29
+
30
+ # Path B: product has no own PreviewRole → inherit access from its taxons.
31
+ # Accessible if the user holds a preview role for any taxon the product belongs to.
32
+ via_taxon = base
33
+ .where.not(id: products_with_own_roles)
34
+ .joins(:taxons)
35
+ .where(spree_taxons: { id: spree_current_user.preview_roles
36
+ .where(previewable_type: taxon_type)
37
+ .select(:previewable_id)
38
+ }
39
+ )
40
+
41
+ # via_taxon uses joins(:taxons) which is incompatible with or — wrap as subquery.
42
+ via_product_role.or(base.where(id: via_taxon.select(:id)))
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,27 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ class PreviewSectionsController < HomepageSectionsController
6
+ before_action :require_spree_current_user
7
+
8
+ # override
9
+ def collection
10
+ allowed_ids = spree_current_user.preview_roles.where(previewable_type: Spree::Taxon.polymorphic_name).pluck(:previewable_id)
11
+
12
+ section_ids = SpreeCmCommissioner::HomepageSectionRelatable
13
+ .where(relatable_type: 'Spree::Taxon', relatable_id: allowed_ids.presence || [])
14
+ .select(:homepage_section_id)
15
+
16
+ @collection ||= model_class.filter_by_segment(params[:homepage_id] || :general)
17
+ .active
18
+ .where(tenant_id: nil)
19
+ .where(preview: true)
20
+ .where(id: section_ids)
21
+ .order(position: :asc)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,18 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ class PreviewTaxonsController < ::Spree::Api::V2::Storefront::TaxonsController
6
+ before_action :require_spree_current_user
7
+
8
+ private
9
+
10
+ def scope(**args)
11
+ allowed_ids = spree_current_user.preview_roles.where(previewable_type: model_class.polymorphic_name).select(:previewable_id)
12
+ super(**args).rewhere(preview: true).where(id: allowed_ids)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ module ProductsControllerDecorator
6
+ def scope
7
+ super.where(preview: false)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ Spree::Api::V2::Storefront::ProductsController.prepend(Spree::Api::V2::Storefront::ProductsControllerDecorator)
@@ -0,0 +1,15 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ module TaxonsControllerDecorator
6
+ def scope
7
+ super.where(preview: false)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ Spree::Api::V2::Storefront::TaxonsController.prepend(Spree::Api::V2::Storefront::TaxonsControllerDecorator)
@@ -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
@@ -39,6 +39,7 @@ module Spree
39
39
  params[:date],
40
40
  params[:route_type],
41
41
  params[:vendor_id],
42
+ params[:featured],
42
43
  params[:number_of_guests],
43
44
  resource_includes&.sort&.join(','),
44
45
  sparse_fields&.sort&.join(','),
@@ -16,6 +16,10 @@ module Spree
16
16
  set_current_tenant(@tenant)
17
17
  end
18
18
 
19
+ def current_vendor
20
+ @current_vendor ||= @tenant.active_vendor
21
+ end
22
+
19
23
  def render_serialized_payload(status = 200)
20
24
  render json: yield, status: status, content_type: content_type
21
25
  end
@@ -0,0 +1,37 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class FreeVoteClaimsController < BaseController
6
+ before_action :require_spree_current_user
7
+
8
+ def create
9
+ show = current_vendor.shows.find(params[:show_id])
10
+
11
+ result = SpreeCmCommissioner::VotingCredits::ClaimFreeVotes.new(
12
+ show: show,
13
+ user: spree_current_user,
14
+ tenant_id: MultiTenant.current_tenant_id,
15
+ votable_type: params[:votable_type],
16
+ votable_id: params[:votable_id]
17
+ ).call
18
+
19
+ if result.failure?
20
+ render_error_payload(result.error.to_s)
21
+ elsif result.value.nil?
22
+ head :no_content
23
+ else
24
+ render_serialized_payload { serialize_resource(result.value) }
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def resource_serializer
31
+ Spree::V2::Tenant::VotingCreditSerializer
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -5,6 +5,7 @@ module Spree
5
5
  class HomepageSectionsController < BaseController
6
6
  def collection
7
7
  @collection ||= scope.filter_by_segment(params[:homepage_id] || :general)
8
+ .where(preview: false)
8
9
  .active
9
10
  .order(position: :asc)
10
11
  end
@@ -0,0 +1,47 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class PreviewProductsController < ProductsController
6
+ before_action :require_spree_current_user
7
+
8
+ # override
9
+ def scope
10
+ taxon_type = model_class.reflect_on_association(:taxons).klass.polymorphic_name
11
+
12
+ # IDs of products that have been explicitly assigned their own PreviewRole.
13
+ products_with_own_roles = SpreeCmCommissioner::PreviewRole
14
+ .where(previewable_type: model_class.polymorphic_name)
15
+ .select(:previewable_id)
16
+
17
+ # Base: all active preview products from the parent scope.
18
+ base = super.where(status: :active, preview: true)
19
+
20
+ # Path A: product has its own PreviewRole → user must hold that role directly.
21
+ # Taxon membership is irrelevant for these products.
22
+ via_product_role = base
23
+ .where(id: products_with_own_roles)
24
+ .where(id: spree_current_user.preview_roles
25
+ .where(previewable_type: model_class.polymorphic_name)
26
+ .select(:previewable_id)
27
+ )
28
+
29
+ # Path B: product has no own PreviewRole → inherit access from its taxons.
30
+ # Accessible if the user holds a preview role for any taxon the product belongs to.
31
+ via_taxon = base
32
+ .where.not(id: products_with_own_roles)
33
+ .joins(:taxons)
34
+ .where(spree_taxons: { id: spree_current_user.preview_roles
35
+ .where(previewable_type: taxon_type)
36
+ .select(:previewable_id)
37
+ }
38
+ )
39
+
40
+ # via_taxon uses joins(:taxons) which is incompatible with or — wrap as subquery.
41
+ via_product_role.or(base.where(id: via_taxon.select(:id)))
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,26 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class PreviewSectionsController < HomepageSectionsController
6
+ before_action :require_spree_current_user
7
+
8
+ # override
9
+ def collection
10
+ allowed_ids = spree_current_user.preview_roles.where(previewable_type: Spree::Taxon.polymorphic_name).pluck(:previewable_id)
11
+
12
+ section_ids = SpreeCmCommissioner::HomepageSectionRelatable
13
+ .where(relatable_type: 'Spree::Taxon', relatable_id: allowed_ids.presence || [])
14
+ .select(:homepage_section_id)
15
+
16
+ @collection ||= scope.filter_by_segment(params[:homepage_id] || :general)
17
+ .active
18
+ .where(preview: true)
19
+ .where(id: section_ids)
20
+ .order(position: :asc)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class PreviewShowsController < ShowsController
6
+ before_action :require_spree_current_user
7
+
8
+ # override
9
+ def scope
10
+ allowed_ids = spree_current_user.preview_roles
11
+ .where(previewable_type: SpreeCmCommissioner::Show.polymorphic_name)
12
+ .select(:previewable_id)
13
+ super.rewhere(preview: true).where(id: allowed_ids)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class PreviewTaxonsController < TaxonsController
6
+ before_action :require_spree_current_user
7
+
8
+ # override
9
+ def scope
10
+ allowed_ids = spree_current_user.preview_roles.where(previewable_type: model_class.polymorphic_name).select(:previewable_id)
11
+ MultiTenant.with(@tenant) do
12
+ model_class.where(preview: true, id: allowed_ids)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -21,7 +21,7 @@ module Spree
21
21
 
22
22
  def scope
23
23
  MultiTenant.with(@tenant) do
24
- model_class.where(tenant_id: MultiTenant.current_tenant_id)
24
+ model_class.where(tenant_id: MultiTenant.current_tenant_id, preview: false)
25
25
  end
26
26
  end
27
27