Pinakes v0.7.62
Overdue-loan recalls (solleciti) and emailing the loan receipt (#360).
Features
- Loan recalls (solleciti): overdue loans can now be chased beyond the
single overdue notification. Automatic recalls repeat at a configurable
interval up to a configurable cap (Settings → Loans → "Solleciti automatici",
off by default; sent by the notifications cron or on admin login). Staff can
also send a manual recall for one loan — from the loan detail page or a
per-row action on the loans list — or for many at once from the loans list
via the bulk action bar — manual recalls ignore
the automatic schedule but share the same per-loan counter
(prestiti.recall_count/last_recall_at, added by
migrate_0.7.62-rc.1.sqland self-healed at runtime). New editable email
templateloan_recall_notificationin all five locales. - Email the loan receipt PDF: next to "Scarica Ricevuta PDF", the loan
detail page now has "Invia Ricevuta via Email", which sends the same PDF as
an attachment to the loan's user (new editable template
loan_receipt_email; the mailer gained in-memory attachment support). - Emails in the recipient's language: user-facing notification emails
(loan warnings/overdue/recalls, receipt, approvals, pickups, returns,
reservations, wishlist, registration and account emails — and per-admin for
admin alerts) now render in the recipient's preferred language
(utenti.locale, the same value that drives their UI language), including
date formats and translated labels, falling back to the installation locale
when the user has none. Password-reset mail already followed the
requester's session language. - Language choice at registration: on multi-language installs the
registration form now offers a "Lingua preferita" select (defaulting to the
language the visitor is browsing in), validated server-side against the
shipped locales. The profile page and the admin user forms already offered
the same choice; together they cover registration, self-service and admin. - Plugin ZIP updates (#358): uploading a plugin ZIP whose name matches an
already-installed plugin now updates it in place — its id, settings, data and
hooks are preserved and its files are swapped atomically — instead of failing
on the existing directory. Updating an already-active plugin now runs its new
lifecycle (onActivate()/ensureSchema()) on the next request via a
pending-update marker, rolling back package, metadata and hooks if the new
version fails to activate — so schema changes shipped in an update are applied
instead of being silently skipped. Covered by contract and per-bundled-plugin
integration tests.
Fixed
- Book announced "ready for pickup" while still on an overdue loan (#366):
a reservation scheduled right after a loan that then went overdue and was
never returned was promoted toda_ritirareon its date alone, emailing the
next patron a wrong "ready for pickup" notice while the book was still out.
Promotion is now gated on a copy being physically free (active loans below the
copy count; a pinned copy must be on the shelf). The full reported sequence is
covered too: rescheduling an open reservation/pickup no longer leaves a stale
pickup_deadlinefor the expiry sweep to cull a valid loan against, the
overdue flip now runs first in the maintenance pass so an unreturned overdue
loan keeps holding its copy, andrenew()refuses a date-overdue loan. - Concurrent circulation actions no longer corrupt state: eight
transactions (approve/return/reject/cancel loan and reservation) resolved the
loan id with a plain read before taking the book lock, so under REPEATABLE
READ their later reads were blind to a competitor that had just committed.
A just-cancelled reservation could be promoted and emailed, and one physical
copy could be committed to two loans. The lookups now run before the
transaction so the first locked read fixes the snapshot, and reservation
promotion claims the row with a state-guarded update. - Pickup confirmation now refuses a copy that is still out on another loan
(prestato), preventing a double issue of the same physical copy. - Admin reservation cancellation now promotes the next reservation in the
queue immediately, like every other path that frees a copy. - Overdue notices and automatic recalls now fire for loans whose book has
been archived (soft-deleted) — the chase-up mail no longer filters those out. - The "reservation available" email can no longer be sent twice when the
retry sweep races the request that promoted it. - Admin direct loans cap the pickup deadline at the due date; bulk loan
extension and reschedules re-check borrower eligibility; expiry audit notes
use the same day the decision was made near midnight.
Internal
- CI: the OWASP ZAP baseline no longer fails on the ISBN/EAN-13 PII-disclosure
false positive, allowlisted narrowly to 13-digit codes on bibliographic pages
(#359). - Release verifiers now require
storage/sessions/.gitkeepand reject every
other entry there (files, symlinks, stray directories), and an unreadable
plugin-update marker is retired instead of permanently blocking future
updates of that plugin.
The ZIP was built twice with an identical SHA-256, audited as the
exact distributable, accompanied by an SPDX SBOM, and signed with
a GitHub artifact provenance attestation.