spree_cm_commissioner 2.13.5 → 2.13.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/app/controllers/spree/admin/base_controller_decorator.rb +14 -0
- data/app/controllers/spree/admin/customer_notifications_controller.rb +16 -1
- data/app/controllers/spree/admin/prepaid_transactions_controller.rb +69 -0
- data/app/controllers/spree/admin/private_sales_controller.rb +6 -0
- data/app/controllers/spree/admin/product_commissions_controller.rb +2 -1
- data/app/controllers/spree/admin/reschedule_policies_controller.rb +148 -0
- data/app/controllers/spree/admin/reschedule_requests_controller.rb +82 -0
- data/app/controllers/spree/admin/store_credit_admin_controller.rb +122 -0
- data/app/controllers/spree/admin/ticket_transfer_rules_controller.rb +7 -0
- data/app/controllers/spree/api/v2/platform/store_credits_controller_decorator.rb +35 -0
- data/app/controllers/spree/api/v2/storefront/account/prepaid_balances_controller.rb +52 -0
- data/app/controllers/spree/api/v2/storefront/account/prepaid_transactions_controller.rb +56 -0
- data/app/controllers/spree/api/v2/storefront/draft_orders_controller.rb +37 -0
- data/app/controllers/spree/api/v2/storefront/prepaid_config_controller.rb +22 -0
- data/app/controllers/spree/api/v2/storefront/reschedule_requests_controller.rb +156 -0
- data/app/controllers/spree/api/v2/storefront/transit/item_availabilities_controller.rb +66 -0
- data/app/controllers/spree/api/v2/tenant/account/prepaid_balances_controller.rb +51 -0
- data/app/controllers/spree/api/v2/tenant/account/prepaid_transactions_controller.rb +53 -0
- data/app/controllers/spree/api/v2/tenant/base_controller.rb +20 -0
- data/app/controllers/spree/api/v2/tenant/draft_orders_controller.rb +42 -0
- data/app/controllers/spree/api/v2/tenant/prepaid_config_controller.rb +29 -0
- data/app/controllers/spree_cm_commissioner/admin/store_credits_controller_decorator.rb +217 -0
- data/app/errors/spree_cm_commissioner/reschedule_policies/invalid_policyable_error.rb +8 -0
- data/app/errors/spree_cm_commissioner/reschedule_policies/not_reschedulable_error.rb +11 -0
- data/app/errors/spree_cm_commissioner/reschedule_requests/cancel_not_allowed_error.rb +6 -0
- data/app/errors/spree_cm_commissioner/reschedule_requests/complete_cash_payment_error.rb +6 -0
- data/app/errors/spree_cm_commissioner/reschedule_requests/eligibility_denied_error.rb +7 -0
- data/app/factory/spree_cm_commissioner/order_telegram_message_factory.rb +6 -2
- data/app/factory/spree_cm_commissioner/reschedule_telegram_message_factory.rb +141 -0
- data/app/finders/spree_cm_commissioner/inventory_items/recently_changed_finder.rb +1 -1
- data/app/helpers/spree/admin/base_helper_decorator.rb +12 -0
- data/app/helpers/spree/admin/reschedule_policies_helper.rb +35 -0
- data/app/helpers/spree/admin/reschedule_requests_helper.rb +79 -0
- data/app/interactors/spree_cm_commissioner/create_ticket.rb +1 -0
- data/app/jobs/spree_cm_commissioner/audit_event_job.rb +29 -0
- data/app/jobs/spree_cm_commissioner/prepaid_reconciliation_cron_job.rb +9 -0
- data/app/jobs/spree_cm_commissioner/prepaid_reverse_top_up_job.rb +16 -0
- data/app/jobs/spree_cm_commissioner/prepaid_setup_types_job.rb +24 -0
- data/app/jobs/spree_cm_commissioner/prepaid_top_up_credit_job.rb +31 -0
- data/app/jobs/spree_cm_commissioner/reschedule_requests/approval_reminder_job.rb +17 -0
- data/app/jobs/spree_cm_commissioner/reschedule_requests/execute_job.rb +20 -0
- data/app/jobs/spree_cm_commissioner/reschedule_requests/expire_overdue_requests_job.rb +12 -0
- data/app/jobs/spree_cm_commissioner/reschedule_requests/expire_request_job.rb +15 -0
- data/app/jobs/spree_cm_commissioner/reschedule_requests/payment_reminder_job.rb +17 -0
- data/app/models/concerns/spree_cm_commissioner/audit_wrapper.rb +139 -0
- data/app/models/concerns/spree_cm_commissioner/integrations/inventory_sync_cachable.rb +9 -0
- data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +2 -1
- data/app/models/concerns/spree_cm_commissioner/order_prepaid_payment.rb +15 -0
- data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +56 -8
- data/app/models/concerns/spree_cm_commissioner/product_type.rb +1 -1
- data/app/models/concerns/spree_cm_commissioner/reschedule_request_state_machine.rb +332 -0
- data/app/models/concerns/spree_cm_commissioner/service_type.rb +1 -1
- data/app/models/concerns/spree_cm_commissioner/store_preference.rb +1 -0
- data/app/models/concerns/spree_cm_commissioner/tenant_preference.rb +6 -0
- data/app/models/concerns/spree_cm_commissioner/variant_metadata.rb +24 -0
- data/app/models/concerns/spree_cm_commissioner/vehicle_kind.rb +1 -1
- data/app/models/spree/core/controller_helpers/currency_decorator.rb +70 -0
- data/app/models/spree_cm_commissioner/audit_event.rb +11 -0
- data/app/models/spree_cm_commissioner/configuration.rb +17 -9
- data/app/models/spree_cm_commissioner/currency_rate.rb +15 -0
- data/app/models/spree_cm_commissioner/current.rb +8 -0
- data/app/models/spree_cm_commissioner/customer_notification.rb +4 -1
- data/app/models/spree_cm_commissioner/exports/import_order_guests_csv.rb +153 -0
- data/app/models/spree_cm_commissioner/guest.rb +21 -0
- data/app/models/spree_cm_commissioner/integration.rb +31 -0
- data/app/models/spree_cm_commissioner/integrations/book_me_bus_v1.rb +64 -0
- data/app/models/spree_cm_commissioner/integrations/larryta.rb +81 -0
- data/app/models/spree_cm_commissioner/integrations/stadium_x_v1.rb +4 -10
- data/app/models/spree_cm_commissioner/integrations/vireak_buntham.rb +82 -0
- data/app/models/spree_cm_commissioner/inventory_item.rb +12 -0
- data/app/models/spree_cm_commissioner/line_item_decorator.rb +6 -2
- data/app/models/spree_cm_commissioner/notification.rb +8 -0
- data/app/models/spree_cm_commissioner/order_decorator.rb +19 -2
- data/app/models/spree_cm_commissioner/payment_decorator.rb +31 -0
- data/app/models/spree_cm_commissioner/payment_method_store_credit_decorator.rb +81 -0
- data/app/models/spree_cm_commissioner/place.rb +2 -0
- data/app/models/spree_cm_commissioner/prepaid_config.rb +59 -0
- data/app/models/spree_cm_commissioner/prepaid_transaction.rb +102 -0
- data/app/models/spree_cm_commissioner/product_decorator.rb +6 -0
- data/app/models/spree_cm_commissioner/reschedule_policy.rb +94 -0
- data/app/models/spree_cm_commissioner/reschedule_policy_assignment.rb +19 -0
- data/app/models/spree_cm_commissioner/reschedule_request.rb +227 -0
- data/app/models/spree_cm_commissioner/reschedule_requests/guest_selection.rb +80 -0
- data/app/models/spree_cm_commissioner/reschedule_snapshot.rb +65 -0
- data/app/models/spree_cm_commissioner/route.rb +1 -0
- data/app/models/spree_cm_commissioner/seat_layout.rb +1 -0
- data/app/models/spree_cm_commissioner/service_calendar.rb +4 -0
- data/app/models/spree_cm_commissioner/stock_item_decorator.rb +4 -0
- data/app/models/spree_cm_commissioner/store_credit_category_decorator.rb +23 -0
- data/app/models/spree_cm_commissioner/store_credit_decorator.rb +142 -0
- data/app/models/spree_cm_commissioner/store_credit_event_decorator.rb +94 -0
- data/app/models/spree_cm_commissioner/store_credit_type_decorator.rb +43 -0
- data/app/models/spree_cm_commissioner/store_decorator.rb +8 -0
- data/app/models/spree_cm_commissioner/tenant.rb +37 -0
- data/app/models/spree_cm_commissioner/trip.rb +2 -1
- data/app/models/spree_cm_commissioner/trip_stop.rb +2 -0
- data/app/models/spree_cm_commissioner/user_decorator.rb +79 -0
- data/app/models/spree_cm_commissioner/variant_decorator.rb +9 -1
- data/app/models/spree_cm_commissioner/vehicle.rb +3 -0
- data/app/models/spree_cm_commissioner/vehicle_type.rb +1 -0
- data/app/models/spree_cm_commissioner/vendor_decorator.rb +53 -0
- data/app/models/spree_cm_commissioner/vendor_place.rb +2 -0
- data/app/notifications/spree_cm_commissioner/reschedule_approved_notification.rb +7 -0
- data/app/notifications/spree_cm_commissioner/reschedule_cancelled_notification.rb +7 -0
- data/app/notifications/spree_cm_commissioner/reschedule_completed_notification.rb +7 -0
- data/app/notifications/spree_cm_commissioner/reschedule_expired_notification.rb +7 -0
- data/app/notifications/spree_cm_commissioner/reschedule_failed_notification.rb +7 -0
- data/app/notifications/spree_cm_commissioner/reschedule_notification_base.rb +41 -0
- data/app/notifications/spree_cm_commissioner/reschedule_payment_reminder_notification.rb +15 -0
- data/app/notifications/spree_cm_commissioner/reschedule_rejected_notification.rb +7 -0
- data/app/notifications/spree_cm_commissioner/reschedule_requested_notification.rb +7 -0
- data/app/overrides/spree/admin/products/_form/offline.html.erb.deface +9 -0
- data/app/overrides/spree/admin/shared/_main_menu/sidebar_reschedule.html.erb.deface +9 -0
- data/app/overrides/spree/admin/shared/_main_menu/sidebar_store_credit.html.erb.deface +8 -0
- data/app/overrides/spree/admin/shared/_order_tabs/reschedule_requests.html.erb.deface +10 -0
- data/app/presenters/spree_cm_commissioner/prepaid_balance_presenter.rb +16 -0
- data/app/queries/spree_cm_commissioner/store_credit_overview_query.rb +75 -0
- data/app/queries/spree_cm_commissioner/user_store_credit_summary_query.rb +53 -0
- data/app/serializables/spree_cm_commissioner/item_availability_sync.rb +10 -0
- data/app/serializers/spree/v2/storefront/prepaid_balance_serializer.rb +15 -0
- data/app/serializers/spree/v2/storefront/prepaid_config_serializer.rb +11 -0
- data/app/serializers/spree/v2/storefront/prepaid_transaction_serializer.rb +17 -0
- data/app/serializers/spree/v2/storefront/user_serializer_decorator.rb +6 -0
- data/app/serializers/spree/v2/tenant/prepaid_balance_serializer.rb +15 -0
- data/app/serializers/spree/v2/tenant/prepaid_config_serializer.rb +16 -0
- data/app/serializers/spree/v2/tenant/prepaid_transaction_serializer.rb +16 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/cart_serializer_decorator.rb +2 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/item_availability_sync_serializer.rb +10 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/reschedule_request_serializer.rb +49 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/reschedule_snapshot_serializer.rb +24 -0
- data/app/services/spree_cm_commissioner/audit_logger.rb +59 -0
- data/app/services/spree_cm_commissioner/checkout/add_store_credit_payments.rb +96 -0
- data/app/services/spree_cm_commissioner/currency_converter/create.rb +27 -0
- data/app/services/spree_cm_commissioner/currency_rates/manage.rb +99 -0
- data/app/services/spree_cm_commissioner/customer_notifications/clone.rb +39 -0
- data/app/services/spree_cm_commissioner/google_maps_geocoder.rb +41 -0
- data/app/services/spree_cm_commissioner/imports/orders/create.rb +24 -11
- data/app/services/spree_cm_commissioner/integrations/base/sync_manager.rb +1 -2
- data/app/services/spree_cm_commissioner/integrations/base/sync_result.rb +11 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/external_client/client.rb +230 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/external_client/connection.rb +147 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/inventory/external_inventory_item_status.rb +52 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/inventory/external_inventory_items.rb +91 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/inventory/unstock_inventory.rb +188 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_inventory.rb +79 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_item_availability.rb +354 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_locations.rb +103 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_nationality_pricing.rb +93 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_product.rb +70 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_route_stops.rb +121 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_routes.rb +128 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_seat_layout.rb +412 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_service_calendars.rb +59 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_trip_stop.rb +224 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_trips.rb +769 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_variant.rb +69 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_vehicle_types.rb +92 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/time_parser.rb +19 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/base.rb +40 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/inventory_item_status.rb +148 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/location.rb +55 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/reservation_cart.rb +85 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/seat.rb +128 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/seat_layout.rb +386 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/stop.rb +84 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/trip.rb +237 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/resources/wallet_payment.rb +48 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/sync_manager.rb +22 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/sync_strategies/full_sync_strategy.rb +116 -0
- data/app/services/spree_cm_commissioner/integrations/book_me_bus_v1/sync_strategies/incremental_sync_strategy.rb +63 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/external_client/client.rb +314 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/external_client/connection.rb +74 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/inventory/external_inventory_item_status.rb +53 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/inventory/external_inventory_items.rb +106 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/inventory/hold_seats.rb +143 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/inventory/release_seats.rb +90 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/inventory/unstock_inventory.rb +143 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_item_availability.rb +325 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_product.rb +72 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_routes.rb +142 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_seat_layout.rb +255 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_stock_item.rb +55 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_trip_stop.rb +171 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_trips.rb +397 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_variant.rb +63 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/polling/sync_vehicle_type.rb +79 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/base.rb +39 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/booking.rb +113 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/booking_session.rb +43 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/booking_transaction.rb +236 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/direction.rb +71 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/passenger.rb +48 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/schedule.rb +150 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/seat.rb +31 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/seat_availability.rb +49 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/resources/seat_layout.rb +211 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/sync_manager.rb +17 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/sync_strategies/full_sync_strategy.rb +79 -0
- data/app/services/spree_cm_commissioner/integrations/larryta/sync_strategies/incremental_sync_strategy.rb +58 -0
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/external_client/client.rb +14 -15
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/polling/sync_matches.rb +7 -4
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/polling/sync_zones.rb +6 -3
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_manager.rb +0 -3
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_strategies/full_sync_strategy.rb +1 -3
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_strategies/incremental_sync_strategy.rb +1 -3
- data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_strategies/webhook_sync_strategy.rb +1 -2
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/external_client/client.rb +170 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/external_client/connection.rb +59 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/inventory/external_inventory_item_status.rb +48 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/inventory/external_inventory_items.rb +92 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/inventory/unstock_inventory.rb +230 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_item_availability.rb +270 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_locations.rb +96 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_nationality_pricing.rb +76 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_product.rb +69 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_routes.rb +110 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_seat_layout.rb +207 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_stock_item.rb +43 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_trip_stop.rb +192 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_trips.rb +377 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_variant.rb +56 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/polling/sync_vehicle_type.rb +51 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/base.rb +32 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/booking.rb +48 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/booking_transaction.rb +46 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/cell.rb +53 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/confirm_booking_request.rb +43 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/location.rb +37 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/schedule.rb +138 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/seat.rb +48 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/resources/seat_layout.rb +79 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/sync_manager.rb +17 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/sync_strategies/full_sync_strategy.rb +87 -0
- data/app/services/spree_cm_commissioner/integrations/vireak_buntham/sync_strategies/incremental_sync_strategy.rb +53 -0
- data/app/services/spree_cm_commissioner/inventory_holds/release.rb +13 -0
- data/app/services/spree_cm_commissioner/inventory_items/generate_permanent_item.rb +48 -0
- data/app/services/spree_cm_commissioner/order_holds/hold.rb +17 -0
- data/app/services/spree_cm_commissioner/order_holds/release.rb +31 -0
- data/app/services/spree_cm_commissioner/place_resolver.rb +95 -0
- data/app/services/spree_cm_commissioner/prepaid/admin_credit.rb +44 -0
- data/app/services/spree_cm_commissioner/prepaid/check_top_up_limits.rb +47 -0
- data/app/services/spree_cm_commissioner/prepaid/freeze.rb +58 -0
- data/app/services/spree_cm_commissioner/prepaid/mutation.rb +78 -0
- data/app/services/spree_cm_commissioner/prepaid/reverse_order_top_up.rb +23 -0
- data/app/services/spree_cm_commissioner/prepaid/reverse_top_up.rb +53 -0
- data/app/services/spree_cm_commissioner/prepaid/top_up.rb +68 -0
- data/app/services/spree_cm_commissioner/prepaid/unfreeze.rb +47 -0
- data/app/services/spree_cm_commissioner/prepaid_metrics.rb +37 -0
- data/app/services/spree_cm_commissioner/prepaid_reconciliation_service.rb +71 -0
- data/app/services/spree_cm_commissioner/reschedule_policies/assign.rb +52 -0
- data/app/services/spree_cm_commissioner/reschedule_policies/create.rb +71 -0
- data/app/services/spree_cm_commissioner/reschedule_policies/exclude.rb +40 -0
- data/app/services/spree_cm_commissioner/reschedule_policies/resolve.rb +109 -0
- data/app/services/spree_cm_commissioner/reschedule_policies/unassign.rb +23 -0
- data/app/services/spree_cm_commissioner/reschedule_policies/update.rb +73 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/authorize_initiator.rb +79 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/build_request.rb +317 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/calculate_fee.rb +71 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/cancel.rb +55 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/check_eligibility.rb +243 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/collect_payment.rb +204 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/execute.rb +252 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/expire_overdue_requests.rb +28 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/expire_request.rb +24 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/guests/update_seats.rb +210 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/initiate.rb +255 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/issue_compensation.rb +67 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/issue_coupon.rb +90 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/issue_store_credit.rb +16 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/notifications/send_approval_reminder.rb +43 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/notifications/send_payment_reminder.rb +46 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/notifications/send_status_update.rb +42 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/seats/hold.rb +137 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/split_line_item.rb +86 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/telegram/notify_operations.rb +37 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/telegram/notify_vendor.rb +51 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/vendors/approve.rb +61 -0
- data/app/services/spree_cm_commissioner/reschedule_requests/vendors/reject.rb +58 -0
- data/app/services/spree_cm_commissioner/topup_draft_orders/create.rb +234 -0
- data/app/services/spree_cm_commissioner/trips/service_calendars/create_or_update.rb +25 -14
- data/app/services/spree_cm_commissioner/trips/update_single_leg.rb +43 -6
- data/app/views/spree/admin/classifications/edit.html.erb +17 -2
- data/app/views/spree/admin/classifications/new.html.erb +18 -2
- data/app/views/spree/admin/customer_notifications/index.html.erb +45 -3
- data/app/views/spree/admin/integrations/_book_me_bus_v1_fields.html.erb +47 -0
- data/app/views/spree/admin/integrations/_form.html.erb +12 -0
- data/app/views/spree/admin/integrations/_larryta_fields.html.erb +58 -0
- data/app/views/spree/admin/integrations/_vireak_buntham_fields.html.erb +75 -0
- data/app/views/spree/admin/integrations/index.html.erb +1 -1
- data/app/views/spree/admin/prepaid_transactions/index.html.erb +102 -0
- data/app/views/spree/admin/prepaid_transactions/show.html.erb +146 -0
- data/app/views/spree/admin/private_sales/_form.html.erb +24 -1
- data/app/views/spree/admin/private_sales/_stock.html.erb +6 -1
- data/app/views/spree/admin/product_commissions/index.html.erb +7 -2
- data/app/views/spree/admin/reschedule_policies/_form.html.erb +65 -0
- data/app/views/spree/admin/reschedule_policies/edit.html.erb +11 -0
- data/app/views/spree/admin/reschedule_policies/index.html.erb +84 -0
- data/app/views/spree/admin/reschedule_policies/new.html.erb +11 -0
- data/app/views/spree/admin/reschedule_requests/_cancel_modal.html.erb +34 -0
- data/app/views/spree/admin/reschedule_requests/_search_form.html.erb +57 -0
- data/app/views/spree/admin/reschedule_requests/_tabs.html.erb +14 -0
- data/app/views/spree/admin/reschedule_requests/index.html.erb +64 -0
- data/app/views/spree/admin/reschedule_requests/show.html.erb +212 -0
- data/app/views/spree/admin/reschedule_requests/state_changes.html.erb +42 -0
- data/app/views/spree/admin/shared/_products.html.erb +3 -0
- data/app/views/spree/admin/shared/_reschedule_policies_tabs.html.erb +22 -0
- data/app/views/spree/admin/shared/_ticket_transfer_details.html.erb +3 -0
- data/app/views/spree/admin/shared/sub_menu/_reschedule.html.erb +4 -0
- data/app/views/spree/admin/store_credit_admin/_tabs.html.erb +31 -0
- data/app/views/spree/admin/store_credit_admin/_tenant_selector.html.erb +17 -0
- data/app/views/spree/admin/store_credit_admin/show.html.erb +200 -0
- data/app/views/spree/admin/store_credits/index.html.erb +224 -0
- data/app/views/spree/admin/tenants/_form.html.erb +33 -0
- data/app/views/spree/admin/ticket_transfer_rules/_form.html.erb +28 -5
- data/config/initializers/spree_dependencies.rb +3 -0
- data/config/initializers/spree_permitted_attributes.rb +1 -0
- data/config/locales/en.yml +351 -0
- data/config/locales/km.yml +270 -0
- data/config/routes.rb +63 -2
- data/db/migrate/20260327143200_create_cm_audit_events.rb +21 -0
- data/db/migrate/20260410045815_create_cm_currency_rates.rb +23 -0
- data/db/migrate/20260411102139_add_metadata_to_cm_inventory_items.rb +6 -0
- data/db/migrate/20260506120000_create_cm_reschedule_policies.rb +38 -0
- data/db/migrate/20260506120001_create_cm_reschedule_requests.rb +67 -0
- data/db/migrate/20260506120002_add_reschedule_count_to_cm_guests.rb +5 -0
- data/db/migrate/20260521140000_create_cm_reschedule_snapshots.rb +38 -0
- data/db/migrate/20260522160000_create_cm_reschedule_policy_assignments.rb +17 -0
- data/db/migrate/20260722000001_create_cm_prepaid_transactions.rb +31 -0
- data/db/migrate/20260722000002_add_constraints_to_store_credits.rb +46 -0
- data/db/migrate/20260722000003_add_expires_at_to_store_credits.rb +6 -0
- data/db/migrate/20260722000004_add_issuer_tenant_id_to_spree_store_credits.rb +7 -0
- data/db/migrate/20260722000005_add_immutability_trigger_to_cm_prepaid_transactions.rb +36 -0
- data/db/migrate/20260918045558_add_prepaid_currency_columns_to_cm_tenants.rb +6 -0
- data/db/migrate/20260923090000_add_public_private_metadata_to_cm_prepaid_transactions.rb +36 -0
- data/db/migrate/20261006073317_add_offline_to_spree_products.rb +6 -0
- data/lib/spree_cm_commissioner/reschedule_policy_rules.rb +75 -0
- data/lib/spree_cm_commissioner/serializable_transaction.rb +38 -0
- data/lib/spree_cm_commissioner/store_credit_currency_summary.rb +11 -0
- data/lib/spree_cm_commissioner/store_credit_summary.rb +11 -0
- data/lib/spree_cm_commissioner/test_helper/factories/integration_factory.rb +11 -0
- data/lib/spree_cm_commissioner/test_helper/factories/prepaid_transaction_factory.rb +12 -0
- data/lib/spree_cm_commissioner/test_helper/factories/reschedule_policy_assignment_factory.rb +7 -0
- data/lib/spree_cm_commissioner/test_helper/factories/reschedule_policy_factory.rb +59 -0
- data/lib/spree_cm_commissioner/test_helper/factories/reschedule_request_factory.rb +51 -0
- data/lib/spree_cm_commissioner/test_helper/factories/reschedule_snapshot_factory.rb +27 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- data/lib/spree_cm_commissioner.rb +4 -0
- data/lib/tasks/prepaid_reconciliation_cron_scheduler.rake +7 -0
- data/lib/tasks/prepaid_setup_types.rake +7 -0
- metadata +269 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c83a53b69842100120ae6f6edea538c4a7335fc9859e52765f4be45ad2506b0c
|
|
4
|
+
data.tar.gz: 61cd2bea3a4a011a385ad636c6f3330bc44e04085eb39ea22c64c96f86c59978
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 18012dec394174123a5fefcb459c369aab56bf075cabb068e695ab42eac92c1f487468047d6201bee6dc2f1f8730147260ec7d4b4dbfa1b4c0f5c98c31720508
|
|
7
|
+
data.tar.gz: 5dae3a462226fe008f4c17d43231d315d45e24a415fd07fe8c564f61694821ba70e037b311f8f653766df763a009e00b2ccf0d8f7bbbaf712e858a50b0cbdf43
|
data/Gemfile.lock
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
module Admin
|
|
3
3
|
module BaseControllerDecorator
|
|
4
|
+
def self.prepended(base)
|
|
5
|
+
base.before_action :set_prepaid_current_actor
|
|
6
|
+
end
|
|
7
|
+
|
|
4
8
|
# even db in read only mode, authorizor still need to run Warden callbacks which will write to db
|
|
5
9
|
# this include update tracked fields such as sign_in_count, last_sign_in_at, etc.
|
|
6
10
|
def authorize!(*args)
|
|
@@ -40,6 +44,16 @@ module Spree
|
|
|
40
44
|
|
|
41
45
|
redirect_back fallback_location: admin_root_path
|
|
42
46
|
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
# See SpreeCmCommissioner::Current#actor -- lets StoreCreditEventDecorator's after_create
|
|
51
|
+
# mirror attribute an admin-triggered authorize/capture/void/credit event (refunds, voids)
|
|
52
|
+
# to whoever is signed in, without threading current_user through Spree core's own
|
|
53
|
+
# Payment/Refund/StoreCredit call chain.
|
|
54
|
+
def set_prepaid_current_actor
|
|
55
|
+
SpreeCmCommissioner::Current.actor = try_spree_current_user
|
|
56
|
+
end
|
|
43
57
|
end
|
|
44
58
|
end
|
|
45
59
|
end
|
|
@@ -2,7 +2,22 @@ module Spree
|
|
|
2
2
|
module Admin
|
|
3
3
|
class CustomerNotificationsController < Spree::Admin::ResourceController
|
|
4
4
|
def index
|
|
5
|
-
@
|
|
5
|
+
@search = model_class.ransack(params[:q])
|
|
6
|
+
@search.sorts = 'created_at desc' if @search.sorts.empty?
|
|
7
|
+
@notifications = @search.result.page(params[:page]).per(params[:per_page] || 25)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# POST /admin/customer_notifications/:id/clone
|
|
11
|
+
def clone
|
|
12
|
+
result = SpreeCmCommissioner::CustomerNotifications::Clone.call(customer_notification: @object)
|
|
13
|
+
|
|
14
|
+
if result.success?
|
|
15
|
+
flash[:success] = Spree.t(:customer_notification_cloned)
|
|
16
|
+
redirect_to edit_admin_customer_notification_url(result.value)
|
|
17
|
+
else
|
|
18
|
+
flash[:error] = Spree.t(:customer_notification_not_cloned, error: result.error.to_s)
|
|
19
|
+
redirect_to admin_customer_notifications_url
|
|
20
|
+
end
|
|
6
21
|
end
|
|
7
22
|
|
|
8
23
|
def model_class
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Admin
|
|
3
|
+
# Two read-only views over the prepaid ledger:
|
|
4
|
+
# - #index: platform-wide filterable browse (GET /admin/store_credit/transactions).
|
|
5
|
+
# - #show: a single ledger row's full detail (GET /admin/store_credit/transactions/:id),
|
|
6
|
+
# reached from both #index's row links and the per-user store_credits Transactions tab.
|
|
7
|
+
# Not nested under a user/store_credit -- PrepaidTransaction has a global id, and one
|
|
8
|
+
# detail page reached from two different listings is simpler than two near-identical ones.
|
|
9
|
+
# Currency, balance, admin, and reason/freeze metadata live here rather than cluttering
|
|
10
|
+
# either listing table.
|
|
11
|
+
class PrepaidTransactionsController < Spree::Admin::BaseController
|
|
12
|
+
TRANSACTIONS_PER_PAGE = 25
|
|
13
|
+
|
|
14
|
+
before_action :load_transaction, only: :show
|
|
15
|
+
|
|
16
|
+
# GET /admin/store_credit/transactions -- the admin/store_credit dashboard's own
|
|
17
|
+
# platform-wide ledger browse (see Spree::Admin::StoreCreditAdminController's other tabs and
|
|
18
|
+
# store_credit_admin/_tabs.html.erb), but its own route/controller rather than a #show?
|
|
19
|
+
# tab=transactions query param, so filters/pagination live in a real URL.
|
|
20
|
+
def index
|
|
21
|
+
@current_tab = 'transactions'
|
|
22
|
+
@tenants = SpreeCmCommissioner::Tenant.order(:name)
|
|
23
|
+
@current_tenant_id = params[:tenant_id].presence&.to_i
|
|
24
|
+
@store = Spree::Store.default
|
|
25
|
+
build_transactions_search
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def show; end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def load_transaction
|
|
33
|
+
@transaction = SpreeCmCommissioner::PrepaidTransaction.find(params[:id])
|
|
34
|
+
@user = @transaction.user
|
|
35
|
+
@store_credit = @transaction.store_credit
|
|
36
|
+
@reversed = SpreeCmCommissioner::PrepaidTransaction
|
|
37
|
+
.exists?(user: @user, transaction_type: 'reverse_top_up', idempotency_key: "admin_reverse:#{@transaction.id}")
|
|
38
|
+
@return_to = safe_return_to
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# So the back link on the detail page returns to the exact filtered/paginated Transactions
|
|
42
|
+
# tab state the admin came from, not a bare reset to page 1 -- only same-admin-namespace
|
|
43
|
+
# paths are honored, since this is attacker-controllable input (an admin could be tricked
|
|
44
|
+
# into following a crafted link) and open-redirecting out of /admin would be a phishing vector.
|
|
45
|
+
def safe_return_to
|
|
46
|
+
candidate = params[:return_to]
|
|
47
|
+
candidate if candidate.present? && candidate.start_with?('/admin/')
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Individual ledger row search/browse -- scoped to whichever tenant the dashboard's own
|
|
51
|
+
# tenant selector has picked (tenant_id: nil => platform-issued, same convention as
|
|
52
|
+
# StoreCreditOverviewQuery), further filtered via Ransack (q[user_email_cont],
|
|
53
|
+
# q[currency_eq], q[transaction_type_eq], q[created_at_gteq]/[created_at_lteq], ...).
|
|
54
|
+
def build_transactions_search
|
|
55
|
+
params[:q] = {} if params[:q].blank?
|
|
56
|
+
@transactions_search = SpreeCmCommissioner::PrepaidTransaction.where(tenant_id: @current_tenant_id).ransack(params[:q])
|
|
57
|
+
@transactions = @transactions_search.result
|
|
58
|
+
.includes(:user, :order, :tenant, :admin, :store_credit)
|
|
59
|
+
.order(created_at: :desc)
|
|
60
|
+
.page(params[:page])
|
|
61
|
+
.per(TRANSACTIONS_PER_PAGE)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def model_class
|
|
65
|
+
SpreeCmCommissioner::PrepaidTransaction
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -14,6 +14,7 @@ module Spree
|
|
|
14
14
|
# partial reads these ivars — without this a rejected save is a 500 instead of a form.
|
|
15
15
|
before_action :load_stock_rows, only: %i[edit update]
|
|
16
16
|
before_action :load_assignable_roles, only: %i[new create edit update]
|
|
17
|
+
before_action :load_offline_product_ids, only: %i[new create edit update]
|
|
17
18
|
|
|
18
19
|
# GET /admin/private_sales
|
|
19
20
|
def index
|
|
@@ -45,6 +46,11 @@ module Spree
|
|
|
45
46
|
@assignable_roles = Spree::Role.non_vendor.order(:name)
|
|
46
47
|
end
|
|
47
48
|
|
|
49
|
+
# The product search only returns `name`, so the form labels offline products from this list.
|
|
50
|
+
def load_offline_product_ids
|
|
51
|
+
@offline_product_ids = Spree::Product.where(offline: true).pluck(:id)
|
|
52
|
+
end
|
|
53
|
+
|
|
48
54
|
def location_after_save
|
|
49
55
|
edit_admin_private_sale_path(@private_sale)
|
|
50
56
|
end
|
|
@@ -42,7 +42,8 @@ module Spree
|
|
|
42
42
|
|
|
43
43
|
def commission_for(product)
|
|
44
44
|
commission_rate = product.commission_rate || product.vendor&.commission_rate || 0
|
|
45
|
-
product
|
|
45
|
+
# A product can have no price in the store currency yet (e.g. a freshly created ticket).
|
|
46
|
+
(product.price || 0) * commission_rate / 100
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
# override
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Admin
|
|
3
|
+
# Platform-admin management of reschedule policies, built like the Payment
|
|
4
|
+
# Methods page: a scoped tab bar (Store / Vendors / Tenants) over the shared
|
|
5
|
+
# ReschedulePolicy table.
|
|
6
|
+
#
|
|
7
|
+
# - Store tab (default): platform policies (vendor_id nil). Full CRUD.
|
|
8
|
+
# - Vendors tab: every vendor policy, read-only.
|
|
9
|
+
# - Tenants tab: vendor policies whose vendor belongs to a tenant, read-only.
|
|
10
|
+
#
|
|
11
|
+
# Create/Update delegate to the existing ReschedulePolicies services (with
|
|
12
|
+
# vendor: nil), which own calculator building, typed rules, and the
|
|
13
|
+
# one-default-per-scope rule.
|
|
14
|
+
class ReschedulePoliciesController < BaseController
|
|
15
|
+
# Only these tabs switch the page into read-only vendor/tenant mode; any
|
|
16
|
+
# other value falls back to the store tab, in the controller and the view
|
|
17
|
+
# alike (see `current_tab`).
|
|
18
|
+
TABS = %w[vendors tenants].freeze
|
|
19
|
+
|
|
20
|
+
before_action :load_policy, only: %i[edit update destroy set_default]
|
|
21
|
+
|
|
22
|
+
helper_method :current_tab
|
|
23
|
+
|
|
24
|
+
def index
|
|
25
|
+
authorize! :manage, SpreeCmCommissioner::ReschedulePolicy
|
|
26
|
+
|
|
27
|
+
@search = collection.ransack(params[:q])
|
|
28
|
+
@reschedule_policies = @search.result
|
|
29
|
+
.order(default: :desc, name: :asc)
|
|
30
|
+
.page(params[:page])
|
|
31
|
+
.per(params[:per_page] || 20)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def new
|
|
35
|
+
authorize! :manage, SpreeCmCommissioner::ReschedulePolicy
|
|
36
|
+
@reschedule_policy = SpreeCmCommissioner::ReschedulePolicy.new(status: :active)
|
|
37
|
+
# Seed a calculator so the fee fields render; the form/services can swap it.
|
|
38
|
+
@reschedule_policy.calculator_type = 'Spree::Calculator::FlatRate'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def create
|
|
42
|
+
authorize! :manage, SpreeCmCommissioner::ReschedulePolicy
|
|
43
|
+
|
|
44
|
+
result = SpreeCmCommissioner::ReschedulePolicies::Create.call(vendor: nil, options: policy_params)
|
|
45
|
+
if result.success?
|
|
46
|
+
flash[:success] = Spree.t(:successfully_created, resource: reschedule_policy_name)
|
|
47
|
+
redirect_to admin_reschedule_policies_path
|
|
48
|
+
else
|
|
49
|
+
@reschedule_policy = result.value || SpreeCmCommissioner::ReschedulePolicy.new
|
|
50
|
+
flash.now[:error] = result.error&.value
|
|
51
|
+
render :new, status: :unprocessable_entity
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def update
|
|
56
|
+
result = SpreeCmCommissioner::ReschedulePolicies::Update.call(policy: @reschedule_policy, options: policy_params)
|
|
57
|
+
if result.success?
|
|
58
|
+
flash[:success] = Spree.t(:successfully_updated, resource: reschedule_policy_name)
|
|
59
|
+
redirect_to admin_reschedule_policies_path
|
|
60
|
+
else
|
|
61
|
+
flash.now[:error] = result.error&.value
|
|
62
|
+
render :edit, status: :unprocessable_entity
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def destroy
|
|
67
|
+
if @reschedule_policy.active?
|
|
68
|
+
# The reschedule flow resolves the store policy from the *active*
|
|
69
|
+
# store policies, so deleting this one would reject every reschedule.
|
|
70
|
+
flash[:error] = Spree.t('admin.reschedule_policies.cannot_delete_active')
|
|
71
|
+
elsif @reschedule_policy.destroy
|
|
72
|
+
flash[:success] = Spree.t(:successfully_removed, resource: reschedule_policy_name)
|
|
73
|
+
else
|
|
74
|
+
flash[:error] = @reschedule_policy.errors.full_messages.to_sentence
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
redirect_to admin_reschedule_policies_path
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Promote this store policy to the active default; the Update service
|
|
81
|
+
# demotes the current default in the same scope first.
|
|
82
|
+
def set_default
|
|
83
|
+
result = SpreeCmCommissioner::ReschedulePolicies::Update.call(
|
|
84
|
+
policy: @reschedule_policy, options: { default: true, status: :active }
|
|
85
|
+
)
|
|
86
|
+
flash[:success] = Spree.t(:successfully_updated, resource: reschedule_policy_name) if result.success?
|
|
87
|
+
flash[:error] = result.error&.value if result.failure?
|
|
88
|
+
redirect_to admin_reschedule_policies_path
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
# nil for the store tab (including unknown/blank `tab` values), otherwise
|
|
94
|
+
# the recognised tab name.
|
|
95
|
+
def current_tab
|
|
96
|
+
params[:tab] if TABS.include?(params[:tab])
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# All statuses are shown (draft/inactive included) so the admin can manage
|
|
100
|
+
# them, so the raw vendor_id/tenant_id filters are used, not the
|
|
101
|
+
# active-only `store_policies` / `vendor_policies` scopes.
|
|
102
|
+
def collection
|
|
103
|
+
base = SpreeCmCommissioner::ReschedulePolicy
|
|
104
|
+
case current_tab
|
|
105
|
+
when 'vendors'
|
|
106
|
+
base.where.not(vendor_id: nil).includes(:vendor)
|
|
107
|
+
when 'tenants'
|
|
108
|
+
base.where.not(vendor_id: nil).where.not(tenant_id: nil).includes(vendor: :tenant)
|
|
109
|
+
else
|
|
110
|
+
base.where(vendor_id: nil)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def load_policy
|
|
115
|
+
authorize! :manage, SpreeCmCommissioner::ReschedulePolicy
|
|
116
|
+
# Edits apply to store policies only; vendor/tenant policies are read-only here.
|
|
117
|
+
@reschedule_policy = SpreeCmCommissioner::ReschedulePolicy.where(vendor_id: nil).find(params[:id])
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Store policies only carry a fee calculator; the typed reschedule rules
|
|
121
|
+
# (cutoff, max-per-booking, vendor approval, ...) belong to vendor policies
|
|
122
|
+
# and are managed elsewhere, so they are not collected here.
|
|
123
|
+
def policy_params
|
|
124
|
+
params.require(:spree_cm_commissioner_reschedule_policy).permit(
|
|
125
|
+
:name,
|
|
126
|
+
:description,
|
|
127
|
+
:status,
|
|
128
|
+
:default,
|
|
129
|
+
:calculator_type,
|
|
130
|
+
calculator_attributes: calculator_attribute_keys
|
|
131
|
+
)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Only the preferences of the selected (whitelisted) calculator subclass
|
|
135
|
+
# are permitted, so preferences never land on the wrong calculator type.
|
|
136
|
+
def calculator_attribute_keys
|
|
137
|
+
type = params.dig(:spree_cm_commissioner_reschedule_policy, :calculator_type)
|
|
138
|
+
return [] unless %w[Spree::Calculator::FlatRate Spree::Calculator::PercentOnLineItem].include?(type)
|
|
139
|
+
|
|
140
|
+
type.constantize.new.defined_preferences.map { |preference| "preferred_#{preference}" }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def reschedule_policy_name
|
|
144
|
+
SpreeCmCommissioner::ReschedulePolicy.model_name.human
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Admin
|
|
3
|
+
class RescheduleRequestsController < Spree::Admin::ResourceController
|
|
4
|
+
helper Spree::Admin::RescheduleRequestsHelper
|
|
5
|
+
|
|
6
|
+
def index
|
|
7
|
+
@search = model_class.includes(:order, :user, :vendor, :vendor_policy).ransack(params[:q])
|
|
8
|
+
@reschedule_requests = @search.result
|
|
9
|
+
.order(created_at: :desc)
|
|
10
|
+
.page(params[:page])
|
|
11
|
+
.per(params[:per_page] || Spree::Backend::Config[:admin_orders_per_page])
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def show
|
|
15
|
+
@reschedule_request = load_request
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def state_changes
|
|
19
|
+
@reschedule_request = model_class.find(params[:id])
|
|
20
|
+
@state_changes = @reschedule_request.state_changes.includes(:user).order(created_at: :desc)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def approve
|
|
24
|
+
run_service(
|
|
25
|
+
SpreeCmCommissioner::RescheduleRequests::Vendors::Approve,
|
|
26
|
+
approved_by: try_spree_current_user,
|
|
27
|
+
success_key: 'approved'
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def reject
|
|
32
|
+
run_service(
|
|
33
|
+
SpreeCmCommissioner::RescheduleRequests::Vendors::Reject,
|
|
34
|
+
rejected_by: try_spree_current_user,
|
|
35
|
+
success_key: 'rejected'
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def cancel
|
|
40
|
+
run_service(
|
|
41
|
+
SpreeCmCommissioner::RescheduleRequests::Cancel,
|
|
42
|
+
cancelled_by: try_spree_current_user,
|
|
43
|
+
reason: params[:cancellation_reason].presence,
|
|
44
|
+
success_key: 'cancelled'
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
# Each service takes the acting admin so audit rows record who acted
|
|
51
|
+
# rather than the request's customer.
|
|
52
|
+
def run_service(service, success_key:, **)
|
|
53
|
+
@reschedule_request = model_class.find(params[:id])
|
|
54
|
+
|
|
55
|
+
result = service.call(reschedule_request: @reschedule_request, **)
|
|
56
|
+
if result.success?
|
|
57
|
+
flash[:success] = t("spree.admin.reschedule_requests.#{success_key}")
|
|
58
|
+
else
|
|
59
|
+
flash[:error] = result.error&.value
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
redirect_to admin_reschedule_request_path(@reschedule_request)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def load_request
|
|
66
|
+
model_class.includes(
|
|
67
|
+
:order, :user, :vendor, :tenant, :vendor_policy, :store_policy,
|
|
68
|
+
previous_request_snapshots: %i[variant line_item],
|
|
69
|
+
new_request_snapshots: %i[variant line_item]
|
|
70
|
+
).find(params[:id])
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def model_class
|
|
74
|
+
SpreeCmCommissioner::RescheduleRequest
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def object_name
|
|
78
|
+
'spree_cm_commissioner_reschedule_request'
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Admin
|
|
3
|
+
class StoreCreditAdminController < Spree::Admin::BaseController
|
|
4
|
+
TABS = %w[overview frozen_store_credits settings].freeze
|
|
5
|
+
|
|
6
|
+
def show
|
|
7
|
+
@current_tab = TABS.include?(params[:tab]) ? params[:tab] : 'overview'
|
|
8
|
+
@tenants = SpreeCmCommissioner::Tenant.order(:name)
|
|
9
|
+
@current_tenant_id = params[:tenant_id].presence&.to_i
|
|
10
|
+
store_credit_overview = SpreeCmCommissioner::StoreCreditOverviewQuery.new(issuer_tenant_id: @current_tenant_id)
|
|
11
|
+
@summary = store_credit_overview.call
|
|
12
|
+
@frozen_store_credits = store_credit_overview.frozen_store_credits
|
|
13
|
+
@config = SpreeCmCommissioner::Configuration.new
|
|
14
|
+
@store = Spree::Store.default
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Covers every prepaid_* Configuration preference editable from the Settings tab -- the
|
|
18
|
+
# currency rate table (keyed by Spree::Store.default#supported_currency_codes -- edit the
|
|
19
|
+
# currency list itself on the store's own admin settings page, not here) and the
|
|
20
|
+
# fraud/velocity limits (Configuration's comment on prepaid_max_topups_per_hour and
|
|
21
|
+
# friends). One form/action since nothing here is ever meaningfully saved independently.
|
|
22
|
+
SETTINGS_KEYS = %i[
|
|
23
|
+
prepaid_currency_rates prepaid_topup_min_amount prepaid_topup_max_amount
|
|
24
|
+
prepaid_max_topups_per_hour prepaid_max_topup_count_daily prepaid_daily_topup_limit
|
|
25
|
+
prepaid_max_balance
|
|
26
|
+
].freeze
|
|
27
|
+
|
|
28
|
+
def update_settings
|
|
29
|
+
config = SpreeCmCommissioner::Configuration.new
|
|
30
|
+
new_values = {
|
|
31
|
+
prepaid_currency_rates: permitted_currency_rates(config),
|
|
32
|
+
prepaid_topup_min_amount: params[:prepaid_topup_min_amount].to_d,
|
|
33
|
+
prepaid_topup_max_amount: params[:prepaid_topup_max_amount].to_d,
|
|
34
|
+
prepaid_max_topups_per_hour: params[:prepaid_max_topups_per_hour].to_i,
|
|
35
|
+
prepaid_max_topup_count_daily: params[:prepaid_max_topup_count_daily].to_i,
|
|
36
|
+
prepaid_daily_topup_limit: params[:prepaid_daily_topup_limit].to_d,
|
|
37
|
+
prepaid_max_balance: params[:prepaid_max_balance].to_d
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
error = invalid_settings_reason(new_values)
|
|
41
|
+
if error
|
|
42
|
+
flash[:error] = error
|
|
43
|
+
return redirect_to admin_store_credit_path(tab: 'settings')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
old_values = SETTINGS_KEYS.index_with { |key| config[key] }
|
|
47
|
+
SETTINGS_KEYS.each { |key| config[key] = new_values[key] }
|
|
48
|
+
|
|
49
|
+
log_settings_change(old_values, new_values)
|
|
50
|
+
|
|
51
|
+
flash[:success] = I18n.t('spree_cm_commissioner.store_credit_admin.settings_updated')
|
|
52
|
+
redirect_to admin_store_credit_path(tab: 'settings')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def run_reconciliation
|
|
56
|
+
report = SpreeCmCommissioner::PrepaidReconciliationService.run_daily
|
|
57
|
+
|
|
58
|
+
if report.drift_count.zero?
|
|
59
|
+
flash[:success] = I18n.t('spree_cm_commissioner.store_credit_admin.reconciliation_success', checked_count: report.checked_count)
|
|
60
|
+
else
|
|
61
|
+
flash[:error] = I18n.t(
|
|
62
|
+
'spree_cm_commissioner.store_credit_admin.reconciliation_drift',
|
|
63
|
+
checked_count: report.checked_count, drift_count: report.drift_count
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
redirect_back fallback_location: admin_store_credit_path
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
# Keyed by the store's current supported-currencies list, so a dropped currency's rate
|
|
73
|
+
# doesn't linger and a new one gets an explicit (fallback 1.0) entry.
|
|
74
|
+
def permitted_currency_rates(config)
|
|
75
|
+
currency_codes = Spree::Store.default.supported_currency_codes
|
|
76
|
+
submitted = params[:prepaid_currency_rates]
|
|
77
|
+
submitted = submitted.is_a?(ActionController::Parameters) ? submitted.permit(*currency_codes) : {}
|
|
78
|
+
|
|
79
|
+
currency_codes.index_with do |currency|
|
|
80
|
+
submitted[currency].presence&.to_f || config.prepaid_currency_rates[currency] || 1.0
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# These preferences are the only fraud/velocity ceiling enforced on store credit money-in
|
|
85
|
+
# (Prepaid::CheckTopUpLimits, Prepaid::TopUp) -- a negative value would be silently treated
|
|
86
|
+
# as "no limit" by comparisons like `amount > limit`, and a non-positive currency rate would
|
|
87
|
+
# corrupt every cross-currency conversion built on it.
|
|
88
|
+
def invalid_settings_reason(values)
|
|
89
|
+
%i[
|
|
90
|
+
prepaid_topup_min_amount prepaid_topup_max_amount prepaid_max_topups_per_hour
|
|
91
|
+
prepaid_max_topup_count_daily prepaid_daily_topup_limit prepaid_max_balance
|
|
92
|
+
].each do |key|
|
|
93
|
+
return "#{key} can't be negative" if values[key].negative?
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if values[:prepaid_topup_min_amount] > values[:prepaid_topup_max_amount] && values[:prepaid_topup_max_amount].positive?
|
|
97
|
+
return 'prepaid_topup_min_amount cannot exceed prepaid_topup_max_amount'
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
values[:prepaid_currency_rates].each do |currency, rate|
|
|
101
|
+
return "currency rate for #{currency} must be positive" unless rate.positive?
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
nil
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# No dedicated audit-log model for Configuration preferences (they're key/value, not an
|
|
108
|
+
# ActiveRecord row PaperTrail can version) -- structured log line is the same pattern
|
|
109
|
+
# #run_reconciliation already uses ("-- see logs") for admin-triggered store credit actions.
|
|
110
|
+
def log_settings_change(old_values, new_values)
|
|
111
|
+
changed = SETTINGS_KEYS.each_with_object({}) do |key, diff|
|
|
112
|
+
diff[key] = { from: old_values[key], to: new_values[key] } if old_values[key] != new_values[key]
|
|
113
|
+
end
|
|
114
|
+
return if changed.empty?
|
|
115
|
+
|
|
116
|
+
Rails.logger.info(
|
|
117
|
+
"[store_credit_settings_change] admin_id=#{try_spree_current_user&.id} changes=#{changed}"
|
|
118
|
+
)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
module Admin
|
|
3
3
|
class TicketTransferRulesController < Spree::Admin::ResourceController
|
|
4
|
+
before_action :load_offline_product_ids, only: %i[new edit create update]
|
|
5
|
+
|
|
4
6
|
def index
|
|
5
7
|
@search = model_class.ransack(params[:q])
|
|
6
8
|
@ticket_transfer_rules = @search.result
|
|
@@ -11,6 +13,11 @@ module Spree
|
|
|
11
13
|
|
|
12
14
|
private
|
|
13
15
|
|
|
16
|
+
# The scope-target search only returns `name`, so the form labels offline products from this list.
|
|
17
|
+
def load_offline_product_ids
|
|
18
|
+
@offline_product_ids = Spree::Product.where(offline: true).pluck(:id)
|
|
19
|
+
end
|
|
20
|
+
|
|
14
21
|
def model_class
|
|
15
22
|
SpreeCmCommissioner::TicketTransferRule
|
|
16
23
|
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Platform
|
|
5
|
+
# Platform::ResourceController#authorize_spree_user only runs CanCan checks when the
|
|
6
|
+
# request carries a user OAuth token (`spree_current_user.present?`); a bare app
|
|
7
|
+
# (client_credentials) token skips it entirely by design, and the base permitted-params
|
|
8
|
+
# list falls back to every column on the model (`json_api_permitted_attributes`). That
|
|
9
|
+
# would let any app token holding the generic `:admin` doorkeeper scope rewrite
|
|
10
|
+
# `amount`/`amount_used`/`user_id`/`issuer_tenant_id` etc. directly -- bypassing the
|
|
11
|
+
# prepaid ledger (cm_prepaid_transactions) and the AdminCredit/ReverseTopUp/Freeze
|
|
12
|
+
# services that exist to keep it consistent, with no record of who changed what.
|
|
13
|
+
#
|
|
14
|
+
# Nothing in this monorepo (docs, internal services, cm-market-app/client) calls this
|
|
15
|
+
# endpoint -- it's just Spree's default auto-generated CRUD from `resources :store_credits`
|
|
16
|
+
# in spree_api's routes, not something built for a known consumer. Rather than maintain a
|
|
17
|
+
# safe-fields allowlist forever (every future Spree::StoreCredit column is a fresh chance
|
|
18
|
+
# to leak through it), the endpoint is disabled outright: every action 404s, same as an
|
|
19
|
+
# unrouted path would. The route itself still exists (routes.rb lives in gems/spree_api,
|
|
20
|
+
# which this repo's decorator-only rule forbids editing directly), so this is done from
|
|
21
|
+
# our own extension gem instead.
|
|
22
|
+
module StoreCreditsControllerDecorator
|
|
23
|
+
def self.prepended(base)
|
|
24
|
+
# prepend_before_action, not before_action -- runs ahead of the base controller's own
|
|
25
|
+
# doorkeeper_authorize!/authorize_spree_user checks, so this 404s unconditionally
|
|
26
|
+
# (same as an unrouted path would) rather than only after an auth check passes.
|
|
27
|
+
base.prepend_before_action { head :not_found }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Spree::Api::V2::Platform::StoreCreditsController.prepend Spree::Api::V2::Platform::StoreCreditsControllerDecorator
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Storefront
|
|
5
|
+
module Account
|
|
6
|
+
# Endpoint: GET /api/v2/storefront/account/prepaid_balances
|
|
7
|
+
# Single endpoint for both "my one balance" and "all my balances" -- an optional
|
|
8
|
+
# ?currency= filters down to one (still an array, just 0-or-1 items), omitted returns
|
|
9
|
+
# every currency the platform has issued the user. Replaces the old separate
|
|
10
|
+
# GET account/prepaid (show) + GET account/prepaid_balances (index) pair: #show
|
|
11
|
+
# couldn't express "no currency given" without an :id segment, and this app's other
|
|
12
|
+
# index/show split forced the response shape (object vs array) to vary by param, which
|
|
13
|
+
# is worse than one endpoint with one fixed (array) shape and an optional filter.
|
|
14
|
+
class PrepaidBalancesController < Spree::Api::V2::ResourceController
|
|
15
|
+
before_action :require_spree_current_user
|
|
16
|
+
|
|
17
|
+
def index
|
|
18
|
+
if params[:currency].present?
|
|
19
|
+
currency = params[:currency].to_s.upcase
|
|
20
|
+
unless Spree::Store.default.supported_currency_codes.include?(currency)
|
|
21
|
+
return render_error_payload("unsupported currency: #{currency}")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
render_serialized_payload { collection_serializer.new(presented_collection(currency)).serializable_hash }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
# Platform-issued prepaid credit only (Spree::StoreCredit#platform_prepaid scope) --
|
|
31
|
+
# storefront is the platform namespace; tenant-issued wallet credit isn't
|
|
32
|
+
# spendable/visible here. Preferred currency sorted first (matches PrepaidConfig's
|
|
33
|
+
# supported_currencies order) rather than left in arbitrary DB order -- the client
|
|
34
|
+
# shouldn't have to inspect is_preferred on every item just to know which one to
|
|
35
|
+
# show first.
|
|
36
|
+
def presented_collection(currency)
|
|
37
|
+
preferred_currency = Spree::Store.default.default_currency
|
|
38
|
+
credits = spree_current_user.store_credits.platform_prepaid(currency).to_a
|
|
39
|
+
credits.sort_by! { |credit| credit.currency == preferred_currency ? 0 : 1 }
|
|
40
|
+
|
|
41
|
+
credits.map { |credit| SpreeCmCommissioner::PrepaidBalancePresenter.new(credit, preferred_currency) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def collection_serializer
|
|
45
|
+
Spree::V2::Storefront::PrepaidBalanceSerializer
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|