Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
10cfa4a
Closures calendar: LibraryCalendar service, calendario_chiusure table…
fabiodalez-dev Oct 27, 2026
2c60b05
Closures calendar: admin page /admin/calendar
fabiodalez-dev Oct 27, 2026
e36e7b8
Apply the closures calendar to due dates, renewals, pickups and night…
fabiodalez-dev Oct 27, 2026
46847a9
Closures calendar: integration test for the loan funnels and nightly …
fabiodalez-dev Oct 27, 2026
25e7ac5
Closures calendar: E2E for the admin page (CRUD, weekly days, preset,…
fabiodalez-dev Oct 27, 2026
75c3cdc
Closures calendar: E2E for due dates, renewals, pickup deadlines and …
fabiodalez-dev Oct 28, 2026
59dd2f0
CHANGELOG: 0.9.0 closures calendar
fabiodalez-dev Oct 28, 2026
884c1a1
Tests: migration-0.9.0 runs every section test; NCIP renewal contract…
fabiodalez-dev Oct 28, 2026
95ce6a9
Tests: CirculationPolicy::dueDateFor() applies the closures calendar
fabiodalez-dev Oct 28, 2026
9997841
Closures calendar: E2E for patron-type due dates and renewals on clos…
fabiodalez-dev Oct 28, 2026
a8e59b1
Changelog and README: closures calendar in the 0.9.0 lists
fabiodalez-dev Oct 28, 2026
2b07ce9
Closing a pickup deadline day moves the deadline in the database
fabiodalez-dev Oct 28, 2026
a65526d
Changelog: a closed pickup deadline day moves the stored deadline
fabiodalez-dev Oct 28, 2026
4227fe3
Closures migration test: seed the 0.8.9 sedi table when git history i…
fabiodalez-dev Oct 28, 2026
b2601fb
Due date and pickup days follow the closures of the chosen copy's branch
fabiodalez-dev Oct 28, 2026
1d708d7
A closed start day moves to the next open day on the server; no queue…
fabiodalez-dev Oct 28, 2026
27abf97
Calendar page: Windows-1252 ICS files import, plain escaping; tests f…
fabiodalez-dev Oct 28, 2026
bb61a14
Changelog: branch closures, closed start days and queue expiry
fabiodalez-dev Oct 28, 2026
d99b407
Approval moves the due date over a closure of the copy's branch; the …
fabiodalez-dev Oct 28, 2026
1fb9a15
Calendar review: approval checks the copy on the extra days, ICS unfo…
fabiodalez-dev Oct 28, 2026
ea96416
Closures calendar: /admin/calendar follows the settings permission
fabiodalez-dev Oct 28, 2026
ca0ee00
Calendar review: ICS events meet the form's limits, a long yearly row…
fabiodalez-dev Oct 28, 2026
df03ae2
LibraryCalendar::branchOfCopy() asks the database once per copy
fabiodalez-dev Oct 28, 2026
487ec78
Mobile API: a request auto-approved on a closed day is reported as pr…
fabiodalez-dev Oct 28, 2026
727b607
Changelog: ICS import limits, calendar permission, mobile request sta…
fabiodalez-dev Oct 28, 2026
b92e312
LibraryCalendar::spanDays(): DateInterval::days is an int here
fabiodalez-dev Oct 28, 2026
5bff840
Merge feat/circulation-policy into feat/closures-calendar
fabiodalez-dev Oct 28, 2026
9321ea1
Merge feat/circulation-policy into feat/closures-calendar
fabiodalez-dev Oct 28, 2026
7c38377
Merge feat/circulation-policy into feat/closures-calendar
fabiodalez-dev Oct 28, 2026
8ab67dc
NCIP CheckOutItem moves the due date with the chosen copy's branch
fabiodalez-dev Oct 28, 2026
3250a1c
Pickup deadline extension stops at the loan's due date
fabiodalez-dev Oct 28, 2026
5264575
Calendar scans step over a one-off closure instead of walking it
fabiodalez-dev Oct 28, 2026
fe0479f
Merge feat/circulation-policy into feat/closures-calendar
fabiodalez-dev Oct 28, 2026
40d50ea
Merge feat/circulation-policy into feat/closures-calendar
fabiodalez-dev Oct 28, 2026
32069d3
Merge origin/feat/closures-calendar into feat/closures-calendar
fabiodalez-dev Oct 28, 2026
50c34ed
Merge feat/circulation-policy into feat/closures-calendar
fabiodalez-dev Oct 28, 2026
8b6183f
Merge feat/circulation-policy into feat/closures-calendar
fabiodalez-dev Oct 28, 2026
64e43dc
Merge remote-tracking branch 'origin/feat/closures-calendar' into fea…
fabiodalez-dev Oct 28, 2026
e009ca7
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 28, 2026
bea392c
Merge remote-tracking branch 'origin/feat/circulation-policy' into HEAD
fabiodalez-dev Oct 28, 2026
7366164
Merge remote-tracking branch 'origin/feat/circulation-policy' into HEAD
fabiodalez-dev Oct 28, 2026
b0d18c8
Tests: load CirculationOutcome with ActionsController; the mobile req…
fabiodalez-dev Oct 28, 2026
97ecc3f
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 28, 2026
c200523
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 28, 2026
65a1a8f
Closures calendar: no 500 before the migration, filed under Settings
fabiodalez-dev Oct 29, 2026
08234e7
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 29, 2026
baa914e
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 30, 2026
961e745
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 30, 2026
51a37e7
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 30, 2026
1fb21f3
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 30, 2026
650936e
ICS import: exclusive midnight DTEND, TZID, plain yearly rules only; …
fabiodalez-dev Oct 30, 2026
9c07676
Calendar page: refused input stays unsaved, every change is one locke…
fabiodalez-dev Oct 30, 2026
bf0a2a2
Reader messages show the stored window: fallback start day, open pick…
fabiodalez-dev Oct 30, 2026
462a2a8
Calendar E2E: teardown removes only this suite's rows, pickup cap, se…
fabiodalez-dev Oct 30, 2026
f7f7baf
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 31, 2026
7df8e21
Calendar loan spec cleans up only its own closures
fabiodalez-dev Oct 31, 2026
af27677
Preset import test proves a missing holiday is inserted
fabiodalez-dev Oct 31, 2026
885035e
Mobile closed-today test expects the exact next open day
fabiodalez-dev Oct 31, 2026
255a36d
Calendar permission test POSTs rotate-link as a non-admin
fabiodalez-dev Oct 31, 2026
52481e7
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 31, 2026
4af2924
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 31, 2026
470296e
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 31, 2026
99fa4d9
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 31, 2026
630c086
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 31, 2026
ccabd2b
Whole-year closure guard counts weekly closed days and covers the preset
fabiodalez-dev Oct 31, 2026
aee246d
ICS import: a yearly rule recurs only on DTSTART's day and without EX…
fabiodalez-dev Oct 31, 2026
bfd1972
A closures read that fails throws instead of yielding an empty calendar
fabiodalez-dev Oct 31, 2026
d6521a0
Request calendar crosses a closure of any length to the next open day
fabiodalez-dev Oct 31, 2026
5f3c546
Loan request answer carries the server's pickup deadline and written …
fabiodalez-dev Oct 31, 2026
e258da4
Pickup emails count the pickup days with the copy's branch closures
fabiodalez-dev Oct 31, 2026
7f4abc8
Closures admin E2E keeps the original weekly closed days across a kil…
fabiodalez-dev Oct 31, 2026
d38374a
Closures E2E: robust to existing closures and a smaller max duration
fabiodalez-dev Oct 31, 2026
212a979
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 31, 2026
07a839f
Loan coherence audit matches the reservation auto-approval call by it…
fabiodalez-dev Oct 31, 2026
1ea59d3
Put the #301 auto-approval docblock back on autoApproveLoanRequest
fabiodalez-dev Oct 31, 2026
c8ed5c9
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 31, 2026
0ab9bed
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 31, 2026
90de006
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Oct 31, 2026
4e5a2ed
Calendar admin: an update re-checks its closure under the lock, ICS w…
fabiodalez-dev Nov 1, 2026
41399a6
Calendar: EXDATE matches DTSTART's time, junk dates are refused, fail…
fabiodalez-dev Nov 1, 2026
9d7962a
Loan request: the pickup estimate keeps the copy's branch when the st…
fabiodalez-dev Nov 1, 2026
0162a39
Book request: the longest return date stops at the last loaded availa…
fabiodalez-dev Nov 1, 2026
cd5c6e1
Calendar loan test: zero-config dates avoid foreign closures, preset …
fabiodalez-dev Nov 1, 2026
ea9590f
Closures E2E: restore moved pickup deadlines, expect foreign closures…
fabiodalez-dev Nov 1, 2026
0470b7d
ICS import: the whole-year guard carries its state from event to event
fabiodalez-dev Nov 1, 2026
ac36cca
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Nov 1, 2026
220118b
Merge branch 'feat/circulation-policy' into feat/closures-calendar
fabiodalez-dev Nov 1, 2026
228abca
Book request: a late start still reaches its full loan window
fabiodalez-dev Nov 1, 2026
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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,22 @@ Full version-by-version history for Pinakes. The README shows only the last thre
- **"Tessera in scadenza" email** (`card_expiring`, five languages). Sent `loans.card_expiry_warning_days` before the card expires (Settings → Loans, default 0 = off) by `cron/automatic-notifications.php` and `cron/full-maintenance.php` (both report the count), once per expiry date (`utenti.card_expiry_warning_sent_for`); a renewed card re-arms it. Fresh installations get the template seeded in the five `data_*.sql` files (row 29) like every other template; upgrades use the shipped text until the template is saved.
- **Administrators only.** Patron types and the rules matrix (Settings → Circulation, the type pages and every form behind them) are reserved to administrators and to custom roles with the settings permission; a staff account is sent back to the dashboard and nothing changes. For staff the Circulation section is absent from the settings navigation and `/admin/settings?tab=circulation` opens the default section without the circulation panel.

### Closures calendar
- **The library's closed days.** A new admin page, Admin → Calendario chiusure (`/admin/calendar`, also linked from Settings → Loans), holds the weekly closed days (`loans.closed_weekdays`) and the closures: single days or periods, once or every year, for every branch or for one. The Italian national holidays come with one click (fixed holidays as yearly closures, Easter and Easter Monday computed for the chosen year), any other calendar can be imported from an ICS file, and a monthly preview shows which days are open.
- **Due dates land on an open day.** The shift lives in `CirculationPolicy::dueDateFor()`, so the per-patron-type durations and renewal days of the circulation policy land on an open day too. A due date that falls on a closed day moves to the next open day: admin loans (proposed and saved), loan edits, reader requests from the website and the app, reservations and their promotion to a loan, renewals and bulk extensions, NCIP CheckOut, RequestItem and RenewItem. A closure of one branch counts for the loans of its copies: when the staff desk lends a copy, an NCIP CheckOutItem binds one, or a request is approved onto one, the due date moves to that branch's next open day (at approval only if the longer window still fits; the NCIP checkout is refused, like the desk's, when the longer window no longer fits). A refused renewal proposes the last open day before the conflict.
- **Pickup days are open days.** The pickup deadline counts only the days the library is open, and the nightly sweep expires no pickup, no scheduled loan and no queue reservation on a closed day; a deadline that falls on a day closed afterwards moves to the next open day when the calendar is saved, so the desk can still hand the book over that day, but never past the loan's due date (a pickup after the loan window is refused), which the calendar save does not move.
- **The request calendars know the closed days.** On the book page and in the admin loan form a closed day cannot be picked as the start (a request that reaches the server with one starts on the next open day); the availability payload carries `closed_dates`, on the website and in the Mobile API `GET /catalog/books/{id}/availability` (additive field). A request auto-approved on a closed day is reported by `POST /reservations` with its real state, `prenotato` (new enum value) with the start day in `start_date` (additive), instead of `da_ritirare`.
- **ICS imports meet the form's limits.** Every event is checked like a closure typed in the form (a yearly closure within a year, a one-off within ten years, end not before start); the import message counts the events discarded. A yearly closure of a year or more, however stored, is applied once as the interval it names and never closes every day; a one-off closure of any admissible length (up to ten years) is stepped over whole, so a date inside it lands on the day after it ends; an ICS date-time in UTC is read in the application timezone. The calendar page follows the settings permission (`settings.manage`), so a custom role with that permission can use it.
- With no closed weekday and no closure configured every date is computed exactly as before.

### Migration

- `migrate_0.9.0-rc.1.sql`, section "MARC bulk import / copy cataloguing": `import_logs.import_type` gains the value `marc`, appended to the enum computed from the current one, so the order of the sections does not matter. Idempotent; existing rows are untouched.
- `migrate_0.9.0-rc.1.sql`, section "Inventory stocktake": creates `inventario_sessioni` (with `azione_mancanti`, the close's choice), `inventario_attese` (with `esito_chiusura`, the bucket frozen at close, and `codice_atteso`/`titolo_atteso`, the copy code and title frozen at opening so a copy deleted mid-session stays identifiable in the report; each added with a guard to a table an earlier build created) and `inventario_scansioni` (UNIQUE `sessione_id, copia_id`) and seeds the "Revisore inventario" role. Additive and idempotent; nothing existing changes.
- `migrate_0.9.0-rc.1.sql`, section "minor features": creates `presenze` (visitor log) and `fornitori` (suppliers), adds `libri.fornitore_id` (ON DELETE SET NULL) and the nullable `libri.rda_content_type`, `rda_media_type`, `rda_carrier_type` overrides. Additive and idempotent.
- `migrate_0.9.0-rc.1.sql`, section "patron-cards-import": `import_logs.import_type` gains the value `users`; the new enum is computed from the current one, so `marc` and any value added by another section are kept.
- `migrate_0.9.0-rc.1.sql`, section "Circulation policy": creates `tipologie_lettore` and `regole_prestito`, seeds the default "Standard" type with every rule NULL, adds `utenti.tipologia_id` (FK `ON DELETE SET NULL`), `utenti.card_expiry_warning_sent_for` and `copie.solo_consultazione`. Idempotent; no existing row changes.
- `migrate_0.9.0-rc.1.sql`, section "closures calendar": creates `calendario_chiusure` (closure intervals, optional yearly recurrence and branch, FK to `sedi` ON DELETE CASCADE); the weekly closed days are the setting `loans.closed_weekdays`, no schema. Additive and idempotent.

## [0.8.9]

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Pinakes is a self-hosted, full-featured ILS for schools, municipalities, and pri
- **Visitor log, catalogue cards, suppliers and RDA types**: a kiosk visitor log with GDPR retention, ISBD catalogue cards as PDF, a suppliers register linked to books, RDA content/media/carrier overrides, and new statistics sections.
- **Library cards and user import**: printable patron cards (CR80 or A4 sheets, Code128 or QR), patron photo from the webcam, and user import from CSV with column mapping.
- **Patron types and circulation rules**: one circulation policy for every channel (desk, website, app, NCIP); loan limit, duration, renewals, request window, reservations and card duration per patron type, a patron type × material rules matrix, consultation-only copies, card renewal and a card-expiry email.
- **Closures calendar**: weekly closed days and closures; due dates and pickup deadlines move to the next open day.
- **Closures calendar** (`/admin/calendar`): weekly closed days, closures per branch or every year, the Italian holidays preset and ICS import; due dates (with the patron type's durations and renewals) move to the next open day, pickup deadlines count open days, and the request calendars refuse a closed start day.
- **Fines and patron ledger**: optional late fines with grace days and a cap, an append-only ledger with payments, waivers and receipts, and an optional debt block.

---
Expand Down
Loading