Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions locale/fr_FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -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é.",
Expand Down
9 changes: 9 additions & 0 deletions tests/loan-request-capped-by-scheduled-loan.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`;
Expand All @@ -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';
Expand Down Expand Up @@ -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}`);
Expand Down
19 changes: 18 additions & 1 deletion tests/loan-window-cap.unit.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,17 @@
$bookIds = [];
/** @var int[] $userIds */
$userIds = [];
$cleanup = static function () use ($db, &$bookIds, &$userIds): void {
/** @var array<string, string|null> $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}");
Expand Down Expand Up @@ -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);
Expand Down
11 changes: 8 additions & 3 deletions tests/loan-window-surfaces.unit.php
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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);
Expand Down
Loading