# Dynamic Service Availability System ## Overview The Dynamic Service Availability System prevents overbooking within a single booking session by tracking service selections across all participants and dynamically adjusting availability in real-time. This ensures that services with limited capacity cannot be overbooked during the form creation process. ## Problem Statement ### Business Challenge - Services have limited availability (e.g., "Advanced Ski Course: 5 available") - Multiple participants in a booking can select the same services - Without dynamic tracking, services could be overbooked within a single booking session - Users need immediate feedback when services become unavailable ### Technical Requirements - Track service selections across all participants in current booking session - Recalculate remaining availability during HTMX form refresh cycles - Hide services that have reached capacity limits - Maintain session-scoped availability (not permanent database changes) - Support all service types with consistent behavior ## Architecture Overview ### Core Components #### 1. ServiceAvailabilityCalculator (`src/Service/ServiceAvailabilityCalculator.php`) **Purpose**: Central service for calculating dynamic availability based on current selections **Key Methods**: ```php public function calculateRemainingAvailability(BookingCreateDto $bookingDto, int $currentParticipantIndex): array public function filterAvailableServices(array $services, BookingCreateDto $bookingDto, int $participantIndex): array public function isServiceAvailable(int $serviceId, BookingCreateDto $bookingDto, int $participantIndex): bool ``` **Responsibilities**: - Calculate service usage across all participants (excluding current participant) - Determine remaining availability per service - Filter service arrays to only include available services - Handle all service types consistently #### 2. Enhanced ParticipantFieldOptionsProvider **Integration Point**: Field option generation with availability filtering **Enhanced Methods**: ```php private function filterServicesByAvailability(array $services, BookingDtoInterface $bookingDto, int $participantIndex): array ``` **Updated Field Providers**: - Courses (`TOKEN_COURSES`) - Additional Services (`TOKEN_ADDITIONAL`) - Board/Meal Plans (`TOKEN_BOARD`) - Rentals (`TOKEN_RENTALS`) - Ski Passes (`TOKEN_SKI_PASS`) - Transportation Services (Outbound/Inbound) ## Implementation Details ### Service Usage Calculation The system tracks how many participants have selected each service: ```php private function calculateServiceUsage(BookingCreateDto $bookingDto, int $currentParticipantIndex): array { $serviceUsage = []; foreach ($bookingDto->participants as $index => $participant) { // Skip current participant to avoid counting their potential selections if ($index === $currentParticipantIndex) { continue; } // Count all service types for this participant $this->countParticipantServiceUsage($participant, $serviceUsage); } return $serviceUsage; } ``` ### Availability Calculation Logic For each service, remaining availability is calculated as: ``` If service.available is null or <= 0: Service is unlimited (always available) Else: Remaining = max(0, original_availability - usage_count) ``` ### Service Type Handling The system handles all major service types: **Single Selection Services**: - Board/Meal Plans - Ski Passes - Transportation (Outbound/Inbound) **Multiple Selection Services**: - Courses - Additional Services - Rentals ### Integration with Form System #### Field Option Filtering Chain ```php 'choices' => $this->filterServicesByAvailability( $this->filterServicesByAgeConstraints( $bookingDto->travel->getAdditionalServicesBySubTypes(Constants::TOKEN_COURSES), $bookingDto, $participantIndex ), $bookingDto, $participantIndex ), ``` #### HTMX Integration - Availability recalculated during each form refresh cycle - Services dynamically hidden when capacity reached - Real-time feedback without full page refresh - Maintains consistency across all participants ## Business Rules ### Availability Behavior - **Available Services**: Shown normally with pricing - **Unavailable Services**: Hidden completely from selection - **High Availability**: Services with very high limits effectively always available - **Per-Participant Limit**: Each participant can select a service maximum once ### Service Capacity Management - **Original Availability**: Parsed from XML data at booking initialization - **Dynamic Availability**: Calculated in real-time based on current selections - **Session Scope**: Availability tracking only within current booking session - **No Persistence**: Changes not saved to database or XML files - **Unlimited Services**: Services with null or ≤0 availability are treated as unlimited - **Company Strategy**: High availability values used for services that should always be bookable ### Edge Cases Handled - Null or missing availability values (treated as unlimited availability) - Zero or negative availability values (treated as unlimited availability) - Services already selected by current participant (not counted against them) - Invalid or missing participant data (gracefully ignored) - Empty service arrays (handled without errors) - Services without availability limits (always remain available) ## Usage Examples ### Scenario 1: Course Selection with Limited Capacity ``` Initial State: - Advanced Ski Course: 3 available Participant 1: Selects Advanced Ski Course → 2 remaining Participant 2: Sees Advanced Ski Course available → Selects it → 1 remaining Participant 3: Sees Advanced Ski Course available → Selects it → 0 remaining Participant 4: Advanced Ski Course hidden (not available) Note: Most services will have unlimited availability (null or high values) and remain visible. ``` ### Scenario 2: Multiple Service Types ``` Services with Limits: - Rental Helmet: 10 available - Advanced Course: 2 available - Premium Board: 5 available As participants select services: - Each selection reduces availability for remaining participants - Services become hidden when capacity reached - Participants see only services they can still book ``` ## Performance Considerations ### Optimization Strategies - **Lightweight Calculations**: Simple arithmetic operations only - **No Database Queries**: All data from memory (DTO objects) - **Cached Service Lists**: Service collections retrieved once per request - **Efficient Filtering**: Array operations with minimal overhead ### Scalability - **Memory Usage**: Minimal additional memory footprint - **Processing Time**: Linear time complexity O(n) where n = participant count - **HTMX Performance**: No impact on response times - **Large Bookings**: Efficient even with many participants - **Unlimited Services**: Zero-cost filtering for services without availability limits ## Configuration ### Service Registration The ServiceAvailabilityCalculator is automatically registered via Symfony's autowiring: ```yaml # config/services.yaml services: _defaults: autowire: true autoconfigure: true App\: resource: '../src/' exclude: - '../src/Entity/' - '../src/Kernel.php' ``` ### Field Provider Integration ```php public function __construct( private readonly ParticipantRoomChoiceLoaderFactory $roomChoiceLoaderFactory, private readonly ServiceAvailabilityCalculator $serviceAvailabilityCalculator, ) { parent::__construct(); } ``` ## Testing Strategy ### Unit Testing Scenarios 1. **Service Usage Calculation**: Test counting across multiple participants 2. **Availability Filtering**: Verify services hidden when capacity reached 3. **Edge Cases**: Handle null values, empty arrays, invalid data 4. **Service Types**: Test all service categories (courses, rentals, etc.) 5. **Current Participant Exclusion**: Ensure current participant selections not counted ### Integration Testing 1. **Form Field Generation**: Verify filtered choices in field options 2. **HTMX Refresh Cycles**: Test availability updates during form interactions 3. **Multiple Participants**: Test complex scenarios with many participants 4. **Service Combinations**: Test mixed service types and availability levels ### Manual Testing Scenarios ``` Test Case 1: Basic Availability Reduction - Create booking with 2 participants - Select service with availability = 2 for participant 1 - Verify participant 2 sees availability reduced - Select same service for participant 2 - Verify service hidden for additional participants Test Case 2: Mixed Service Types - Test courses, rentals, and transportation together - Verify each service type respects availability limits - Confirm services with high limits remain available Test Case 3: HTMX Integration - Make service selections via HTMX form refresh - Verify real-time availability updates - Test form submission and navigation between steps ``` ## Error Handling ### Graceful Degradation - **Missing Availability Data**: Treats as unavailable (hidden) - **Invalid Service Objects**: Safely ignored in calculations - **Corrupted Participant Data**: Skips invalid participants - **Service Lookup Failures**: Continues processing other services ### Logging and Monitoring - No explicit logging (availability is business logic, not error condition) - Integrates with existing form processing error handling - Symfony debug toolbar shows service container usage ## Future Enhancements ### Potential Improvements 1. **Availability Display**: Show remaining count in service labels (optional) 2. **Reservation System**: Temporary hold on services during selection 3. **Priority Booking**: VIP participants get access to limited services first 4. **Cross-Session Tracking**: Track availability across multiple booking sessions 5. **Analytics**: Collect data on service demand and capacity utilization ### Performance Optimizations 1. **Caching Layer**: Cache availability calculations for identical participant sets 2. **Lazy Loading**: Only calculate availability for visible services 3. **Background Updates**: Pre-calculate availability for common scenarios 4. **Delta Updates**: Only recalculate changed services during HTMX updates ## Troubleshooting ### Common Issues **Services Always Hidden**: - Check if services have availability limits set (should be null/0 for unlimited) - Verify service ID matching between travel data and participant selections - Confirm participant data structure is correct - Most services should be unlimited and always visible **Availability Not Updating**: - Verify HTMX integration is working - Check that form refresh includes all participant data - Ensure ServiceAvailabilityCalculator is being called **Performance Problems**: - Review participant count and service selection complexity - Check for inefficient service lookups or data processing - Monitor memory usage with large booking sessions ### Debugging Tools ```php // Debug availability calculation $calculator = $container->get(ServiceAvailabilityCalculator::class); $availability = $calculator->calculateRemainingAvailability($bookingDto, $participantIndex); dump($availability); // Debug service filtering $filtered = $calculator->filterAvailableServices($services, $bookingDto, $participantIndex); dump($filtered); ``` ## Integration Points ### Dependencies - `App\BusProNet\Model\Service` - Service data objects - `App\Form\Model\BookingCreateDto` - Booking and participant data - `App\BusProNet\Constants` - Service type constants - `App\BusProNet\Utility\DirectionMapper` - Transportation direction mapping ### Related Systems - **Field State System**: Availability filtering integrates with conditional field states - **Pricing System**: Available services included in pricing calculations - **HTMX Updates**: Availability changes trigger form refreshes - **Form Handlers**: Service selections processed by specialized field handlers ## Future Enhancements ### Planned: API Validation Stage **Important Note**: The current availability system uses XML data that may become outdated during the booking creation process. A future enhancement will implement a **validation stage** that checks final service selections against real-time availability via the BusProNet API before final booking confirmation. #### Planned Implementation - **Pre-Submission Validation**: Before final booking submission, validate all selected services against current API availability - **Real-Time Check**: Call BusProNet API to get current availability status - **Conflict Resolution**: Handle cases where selected services are no longer available - **User Feedback**: Provide clear messaging when services become unavailable during booking process #### Technical Integration Points ```php // Future validation service (planned) class BookingValidationService { public function validateServiceAvailability(BookingCreateDto $bookingDto): ValidationResult; public function resolveAvailabilityConflicts(BookingCreateDto $bookingDto): ConflictResolution; } ``` #### Business Logic - **Session Availability**: Current system prevents overbooking within single booking session - **API Validation**: Future system will prevent overbooking across all booking sessions system-wide - **Two-Stage Protection**: Provides both immediate feedback and final validation This planned enhancement will complement the existing dynamic availability system by adding a final validation layer that ensures booking integrity against the authoritative BusProNet API data. --- **Implementation Status**: ✅ **Completed and Tested** **Last Updated**: January 2025 **Integration**: Seamless with existing form system **Performance**: Optimized for real-time updates **Testing**: Verified working in development environment **Future Enhancement**: API validation stage planned for booking finalization **Documentation**: Comprehensive with examples and troubleshooting