The 'Any item' option in the bookings modal is currently not correctly calculating maximum available booking window correctly and we have older code left behind that attempted to allow any item at a biblio level without taking into account item type limitations. This bug acts to correct that by implementing an optimized algorithm for picking the item at booking time to try and maximize the pool of available items for future bookings.
Created attachment 183211 [details] [review] Bug 40134: Implement dynamic item assignment with smart window maximization This patch implements smart window maximization for "any item of itemtype X" bookings using dynamic item pool reduction. The core principle is "never re-add items to pool" - once an item is removed from the available pool because it becomes unavailable, it's never re-added even if it becomes available again later. This ensures optimal resource allocation and maximum booking windows. Key features: - Dynamic item pool reduction algorithm in isDateInMaximumWindow() - Smart itemtype availability calculation in isDateDisabledForItemtype() - Optimal item selection for booking submission in selectOptimalItem() - Future availability calculation to choose best items - Comprehensive JSDoc documentation with detailed examples The algorithm works by: 1. Starting with items available on the selected start date 2. Walking through each day from start to target end date 3. Removing items from pool when they become unavailable 4. Never re-adding items even if they become available again later 5. Disabling dates when no items remain in pool This maximizes booking windows while preventing conflicts by being conservative about item availability. Test plan: 1. Apply patch and restart services 2. Navigate to a biblio detail page with bookable items of multiple types 3. Click "Place booking" and select a patron and pickup location 4. Select an itemtype (not a specific item) to enable "any item" mode 5. Select a start date - verify end date options are maximized based on available items across the booking period 6. Test with complex booking scenarios where items have overlapping bookings 7. Verify that the algorithm correctly reduces the item pool as items become unavailable and extends booking windows as far as possible 8. Confirm that items which become available again later in the period are not re-added to the pool (key algorithm principle) 9. Submit booking and verify optimal item is selected automatically
Created attachment 186371 [details] [review] Bug 40134: Implement dynamic item assignment with smart window maximization This patch implements smart window maximization for "any item of itemtype X" bookings using dynamic item pool reduction. The core principle is "never re-add items to pool" - once an item is removed from the available pool because it becomes unavailable, it's never re-added even if it becomes available again later. This ensures optimal resource allocation and maximum booking windows. Key features: - Dynamic item pool reduction algorithm in isDateInMaximumWindow() - Smart itemtype availability calculation in isDateDisabledForItemtype() - Optimal item selection for booking submission in selectOptimalItem() - Future availability calculation to choose best items - Comprehensive JSDoc documentation with detailed examples The algorithm works by: 1. Starting with items available on the selected start date 2. Walking through each day from start to target end date 3. Removing items from pool when they become unavailable 4. Never re-adding items even if they become available again later 5. Disabling dates when no items remain in pool This maximizes booking windows while preventing conflicts by being conservative about item availability. Test plan: 1. Apply patch and restart services 2. Navigate to a biblio detail page with bookable items of multiple types 3. Click "Place booking" and select a patron and pickup location 4. Select an itemtype (not a specific item) to enable "any item" mode 5. Select a start date - verify end date options are maximized based on available items across the booking period 6. Test with complex booking scenarios where items have overlapping bookings 7. Verify that the algorithm correctly reduces the item pool as items become unavailable and extends booking windows as far as possible 8. Confirm that items which become available again later in the period are not re-added to the pool (key algorithm principle) 9. Submit booking and verify optimal item is selected automatically Signed-off-by: Thibaud Guillot <thibaud.guillot@biblibre.com>
Created attachment 190800 [details] [review] Bug 40134: Implement optimal item selection for 'any item' bookings This patch implements an algorithm for selecting the item that would leave the largest gap after the proposed booking to maximize the opportunity for future bookings. Key Changes: Koha::Booking: - Added set_itemtype_filter() method for transient itemtype parameter - Added _select_optimal_item() method using single aggregated query * Groups bookings by item_id with MIN(start_date) * Selects items with no future bookings (optimal) * Among items with bookings, selects one with latest next booking * O(1) complexity - single database query with GROUP BY - Updated _assign_item_for_booking() to use optimal selection * Filters by itemtype when itemtype_id is provided * Replaces previous random selection (RAND()) API Changes: - Added itemtype_id parameter support to add() endpoint - Added itemtype_id parameter support to update() endpoint - Validates exactly one of item_id or itemtype_id is provided - itemtype_id is transient - not persisted to database - Extracted and applied via set_itemtype_filter() before store() Architecture: - itemtype_id is NOT a database column (transient only) - Used as selection criterion, then discarded Test plan: 1. Apply patch and restart services 2. Create "any item" booking via staff interface 3. Verify item with longest future availability is selected 4. Test via API with itemtype_id parameter 5. Verify itemtype_id is not persisted to database 6. Run t/db_dependent/Koha/Booking.t 7. Run t/db_dependent/api/v1/bookings.t
Created attachment 190801 [details] [review] Bug 40134: Add unit tests for optimal item selection This patch adds unit tests for the server-side optimal item selection functionality. t/db_dependent/Koha/Booking.t: - Added subtest for _select_optimal_item() method (7 tests) * Tests with no items (returns undef) * Tests with single item (returns that item) * Tests with multiple items (selects item with longest future availability) * Tests optimal selection across items with different booking patterns * Tests handling of cancelled bookings (ignored in selection) * Tests iterator reset after selection - Added subtest for _assign_item_for_booking() with itemtype filtering (6 tests) * Tests booking without item_id or itemtype_id (selects any available) * Tests booking with itemtype_id (filters by type) * Tests optimal selection within filtered itemtype * Tests exception when no items of specified type available - Added integration test for full optimal selection workflow (5 tests) * Creates 3 items with different future booking patterns * Verifies items selected in optimal order (B→C→A) * Demonstrates preservation of constrained resources * Validates complete algorithm chain * Tests booking failure when all items exhausted t/db_dependent/api/v1/bookings.t: - Added subtest for itemtype_id parameter validation (16 tests) * Tests successful booking creation with itemtype_id * Tests that item is automatically assigned * Tests validation: cannot specify both item_id and itemtype_id * Tests validation: must specify either item_id or itemtype_id * Tests clash detection with itemtype-based bookings - Fixed duplicate booking_id test to use different dates * Prevents clash detection from masking duplicate ID constraint - Updated test count from 3 to 8 subtests in Booking.t - Updated test count to match actual assertions (16 not 18) Test Coverage: - Internal method testing: _select_optimal_item() - Integration testing: _assign_item_for_booking() - API endpoint testing: add() with itemtype_id - End-to-end workflow validation - Edge cases: empty sets, single items, ties, cancelled bookings Test plan: 1. prove t/db_dependent/Koha/Booking.t 2. prove t/db_dependent/api/v1/bookings.t 3. Verify all tests pass
Created attachment 190802 [details] [review] Bug 40134: Implement client-side improvements for bookings This patch implements client-side improvements for the bookings modal, including smart window maximization and hybrid submission logic that delegates optimal item selection to the server. Smart Window Maximization: - Implements "never re-add items to pool" algorithm - Dynamic item pool reduction based on availability - isDateDisabledForItemtype() calculates available dates - isDateInMaximumWindow() validates end dates against item pool - Provides real-time calendar feedback to users Hybrid Submission Logic: For "any item" bookings, the client now uses a smart hybrid approach: 1. Filters items by selected itemtype 2. Checks availability for the booking period 3. If 0 items available: Shows error (no suitable item) 4. If 1 item available: Sends item_id (optimization - skips server) 5. If 2+ items available: Sends itemtype_id (delegates to server) This minimizes server round-trips while delegating complex optimal selection logic to the server when multiple choices exist. Test plan: 1. Navigate to biblio detail page with bookable items 2. Click "Place booking" and select patron/pickup location 3. Select itemtype (not specific item) - "any item" mode 4. Select start date - verify end dates are maximized 5. Verify calendar disables dates when no items available 6. Submit booking - verify it succeeds 7. Check that optimal item was selected (server-side) 8. Test edit mode with "any item" selection 9. Verify both create and edit use same logic
Created attachment 190803 [details] [review] Bug 40134: Add Cypress tests for booking improvements This patch adds end-to-end Cypress tests for the booking modal improvements, including smart window maximization and server-side optimal item selection. New Tests: 1. "should successfully submit an 'Any item' booking with server-side optimal item selection" - Tests the complete "any item" booking workflow - Selects itemtype and "Any item" option (item_id = 0) - Submits booking form - Verifies modal closes without errors - Validates booking created in database - Confirms item was assigned by server - Verifies server-side optimal selection completed - Test status: PASSING 2. "should maximize booking window by dynamically reducing available items during overlaps" - Creates 4 items with strategic booking patterns - Tests "never re-add items to pool" algorithm - Validates 5 scenarios: a) Full window when all items available b) Reduced window when Item 1 becomes unavailable c) Further reduction when Items 1&2 unavailable d) Minimal window with Items 1,2,3 unavailable e) No availability when all items booked - Demonstrates smart window maximization - Test status: PASSING 3. "should handle edit mode date initialization correctly" - Tests fix for edit mode race condition - Verifies dates populate correctly when editing booking - Ensures itemtype is set before date initialization - Tests that flatpickr dates are set properly - Test status: PASSING Updated Tests: - "should successfully submit a booking" * Updated to use day+5 start date (accounts for 3-day lead period) * Ensures test doesn't fail on past-date constraint - Updated test comments and log messages to reflect server-side selection * Changed from "client-side selectOptimalItem()" references * Updated to "server-side optimal item selection" * Clarified that server performs the optimal selection Test Coverage: - End-to-end "any item" booking submission - Smart window maximization algorithm - Edit mode date initialization - Server-side optimal selection validation - Database verification of assigned items - Modal interaction and form submission Test plan: 1. Start KTD environment 2. Ensure Cypress is installed: yarn install 3. Run: npx cypress run --spec "t/cypress/integration/Circulation/bookingsModalBasic_spec.ts" 4. Verify all 11 tests pass 5. Optionally run in interactive mode: npx cypress open