diff --git a/locale/fr_FR.json b/locale/fr_FR.json index 93291feb0..a31cc8258 100644 --- a/locale/fr_FR.json +++ b/locale/fr_FR.json @@ -8374,11 +8374,11 @@ "Se lo stesso file è allegato anche ad altre schede, vale la visibilità più aperta tra quelle schede.": "Si le même fichier est aussi joint à d’autres notices, la visibilité la plus ouverte parmi ces notices s’applique.", "Gestione allegati digitali comprende anche l’apertura degli allegati riservati: chi può cambiare la visibilità di un file può comunque aprirlo, quindi i due permessi vengono salvati insieme.": "La gestion des pièces jointes numériques comprend aussi l’ouverture des pièces jointes réservées : qui peut modifier la visibilité d’un fichier peut de toute façon l’ouvrir, les deux autorisations sont donc enregistrées ensemble.", "Diritti esistenti": "Droits existants", - "Durata massima del prestito: %1$d giorni (restituzione entro il %2$s). Dal %3$s il libro è già prenotato.": "Durée maximale du prêt : %1$d jours (retour avant le %2$s). À partir du %3$s, le livre est déjà réservé.", + "Durata massima del prestito: %1$d giorni (restituzione entro il %2$s). Dal %3$s il libro è già prenotato.": "Durée maximale du prêt : %1$d jours (retour au plus tard le %2$s). À partir du %3$s, le livre est déjà réservé.", "Giorno non disponibile (%1$s): %2$s": "Jour non disponible (%1$s) : %2$s", "Il periodo richiesto supera la durata massima consentita.": "La période demandée dépasse la durée maximale de prêt autorisée.", "Da questa data di inizio non è possibile alcun prestito: dal %s il libro è già prenotato. Scegli un'altra data.": "Aucun prêt n'est possible à partir de cette date : à partir du %s, le livre est déjà réservé. Choisissez une autre date.", - "Durata massima del prestito: 1 giorno (restituzione entro il %1$s). Dal %2$s il libro è già prenotato.": "Durée maximale du prêt : 1 jour (retour avant le %1$s). À partir du %2$s, le livre est déjà réservé.", + "Durata massima del prestito: 1 giorno (restituzione entro il %1$s). Dal %2$s il libro è già prenotato.": "Durée maximale du prêt : 1 jour (retour au plus tard le %1$s). À partir du %2$s, le livre est déjà réservé.", "Rinnovo non possibile: dal %s il libro è già prenotato.": "Aucune prolongation possible : à partir du %s, le livre est déjà réservé.", "Rinnovo possibile al massimo di 1 giorno (fino al %1$s): dal %2$s il libro è già prenotato.": "Prolongation possible d'au plus 1 jour (jusqu'au %1$s) : à partir du %2$s, le livre est déjà réservé.", "Rinnovo possibile al massimo di %1$d giorni (fino al %2$s): dal %3$s il libro è già prenotato.": "Prolongation possible d'au plus %1$d jours (jusqu'au %2$s) : à partir du %3$s, le livre est déjà réservé.", diff --git a/tests/loan-request-capped-by-scheduled-loan.spec.js b/tests/loan-request-capped-by-scheduled-loan.spec.js index 904eaf80d..d3f475266 100644 --- a/tests/loan-request-capped-by-scheduled-loan.spec.js +++ b/tests/loan-request-capped-by-scheduled-loan.spec.js @@ -84,6 +84,7 @@ test.describe('Book page: loan request capped by a scheduled loan', () => { let copyId = 0; let seededLoanId = 0; let borrowerId = 0; + let bookSnapshot = ''; let today = ''; const RUN = Date.now().toString(36).slice(-6); const BORROWER_EMAIL = `lw-cap-${RUN}@example.invalid`; @@ -109,6 +110,9 @@ test.describe('Book page: loan request capped by a scheduled loan', () => { ); if (!bookId) return; copyId = parseInt(dbQuery(`SELECT id FROM copie WHERE libro_id=${bookId} LIMIT 1`) || '0', 10); + // The request may be auto-approved (loans.auto_approve_requests), which + // recomputes the title's derived columns: restore them afterwards. + bookSnapshot = dbQuery(`SELECT CONCAT(IFNULL(copie_disponibili,'NULL'),'\t',IFNULL(stato,'')) FROM libri WHERE id=${bookId}`); // The application's clock (DateHelper::today(): app.timezone, default // Europe/Rome), not the database session's: near midnight they differ. const tz = dbQuery("SELECT setting_value FROM system_settings WHERE category='app' AND setting_key='timezone' LIMIT 1") || 'Europe/Rome'; @@ -140,6 +144,11 @@ test.describe('Book page: loan request capped by a scheduled loan', () => { dbQuery(`DELETE FROM prestiti WHERE id=${seededLoanId}`); dbQuery(`DELETE FROM prestiti WHERE libro_id=${bookId} AND utente_id=(SELECT id FROM utenti WHERE email='${ADMIN_EMAIL.replace(/'/g, "''")}' LIMIT 1) AND data_prestito>='${today}'`); dbQuery(`UPDATE copie SET stato='disponibile' WHERE id=${copyId}`); + const [avail, stato] = bookSnapshot.split('\t'); + if (avail !== undefined && stato !== undefined) { + const availSql = avail === 'NULL' ? 'NULL' : String(parseInt(avail, 10) || 0); + dbQuery(`UPDATE libri SET copie_disponibili=${availSql}, stato='${stato.replace(/'/g, "''")}' WHERE id=${bookId}`); + } } if (borrowerId) { dbQuery(`DELETE FROM prestiti WHERE utente_id=${borrowerId}`); diff --git a/tests/loan-window-cap.unit.php b/tests/loan-window-cap.unit.php index 6b50b60dd..c63f96ef1 100644 --- a/tests/loan-window-cap.unit.php +++ b/tests/loan-window-cap.unit.php @@ -66,7 +66,17 @@ $bookIds = []; /** @var int[] $userIds */ $userIds = []; -$cleanup = static function () use ($db, &$bookIds, &$userIds): void { +/** @var array $savedLoanSettings */ +$savedLoanSettings = []; +$cleanup = static function () use ($db, &$bookIds, &$userIds, &$savedLoanSettings): void { + $settings = new SettingsRepository($db); + foreach ($savedLoanSettings as $key => $value) { + if ($value === null) { + $settings->delete('loans', $key); + } else { + $settings->set('loans', $key, $value); + } + } foreach ($bookIds as $id) { $db->query("DELETE FROM prestiti WHERE libro_id = {$id}"); $db->query("DELETE FROM prenotazioni WHERE libro_id = {$id}"); @@ -138,6 +148,13 @@ }; $settings = new SettingsRepository($db); +// The scenario needs a default longer than the conflict offset: pin the two +// durations for the run (the installation's values are restored by $cleanup). +foreach (['loan_duration_days', 'max_loan_duration_days'] as $key) { + $savedLoanSettings[$key] = $settings->get('loans', $key); +} +$settings->set('loans', 'loan_duration_days', '30'); +$settings->set('loans', 'max_loan_duration_days', '90'); $defaultDays = $settings->loanDurationDays(); $maxDays = max(1, (int) ($settings->get('loans', 'max_loan_duration_days', '90') ?? 90)); $defaultDays = min($defaultDays, $maxDays); diff --git a/tests/loan-window-surfaces.unit.php b/tests/loan-window-surfaces.unit.php index 3753fc00b..9d470fe20 100644 --- a/tests/loan-window-surfaces.unit.php +++ b/tests/loan-window-surfaces.unit.php @@ -170,6 +170,13 @@ }; $settings = new SettingsRepository($db); +// The scenario needs a default longer than the conflict offset: pin the two +// durations for the run (the installation's values are restored by $cleanup). +foreach (['loan_duration_days', 'max_loan_duration_days'] as $key) { + $savedLoanSettings[$key] = $settings->get('loans', $key); +} +$settings->set('loans', 'loan_duration_days', '30'); +$settings->set('loans', 'max_loan_duration_days', '90'); $maxDays = max(1, (int) ($settings->get('loans', 'max_loan_duration_days', '90') ?? 90)); $defaultDays = min($settings->loanDurationDays(), $maxDays); $offset = max(2, min(5, $defaultDays - 1)); @@ -405,9 +412,7 @@ // A lower configured maximum must govern every default, including Mobile API // requests without end_date. A commitment at +10 must not reject a valid 7-day // request just because the configured standard duration is 30 days. -foreach (['loan_duration_days', 'max_loan_duration_days'] as $key) { - $savedLoanSettings[$key] = $settings->get('loans', $key); -} +// The installation's values were saved at the top; $cleanup restores them. $settings->set('loans', 'loan_duration_days', '30'); $settings->set('loans', 'max_loan_duration_days', '7'); [$boundedBook, $boundedCopies] = $mkBook(1, 3);