diff --git a/storage/plugins/mobile-api/src/Controllers/ActionsController.php b/storage/plugins/mobile-api/src/Controllers/ActionsController.php index d725b63d..e65162b4 100644 --- a/storage/plugins/mobile-api/src/Controllers/ActionsController.php +++ b/storage/plugins/mobile-api/src/Controllers/ActionsController.php @@ -210,9 +210,13 @@ public function requestReservation(Request $request, ResponseInterface $response // Decide loan vs reservation using the SAME availability gate the web // uses (ReservationManager), so the two surfaces agree. Immediate loan - // only when the user asked for "now" (no date) and a copy is free. + // when the user asked for "now" — either no date, OR today's date with + // a copy free. The app's "Request loan" on an available title sends + // today (its date picker pre-selects the first free day = today), so + // without the today case that flow wrongly became a reservation + // instead of a pending loan. A FUTURE date is always a reservation. $immediate = false; - if ($desired === '') { + if ($desired === '' || $desired === date('Y-m-d')) { $manager = new \App\Controllers\ReservationManager($this->db); $immediate = $manager->isBookAvailableForImmediateLoan($bookId, null, null, $userId); } diff --git a/tests/mobile-api-behaviors.spec.js b/tests/mobile-api-behaviors.spec.js index 672e26a0..fd437796 100644 --- a/tests/mobile-api-behaviors.spec.js +++ b/tests/mobile-api-behaviors.spec.js @@ -600,4 +600,25 @@ test.describe('Reservations', () => { clearBorrowerReservations(); } }); + + test('26) {available book, desired_date=today} → immediate loan (type=loan), not a reservation', async ({ request }) => { + // The app's "Request loan" on an AVAILABLE title sends today's date (its + // date picker pre-selects the first free day = today). The backend must + // treat today + a free copy as an immediate pending loan, not a + // reservation — otherwise the available-now flow silently queues instead + // of requesting a loan. A FUTURE date stays a reservation (test 22). + clearBorrowerReservations(); + try { + const book = pickAvailableBook(); + const res = await call(request, 'POST', '/reservations', { + token: ctx.userToken, + body: { book_id: book, desired_date: todayYmd() }, + }); + expect(res.status(), 'created').toBe(201); + const j = await jsonOf(res); + expect(j?.data?.type, 'today + available → immediate loan').toBe('loan'); + } finally { + clearBorrowerReservations(); + } + }); });