Commit Graph

62074 Commits

Author SHA1 Message Date
Paul Derscheid 15dfbc2f08
Bug 41898: (QA follow-up) Only list uncollected bookings on the pending list
The pending bookings page lists items that have not been collected yet,
but the status filter added when the server-side active pre-filter was
removed included 'issued' bookings, so bookings reappeared on the list
after the item had been checked out.

- restrict the status filter to 'new'

Test plan:
1. Place a booking starting today and open Circulation > Bookings to
   collect; the booking is listed
2. Check the item out to the booking patron; reload the list and
   confirm the booking is gone

Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:37 +00:00
Martin Renvoize 91556ada91
Bug 41898: Load additional-filters.js on the circulation page
The rewrite of js/tables/bookings.js in this patchset builds on
AdditionalFilters, which is provided by js/additional-filters.js. The
patron details page loads that asset but the circulation page did not,
so the bookings tab on circ/circulation.pl died with 'AdditionalFilters
is not defined' and the table never initialised.

Test plan:
1) Check out to a patron with bookings and open the Bookings tab on
   the checkout page; confirm the table renders and no JS error is
   logged in the console

Sponsored-by: Westminster City Council <https://www.westminster.gov.uk/>
Signed-off-by: Hannah Dunne-Howrie <hdunne-howrie@westminster.gov.uk>
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:36 +00:00
Martin Renvoize 40b9c8e29c
Bug 41898: Update tests for new booking status lifecycle
- Bookings.t: rewrite filter_by_active to verify status-based filtering;
  new/issued are active, cancelled/completed are not; past-window 'new'
  bookings (uncollected) remain active
- Booking.t: checkout relation uses 'issued'; old_checkout uses 'completed';
  clash detection covers issued transition; notice test updated
- Circulation.t: AddIssue sets 'issued'; AddReturn sets 'completed'; plan
  expanded to cover the full checkout → return lifecycle

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Sponsored-by: Westminster City Council <https://www.westminster.gov.uk/>
Signed-off-by: Hannah Dunne-Howrie <hdunne-howrie@westminster.gov.uk>
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:35 +00:00
Martin Renvoize bf8237ae54
Bug 41898: Fix callers of bookings API after removing server-side active filter
The bookings REST controller previously applied filter_by_active (status IN
'new','issued') as a server-side pre-filter. Removing that pre-filter means
callers that relied on it must now supply an explicit status filter.

- pendingbookings.tt: add status filter to additional_filters so the
  pending-bookings-to-collect page still shows only new+issued bookings
- place_booking.js: correct status values from the obsolete 'pending','active'
  (never valid in the schema) to the correct 'new','issued'

Test plan:
1. Navigate to Circulation > Bookings to collect.
   - Confirm only bookings with status "New" or "Issued" are listed.
   - Complete a booking (check out then return the item) and confirm it
     disappears from the list.
2. On any biblio with multiple bookings, open the Place booking modal.
   - Create overlapping bookings where one is completed or cancelled.
   - Confirm the modal does not treat completed/cancelled bookings as active
     conflicts when checking availability.

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Sponsored-by: Westminster City Council <https://www.westminster.gov.uk/>
Signed-off-by: Hannah Dunne-Howrie <hdunne-howrie@westminster.gov.uk>
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:34 +00:00
Martin Renvoize c7673a9d6a
Bug 41898: Introduce 'issued' status and update booking display/filtering
Implements a 4-state booking lifecycle: new → issued (on checkout) →
completed (on return), plus cancelled at any point.

Backend changes:
- AddIssue sets booking status to 'issued' (was incorrectly 'completed')
- AddReturn transitions 'issued' → 'completed' via booking_id on the issue
- filter_by_active updated to status-based: status IN ('new', 'issued');
  previously it filtered by date range — active/inactive now reflect booking
  lifecycle rather than the current date relative to start/end dates
- _is_final_status_transition reverted to only skip clash detection for
  terminal statuses 'cancelled' and 'completed'; 'issued' is active and does
  not bypass clash detection
- GET /api/v1/bookings no longer pre-filters to active bookings server-side;
  callers must supply an explicit status filter if they need active-only

Display changes:
- Patron bookings tab: replaces the date-based expired/active toggle with
  per-status filters (completed, cancelled) using the AdditionalFilters
  framework; active bookings (new/issued) are shown by default
- Biblio bookings list: same status-based filters; removes the date-based
  expired filter that was still present
- Status column is always visible on both views (was previously hidden on
  the patron tab unless expired bookings were shown)
- Status badges: new=bg-success, issued=bg-info, completed/cancelled=bg-secondary
- Timeline isActive check corrected to ['new','issued']
- patron-detail-tabs.inc: rename expired_bookings_count → closed_bookings_count
  to reflect status-based (not date-based) counting
- Filter button colour fixed to use $link-color so both views are consistent

Test plan:
1. Enable the EnableBookings system preference.
2. Create a biblio with at least one bookable item.
3. Place a booking for a patron via the biblio's Bookings tab.
   - The booking should appear with status badge "New" (green).
4. Check out the booked item to that patron (Circulation → Check out).
   - Reload the Bookings tab; the booking should now show "Issued" (blue).
5. Return the item (Circulation → Check in).
   - Reload the Bookings tab; the booking should now show "Completed" (grey).
6. Place a second booking and cancel it from the Bookings tab Actions menu.
   - The booking should show "Cancelled" (grey), with the reason if provided.
7. On the biblio Bookings tab:
   - Completed and cancelled bookings are hidden by default.
   - Clicking "Include completed" shows completed bookings.
   - Clicking "Include cancelled" shows cancelled bookings.
   - Both can be toggled independently.
   - The Status column is always visible.
8. Navigate to the patron's detail page → Bookings tab.
   - Active bookings (new/issued) are shown by default.
   - Completed and cancelled can be toggled independently using the same
     filter buttons.
   - The Status column is always visible.
9. Confirm via the REST API that GET /api/v1/bookings (no filters) returns
   bookings of all statuses, not just active ones.

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Sponsored-by: Westminster City Council <https://www.westminster.gov.uk/>
Signed-off-by: Hannah Dunne-Howrie <hdunne-howrie@westminster.gov.uk>
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:33 +00:00
Martin Renvoize fde2bc5680
Bug 41898: Automated Schema Update
Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Sponsored-by: Westminster City Council <https://www.westminster.gov.uk/>
Signed-off-by: Hannah Dunne-Howrie <hdunne-howrie@westminster.gov.uk>
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:32 +00:00
Martin Renvoize 4633d37931
Bug 41898: Add atomicupdate and update schema for 'issued' booking status
Extends bookings.status enum to ('new', 'cancelled', 'issued', 'completed').

Migrates existing data intelligently: rows with an active checkout in the
issues table (via booking_id) are updated to 'issued'; rows whose item has
already been returned retain 'completed', which is already correct under the
new scheme where 'completed' means the booking was fully fulfilled and the
item returned.

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Sponsored-by: Westminster City Council <https://www.westminster.gov.uk/>
Signed-off-by: Hannah Dunne-Howrie <hdunne-howrie@westminster.gov.uk>
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:31 +00:00
Brendan Lawlor 5f99bbb772
Bug 41879: (QA follow-up) Add pod for new sub _pickup_valid_for_item
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:30 +00:00
Lucas Gass ada88e3a83
Bug 41879: (follow-up) Add a test
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:29 +00:00
Lucas Gass 7d1521b943
Bug 41879: (follow-up) Bail with error code when no items on target bib
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:28 +00:00
Lucas Gass 9ca6d01f22
Bug 41879: (follow-up) Add helper function to avoid repeating code
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:27 +00:00
Lucas Gass 7f331590fa
Bug 41879: update move_hold unit tests
Signed-off-by: Eric Phetteplace <phette23@gmail.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:26 +00:00
Lucas Gass 47dd1f1844
Bug 41879: Disallow moves to invalid pickup locations for next available holds
Signed-off-by: Eric Phetteplace <phette23@gmail.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:25 +00:00
Lucas Gass 560efd018b
Bug 41879: Disallow moves to invalid pickup locations for items
To test:

1. Set ReservesControlBranch is set to 'item's home library'
2. Set the deafult 'Holds and bookings policies by item type' for the itype BK to allow holds from patrons from any library, but pickup at CPL only
3. Have a bib owned by another library ( I used MPL ), and bib owned by CPL. Both are the item type BK, and each has one item on the bib.
4. Place a hold for a patron on bib A for pickup at MPL
5. Move the hold to bib B
6. Note that the hold moves successfully, and is still set for pickup at MPL
7. Login and try to trigger the hold, nothing happens
8. APPLY PATCH, restart_all
9. Try spets 3 - 6 again. This time on step 6 you should see a message that the hold cannot be moved.

Signed-off-by: Eric Phetteplace <phette23@gmail.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:24 +00:00
Jonathan Druart 4c3e4b8bd7
Bug 42396: Adjust 'use' statements
Always include the plural version, not the singular one (the plural
imports the singular)

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Laura_Escamilla <laura.escamilla@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:23 +00:00
Olivia Reynolds 0670379b76
Bug 42396: Remove C4::Stats::UpdateStats and delete module
This patch replaces all calls to C4::Stats::UpdateStats with the wrapped
function from the Koha namespace. As this is the last function in C4::Stats, the
module is also deleted.

Several tests depended on transitive includes to Koha::Statistics through
C4::Stats. I've identified uses of that module by searching, and added the
necessary use in each case. I've run `prove -r t xt` with this patch applied and
didn't hit any relevant errors.

Test plan:
1. Inspect the patch, and verify each changed call maintains the same parameters.
2. Apply patch.
3. Prove the following files:
   - t/db_dependent/Accounts.t
   - t/db_dependent/Circulation.t
   - t/db_dependent/Koha/Account.t
   - t/db_dependent/Koha/ILL/Request.t
   - t/db_dependent/Koha/Item.t
   - t/db_dependent/Koha/Pseudonymization.t
   - t/db_dependent/Koha/Recalls.t

Sponsored-by: Open Fifth

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Laura_Escamilla <laura.escamilla@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:22 +00:00
Emily Lamancusa 9cad4c5c8d
Bug 20573: Make NotifyBorrowerDeparture's date math consistent with MembershipExpiryDaysNotice
To test:
1. Set both system preferences NotifyBorrowerDeparture and
   MembershipExpiryDaysNotice to 10 days
2. Create or edit a patron account with the following settings:
    - expiry date is exactly 10 days in the future
    - add an email address on the patron account
    - set messaging preferences to receive email notices for Patron
      account expiry
3. Run the membership expiry cron job to generate a notice:
   ktd --shell
   perl misc/cronjobs/membership_expiry.pl -c
4. Check notices tab on patron account
--> A membership expiry notice has been queued for the patron
5. Click back to the patron details
--> Note that there is no alert that the patron account will be expiring
    soon
6. Apply patch and restart_all
7. Refresh the patron details page
--> You should now see an alert that the account will be expiring soon

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:18 +00:00
Emmi Takkinen fdf37a2630
Bug 42699: Display Koha error message when attempting to save non-unique barcode in Edit serials
When you edit an already received serial from Serial collection page
using Edit serials and edit items buttons, and try to save a barcode
that already exists on another item, saving leads to Error 500.
There is no barcode validation for editing serials, this patch adds it.

To test:
1. Go to serials module.
2. Search for a serial that is not expired to edit and click on the title.
3. Go to Serial collection from the menu.
4. Use the checkboxes to mark an already received (eg. usually older,
arrived, not expected) serial and click Edit serials.
5. Click on Edit item.
6. Replace the barcode with a unique barcode that is already in use
and hit save.
=> Error 500 is raised.
7. Apply this patch and restart services.
8. Attempt to save serial with already used barcode again.
=> A standard Koha error message is displayed stating that barcode
must be unique.

Sponsored-by: Koha-Suomi Oy
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:13:09 +00:00
Martin Renvoize 7355dade3b
Bug 43071: Isolate holds_per_day-sensitive tests from ambient circulation rules
t/db_dependent/Holds.t, t/db_dependent/ILSDI_Services.t, and
t/db_dependent/api/v1/holds.t all fail if a site has a global
'Holds Allowed (Daily)' circulation rule configured, because
C4::Reserves::CanItemBeReserved checks holds_per_day before
holds_per_record/reservesallowed, so an ambient daily-cap rule
returns tooManyReservesToday and masks the status these tests
actually exercise (tooManyReserves, noReservesAllowed, OK, or the
API's hold_limit override). The affected subtests never set
holds_per_day themselves, so they unintentionally depend on the
database having no such rule configured.

Test plan:
1. On a fresh db, set a global 'Holds Allowed (Daily)' circulation
   rule (Administration > Circulation and fine rules) to 1.
2. prove t/db_dependent/Holds.t t/db_dependent/ILSDI_Services.t
   t/db_dependent/api/v1/holds.t
   => fails before this patch, passes after.
3. Remove the circulation rule and re-run the same tests.
   => still passes (no regression for the default/unconfigured case).

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:13:08 +00:00
Paul Derscheid fd3addf35f
Bug 38358: (QA follow-up) Return opened panel from dateChanged
Builds on the panel-detection logic by having dateChanged report which
panel it opens, so onChange can adjust that panel's "To date" picker
directly without waiting on the slide animations to settle.

- dateChanged returns "showHoliday" when editing, "newHoliday" when adding
- onChange maps that to datecancelrange/dateofrange, in place of the
  setTimeout and the :visible check

Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:13:07 +00:00
Emmi Takkinen aaa3954c8f
Bug 38358: Do not allow selecting past dates when editing existing holidays "To date" input
When you are editing holidays "To date" inputs calendar doesn't follow the
"From date" date. Instead calendar offers you today's date/month and
you must click calendar forward to the right date/month. This patch
adds check to determine if user is adding new holiday or editing
existing one and adjusts the code to disable past dates counting from
selected "From date" in both cases.

To test:
1. Navigate to calendar tool (Tools->Calendar).
2. Add new holiday and select "To date" for it.
=> Note that you are unable to select dates past from "From date".
3. Edit existing holiday and select "To date" for it.
=> Note that "To date" input calendar allows selecting dates past
"From date".
=> If you chosed holiday from incoming month, note that "To date"
calendar still opens up to this month.
4. Apply this patch, restart services if needed.
5. Edit existing holiday again.
=> Note that now "To date" inputs calendar works the same way as
it is working when your adding a new holiday.

Sponsored-by: Koha-Suomi Oy
Signed-off-by: Laura_Escamilla <laura.escamilla@bywatersolutions.com>
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:13:06 +00:00
Kyle M Hall 430281a59a
Bug 43101: search_for_data_inconsistencies.pl inlines every biblionumber in the catalog as an OR chain
Queries that use OR instead of IN are less efficient and can lead to
situations where the query takes a very long time to complete due to
row level scanning. We found one instance where they query had been
running for over 10 days!

This patch makes ids() return a subquery instead of a list of results,
and switches the callers to using IN. The database now resolves the set
itself and the queries stay a constant size no matter how large the
catalog is. This is super efficient because the subquery doesn't even
execute as a separate query since biblionumber is the primary key!

Test Plan:
1) Apply the unit tests patch
2) prove t/db_dependent/Koha/Database/DataInconsistency.t
3) Note the ids() subtest fails
4) Apply this patch
5) prove t/db_dependent/Koha/Database/DataInconsistency.t
6) Note all tests pass!
7) Run misc/maintenance/search_for_data_inconsistencies.pl
8) Note the output is the same as before the patch
9) Bonus: Re-run it with DBIC_TRACE=1 and note the queries now use
   IN (SELECT ...) instead of one OR clause per bib!

Signed-off-by: David Nind <david@davidnind.com>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:13:05 +00:00
Kyle M Hall 94695b74a1
Bug 43101: Add unit tests
Signed-off-by: David Nind <david@davidnind.com>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:13:04 +00:00
Matt Blenkinsop a5d12147f6
Bug 43121: Convert all groups to use explicit labels
Test plan:
1) Install another language
2) Navigate to a vendor record
3) The group names should translate correctly and display in the split screen view

Signed-off-by: Michaela <michaela.sieber@kit.edu>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:13:03 +00:00
Matt Blenkinsop 53e9d026ce
Bug 43121: Use an explicit label for group and tab names rather than a translated string
Signed-off-by: Michaela <michaela.sieber@kit.edu>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:13:02 +00:00
Saiful Amin b98a0c87fb
Bug 42861: System warning about missing encryption_key on a fresh instance
This patch updates the koha-create command to generate the random encryption key
using pwgen with the number of characters set to 32.

Test plan:
1. In ktd, create a new instance:
    - sudo koha-create --create-db test1
    - Check the /etc/koha/sites/test1/koha-conf.xml file and verify that the encryption_key
    is having the placeholder text '__ENCRYPTION_KEY__'

2. Apply the patch and copy the new koha-create command
    - git bz apply 42861
    - sudo cp /kohadevbox/koha/debian/scripts/koha-create /usr/sbin/koha-create

3. In ktd, create a new instance:
    - sudo koha-create --create-db test2
    - Check the /etc/koha/sites/test2/koha-conf.xml file and verify that the encryption_key
    is generated and has 32 characters random string.

4. Sign-off

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:13:01 +00:00
Matt Blenkinsop 7b199653ed
Bug 43119: Restore item and hold links to the log viewer
INSERT INTO action_logs (timestamp, user, module, action, object, info, interface) VALUES
    (NOW(), 5, 'CIRCULATION', 'ISSUE', 5,
     '{"issue":1,"branchcode":"CPL","itemnumber":1,"confirmations":["TOO_MANY"],"forced":["DEBT"]}', 'intranet'),
    (NOW(), 5, 'CIRCULATION', 'RETURN', 5,
     '1', 'intranet'),
    (NOW(), 5, 'HOLDS', 'CREATE', 7,
     '{"hold":7,"biblionumber":1,"itemnumber":1,"confirmations":["HIGHHOLDS"],"forced":["HOLD_POLICY_OVERRIDE"]}', 'intranet');

Test plan:
1) Without the patch applied, run the populate SQL above against the kohadev database.
2) Go to Tools > Log viewer
3) Under Modules, select only Circulation - leave the date range empty and click Submit.
4) Observe the ISSUE row's Info column shows the raw JSON payload as plain text, and the RETURN row shows the bare itemnumber "1" as plain text - neither is a clickable link to the item.
5) Go back, select only the Holds module, click Submit, and observe the CREATE row's Info column likewise shows raw JSON with no hold/item links.
6) Apply the patch.
7) Run yarn build && restart_all, then hard refresh the browser.
8) Repeat step 3.
9) Confirm the ISSUE row's Info column now shows a link reading "Item <barcode> CPL" (e.g. Item 3999900000001 CPL) - the branchcode comes from the JSON payload - and clicking it opens the item page. Override information is also displayed correctly
10) Confirm the RETURN row's Info column now shows a link reading "Item <barcode>" (no branchcode) opening the same item's detail page - verifying the bare-itemnumber payload path.
11) Repeat step 5. Confirm the CREATE row now shows "Hold 7 on biblio 1" linking to reserve/request.pl?biblionumber=1, followed by " - Item 1" linking to the item detail page. Override information is also displayed correctly

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:13:00 +00:00
Samuel Sowanick 1254997e69
Bug 42928: Update log viewer action terminology - Return to Check-in
The log viewer results table uses "Return" for "Check-in" actions.

See the terminology list:
https://wiki.koha-community.org/wiki/Terminology#C

Test plan:
1. Check out an item to a patron, then check it back in again.
2. Go to the log view (Tools > Additional tools > Log viewer).
3. Click "Submit".
4. Note that the action column for the results shows "Return".
5. Apply the patch.
6. Refresh the log viewer page.
6. Note that the action column now correctly shows "Check-in".

Signed-off-by: Laura_Escamilla <laura.escamilla@bywatersolutions.com>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:59 +00:00
Tomás Cohen Arazi 7716fac58d
Bug 34451: Add CSRF protection for REST API (cookie-auth only)
This patch adds Koha::REST::Plugin::CSRF which enforces CSRF token
validation on state-changing requests (POST/PUT/DELETE/PATCH) when
a CGISESSID cookie is present.

Requests using OAuth2 or HTTP Basic Auth are not affected since they
don't use session cookies and are inherently immune to CSRF.

The client-side already sends the CSRF-TOKEN header via http-client.js
(added in Bug 34478). This patch adds the server-side enforcement.

Test plan:
1. Apply patch
2. Run:
   $ ktd --shell
   k$ prove t/db_dependent/Koha/REST/Plugin/CSRF.t
=> SUCCESS: Tests pass!
3. Run:
   k$ prove t/db_dependent/api/v1/csrf.t
=> SUCCESS: Tests pass!
4. Tests cover:
   - GET/HEAD/OPTIONS requests bypass CSRF check
   - POST/PUT/DELETE without CGISESSID bypass CSRF check
   - POST with CGISESSID but no CSRF-TOKEN returns 403
   - POST with CGISESSID and invalid CSRF-TOKEN returns 403
   - POST with CGISESSID and valid CSRF-TOKEN passes through
   - PUT and DELETE enforcement
5. Sign off :-D

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:58 +00:00
Tomás Cohen Arazi 058c476bc7
Bug 42732: Migrate catalogue/detail.tt addToBundle $.post to api-client.js
Replace direct $.post() to /api/v1/items/{id}/bundled_items with
ItemAPIClient.bundled_items.add(), which uses http-client.js and
automatically includes the CSRF token header.

The caller passes return_response: true because it needs access to
the raw response status code and body for fine-grained error handling
(different user-facing messages for 409/already_bundled, 409/checked_out,
409/reserved, 404/not_found, 400/failed_nesting). http-client.js
default behavior throws a plain Error on non-2xx responses, losing
the status code and error_code fields the UI needs.

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:57 +00:00
Tomás Cohen Arazi e2b0883811
Bug 42731: Migrate modal-claims.inc $.post to api-client.js
Replace direct $.post('/api/v1/return_claims', ...) with the new
ClaimAPIClient, which uses http-client.js and automatically includes
the CSRF token header.

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:56 +00:00
Tomás Cohen Arazi f5a1f41d35
Bug 42730: Migrate place_booking.js $.post to api-client.js
Replace direct $.post() and $.ajax() calls to /api/v1/bookings with
the new BookingAPIClient, which uses http-client.js and automatically
includes the CSRF token header.

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:55 +00:00
Pedro Amorim f5e8cdb399
Bug 43139: Sort by ID DESC instead of ID ASC
Test plan:
1) Enable ILLModule
2) Ensure at least 2 ILL requests exist
3) Go to <staff_url>/cgi-bin/koha/ill/home
4) Confirm the most recent ILL request (highest ID) shows first

Signed-off-by: Michaela <michaela.sieber@kit.edu>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:54 +00:00
Aleisha Amohia 33f4336974
Bug 33364: Prevent Holds to Pull from including items with pending recalls
This patch checks for waiting or in_transit items allocated to recalls, and excludes them from the Holds to Pull report.

To test:

1. Enable UseRecalls and set the recalls-related circulation rules
2. Check out an item to Patron A
3. Place an item-level hold on the item for Patron B
4. Log into the OPAC as Patron C and search for the item. Place an item-level recall on the item.
5. Go back to the staff interface and check in the item. This should prompt a pop-up to confirm the recall as waiting. Confirm the recall as waiting.
6. Go to Circulation -> Holds to pull.
7. Notice that even though the item is allocated for a recall, the hold is showing. It should not be showing in this page.

8. Apply the patch, restart services and refresh the page

9. The item allocated to a recall should no longer be showing in the Holds to pull report

10. Go to the record detail page and go to the Recalls tab
11. Revert the recall from its waiting status
12. Go back to the Holds to pull page. The record will now show for the hold.
13. Go to the Circulation -> Check in page
14. Go to the menu in the top-right of the page and click Set library
15. Change your library to a different one
16. Check in the item to confirm the recall as waiting and trigger a transfer
17. Go back to the Holds to pull page
19. The item should no longer show on the Holds to pull report

20. Confirm tests pass t/db_dependent/Koha/Holds.t

Sponsored-by: Catalyst IT
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:53 +00:00
Jonathan Druart f2cca7c630
Bug 43110: (bug 28308 follow-up) Disable checkboxes on the detail view
Checkboxes must be disabled on the detail page as we cannot submit the
changes.

Test plan:
1 - Edit a patron, set a value for advance notices, do not check any transports
2 - View the details for the patron:
    http://localhost:8081/cgi-bin/koha/members/moremember.pl?borrowernumber=5
3 - Note the checkboxes are disabled

QA: The logic is a bit weird as messaging_form_inactive is not set if the
pref is off, but the table is not displayed in this case.

Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:52 +00:00
Lucas Gass 3fac313a53
Bug 42930: Replace title with borrowertitle
To test:
1. Have some overdues
2. Go to circ/overdue.pl
3. Notice the the 'Patron' column the name is prefixed with the biblio title
4. This is meant to be the borrowertitle
5. APPLY PATCH, restart_all
6. Now you should see only the borrower/parton name and info.
7. Edit the patron and add a " Salutation"
8. In the overdue report this should now display

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Jan Kissig <bibliothek@th-wildau.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:51 +00:00
Lucas Gass 35bbb013ba
Bug 42166: Check for 008 before adding the date to it
To test:
1. Edit or create a framework that does not require the 008.
2. Make or edit a record in that framework without a 008.
3. Turn on EnableAdvancedCatalogingEditor, and make sure you are using the advanced editor.
4. Go to the record from step 2 and click "Edit as new (duplicate)"
5. The record won't load in the advanced editor.
6. APPLY PATCH
7. Try again, this time the record should load when using "Edit as new (duplicate)".

Signed-off-by: Roman Dolny <roman.dolny@jezuici.pl>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:50 +00:00
Jonathan Druart 96de51c53d
Bug 42981: Prevent toLowerCase to fail when comparing int and string
If stateSave is turned on and one of the option has a value with _id set
to an Integer, toLowerCase is not available and a JS error is raised:
 Uncaught (in promise) TypeError: this._id.toLowerCase is not a function

Test plan:
1.) Admin - table settings: cgi-bin/koha/admin/columns_settings.pl#eholdings|packages
Activate checkbox "Save search state on page change"

2.) Go to ERM : cgi-bin/koha/erm/eholdings/local/packages
And create two packages:
 - One package with My Vendor assigned.
 - One package without a vendor.

3.) Filter the package results list by My Vendor. --> List is filtered to one package

4.) Leave the page, for example by navigating to Agreements.
Return to the Packages list and you will see the issue  (See also attached Screenshot)

=> Without the patch
The package results list becomes unresponsive / hangs after returning to the page.

=> With the patch
The package results list should load and display the filtered results correctly.

Signed-off-by: Michaela Sieber <michaela.sieber@kit.edu>
Signed-off-by: Matt Blenkinsop <matt.blenkinsop@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:49 +00:00
Pedro Amorim 959ba68ad0
Bug 42489: DBRev 26.06.00.005
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:48 +00:00
Tomás Cohen Arazi 3edd575263
Bug 42489: Pass unallocated mode to real-time holds queue
When RealTimeHoldsQueueUnallocated is enabled, the background job
passes unallocated => 1 and delete => 0 to update_queue_for_biblio.
This skips deleting existing queue entries and only processes holds
not already allocated, matching the --unallocated behavior of
build_holds_queue.pl.

Test plan:
1. Apply patches
2. Run:
   $ ktd --shell
  k$ prove t/db_dependent/Koha/BackgroundJob/BatchUpdateBiblioHoldsQueue.t
=> SUCCESS: Tests pass!
3. Tests verify:
   - delete => 1 and no unallocated when pref is off
   - delete => 0 and unallocated => 1 when pref is on
4. Sign off :-D

Signed-off-by: Alex Carver [Acerock7] <alex@rcls.org>
Sponsored-by: The Main Library Alliance <https://www.mainlib.org/>
Signed-off-by: Alex Carver [Acerock7] <alex@rcls.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:47 +00:00
Tomás Cohen Arazi 5345fbf908
Bug 42489: Add RealTimeHoldsQueueUnallocated syspref
Adds a new system preference to control whether the real-time
holds queue processes only unallocated holds (faster, additive)
or rebuilds the entire queue for the affected biblio (current
behavior, more thorough).

Default is off (preserving current behavior).

Test plan:
1. Apply patch
2. Run:
   $ ktd --shell
  k$ perl installer/data/mysql/updatedatabase.pl
=> SUCCESS: New preference added
3. Verify RealTimeHoldsQueueUnallocated appears in
   Administration > System preferences > Circulation
4. Sign off :-D

Signed-off-by: Alex Carver [Acerock7] <alex@rcls.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:46 +00:00
Pedro Amorim 2431af105d
Bug 40445: DBRev 26.06.00.005
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:45 +00:00
Martin Renvoize 09642be4db
Bug 40445: (follow-up) Add default cud- op value to cashup selected form
The two-phase cashup workflow added a 'Cashup selected registers' modal
whose form's op hidden input had an empty value, populated at runtime by
JavaScript. xt/find-missing-op-in-forms.t parses templates statically and
flagged the empty op value as not starting with 'cud-' (see bug 34478).

Set a sensible default of 'cud-cashup_start'; the JS click handlers still
overwrite the value before submission, so behaviour is unchanged.

Test plan:
1. prove xt/find-missing-op-in-forms.t => now passes

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:44 +00:00
Martin Renvoize a4ba11af06
Bug 40445: (follow-up) Fix TT in Tag issue
Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:43 +00:00
Pedro Amorim f1766a206b
Bug 40445: (RM follow-up): Fix POD copy paste
Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:42 +00:00
Martin Renvoize 53d498e145
Bug 40445: (QA follow-up) Show cashup summary modal when completing from registers.pl
When a single-register cashup is completed from the Cash summary page
(registers.pl), pos/register.pl redirected back to registers.pl without
the '#cashup-<id>' hash, so the hash-watcher in registers.tt never
triggered the cashup summary modal. Completing the same cashup from the
Transaction history page (register.pl) worked because that branch
already appended the hash.

Append '#cashup-<id>' to the registers.pl redirect URL too, so both the
'Record cashup' (quick) and 'Complete cashup' (two-phase) actions on the
Cash summary page open the summary modal on return.

Test plan:
1. Have at least one register with cash transactions.
2. From Cash summary (registers.pl), click 'Record cashup' on a register
   with no cashup in progress; complete it.
   => On return, cashup summary modal opens automatically.
3. Start a two-phase cashup; from Cash summary click 'Complete cashup'
   and confirm.
   => On return, cashup summary modal opens automatically.
4. From Transaction history (register.pl), complete a cashup as before.
   => Modal still opens (regression check).

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:41 +00:00
Martin Renvoize c51f4b7cca
Bug 40445: (QA follow-up) Add missing [% USE raw %] in cashup_summary.inc
The cashup summary modal include uses the \$raw filter on
Koha.CSPNonce, but never declared [% USE raw %]. The QA template
tester (which processes templates through a fresh Template::Toolkit
instance) reports "valid_template: filter not found" because the
\$raw plugin is not loaded.

Add [% USE raw %] at the top of the include, matching the convention
used by other includes that emit nonce attributes (e.g.
includes/str/cataloging_additem.inc).

Test plan:
1. Run qa-test-tools against the bug branch -- cashup_summary.inc
   passes valid_template.
2. Open the cashup summary modal -- the <style> tag still receives
   a CSP nonce and the embedded styles render correctly.

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:40 +00:00
Martin Renvoize 7798332412
Bug 40445: (follow-up) Show actual cash removed alongside expected on discrepancy
The cashup summary modal labelled the per-payment-type cash row "Cash
removed from register", but the value came from the session
transactions (with CASHUP_SURPLUS/DEFICIT lines excluded), so it
represented the *expected* cash, not the amount actually counted at
cashup. When a deficit or surplus existed, the reconciliation row
appeared as the difference between two figures the user could not
both see.

Render two rows when surplus_total or deficit_total is set on a
completed cashup: "Expected cash total" (from total_grouped) and
"Cash removed from register" (from cash_register_actions.amount,
already exposed as data.amount). The reconciliation row below now
visibly equals the difference. Balanced cashups and the in-progress
preview keep the original single-row display, and the negative-cashup
branch flips the labels to "Expected cash to add to register" /
"Cash added to register".

Test plan:
1. Configure a cash register and record a few cash payments.
2. Trigger a cashup, enter the exact expected amount; open the
   summary modal -- a single "Cash removed from register" row is
   shown matching the expected total.
3. Repeat with an amount lower than expected -- "Expected cash
   total" and "Cash removed from register" both appear, and the
   "Cashup deficit" row is the difference.
4. Repeat with an amount higher than expected -- the actual row
   exceeds the expected row and "Cashup surplus" matches the gap.

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:39 +00:00
Martin Renvoize 86d034e628
Bug 40445: (follow-up) Clarify cashup summary cash row wording and sign
A cashup is the process of removing cash from the till to send it
to the bank, so labelling the headline cash line "Cash added to
register" was confusing. Re-word it to "Cash removed from register"
in the typical case and "Cash added to register" in the edge case
where the cashier has had to top up the till to cover refunds.

While doing this, align the sign convention of total_grouped with
that of the overall total field on the summary: positive means cash
collected (i.e. removed from the register at cashup time), negative
means cash had to be added back into the register. Previously
total_grouped used the raw accounting sign (credits negative, debits
positive), so a typical $2 cash session would display "Total: 2.00"
and "Cash added to register: -2.00", which was internally
inconsistent. The conditions on the cash and other-payment-type rows
in cashup_modal.js are swapped to match the new convention.

Test plan:
1. Apply the patch and rebuild JS (yarn js:build) inside KTD.
2. As a staff user with permission to manage cash registers, take
   payments via several payment types (some cash, some not).
3. Trigger a cashup and view the cashup summary modal.
4. Confirm the "Total" line and the per-payment-type lines all
   display as positive amounts in the typical "money was collected"
   scenario, with the cash row labelled "Cash removed from
   register".
5. Issue some refunds large enough to exceed cash collected, then
   cash up again.
6. Confirm the cash row is labelled "Cash added to register" with a
   negative amount and the per-non-cash-type rows are labelled
   "{type} to add" where applicable.
7. Run prove t/db_dependent/Koha/Cash/Register/Cashup.t and prove
   t/db_dependent/Koha/Cash/Register.t inside KTD; both should
   pass.

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:38 +00:00
Martin Renvoize 463b92df52
Bug 40445: (QA follow-up) Add nonce, POD, and test isolation
This patch resolves three QA findings on the cashup reconciliation
work:

* Add a CSP nonce to the inline <style> block in cashup_summary.inc
  so it is not flagged by the forbidden_patterns check.
* Document the private _get_session_end method on
  Koha::Cash::Register::Cashup so pod_coverage passes.
* Mock CashupReconciliationNoteRequired to 0 in
  t/db_dependent/Koha/Cash/Register/Cashup.t so the test does not
  depend on the site preference value (it previously failed when
  the syspref was set to "Require" because surpluses and deficits
  are created without a reconciliation note in this test).

Test plan:
1. Apply the patch.
2. Set CashupReconciliationNoteRequired to "Require".
3. Run prove t/db_dependent/Koha/Cash/Register/Cashup.t -- it should
   pass.
4. Set CashupReconciliationNoteRequired to "Don't require" and
   re-run -- it should still pass.
5. Run koha-qa.pl on the bug branch -- pod_coverage on
   Koha/Cash/Register/Cashup.pm and forbidden_patterns on
   cashup_summary.inc should now pass.

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:37 +00:00