spree_cm_commissioner 2.8.3 → 2.8.4.pre.pre2
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.
- checksums.yaml +4 -4
- data/.github/workflows/test_and_build_gem.yml +2 -1
- data/.gitignore +6 -0
- data/Gemfile.lock +1 -1
- data/app/controllers/concerns/spree_cm_commissioner/content_cachable.rb +2 -0
- data/app/controllers/spree/admin/guests_controller.rb +1 -1
- data/app/controllers/spree/admin/homepage_section_controller.rb +4 -1
- data/app/controllers/spree/admin/order_ticket_transfers_controller.rb +25 -0
- data/app/controllers/spree/admin/stock_managements_controller.rb +1 -1
- data/app/controllers/spree/admin/system/waiting_room_controller.rb +15 -0
- data/app/controllers/spree/admin/taxons_controller_decorator.rb +24 -0
- data/app/controllers/spree/admin/ticket_transfer_rules_controller.rb +47 -0
- data/app/controllers/spree/admin/ticket_transfers_controller.rb +61 -0
- data/app/controllers/spree/api/v2/storefront/account/recipients_controller.rb +32 -0
- data/app/controllers/spree/api/v2/storefront/guests_controller.rb +7 -5
- data/app/controllers/spree/api/v2/storefront/homepage_sections_controller.rb +1 -0
- data/app/controllers/spree/api/v2/storefront/preview_products_controller.rb +48 -0
- data/app/controllers/spree/api/v2/storefront/preview_sections_controller.rb +27 -0
- data/app/controllers/spree/api/v2/storefront/preview_taxons_controller.rb +18 -0
- data/app/controllers/spree/api/v2/storefront/products_controller_decorator.rb +15 -0
- data/app/controllers/spree/api/v2/storefront/taxons_controller_decorator.rb +15 -0
- data/app/controllers/spree/api/v2/storefront/ticket_transfer_acceptances_controller.rb +32 -0
- data/app/controllers/spree/api/v2/storefront/ticket_transfers_controller.rb +117 -0
- data/app/controllers/spree/api/v2/storefront/transit/draft_orders_controller.rb +3 -1
- data/app/controllers/spree/api/v2/tenant/ad_campaigns_controller.rb +25 -0
- data/app/controllers/spree/api/v2/tenant/base_controller.rb +4 -0
- data/app/controllers/spree/api/v2/tenant/episodes_controller.rb +38 -0
- data/app/controllers/spree/api/v2/tenant/free_vote_claim_controller.rb +58 -0
- data/app/controllers/spree/api/v2/tenant/homepage_sections_controller.rb +1 -0
- data/app/controllers/spree/api/v2/tenant/preview_products_controller.rb +47 -0
- data/app/controllers/spree/api/v2/tenant/preview_sections_controller.rb +26 -0
- data/app/controllers/spree/api/v2/tenant/preview_shows_controller.rb +19 -0
- data/app/controllers/spree/api/v2/tenant/preview_taxons_controller.rb +19 -0
- data/app/controllers/spree/api/v2/tenant/products_controller.rb +1 -1
- data/app/controllers/spree/api/v2/tenant/show_contestants_controller.rb +52 -0
- data/app/controllers/spree/api/v2/tenant/show_elimination_sessions_controller.rb +57 -0
- data/app/controllers/spree/api/v2/tenant/show_people_controller.rb +49 -0
- data/app/controllers/spree/api/v2/tenant/show_person_assignments_controller.rb +36 -0
- data/app/controllers/spree/api/v2/tenant/shows_controller.rb +36 -0
- data/app/controllers/spree/api/v2/tenant/taxons_controller.rb +1 -1
- data/app/controllers/spree/api/v2/tenant/transit/draft_orders_controller.rb +3 -1
- data/app/controllers/spree/api/v2/tenant/trips_controller.rb +11 -0
- data/app/controllers/spree/api/v2/tenant/vote_packages_controller.rb +33 -0
- data/app/controllers/spree/api/v2/tenant/votes_controller.rb +94 -0
- data/app/controllers/spree/api/v2/tenant/voting_contestants_controller.rb +43 -0
- data/app/controllers/spree/api/v2/tenant/voting_credit_transactions_controller.rb +41 -0
- data/app/controllers/spree/api/v2/tenant/voting_credits_controller.rb +31 -0
- data/app/controllers/spree_cm_commissioner/admin/orders_controller_decorator.rb +7 -3
- data/app/controllers/spree_cm_commissioner/admin/products_controller_decorator.rb +19 -0
- data/app/finders/spree_cm_commissioner/events/find_matches.rb +1 -0
- data/app/helpers/spree_cm_commissioner/admin/guest_helper.rb +10 -0
- data/app/helpers/spree_cm_commissioner/admin/homepage_segment_helper.rb +2 -0
- data/app/helpers/spree_cm_commissioner/admin/ticket_transfers_helper.rb +32 -0
- data/app/interactors/spree_cm_commissioner/pin_code_sender.rb +62 -5
- data/app/interactors/spree_cm_commissioner/waiting_guests_caller.rb +86 -10
- data/app/jobs/spree_cm_commissioner/expire_ticket_transfer_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/show_contestants/import_job.rb +9 -0
- data/app/jobs/spree_cm_commissioner/telegram_gateway/pin_code_sender_job.rb +18 -0
- data/app/jobs/spree_cm_commissioner/vendor_route_metrics/decrease_trip_count_job.rb +17 -0
- data/app/jobs/spree_cm_commissioner/vendor_route_metrics/increase_trip_count_job.rb +14 -0
- data/app/jobs/spree_cm_commissioner/vote_fraud_event_job.rb +9 -0
- data/app/jobs/spree_cm_commissioner/voting_credit_allocation_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/voting_credit_de_allocation_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/waiting_room/publish_lobby_path_job.rb +17 -0
- data/app/jobs/spree_cm_commissioner/waiting_room/stamp_queue_positions_job.rb +84 -0
- data/app/models/concerns/spree_cm_commissioner/homepage_section_bitwise.rb +2 -1
- data/app/models/concerns/spree_cm_commissioner/line_item_guests_concern.rb +16 -2
- data/app/models/concerns/spree_cm_commissioner/line_item_open_dated_trippable.rb +50 -7
- data/app/models/concerns/spree_cm_commissioner/line_item_transitable.rb +0 -1
- data/app/models/concerns/spree_cm_commissioner/line_items_filter_scope.rb +5 -0
- data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +2 -1
- data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +10 -0
- data/app/models/concerns/spree_cm_commissioner/product_type.rb +1 -1
- data/app/models/concerns/spree_cm_commissioner/taxon_kind.rb +3 -1
- data/app/models/concerns/spree_cm_commissioner/ticket_transferable.rb +15 -0
- data/app/models/concerns/spree_cm_commissioner/voting_session_live_stream_concern.rb +41 -0
- data/app/models/spree_cm_commissioner/guest.rb +20 -0
- data/app/models/spree_cm_commissioner/import.rb +1 -1
- data/app/models/spree_cm_commissioner/imports/import_contestant.rb +7 -0
- data/app/models/spree_cm_commissioner/line_item_decorator.rb +7 -0
- data/app/models/spree_cm_commissioner/maintenance_tasks/voting_session.rb +36 -0
- data/app/models/spree_cm_commissioner/notification.rb +10 -1
- data/app/models/spree_cm_commissioner/order_decorator.rb +13 -3
- data/app/models/spree_cm_commissioner/payment_decorator.rb +19 -0
- data/app/models/spree_cm_commissioner/payment_method_decorator.rb +8 -0
- data/app/models/spree_cm_commissioner/pin_code.rb +62 -9
- data/app/models/spree_cm_commissioner/preview_role.rb +8 -0
- data/app/models/spree_cm_commissioner/product_decorator.rb +56 -0
- data/app/models/spree_cm_commissioner/reserved_block.rb +2 -0
- data/app/models/spree_cm_commissioner/role_decorator.rb +4 -1
- data/app/models/spree_cm_commissioner/role_user_decorator.rb +8 -0
- data/app/models/spree_cm_commissioner/show.rb +169 -0
- data/app/models/spree_cm_commissioner/show_contestant.rb +39 -0
- data/app/models/spree_cm_commissioner/show_contestant_image.rb +11 -0
- data/app/models/spree_cm_commissioner/show_contestant_video.rb +15 -0
- data/app/models/spree_cm_commissioner/show_episode.rb +108 -0
- data/app/models/spree_cm_commissioner/show_person.rb +15 -0
- data/app/models/spree_cm_commissioner/show_person_assignment.rb +21 -0
- data/app/models/spree_cm_commissioner/show_person_image.rb +11 -0
- data/app/models/spree_cm_commissioner/stock/availability_validator_decorator.rb +4 -0
- data/app/models/spree_cm_commissioner/stock/order_availability_checker.rb +3 -0
- data/app/models/spree_cm_commissioner/taxon_ad_banner.rb +12 -0
- data/app/models/spree_cm_commissioner/taxon_decorator.rb +24 -0
- data/app/models/spree_cm_commissioner/taxonomy_decorator.rb +19 -1
- data/app/models/spree_cm_commissioner/tenant.rb +9 -0
- data/app/models/spree_cm_commissioner/ticket_transfer.rb +191 -0
- data/app/models/spree_cm_commissioner/ticket_transfer_rule.rb +88 -0
- data/app/models/spree_cm_commissioner/trip.rb +33 -12
- data/app/models/spree_cm_commissioner/user_decorator.rb +18 -0
- data/app/models/spree_cm_commissioner/variant_decorator.rb +4 -0
- data/app/models/spree_cm_commissioner/variant_options.rb +4 -0
- data/app/models/spree_cm_commissioner/vendor_decorator.rb +5 -0
- data/app/models/spree_cm_commissioner/vendor_route_metric.rb +25 -0
- data/app/models/spree_cm_commissioner/vote.rb +16 -0
- data/app/models/spree_cm_commissioner/vote_fraud_event.rb +20 -0
- data/app/models/spree_cm_commissioner/voting_contestant.rb +100 -0
- data/app/models/spree_cm_commissioner/voting_credit.rb +72 -0
- data/app/models/spree_cm_commissioner/voting_credit_transaction.rb +55 -0
- data/app/models/spree_cm_commissioner/voting_session.rb +216 -0
- data/app/models/spree_cm_commissioner/voting_session_stat.rb +8 -0
- data/app/notifications/spree_cm_commissioner/ticket_transfer_accepted_notification.rb +48 -0
- data/app/notifications/spree_cm_commissioner/ticket_transfer_completed_notification.rb +48 -0
- data/app/notifications/spree_cm_commissioner/ticket_transfer_expired_notification.rb +46 -0
- data/app/notifications/spree_cm_commissioner/ticket_transfer_received_notification.rb +48 -0
- data/app/notifications/spree_cm_commissioner/ticket_transfer_rejected_notification.rb +48 -0
- data/app/overrides/spree/admin/products/_form/allow_gift_transfer.html.erb.deface +9 -0
- data/app/overrides/spree/admin/products/_form/allow_transfer.html.erb.deface +9 -0
- data/app/overrides/spree/admin/products/_form/preview_checkbox.html.erb.deface +9 -0
- data/app/overrides/spree/admin/shared/_order_tabs/ticket_transfers.html.erb.deface +10 -0
- data/app/overrides/spree/admin/shared/sub_menu/_product/ticket_transfers_tab.html.erb.deface +3 -0
- data/app/overrides/spree/admin/taxons/_form/assets_form.html.erb.deface +10 -1
- data/app/overrides/spree/admin/taxons/_form/preview_checkbox.html.erb.deface +7 -0
- data/app/queries/spree_cm_commissioner/multi_leg_trips_query.rb +31 -8
- data/app/queries/spree_cm_commissioner/recipient_searcher_query.rb +14 -0
- data/app/queries/spree_cm_commissioner/single_leg_trips_query.rb +57 -5
- data/app/serializers/spree/v2/storefront/homepage_section_serializer.rb +1 -1
- data/app/serializers/spree/v2/storefront/line_item_serializer_decorator.rb +4 -2
- data/app/serializers/spree/v2/storefront/product_serializer_decorator.rb +5 -2
- data/app/serializers/spree/v2/storefront/role_serializer.rb +1 -1
- data/app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb +4 -1
- data/app/serializers/spree/v2/tenant/ad_campaign_serializer.rb +13 -0
- data/app/serializers/spree/v2/tenant/advertisement_serializer.rb +11 -0
- data/app/serializers/spree/v2/tenant/dynamic_field_serializer.rb +1 -1
- data/app/serializers/spree/v2/tenant/free_vote_claim_serializer.rb +9 -0
- data/app/serializers/spree/v2/tenant/homepage_section_serializer.rb +1 -1
- data/app/serializers/spree/v2/tenant/role_serializer.rb +1 -1
- data/app/serializers/spree/v2/tenant/show_contestant_serializer.rb +21 -0
- data/app/serializers/spree/v2/tenant/show_episode_serializer.rb +20 -0
- data/app/serializers/spree/v2/tenant/show_person_assignment_serializer.rb +16 -0
- data/app/serializers/spree/v2/tenant/show_person_serializer.rb +13 -0
- data/app/serializers/spree/v2/tenant/show_serializer.rb +21 -0
- data/app/serializers/spree/v2/tenant/video_serializer.rb +9 -0
- data/app/serializers/spree/v2/tenant/vote_package_serializer.rb +12 -0
- data/app/serializers/spree/v2/tenant/vote_serializer.rb +14 -0
- data/app/serializers/spree/v2/tenant/voting_contestant_serializer.rb +23 -0
- data/app/serializers/spree/v2/tenant/voting_credit_serializer.rb +10 -0
- data/app/serializers/spree/v2/tenant/voting_credit_transaction_serializer.rb +14 -0
- data/app/serializers/spree/v2/tenant/voting_session_serializer.rb +21 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/dynamic_field_serializer.rb +1 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/guest_minimal_serializer.rb +30 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb +3 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/line_item_minimal_serializer.rb +57 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/pin_code_serializer.rb +1 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/recipient_serializer.rb +13 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/reserved_block_serializer.rb +1 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/ticket_transfer_minimal_serializer.rb +17 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/ticket_transfer_serializer.rb +20 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_query_result_serializer.rb +4 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_result_serializer.rb +2 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_stop_serializer.rb +1 -0
- data/app/services/spree_cm_commissioner/api_caches/invalidate.rb +12 -0
- data/app/services/spree_cm_commissioner/cart/add_guest.rb +6 -1
- data/app/services/spree_cm_commissioner/cart/create_guest.rb +61 -0
- data/app/services/spree_cm_commissioner/cart/remove_guest.rb +6 -1
- data/app/services/spree_cm_commissioner/check_ins/create_bulk.rb +11 -5
- data/app/services/spree_cm_commissioner/checkout/complete_decorator.rb +22 -0
- data/app/services/spree_cm_commissioner/fraud_check.rb +299 -0
- data/app/services/spree_cm_commissioner/imports/contestants/create.rb +153 -0
- data/app/services/spree_cm_commissioner/inventory_items/reset.rb +1 -1
- data/app/services/spree_cm_commissioner/open_dated_trips/redeem.rb +80 -36
- data/app/services/spree_cm_commissioner/reserved_blocks/reserve.rb +2 -1
- data/app/services/spree_cm_commissioner/show_contestants/normalize_video_highlights.rb +57 -0
- data/app/services/spree_cm_commissioner/show_persons/contestant_assigner.rb +51 -0
- data/app/services/spree_cm_commissioner/telegram_gateway/pin_code_sender.rb +99 -0
- data/app/services/spree_cm_commissioner/ticket_transfers/accept.rb +132 -0
- data/app/services/spree_cm_commissioner/ticket_transfers/cancel.rb +54 -0
- data/app/services/spree_cm_commissioner/ticket_transfers/complete.rb +68 -0
- data/app/services/spree_cm_commissioner/ticket_transfers/eligibility_checker.rb +99 -0
- data/app/services/spree_cm_commissioner/ticket_transfers/expire.rb +47 -0
- data/app/services/spree_cm_commissioner/ticket_transfers/fee_calculator.rb +57 -0
- data/app/services/spree_cm_commissioner/ticket_transfers/initiate.rb +98 -0
- data/app/services/spree_cm_commissioner/ticket_transfers/preview_rule.rb +38 -0
- data/app/services/spree_cm_commissioner/ticket_transfers/reject.rb +55 -0
- data/app/services/spree_cm_commissioner/ticket_transfers/transfer_guest.rb +29 -0
- data/app/services/spree_cm_commissioner/transit_order/create.rb +48 -14
- data/app/services/spree_cm_commissioner/trips/add_ons/create.rb +124 -0
- data/app/services/spree_cm_commissioner/trips/add_ons/update_price.rb +36 -0
- data/app/services/spree_cm_commissioner/trips/create_multi_leg.rb +1 -2
- data/app/services/spree_cm_commissioner/trips/variants/create.rb +1 -2
- data/app/services/spree_cm_commissioner/url_embed/youtube_embed.rb +44 -0
- data/app/services/spree_cm_commissioner/vendor_route_metrics/decrease_trip_count.rb +19 -0
- data/app/services/spree_cm_commissioner/vendor_route_metrics/increase_trip_count.rb +24 -0
- data/app/services/spree_cm_commissioner/vote_counters/audit_counters.rb +43 -0
- data/app/services/spree_cm_commissioner/vote_counters/base.rb +31 -0
- data/app/services/spree_cm_commissioner/vote_counters/increment.rb +44 -0
- data/app/services/spree_cm_commissioner/vote_counters/per_contestant_counter.rb +68 -0
- data/app/services/spree_cm_commissioner/vote_counters/rebuild_from_db.rb +70 -0
- data/app/services/spree_cm_commissioner/vote_counters/snapshot_to_db.rb +113 -0
- data/app/services/spree_cm_commissioner/vote_credit_deductor.rb +68 -0
- data/app/services/spree_cm_commissioner/vote_package/create.rb +145 -0
- data/app/services/spree_cm_commissioner/vote_package/update.rb +91 -0
- data/app/services/spree_cm_commissioner/vote_processor.rb +144 -0
- data/app/services/spree_cm_commissioner/voting_contestants/advancer.rb +335 -0
- data/app/services/spree_cm_commissioner/voting_contestants/assigner.rb +40 -0
- data/app/services/spree_cm_commissioner/voting_contestants/bulk_updater.rb +106 -0
- data/app/services/spree_cm_commissioner/voting_credits/allocate.rb +77 -0
- data/app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb +116 -0
- data/app/services/spree_cm_commissioner/voting_credits/credit_calculator.rb +35 -0
- data/app/services/spree_cm_commissioner/voting_credits/de_allocate.rb +87 -0
- data/app/services/spree_cm_commissioner/voting_leaderboards/calculate_score.rb +67 -0
- data/app/services/spree_cm_commissioner/voting_leaderboards/combined_result.rb +190 -0
- data/app/services/spree_cm_commissioner/voting_sessions/finalize.rb +105 -0
- data/app/services/spree_cm_commissioner/waiting_room/publish_lobby_path.rb +48 -0
- data/app/services/spree_cm_commissioner/waiting_room_lobby_metadata_fetcher.rb +47 -0
- data/app/services/telegram_gateway_adapter/client.rb +124 -0
- data/app/views/spree/admin/guests/_table.html.erb +82 -87
- data/app/views/spree/admin/homepage_section/_form.html.erb +5 -0
- data/app/views/spree/admin/order_ticket_transfers/show.html.erb +7 -0
- data/app/views/spree/admin/shared/_ticket_transfer_details.html.erb +226 -0
- data/app/views/spree/admin/shared/_ticket_transfer_tabs.html.erb +14 -0
- data/app/views/spree/admin/system/waiting_room/show.html.erb +53 -0
- data/app/views/spree/admin/ticket_transfer_rules/_form.html.erb +193 -0
- data/app/views/spree/admin/ticket_transfer_rules/edit.html.erb +11 -0
- data/app/views/spree/admin/ticket_transfer_rules/index.html.erb +79 -0
- data/app/views/spree/admin/ticket_transfer_rules/new.html.erb +11 -0
- data/app/views/spree/admin/ticket_transfers/_search_form.html.erb +70 -0
- data/app/views/spree/admin/ticket_transfers/_tabs.html.erb +14 -0
- data/app/views/spree/admin/ticket_transfers/index.html.erb +74 -0
- data/app/views/spree/admin/ticket_transfers/show.html.erb +13 -0
- data/app/views/spree/admin/ticket_transfers/state_changes.html.erb +47 -0
- data/config/initializers/spree_permitted_attributes.rb +8 -0
- data/config/locales/en.yml +111 -7
- data/config/locales/km.yml +105 -4
- data/config/routes.rb +51 -0
- data/db/migrate/20260303101440_add_allow_transfer_to_spree_products.rb +5 -0
- data/db/migrate/20260303101907_create_cm_ticket_transfers.rb +32 -0
- data/db/migrate/20260303102316_add_state_to_cm_guests.rb +7 -0
- data/db/migrate/20260309230148_create_cm_show_people.rb +14 -0
- data/db/migrate/20260309230149_create_cm_show_people_assignments.rb +16 -0
- data/db/migrate/20260310082711_create_cm_show_contestants.rb +28 -0
- data/db/migrate/20260310082720_create_cm_voting_sessions.rb +21 -0
- data/db/migrate/20260310082721_create_cm_voting_contestants.rb +23 -0
- data/db/migrate/20260310082734_add_voting_fields_to_spree_taxons.rb +9 -0
- data/db/migrate/20260310082735_add_type_to_spree_products.rb +6 -0
- data/db/migrate/20260310082749_create_cm_voting_credits.rb +27 -0
- data/db/migrate/20260326080200_create_cm_voting_credit_transactions.rb +27 -0
- data/db/migrate/20260330160000_create_cm_votes.rb +25 -0
- data/db/migrate/20260401072500_add_advanced_from_to_cm_voting_contestants.rb +7 -0
- data/db/migrate/20260402000001_add_voting_credit_scope_to_spree_taxons.rb +6 -0
- data/db/migrate/20260402000002_rename_scopeable_to_votable_in_cm_voting_credits.rb +12 -0
- data/db/migrate/20260403070000_add_name_to_cm_voting_sessions.rb +5 -0
- data/db/migrate/20260406000001_add_vendor_id_to_voting_tables.rb +6 -0
- data/db/migrate/20260406000001_rename_votes_remaining_to_amount_in_cm_voting_credits.rb +11 -0
- data/db/migrate/20260408085255_add_show_id_and_vendor_id_to_cm_voting_sessions.rb +9 -0
- data/db/migrate/20260408100000_create_cm_ticket_transfer_rules.rb +28 -0
- data/db/migrate/20260410120000_add_transfer_type_to_cm_ticket_transfers.rb +5 -0
- data/db/migrate/20260420000001_rename_type_to_credit_type_in_cm_voting_credits.rb +25 -0
- data/db/migrate/20260422000001_create_cm_vote_fraud_events.rb +23 -0
- data/db/migrate/20260422120000_add_settlement_status_to_cm_ticket_transfers.rb +6 -0
- data/db/migrate/20260423000001_add_preview_to_taxons_products_and_sections.rb +11 -0
- data/db/migrate/20260423000002_create_preview_roles.rb +24 -0
- data/db/migrate/20260507000000_add_index_to_cm_ticket_transfers_completed_at.rb +5 -0
- data/db/migrate/20260515120000_add_public_metadata_to_cm_voting_sessions.rb +5 -0
- data/db/migrate/20260518090920_add_unique_voter_count_to_voting_contestants.rb +5 -0
- data/db/migrate/20260518094322_create_cm_voting_session_stats.rb +17 -0
- data/db/migrate/20260520000000_add_number_to_cm_ticket_transfers.rb +6 -0
- data/db/migrate/20260520000001_add_scoring_model_to_cm_voting_sessions.rb +5 -0
- data/db/migrate/20260520000001_optimize_cm_votes_indexes.rb +22 -0
- data/db/migrate/20260525042257_add_vote_number_to_cm_voting_contestants.rb +18 -0
- data/db/migrate/20260527035430_add_confirmed_rank_to_cm_voting_contestants.rb +5 -0
- data/db/migrate/20260527062005_add_eliminated_at_to_cm_show_contestants.rb +5 -0
- data/db/migrate/20260603063652_add_parent_to_voting_sessions.rb +9 -0
- data/db/migrate/20260603090000_add_session_type_to_cm_voting_sessions.rb +6 -0
- data/db/migrate/20260605120908_add_description_to_cm_dynamic_fields.rb +5 -0
- data/db/migrate/20260608000000_add_allow_gift_transfer_to_spree_products.rb +5 -0
- data/db/migrate/20260608000000_add_display_to_cm_voting_sessions.rb +5 -0
- data/db/migrate/20260608000000_create_cm_vendor_route_metrics.rb +25 -0
- data/db/migrate/20260608152251_add_gender_to_cm_reserved_blocks.rb +5 -0
- data/db/migrate/20260610000001_add_delivery_channel_to_cm_pin_codes.rb +6 -0
- data/db/migrate/20260610000001_drop_is_open_dated_from_cm_trips.rb +6 -0
- data/db/migrate/20260614000001_change_code_limit_in_cm_pin_codes.rb +5 -0
- data/db/migrate/20260615000002_add_index_to_code_on_cm_pin_codes.rb +5 -0
- data/docs/sql/jsonb_query_guide.md +57 -0
- data/lib/spree_cm_commissioner/test_helper/factories/show_episode_factory.rb +12 -0
- data/lib/spree_cm_commissioner/test_helper/factories/show_factory.rb +120 -0
- data/lib/spree_cm_commissioner/test_helper/factories/ticket_transfer_factory.rb +18 -0
- data/lib/spree_cm_commissioner/test_helper/factories/vendor_route_metric_factory.rb +10 -0
- data/lib/spree_cm_commissioner/test_helper/factories/vote_credit_factory.rb +37 -0
- data/lib/spree_cm_commissioner/test_helper/factories/vote_factory.rb +28 -0
- data/lib/spree_cm_commissioner/test_helper/factories/voting_credit_transaction_factory.rb +11 -0
- data/lib/spree_cm_commissioner/test_helper/factories/voting_session_factory.rb +11 -0
- data/lib/spree_cm_commissioner/transit/trip_form.rb +0 -13
- data/lib/spree_cm_commissioner/trip_query_result.rb +46 -6
- data/lib/spree_cm_commissioner/trip_result.rb +9 -7
- data/lib/spree_cm_commissioner/version.rb +1 -1
- data/lib/spree_cm_commissioner.rb +7 -1
- data/lib/tasks/backfill_route_metrics.rake +21 -0
- data/lib/tasks/backfill_vendor_route_metrics.rake +23 -0
- data/spree_cm_commissioner.gemspec +1 -1
- metadata +217 -6
- data/app/services/spree_cm_commissioner/trips/create_open_dated_trip.rb +0 -98
- data/app/services/spree_cm_commissioner/trips/update_open_dated_trip.rb +0 -67
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Tenant
|
|
5
|
+
class VotingContestantsController < BaseController
|
|
6
|
+
before_action :load_voting_session, only: %i[index]
|
|
7
|
+
|
|
8
|
+
# override
|
|
9
|
+
def collection
|
|
10
|
+
@collection ||= @voting_session
|
|
11
|
+
.voting_contestants
|
|
12
|
+
.includes(:voting_session, :show_contestant,
|
|
13
|
+
:show_contestant_images, :show_contestant_videos,
|
|
14
|
+
:advanced_to, :advanced_from
|
|
15
|
+
)
|
|
16
|
+
.order(:id)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# override
|
|
20
|
+
def collection_serializer
|
|
21
|
+
Spree::V2::Tenant::VotingContestantSerializer
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
# override
|
|
27
|
+
def model_class
|
|
28
|
+
SpreeCmCommissioner::VotingSession
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# override
|
|
32
|
+
def scope
|
|
33
|
+
current_vendor.voting_sessions.includes(:episode)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def load_voting_session
|
|
37
|
+
@voting_session = scope.find(params[:voting_session_id])
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Tenant
|
|
5
|
+
class VotingCreditTransactionsController < BaseController
|
|
6
|
+
before_action :require_spree_current_user
|
|
7
|
+
|
|
8
|
+
def index
|
|
9
|
+
render_serialized_payload do
|
|
10
|
+
serialize_collection(collection)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def collection
|
|
17
|
+
@collection ||= scope.page(params[:page])
|
|
18
|
+
.per(params[:per_page])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def scope
|
|
22
|
+
scope = spree_current_user.voting_credit_transactions
|
|
23
|
+
.includes(:vote_credit, :originator)
|
|
24
|
+
.order(created_at: :desc)
|
|
25
|
+
|
|
26
|
+
scope = scope.by_action(params[:actions].split(',')) if params[:actions].present?
|
|
27
|
+
scope
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def model_class
|
|
31
|
+
SpreeCmCommissioner::VotingCreditTransaction
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def collection_serializer
|
|
35
|
+
Spree::V2::Tenant::VotingCreditTransactionSerializer
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Tenant
|
|
5
|
+
class VotingCreditsController < BaseController
|
|
6
|
+
before_action :require_spree_current_user
|
|
7
|
+
|
|
8
|
+
def index
|
|
9
|
+
render_serialized_payload do
|
|
10
|
+
serialize_resource(voting_credit)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def voting_credit
|
|
17
|
+
spree_current_user.voting_credits.active.eligible(load_voting_session).first
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def load_voting_session
|
|
21
|
+
@voting_session ||= SpreeCmCommissioner::VotingSession.find(params[:voting_session_id])
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def resource_serializer
|
|
25
|
+
Spree::V2::Tenant::VotingCreditSerializer
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -40,9 +40,13 @@ module SpreeCmCommissioner
|
|
|
40
40
|
|
|
41
41
|
# override
|
|
42
42
|
def cancel
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
if @order.guests_in_transfer?
|
|
44
|
+
flash[:error] = Spree.t('admin.orders.cannot_cancel_guest_transfer')
|
|
45
|
+
else
|
|
46
|
+
internal_note = params.dig(:order, :internal_note)
|
|
47
|
+
@order.canceled_by(try_spree_current_user, cancellation_reason: internal_note)
|
|
48
|
+
flash[:success] = Spree.t(:order_canceled)
|
|
49
|
+
end
|
|
46
50
|
redirect_back fallback_location: spree.edit_admin_order_url(@order)
|
|
47
51
|
end
|
|
48
52
|
|
|
@@ -6,6 +6,7 @@ module SpreeCmCommissioner
|
|
|
6
6
|
base.around_action :set_writing_role, only: %i[index]
|
|
7
7
|
base.after_action :set_tenant_after_update, only: %i[update]
|
|
8
8
|
base.before_action :merge_industry_taxons_into_taxons, only: [:update]
|
|
9
|
+
base.before_action :normalize_sti_product_params, only: %i[create update]
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
# Override
|
|
@@ -51,6 +52,24 @@ module SpreeCmCommissioner
|
|
|
51
52
|
|
|
52
53
|
protected
|
|
53
54
|
|
|
55
|
+
# Some admin forms submit STI records under their class key while
|
|
56
|
+
# Spree update reads :product. Merge both into :product so all edited
|
|
57
|
+
# fields are persisted for STI product subclasses.
|
|
58
|
+
def normalize_sti_product_params
|
|
59
|
+
return unless @product
|
|
60
|
+
|
|
61
|
+
sti_key = @product.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[:product]
|
|
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[:product] = merged_params
|
|
71
|
+
end
|
|
72
|
+
|
|
54
73
|
def merge_industry_taxons_into_taxons
|
|
55
74
|
return if params[:product].blank?
|
|
56
75
|
|
|
@@ -61,6 +61,16 @@ module SpreeCmCommissioner
|
|
|
61
61
|
'badge-info'
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
|
+
|
|
65
|
+
def guest_state_badge(guest)
|
|
66
|
+
badge_class = case guest.state
|
|
67
|
+
when 'active' then 'badge-success'
|
|
68
|
+
when 'locked' then 'badge-warning'
|
|
69
|
+
when 'transferred' then 'badge-secondary'
|
|
70
|
+
else 'badge-info'
|
|
71
|
+
end
|
|
72
|
+
content_tag(:span, guest.state.humanize, class: "badge #{badge_class}")
|
|
73
|
+
end
|
|
64
74
|
end
|
|
65
75
|
end
|
|
66
76
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Admin
|
|
3
|
+
module TicketTransfersHelper
|
|
4
|
+
def ticket_transfer_state_badge(state)
|
|
5
|
+
badge_class = case state
|
|
6
|
+
when 'pending' then 'badge-warning'
|
|
7
|
+
when 'accepted' then 'badge-info'
|
|
8
|
+
when 'completed' then 'badge-success'
|
|
9
|
+
when 'rejected' then 'badge-danger'
|
|
10
|
+
when 'expired' then 'badge-danger' # rubocop:disable Lint/DuplicateBranch
|
|
11
|
+
when 'cancelled' then 'badge-secondary'
|
|
12
|
+
else 'badge-secondary' # rubocop:disable Lint/DuplicateBranch
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
content_tag(:span, state.humanize, class: "badge #{badge_class}")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def ticket_transfer_settlement_status_badge(ticket_transfer)
|
|
19
|
+
badge_class = case ticket_transfer.settlement_status
|
|
20
|
+
when 'pending' then 'badge-warning'
|
|
21
|
+
when 'ready' then 'badge-info'
|
|
22
|
+
when 'disputed' then 'badge-danger'
|
|
23
|
+
when 'resolved' then 'badge-primary'
|
|
24
|
+
when 'completed' then 'badge-success'
|
|
25
|
+
else 'badge-secondary'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
content_tag(:span, ticket_transfer.settlement_status.humanize, class: "badge #{badge_class}")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -6,7 +6,7 @@ module SpreeCmCommissioner
|
|
|
6
6
|
context.fail!(message: I18n.t('pincode_sender.pincode.blank')) if context.pin_code.nil?
|
|
7
7
|
|
|
8
8
|
if context.pin_code.phone_number?
|
|
9
|
-
|
|
9
|
+
send_phone_otp
|
|
10
10
|
else
|
|
11
11
|
send_email
|
|
12
12
|
end
|
|
@@ -16,18 +16,73 @@ module SpreeCmCommissioner
|
|
|
16
16
|
|
|
17
17
|
private
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
# Channel selection for phone-number OTPs lives here so the routing is
|
|
20
|
+
# easy to find and reason about. Telegram Gateway is primary; SMS is the fallback.
|
|
21
|
+
def send_phone_otp
|
|
22
|
+
if telegram_gateway_available?
|
|
23
|
+
send_via_telegram_gateway
|
|
24
|
+
else
|
|
25
|
+
send_via_sms
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def telegram_gateway_available?
|
|
30
|
+
return false unless telegram_gateway_enabled?
|
|
31
|
+
return false if telegram_gateway_api_key.blank?
|
|
32
|
+
|
|
33
|
+
ability = telegram_gateway_adapter.check_send_ability(context.pin_code.contact)
|
|
34
|
+
context.telegram_gateway_request_id = ability.request_id if ability.ok?
|
|
35
|
+
ability.ok?
|
|
36
|
+
rescue TelegramGatewayAdapter::Error, Faraday::Error
|
|
37
|
+
false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def send_via_telegram_gateway
|
|
20
41
|
from_number = sms_from_number
|
|
21
42
|
return if from_number.blank?
|
|
22
43
|
|
|
23
|
-
|
|
44
|
+
# Save the chosen channel + the Telegram request_id synchronously so
|
|
45
|
+
# (a) the API response already reflects the channel for the mobile UI,
|
|
46
|
+
# (b) verification can reach Telegram even if the user submits the OTP
|
|
47
|
+
# before the async send job has run. For Telegram-delivered codes
|
|
48
|
+
# the `code` column stores Telegram's request_id (Telegram generates
|
|
49
|
+
# and holds the actual OTP).
|
|
50
|
+
context.pin_code.update!(
|
|
51
|
+
delivery_channel: :telegram_gateway,
|
|
52
|
+
code: context.telegram_gateway_request_id
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
SpreeCmCommissioner::TelegramGateway::PinCodeSenderJob.perform_later(
|
|
56
|
+
pin_code_id: context.pin_code.id,
|
|
57
|
+
from: from_number,
|
|
58
|
+
request_id: context.telegram_gateway_request_id,
|
|
59
|
+
tenant_id: context.pin_code&.application&.tenant_id
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def send_via_sms
|
|
64
|
+
from_number = sms_from_number
|
|
65
|
+
return if from_number.blank?
|
|
66
|
+
|
|
67
|
+
SpreeCmCommissioner::SmsPinCodeJob.perform_later(
|
|
68
|
+
pin_code_id: context.pin_code.id,
|
|
24
69
|
from: from_number,
|
|
25
70
|
to: context.pin_code.contact,
|
|
26
71
|
body: I18n.t('pincode_sender.sms.body', code: context.pin_code.code, readable_type: context.pin_code.readable_type),
|
|
27
72
|
tenant_id: context.pin_code&.application&.tenant_id
|
|
28
|
-
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def telegram_gateway_enabled?
|
|
77
|
+
ENV['TELEGRAM_GATEWAY_ENABLED'] == 'yes'
|
|
78
|
+
end
|
|
29
79
|
|
|
30
|
-
|
|
80
|
+
def telegram_gateway_api_key
|
|
81
|
+
@telegram_gateway_api_key ||= ENV.fetch('TELEGRAM_GATEWAY_API_KEY', nil)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def telegram_gateway_adapter
|
|
85
|
+
@telegram_gateway_adapter ||= TelegramGatewayAdapter::Client.new
|
|
31
86
|
end
|
|
32
87
|
|
|
33
88
|
def sms_from_number
|
|
@@ -38,6 +93,8 @@ module SpreeCmCommissioner
|
|
|
38
93
|
end
|
|
39
94
|
|
|
40
95
|
def send_email
|
|
96
|
+
context.pin_code.update!(delivery_channel: :email)
|
|
97
|
+
|
|
41
98
|
SpreeCmCommissioner::PinCodeMailer.send_pin_code(
|
|
42
99
|
context.pin_code.id,
|
|
43
100
|
context.pin_code.readable_type,
|
|
@@ -3,6 +3,9 @@ require 'google/cloud/firestore'
|
|
|
3
3
|
# TODO: alert when available_slots is negative.
|
|
4
4
|
module SpreeCmCommissioner
|
|
5
5
|
class WaitingGuestsCaller < BaseInteractor
|
|
6
|
+
MIN_WAIT_TO_ENTER_SECONDS = 120 # 2 min floor — Waiting Room step (full journey, more uncertainty)
|
|
7
|
+
MIN_QUEUE_TO_ENTER_SECONDS = 60 # 1 min floor — Queue step (position assigned, one caller cycle minimum)
|
|
8
|
+
|
|
6
9
|
def call
|
|
7
10
|
available_slots = fetch_available_slots
|
|
8
11
|
return unless available_slots.positive?
|
|
@@ -10,7 +13,12 @@ module SpreeCmCommissioner
|
|
|
10
13
|
long_waiting_guests = fetch_long_waiting_guests(available_slots)
|
|
11
14
|
calling_all(long_waiting_guests)
|
|
12
15
|
|
|
13
|
-
mark_as(
|
|
16
|
+
mark_as(
|
|
17
|
+
full: long_waiting_guests.size >= available_slots,
|
|
18
|
+
available_slots: available_slots - long_waiting_guests.size,
|
|
19
|
+
avg_wait_to_enter_seconds: compute_avg_wait_to_enter_seconds(long_waiting_guests),
|
|
20
|
+
avg_queue_to_enter_seconds: compute_avg_queue_to_enter_seconds(long_waiting_guests)
|
|
21
|
+
)
|
|
14
22
|
end
|
|
15
23
|
|
|
16
24
|
def fetch_available_slots
|
|
@@ -19,18 +27,47 @@ module SpreeCmCommissioner
|
|
|
19
27
|
max_sessions - active_sessions
|
|
20
28
|
end
|
|
21
29
|
|
|
22
|
-
# This query
|
|
23
|
-
# Client
|
|
30
|
+
# This query requires an index; create it in Firebase beforehand.
|
|
31
|
+
# Client must create waiting_guests documents with :queued_at and :allow_to_enter_room_at set to nil to allow filter + order queries.
|
|
32
|
+
#
|
|
33
|
+
# Yesterday's guests are always older than today's, so fill from yesterday first, then use any
|
|
34
|
+
# leftover slots for today. This way no one queued before the midnight rollover gets skipped.
|
|
35
|
+
# e.g. 5 slots, 2 waiting in yesterday -> take both, then take 3 from today.
|
|
24
36
|
def fetch_long_waiting_guests(available_slots)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
37
|
+
previous_guests = eligible_guests_in(previous_records_path, available_slots)
|
|
38
|
+
|
|
39
|
+
# Pre-flip window: the lobby pointer still points at yesterday, so both paths resolve to the
|
|
40
|
+
# same partition — return now to avoid querying (and double-counting) it twice.
|
|
41
|
+
return previous_guests if records_path == previous_records_path
|
|
42
|
+
|
|
43
|
+
remaining_slots = available_slots - previous_guests.size
|
|
44
|
+
return previous_guests if remaining_slots <= 0
|
|
45
|
+
|
|
46
|
+
previous_guests + eligible_guests_in(records_path, remaining_slots)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def eligible_guests_in(records_path, limit)
|
|
50
|
+
firestore.col(records_path)
|
|
28
51
|
.where('allow_to_enter_room_at', '==', nil)
|
|
29
52
|
.order('queued_at')
|
|
30
|
-
.limit(
|
|
53
|
+
.limit(limit)
|
|
31
54
|
.get.to_a
|
|
32
55
|
end
|
|
33
56
|
|
|
57
|
+
# Published path is authoritative; fall back to the server's own date if not yet published.
|
|
58
|
+
def records_path
|
|
59
|
+
lobby_data&.dig(:waiting_guests_records_path).presence || default_records_path(current_date)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Drain target is derived from the server date, never the (possibly stale) lobby pointer.
|
|
63
|
+
def previous_records_path
|
|
64
|
+
default_records_path(previous_date)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def default_records_path(date)
|
|
68
|
+
"waiting_guests/#{date}/records"
|
|
69
|
+
end
|
|
70
|
+
|
|
34
71
|
# For alert waiting guests to enter room, we just update :allow_to_enter_room_at.
|
|
35
72
|
# App will listen to firebase & start refresh session token to enter room.
|
|
36
73
|
def calling_all(waiting_guests)
|
|
@@ -45,9 +82,48 @@ module SpreeCmCommissioner
|
|
|
45
82
|
Time.zone.now.strftime('%Y-%m-%d')
|
|
46
83
|
end
|
|
47
84
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
85
|
+
def previous_date
|
|
86
|
+
1.day.ago.strftime('%Y-%m-%d')
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# merge: true so we preserve the published `waiting_guests_records_path` on the lobby doc.
|
|
90
|
+
def mark_as(full:, available_slots:, avg_wait_to_enter_seconds: nil, avg_queue_to_enter_seconds: nil)
|
|
91
|
+
data = { full: full, available_slots: available_slots }
|
|
92
|
+
data[:avg_wait_to_enter_seconds] = avg_wait_to_enter_seconds if avg_wait_to_enter_seconds
|
|
93
|
+
data[:avg_queue_to_enter_seconds] = avg_queue_to_enter_seconds if avg_queue_to_enter_seconds
|
|
94
|
+
lobby_document.set(data, merge: true)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Average total wait (queued_at → allow_to_enter_room_at) for just-called guests.
|
|
98
|
+
# Used for the Waiting Room step ETA: how long until I get in from when I joined.
|
|
99
|
+
def compute_avg_wait_to_enter_seconds(guests)
|
|
100
|
+
return nil if guests.empty?
|
|
101
|
+
|
|
102
|
+
now = Time.zone.now
|
|
103
|
+
total = guests.sum { |doc| (now - doc.data[:queued_at].to_time).to_f }
|
|
104
|
+
[(total / guests.size).round, MIN_WAIT_TO_ENTER_SECONDS].max
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Average queue wait (position_set_at → allow_to_enter_room_at) for just-called guests.
|
|
108
|
+
# Used for the Queue step ETA: how long after getting a position until I actually enter.
|
|
109
|
+
# Returns nil when none of the called guests had position_set_at (first run, or no prior stamp).
|
|
110
|
+
def compute_avg_queue_to_enter_seconds(guests)
|
|
111
|
+
return nil if guests.empty?
|
|
112
|
+
|
|
113
|
+
now = Time.zone.now
|
|
114
|
+
positioned = guests.select { |doc| doc.data[:position_set_at] }
|
|
115
|
+
return nil if positioned.empty?
|
|
116
|
+
|
|
117
|
+
total = positioned.sum { |doc| (now - doc.data[:position_set_at].to_time).to_f }
|
|
118
|
+
[(total / positioned.size).round, MIN_QUEUE_TO_ENTER_SECONDS].max
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def lobby_data
|
|
122
|
+
@lobby_data ||= lobby_document.get.data
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def lobby_document
|
|
126
|
+
@lobby_document ||= firestore.col('waiting_rooms').doc('lobby')
|
|
51
127
|
end
|
|
52
128
|
|
|
53
129
|
def fetch_max_sessions
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class ExpireTicketTransferJob < ApplicationJob
|
|
3
|
+
def perform(transfer_id)
|
|
4
|
+
ticket_transfer = SpreeCmCommissioner::TicketTransfer.find_by(id: transfer_id)
|
|
5
|
+
return unless ticket_transfer
|
|
6
|
+
|
|
7
|
+
SpreeCmCommissioner::TicketTransfers::Expire.call(ticket_transfer: ticket_transfer)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module TelegramGateway
|
|
3
|
+
# options = { pin_code_id:, from:, request_id:, tenant_id: }
|
|
4
|
+
class PinCodeSenderJob < SpreeCmCommissioner::SmsJob
|
|
5
|
+
def perform(options = {})
|
|
6
|
+
pin_code = SpreeCmCommissioner::PinCode.find_by(id: options[:pin_code_id])
|
|
7
|
+
return if pin_code.nil?
|
|
8
|
+
|
|
9
|
+
SpreeCmCommissioner::TelegramGateway::PinCodeSender.call(
|
|
10
|
+
pin_code: pin_code,
|
|
11
|
+
from: options[:from],
|
|
12
|
+
request_id: options[:request_id],
|
|
13
|
+
tenant_id: options[:tenant_id]
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module VendorRouteMetrics
|
|
3
|
+
class DecreaseTripCountJob < ApplicationUniqueJob
|
|
4
|
+
def perform(options = {})
|
|
5
|
+
vendor_route_metric = SpreeCmCommissioner::VendorRouteMetric.find_by(
|
|
6
|
+
vendor_id: options[:vendor_id],
|
|
7
|
+
origin_place_id: options[:origin_place_id],
|
|
8
|
+
destination_place_id: options[:destination_place_id],
|
|
9
|
+
route_type: options[:route_type]
|
|
10
|
+
)
|
|
11
|
+
return if vendor_route_metric.nil?
|
|
12
|
+
|
|
13
|
+
SpreeCmCommissioner::VendorRouteMetrics::DecreaseTripCount.call(vendor_route_metric: vendor_route_metric)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module VendorRouteMetrics
|
|
3
|
+
class IncreaseTripCountJob < ApplicationUniqueJob
|
|
4
|
+
def perform(options = {})
|
|
5
|
+
SpreeCmCommissioner::VendorRouteMetrics::IncreaseTripCount.call(
|
|
6
|
+
vendor_id: options[:vendor_id],
|
|
7
|
+
origin_place_id: options[:origin_place_id],
|
|
8
|
+
destination_place_id: options[:destination_place_id],
|
|
9
|
+
route_type: options[:route_type]
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class VotingCreditAllocationJob < ApplicationUniqueJob
|
|
3
|
+
def perform(options = {})
|
|
4
|
+
order = Spree::Order.find_by(id: options[:order_id])
|
|
5
|
+
return if order.nil?
|
|
6
|
+
|
|
7
|
+
SpreeCmCommissioner::VotingCredits::Allocate.new(order).call
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class VotingCreditDeAllocationJob < ApplicationUniqueJob
|
|
3
|
+
def perform(options = {})
|
|
4
|
+
order = Spree::Order.find_by(id: options[:order_id])
|
|
5
|
+
return if order.nil?
|
|
6
|
+
|
|
7
|
+
SpreeCmCommissioner::VotingCredits::DeAllocate.new(order).call
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# waiting_room_lobby_path_publisher:
|
|
2
|
+
# cron: "0 0 * * * Asia/Phnom_Penh" # Once per day at local midnight (date rollover)
|
|
3
|
+
# class: "SpreeCmCommissioner::WaitingRoom::PublishLobbyPathJob"
|
|
4
|
+
module SpreeCmCommissioner
|
|
5
|
+
module WaitingRoom
|
|
6
|
+
class PublishLobbyPathJob < ApplicationJob
|
|
7
|
+
queue_as :waiting_room
|
|
8
|
+
|
|
9
|
+
def perform
|
|
10
|
+
return if ENV['WAITING_ROOM_DISABLED'] == 'yes'
|
|
11
|
+
|
|
12
|
+
# call! so a publish failure raises (Sidekiq retries/alerts) instead of being silently swallowed.
|
|
13
|
+
SpreeCmCommissioner::WaitingRoom::PublishLobbyPath.call!
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require 'google/cloud/firestore'
|
|
2
|
+
|
|
3
|
+
module SpreeCmCommissioner
|
|
4
|
+
module WaitingRoom
|
|
5
|
+
class StampQueuePositionsJob < ApplicationJob
|
|
6
|
+
queue_as :waiting_room
|
|
7
|
+
|
|
8
|
+
STAMP_LIMIT = (ENV['WAITING_ROOM_POSITION_STAMP_LIMIT'] || 1000).to_i
|
|
9
|
+
|
|
10
|
+
def perform
|
|
11
|
+
paths = stamp_paths
|
|
12
|
+
avg_queue_to_enter = lobby_data[:avg_queue_to_enter_seconds].to_i
|
|
13
|
+
total = paths.sum { |p| waiting_count(p) }
|
|
14
|
+
return if total.zero?
|
|
15
|
+
|
|
16
|
+
stamped_at = Time.zone.now
|
|
17
|
+
global = 0
|
|
18
|
+
|
|
19
|
+
paths.each do |path|
|
|
20
|
+
remaining = STAMP_LIMIT - global
|
|
21
|
+
break if remaining <= 0
|
|
22
|
+
|
|
23
|
+
docs = waiting_query(path).order('queued_at').limit(remaining).get.to_a
|
|
24
|
+
docs.each_slice(500) do |slice|
|
|
25
|
+
firestore.batch do |b|
|
|
26
|
+
slice.each do |doc|
|
|
27
|
+
global += 1
|
|
28
|
+
data = {
|
|
29
|
+
position: global,
|
|
30
|
+
queue_total: total,
|
|
31
|
+
estimated_wait_seconds: avg_queue_to_enter,
|
|
32
|
+
status_updated_at: stamped_at
|
|
33
|
+
}
|
|
34
|
+
data[:position_set_at] = stamped_at unless doc.data[:position_set_at]
|
|
35
|
+
b.update(doc.ref, data)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def stamp_paths
|
|
45
|
+
[previous_records_path, records_path]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def records_path
|
|
49
|
+
default_records_path(Time.zone.now.strftime('%Y-%m-%d'))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def previous_records_path
|
|
53
|
+
default_records_path(1.day.ago.strftime('%Y-%m-%d'))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def default_records_path(date)
|
|
57
|
+
"waiting_guests/#{date}/records"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def waiting_count(path)
|
|
61
|
+
waiting_query(path).aggregate_query.add_count.get.first.get.to_i
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def waiting_query(path)
|
|
65
|
+
firestore.col(path).where('allow_to_enter_room_at', '==', nil)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def lobby_data
|
|
69
|
+
@lobby_data ||= firestore.col('waiting_rooms').doc('lobby').get.data || {}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def firestore
|
|
73
|
+
@firestore ||= Google::Cloud::Firestore.new(
|
|
74
|
+
project_id: service_account[:project_id],
|
|
75
|
+
credentials: service_account
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def service_account
|
|
80
|
+
@service_account ||= Rails.application.credentials.cloud_firestore_service_account
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|