Skip to content

Release 1.11.0 (versionCode 25) - #57

Merged
fabiodalez-dev merged 48 commits into
mainfrom
release/1.11.0
Nov 2, 2026
Merged

fabiodalez-dev merged 48 commits into
mainfrom
release/1.11.0

Conversation

@fabiodalez-dev

Copy link
Copy Markdown
Owner

What this release contains

  • Align with Pinakes 0.9.0: feature toggles, closures calendar, patron-type rules, fines ledger #55 and Follow-up alignment with Pinakes 0.9.0: consultation switch, closed-day approvals, offline quotes #56: alignment with Pinakes 0.9.0 (feature toggles, closures calendar, patron-type rules, consultation-only copies, refusal codes, balance and movements, closed-day approvals, quoted searches offline, staff reservation override, library card barcode, the copy editor's consultation switch).
  • Every Pinakes 0.9.0 admin tool in the staff area (Mobile API 1.10.0, Pinakes 0.9.0-rc.2): stocktake with the phone as scanner, patron tools and the ledger at the desk, the visitor log and patron CSV import, library settings with patron types, the rules matrix and the closures calendar, suppliers, MARC import and export, copy cataloguing, catalogue cards, and statistics. Each area appears only when the server's /health flag and the account allow it; a refused action (403 not_permitted) shows the server message and keeps the staff area.
  • Fixes found by driving the app against a real 0.9.0-rc.2 server: the hub follows a module switch at once, the patron type editor's inherited hint, the staff ledger wording, the import error report button, MARC jobs whose list answers late, and a stocktake status poll that could reopen a closed session on screen.

Release commit: versionCode 24 → 25, versionName 1.10.0 → 1.11.0, the 1.11.0 section in STATUS.md.

Checks

  • 682 debug unit tests, none failed; lint and assembleDebug.
  • The full device suite (134 tests) on an Android emulator, twice in a row with no failure.
  • tools/e2e/live_smoke_090.py: every staff area driven with real taps on an emulator against a server running Pinakes 0.9.0-rc.2, as administrator and as staff, 12/12 steps, with the server checked after each step and the test data removed afterwards.
  • No change to the Room cache (version 3).

Merging this PR supersedes #55 and #56.

…type rules, fines ledger

Read the three 0.9.0 flags from /health (fines, closures_calendar, circulation_rules), default off and forced off in catalogue mode; the /health mapping is now a pure InstanceFeatures.from().

Closures calendar: closed_dates paints closed days grey with their own legend entry, they cannot be a start, a tap says the library is closed, the pre-selected day skips them, and the proposed return always lands on an open day (forward for the default return, back for a shortened end_date).

Circulation rules: with circulation_rules on, the book page reads loanable and reservations_enabled and keeps the loan button visible but disabled with the reason. The 409 codes not_loanable, reservations_disabled, reservation_limit_reached, media_limit_reached, max_loans_reached and not_eligible with meta.reason are mapped for readers and staff; unknown codes keep the server message.

Balance and movements page behind features.fines (GET /me/movements); a 404 feature_disabled closes it. Additive patron_type on the profile and consultation_only on staff copies.

48 new unit tests (pure functions plus a Robolectric check of the disabled button).
…ements page

- A default return moved forward over closed days now stops on the last open day before a fully-booked day it meets, capped and sent as end_date, mirroring the server's renewal cap. Without max_loan_duration_days (servers before closures existed) the default return is left as it is.
- Movements: LazyColumn keys fall back to the row position when the server omits an id; the accrued total shows even without per-loan rows; a failed refresh keeps the balance and says it may be out of date.
- Document why the calendar follows closed_dates rather than the closures_calendar flag.
…aff loan form, balance fallback

- fines, closures_calendar and circulation_rules also need app_access_enabled, like every other area flag.
- Staff new loan/reservation: both date pickers refuse the library's closed days, and submit names a closed start or return instead of sending it.
- Movements: a missing balance_cents falls back to the decimal balance.
Picking another book or user while the availability was still loading let the older answer end the loading and, on failure, clear the calendar, so the form could be sent without the closures check. Each request now carries a number and an answer to a superseded request is dropped.
…ay approvals, offline quotes

Consultation-only copies have their own /health switch since Pinakes 0.9.0
(features.consultation_only), independent of circulation_rules. The book
page fetched the reader's rules only with circulation_rules on, so with the
patron-type rules off a consultation-only title still offered the loan
button and the request was refused after sending. It now asks with either
switch; a server without the key keeps tying it to circulation_rules.

POST /reservations answers status prenotato with start_date when a request
is auto-approved while the library is closed today: the loan starts on the
next open day. The app read every auto-approved request as ready for pickup;
the confirmation now names the day the book can be picked up.

Quoted queries ask the server for the exact spelling (Pinakes #501). The
offline search key folds accents and cannot tell spellings apart, and it was
searching for the quote characters themselves, so a quoted query found
nothing offline. The quotes are dropped as the server reads them (straight
and typographic; an inch mark after a digit stays).

The health sample gains automatic_fines, debt_block and consultation_only.
automatic_fines and debt_block need no client change: the server already
leaves accruing fines out and sends a null threshold with debt blocking off.
…site

The website's "new reservation" form has "Ignora i limiti di prenotazione
della tipologia del lettore" when patron-type rules are on, and the Mobile
API reads override_limits on POST /staff/reservations (Pinakes 0.9.0). The
app had no way to send it, so a reservation refused with
reservation_limit_reached could only be made on the website.

The staff new-reservation form shows the same switch when
features.circulation_rules is on. override_limits is sent only when it is
ticked and the rules are on; otherwise the key is left out, so an older
server never sees it.
The author and publisher filters are quoted for an exact spelling as the
text query is (Pinakes #501). Offline they were escaped as typed, so
'"Italo Calvino"' looked for the quote characters and found nothing.
SearchText.filterPattern() drops the quotes as the server reads them, for
both filters, and the DAO test goes through it.
Pinakes 0.9.0 prints library cards with the card code (codice_tessera) as a
Code128 barcode by default, and the desk and the visitor-log kiosk scan it.
The profile only showed the number as text. It now draws the same Code128
under the card details, black on white whatever the theme, so the reader
can show the phone at the desk. As on the printed card only printable ASCII
is encoded; otherwise the text line alone stays. The barcode is generated
with the ZXing core the scanner already uses, and a unit test reads every
drawn code back with ZXing's Code128 reader.
Pinakes 0.9.0 lets staff keep a single copy for in-house consultation from
the book page. The app showed the flag on a copy but could not change it.
With features.consultation_only on, the copy editor now has the switch;
consultation_only is sent on PUT /staff/copies/{id} only for an existing
copy and only when it changed, so saving a copy untouched sends no flag.
The server applies it through the book page's own toggle and refuses it
for a copy on loan, awaiting pickup or requested (Pinakes #516).
The app reads the Mobile API 1.10.0 flags: the state of every 0.9.0 module and a staff_<area> flag for each 0.9.0 staff area. They live in Staff090Flags (one encrypted JSON value) and become Staff090Areas, which hides every tool offline and for readers, and the circulation ones in catalogue mode.

The staff hub gains a front desk, a cataloguing and a library section. Each area (stocktake, patrons, library, catalogue) registers its own screens in its package under ui/screens/staff/s090 and gets its Retrofit service from NetworkModule.typedApi(), so the areas can be built without touching shared files. The screens are placeholders for now.
The Library section of the staff hub opens three real screens on the Mobile API 1.10.0 areas library_settings, circulation_rules and closures_calendar.

Library settings has the web's Modules, Loans and Cards tabs. Module switches carry their help and what they depend on; without can_configure the state is shown as plain values and there is no save action. Every loan setting has its own input, amounts in the server currency parsed to cents from the decimal text; fields outside editable_fields are shown as values, so Staff never sends the money policy. A save sends only the changed keys.

Patron types and loan rules lists the types with a sheet for every field (an empty field inherits, the inherited value is the hint), default, order and delete; the rules matrix edits patron type (all types included) by material and saves the changed cells together. The closures calendar shows the month, by branch, with the reasons of each closed day, and edits weekly closed days, closures, yearly switches, the national holiday preset, the library's own holidays of every kind and imports an ICS file.

An administrator-only refusal (403, or 404 feature_disabled for Staff with the module off) closes the screen with the server's message. That 403 uses the same forbidden code as the staff role gate, so the repository asks a route every staff member reads before dropping the role.

Unit tests decode bodies captured from a 0.9.0 server and cover amounts, partial bodies, validation, the month grid and the error mapping; Robolectric tests check what each role sees; LibrarySettingsFlowTest drives the main path of each screen.
…tools

The server admits only administrators to patron types, loan rules and the closures calendar (the web's mayConfigure()), even though it advertises the areas to every staff account. A Staff account saw the two entries and got nothing but a refusal; they are now shown to administrators only.
…inakes 0.9.0

The staff patron screen gains a patron section, shown only when the server offers one of its tools: the photo (taken or picked, re-encoded, fetched with the token and kept in memory only), the patron type with its picker and the "set the expiry from the type" switch, card renewal with an Idempotency-Key, the card PDF, and the balance with the debt block, which opens the new ledger screen.

The ledger screen reuses the reader's balance card and rows: fines accruing on open loans, paged movements with who recorded them and the payment receipt PDF, and the payment, charge and waiver sheets. Amounts are parsed from the typed decimal straight to cents, and every write carries an Idempotency-Key kept across retries of the same submission.

The staff return shows the late fine a return today charges and lets the desk waive it with a mandatory reason (waive_fine, waive_reason).

The front desk gains the visitor log: the camera reads the card in CODE 128 or QR (a new format set in BarcodeAnalyzer), manual entry, branch choice, a result card with a short tone, guests, and a statistics tab with the periods, day and hour bars, latest visits, the CSV export and, for administrators, retention and purge. The patron CSV import uploads a file with the chosen separator, shows the preview, maps every column to a field with the required ones marked, starts with or without invitations and drives the chunks with the catalogue import's rules, then shows the counters and the error report.

A 403 not_permitted (one action refused to a staff account that keeps its role) shows the server message and never drops the staff area. Models, unit tests and instrumented fixtures come from responses captured on a real 0.9.0-rc.1 server.
…ays forbidden

The server now answers 403 not_permitted when a 0.9.0 area refuses one action to a staff account, and keeps forbidden for the staff role gate. The repository drops the check against the settings modules route it made after every 403: all calls go through staffCall again, which drops the role only on forbidden.

not_permitted shows the server's message. On a screen's first load it closes that screen (the account may not use it at all, as with 404 feature_disabled for Staff); a refused action inside a screen shows the message and the screen stays, with the settings list scrolled to the banner. The 403 samples are captured again from the server, plus the role gate's forbidden for comparison.
…he 0.9.0 staff tools branch

# Conflicts:
#	i18n/de.json
#	i18n/en.json
#	i18n/fr.json
#	i18n/it.json
…r entries

The merged scaffolding shows the patron types and closures calendar entries to administrators only; the model test now checks that a Staff account sees the library settings and an administrator all three.
… cataloguing)

The Cataloguing section of the staff hub opens three screens on the Mobile API 1.10.0 routes. Suppliers lists the library's suppliers with their book counts and search, creates one with an Idempotency-Key kept across retries of the same form, shows the server's field errors under each field, edits as a patch (a cleared field goes as null) and deletes after saying how many books lose the link. Every control follows can_create, can_edit and can_delete.

MARC imports a file picked with the system document picker, with the flavour, encoding, duplicate mode, supplier and cover choices of /staff/marc/options, shows the preview the upload answers, then drives the chunks with the CSV import's ImportProgress (out-of-order recovery, busy retry, gone, pause, resume, cancel), resumable after process death through the saved job id. Its export tab downloads the whole catalogue or chosen books (at most 1000) in MARC 21, MARCXML or UNIMARC, and prints the chosen books' catalogue cards (at most 200).

Copy cataloguing searches the chosen catalogues by ISBN, ISSN, title, author or any field (an EAN-13 can be scanned; a 977 code becomes its ISSN), lets the results be selected and imports them with an Idempotency-Key, then links each new or updated record to its staff screen. Without the z39-server plugin the screen explains why instead of showing the form.

The book form gains the supplier picker and the RDA content, media and carrier selects only while the server's lookups carry their lists, so the keys are never sent with the modules off; "derived" names the value the server derives. The record's screen prints its catalogue card and exports it in MARC: the staff book list has no selection mode, so the multi-book actions live in the MARC screen.

The routes' raw meta is read for meta.fields and meta.can_create without touching the shared Meta. A 403 not_permitted shows the server message and keeps the staff role. Tests: model parsing against bodies captured from the slot server, forms and request bodies, limits, error wording, three Robolectric UI tests, and eight instrumented flows.
…the 0.9.0 staff tools branch

# Conflicts:
#	STATUS.md
#	i18n/de.json
#	i18n/en.json
#	i18n/fr.json
#	i18n/it.json
The patron import screen reads GET /staff/user-imports and lists the operator's unfinished jobs under the upload form: the file, whether it started and how many rows are done. Resume reopens the job as the server has it now, on the column mapping when it has not started, else in the chunk loop; Cancel stops it without opening it. A job started on another device, or whose id the app lost, can now be finished. The list also carries completed and cancelled jobs until they expire; those are not offered.

Parsing, UI and instrumented coverage use the list the server answered.
# Conflicts:
#	i18n/de.json
#	i18n/en.json
#	i18n/fr.json
#	i18n/it.json
The import tab now reads GET /staff/marc/imports (the caller's unexpired MARC jobs, newest first) and lists the unfinished ones above the form, with their progress, a Resume that continues the chunk loop from the server's position and a Cancel. An import started on another device, or whose id the app lost, can be finished from here; the saved job id still restores the one in progress after process death. The list reloads when the screen resumes and after a new import, and pages with the server's cursor.

Tests: the list body captured from the slot server is parsed (paging meta, every status) and filtered to the open jobs; two instrumented flows resume a listed job and cancel one.
The hub's Stocktake and Statistics entries open real screens on the Mobile API 1.10.0 areas stocktake and statistics.

Stocktake lists the sessions by status with their counters and opens a new one from the server's options (name, note, branch, shelf and level, genre, media type, shelfmark range) under an Idempotency-Key. The session screen is a continuous scanner: a label held under the camera is sent once, reading it again after moving it away (or after another label) is sent and recorded by the server as a repeat, as the web scan page does. Typed or keyboard-wedge codes go the same way. Every scan carries the shelf being checked and its own Idempotency-Key, reused only by the automatic retry after a dropped connection. The answer shows at once in the outcome's web colour, with the web's tone played through an AudioTrack only when the ringer is in normal mode, and a haptic tick. Counters, the latest scans (by outcome, only mine) and the status are polled every four seconds while the screen is visible. A .txt/.csv code list can be uploaded. Closing says how many copies are missing and offers to mark them lost only when the server says the user may (also to finish a mark-lost close cut short); a refused mark-lost (403 not_permitted) shows the server's message and keeps the staff role. Cancel asks first. The report lists rows by category and downloads the CSV or the PDF. Offline the screens leave and nothing is queued.

Statistics shows the overview, loans by month, state and material as plain bars, the most borrowed books (a tap opens the staff book) and the most active readers, then only the sections the server sends: patron types, fines, stocktake, visitors and the fines report with its period, amounts in the server's currency, and its CSV.

Unit and Robolectric tests decode bodies captured from a 0.9.0 server and cover the scan gate, the tone and colour mapping, the create form, the close choices, upload limits, error mapping, bars and money. StocktakeFlowTest drives the main paths against FakePinakes.
… branch

# Conflicts:
#	STATUS.md
#	i18n/de.json
#	i18n/en.json
#	i18n/fr.json
#	i18n/it.json
Patron types, loan rules and the closures calendar are now shown to administrators only, so the flow that opened them as Staff could not find the entry. One test now checks that a Staff account sees Library settings and neither of the two administrator entries. The other opens the patron types as an account the app still takes for an administrator while the server answers 403 not_permitted (demoted on the server): the screen closes with the server's message, the hub is back and the stored role is kept.
… keep the staff role

Run on an emulator, six flows timed out. Five called scrollListTo before opening the visitor log or the patron import, but the staff hub is a scrolling Column, not a lazy list, so no node offers scroll-to-node and the wait never ends; click() already scrolls the row into view. The ledger flow waited for "Lost library card" as a whole text, but the reader movement row writes the description inside its detail line, so it now matches a substring. The refused-purge flow asserted the stored role after the app had refreshed it from the fake /me, which answered an administrator; the fake now answers the Staff account's own /me. No app change was needed.
Since the 0.9.0 alignment a not_eligible refusal shows the precise reason from meta.reason; the flow still waited for the generic wording and timed out. It now waits for the suspended-patron message its fixture asks for.
tools/e2e/live_smoke_090.py (wrapper live-smoke-090.sh) drives the real app against a real Pinakes server running Mobile API 1.10.0, with taps and typed text only for the actions under test: the hub's 0.9.0 sections and a module switched off and on from Library settings, a loan setting, patron types (create, edit, delete), a rules-matrix cell, a closure day checked in the month grid and in availability closed_dates, a reader's patron section (card renewal, patron type with the expiry recomputed, ledger charge and partial payment, payment receipt PDF), a late return with the fine waived, a stocktake session with a known and an unknown code through to the report, the visitor log (member, unknown card, guest), the patron CSV import and a MARC import through the system document picker, the MARC export, suppliers set on a book in the book form, statistics, and a staff account (admin-only entries hidden, an admin-only save refused in place with the server message and the staff area kept).

The API and the database are used only for fixtures, server-side checks and cleanup. Module switches and the daily fine are restored, every record the run created is removed, and the run reports what is left on the server (all zero on success). One screenshot per step goes to PINAKES_E2E_SHOTS. ui.py nodes now carry checkable, enabled and scrollable.
Switching a module on or off in Library settings now re-reads /health right after the save, so the staff hub drops or shows the entry at once instead of on the next app start.

The patron type editor's "Empty: ..." hint no longer repeats the type's own value: while the type sets a number, the hint shows the global value it would fall back to (an empty renewal period still follows the loan duration the type gets).

The patron ledger at the desk words the balance and the debt block about the patron instead of addressing the reader, and the finished patron import only offers the error report when it has rows.
…alogue flows

The two open-job flows failed in the full instrumented suite. The real cause is an app bug: the import tab is a lazy list, and a lazy list keeps its first visible item in place when items are inserted before it. When GET /staff/marc/imports answered after the form had been drawn (a slower emulator, or a slow connection), the open jobs were inserted above the form and ended up above the top of the screen, invisible unless the user scrolled up. Run alone the answer happened to arrive first. The tab now starts with a fixed hint row, so the late jobs are inserted below it and stay visible; the flows hold the list answer back for 1.5 s (a new delayMs option of FakePinakes.json) and fail without the fix. The supplier list gets the same care: a supplier created before the first row keeps the list at the top.

The other catalogue failures were test timing, not state left by earlier tests: the supplier sheet was typed into while it slid in, and a dropdown was tapped while the keyboard was closing. The flows now wait for the sheet, check the typed text landed, and repeat a tap until the server or the menu answers.
…t errors button

Step 1 now requires the Suppliers entry to leave the hub as soon as the module is switched off in Library settings, and to come back when it is switched on again, with no app restart. Step 3 requires the Card validity hint of a type that sets 400 to show the global fallback ("Empty: —" here), read inside that field only. Step 7 requires a finished patron import with 0 errors to show no errors-download button.
… sends codes with Done

StocktakeFlowTest.closingStatesTheMissingCountAndLeavesThemAlone failed now and then because of an app race. The session screen polls the state every 4 s; a poll that left before the close and answered after it still said "open", withStatus() put the session back to open (Close and Cancel shown again, scanning allowed) and the status change triggered a reload. A real server reloads the closed session a moment later, but the screen flickered back to an open session in between, and a close already done looked undone. Now a poll answer is dropped when the session changed while it was in flight, and closed or cancelled are final in withStatus(), so a stale "open" never reopens it. The flow now closes while an 8 s poll is in flight and checks the session is still closed after it answers: it fails on the old code every time.

Staff090PatronsFlowTest.theDeskRecordsACardAndSaysWhenItIsUnknown was test timing. The desk column sits under imePadding: on a loaded emulator the keyboard's show animation took about ten seconds at 400 ms a frame, the column kept moving, and the tap on the field's search icon, computed from the previous layout, landed beside it, so the second code was never sent. A person taps where the icon is, and the Done key and a wedge reader do not depend on the layout. The flow now sends each code with the keyboard's Done action and checks the server saw it.
… is sent

createAClubThenChangeAMemberRole failed once in a full instrumented run: no POST /bookclub/staff/clubs was sent after the tap on Save. The name was typed while the form sheet was still sliding in and the tap, computed from the moving layout, could land beside the button. The flow now waits for the sheet and repeats the tap until the server sees the create (at most three taps, never a second create).
createAWorkAddAnExpressionAndLinkABook failed in a later full run the same way the club create had: the tap on the sheet's Save sent nothing. The forms in Tier 2 sheets move while the sheet slides in and the keyboard animates above them, which takes seconds on a loaded emulator. AppFlowTest.tapUntilSent() waits for the button and taps it until one more request reaches the fake server (at most three taps, never a second request); every form-save of Tier2FlowTest uses it.
versionCode 24 to 25, versionName 1.10.0 to 1.11.0, and the 1.11.0 section in STATUS.md: the alignment with Pinakes 0.9.0 and every 0.9.0 admin tool in the staff area (Mobile API 1.10.0, Pinakes 0.9.0-rc.2), with the fixes found by the live run.
@fabiodalez-dev

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 22830532-5116-4a63-a76d-9c7d3b62dbe4
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Action not completed

Review skipped: 379 files exceed the limit of 100.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No blocking issues. A few state bugs throw away unsaved edits or replay feedback, details inline.

Reviewed changes

This review covers all 44 commits on release/1.11.0. This is not a version-bump-only release: it brings in the whole Pinakes 0.9.0 staff toolset and aligns the patron side with 0.9.0.

  • Flags and gating: Staff090Flags is persisted per instance, and Staff090Areas gates every hub row and screen. All new /health fields default to off, so older servers see none of this. No new OkHttp client was added, so CleartextGuardInterceptor coverage holds.
  • Stocktake: one queue sends scans in order with idempotency keys, a 4s poll runs only while the session is open, and there is a session report.
  • Patron desk: chunked CSV import that can be resumed, visitor check-in, and a ledger with payments and waivers.
  • Library settings: modules, loans and cards tabs, plus patron types, the rules matrix and the closures calendar.
  • Cataloguing: suppliers, MARC import and export jobs, and copy cataloguing.
  • Patron side: the loan window and calendar handle closed days, consultation-only copies, the movements screen, the card barcode and the staff reservation override.
  • Release bookkeeping: versionCode 25 / versionName 1.11.0 and the STATUS.md section. The i18n keys match across en, it, de and fr, and the copy has no em dashes.

A 403 not_permitted really does keep the staff role (isForbidden() only matches forbidden). The chunk resume offsets and the scan queue ordering also look correct.

ℹ️ Some modules do not close their screen when switched off

A 404 feature_disabled closes the stocktake, attendance, ledger and library screens. The suppliers, MARC, copy cataloguing, patron import and statistics screens do not handle it: they stay open showing the server error until /health refreshes the flag. This is not a leak, since the user stays in the staff area.

Technical details
# `feature_disabled` not handled on catalogue/import/statistics screens

## Affected sites
- `ui/screens/staff/s090/catalogue/Staff090CatalogueSuppliersScreen.kt`, `Staff090CatalogueMarcScreen.kt`, `Staff090CatalogueCopyCatalogingScreen.kt`
- `ui/screens/staff/s090/patrons/Staff090PatronsImportScreen.kt`
- `ui/screens/staff/s090/stocktake/Staff090StatisticsScreen.kt`

## Required outcome
- A `feature_disabled` failure leaves the screen (and ideally triggers `auth.refreshHealth()`), matching `LibraryUi.closesScreen` / `Staff090StocktakeErrors.moduleOff`.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using claude-opus-5-5 | 𝕏

The session screen polls every four seconds and reads the first page of the chosen outcome again. Once the operator had loaded the last page the cursor was null, the guard no longer held, and the poll put page one back: every row past the 50th disappeared a few seconds after "Show more", which was offered again. The list now remembers that it was paged; a poll then adds the newer scans on top and keeps the pages already read with their cursor. Changing the outcome or "only mine" starts over from page one. Found by the live run against a 0.9.0-rc.2 server with 55 scans of one outcome.
Step 5b opens a stocktake on a shelf with a 60-copy book, uploads a list of 55 codes through the system document picker, and checks the paging that Pinakes 0.9.0-rc.2 moved to keyset cursors: every scan and report row once through the API pages, the found-scan list keeping its second page after "Show more" and after the screen's polls, and the report loading its second page on scroll with every row once, in the server's order. The session is cancelled from the app and removed in the cleanup with its shelf and book.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No blocking issues. The paging fix works; one gap case is noted inline.

Reviewed changes

This review covers the two commits pushed since the review at bb19024.

  • Stocktake outcome list keeps its pages: there is a new filteredPaged flag and a pure Staff090StocktakeMerge.refreshedFiltered. Once the operator has used "Show more", the 4s poll puts newer scans on top and keeps the pages already loaded and their cursor. Before, the poll swapped the list back to page one. setMine and setOutcomeFilter now clear the list, the cursor and the flag. A unit test covers the merge.
  • Live smoke step 5b: it uploads a code list of 55 scans against 60 copies, then checks the server's own paging, the app's "Show more" across two polls, and that the report scroll loads every row once. Cleanup and the leftover counts now also cover the E2P shelf and the paging session.

None of the six earlier inline threads is touched by these commits, so they all stay open.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using claude-opus-5-5 | 𝕏

…ion reloads, paging gaps

Closures calendar: every load() (another month or branch, a save elsewhere) reset the weekday ticks and the yearly switches to the server's, so ticks not yet saved disappeared with their Save button. A reload now keeps what the operator changed since the last answer and takes the server's value for the rest; a new holiday preview of the same country keeps the ticked and unticked rules that are still listed.

Library settings: a modules save re-reads the loans and cards tabs and reset them, losing an unsaved fine or card edit. The re-read now keeps every field the operator edited and did not save.

Stocktake and visitor log: the scan feedback and the desk tone were state replayed by LaunchedEffect whenever the screen entered composition again (a rotation, coming back from a book), so the last tone and haptic played with nothing scanned. Each is marked played once it sounds.

Stocktake: reload() wrote the session with no staleness check, so a reload answered after this device closed or cancelled the session brought the open session back with scanning, Close and polling. It follows the poll's rule: an answer is dropped when the status changed while it was in flight, and an open answer never replaces a closed or cancelled session.

Stocktake: when more than a page of new scans arrived between two refreshes of a paged outcome list, page one did not overlap the list and the scans in between never showed. Without overlap the list now starts again from page one and its cursor.

Hub: catalogue cards alone opened a Cataloguing header with no row under it (the cards are on the book screen). The section now needs MARC or suppliers.

Loan request: a return cut to max_loan_duration_days that fell on a closed day walked forward to the next open day and was sent as an end_date past the maximum. A return cut to the maximum moves back to the last open day; the full duration still moves forward as the server's due date does.

Tests: reload and paging rules, the reseed rules of the calendar and the loans form, the one-shot tone, the hub flag, the loan window with a maximum below the duration, and a Robolectric test of the hub section.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found. All seven earlier findings are fixed and the fixes hold up.

Reviewed changes

This review covers the single commit pushed since the review at f9e5d8b. It is a set of fixes for the earlier review findings, and each one comes with a unit test.

  • Max-capped loan returns: a return cut to max_loan_duration_days that lands on a closed day now moves back to the last open day. Only the full loan duration still moves forward, the way the server's due date does.
  • Closures calendar drafts: CalendarGrid.reseedWeekdays and reseedYearly keep the ticks the operator changed since the last server answer, across a month change, a branch change or a save. The weekdays are global (the branch picker is only a preview), so keeping them across branches is correct. The holiday preview keeps the operator's ticks within the same country, using a new touched flag.
  • Library settings drafts: after a modules save, refreshSecondary re-reads Loans field by field with LoansForm.reseed and keeps any unsaved Cards edit.
  • One-shot desk feedback: the visitor log records the last toneTick it played, and stocktake clears feedback once the tone sounds. feedback only drives the tone and haptic, so clearing it hides nothing on screen.
  • Stale stocktake reload: Staff090StocktakeMerge.acceptReload drops an answer if the session status changed while the request was out. It also never lets an open answer reopen a closed or cancelled session.
  • Paged outcome list gap: if page one shares no scan with the current list, the list starts again from page one and its cursor. This also covers an empty page one.
  • Hub Cataloguing header: anyCatalogue now needs suppliers or marc, so catalogue cards on their own no longer show an empty header.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

@fabiodalez-dev
fabiodalez-dev merged commit cc521b6 into main Nov 2, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant