Commit Graph

62086 Commits

Author SHA1 Message Date
Chris Nighswonger 60ee7fb527
Bug 41717: Bump PDF::Reuse and PDF::Reuse::Barcode versions
PDF::Reuse 0.43 fixes a TTFont/docProxy lifecycle bug that caused
prEnd() to crash when TrueType fonts were used.

PDF::Reuse::Barcode 0.09 is the current stable release.

Testing plan:

- Start ktd, note older perl modules versions under About Koha
- Apply patch
- ktd --shell
- sudo cpan -i PDF:Reuse; sudo cpan -i PDF:Reuse:Barcode
- Let cpan auto set itself up (not sure if sudo is needed above)
- `restart_all`
- Revisit About Koha; module versions should be 0.43 & 0.09 respectively

Signed-off-by: Eric Phetteplace <ephetteplace@cca.edu>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-28 14:32:22 +00:00
Pedro Amorim cfe7440dd7
Revert "Bug 34451: Add CSRF protection for REST API (cookie-auth only)"
This reverts commit 7716fac58d.

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 16:06:36 +00:00
Andrii Nugged a445bb0ab3
Bug 42845: Add ILL config endpoint for Vue app
The ILL Vue app currently reads ILLModule and ILLPartnerCode through the system preferences service. That service requires parameters/manage_sysprefs, so a staff user with ILL permissions but without system preference administration permissions cannot load the ILL module.

Add an ILL-specific /ill/config REST endpoint protected by the ill permission, and switch the ILL Vue app to load its configuration from that endpoint. This follows the module-specific configuration endpoint pattern introduced by bug 33606 for the same manage_sysprefs problem in the ERM Vue app.

Security/compatibility considerations: the endpoint returns only allow-listed, non-secret ILL configuration values needed by the ILL Vue app. It does not expose the generic system preferences API, and /api/v1/sysprefs remains restricted to parameters/manage_sysprefs.

Regression test coverage: the new API test verifies that a staff user with the ill permission can read /ill/config, that a staff user without the ill permission cannot read it, and that the same ILL-only user still cannot access /api/v1/sysprefs.

Test plan:

1. prove -I. -It/lib t/db_dependent/api/v1/ill_config.t

2. prove -I. -It/lib t/db_dependent/api/v1/ill_users.t

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

Amended-by: Jonathan Druart
chmod +x t/db_dependent/api/v1/ill_config.t

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:48 +00:00
Emily Lamancusa 769e565890
Bug 38361: (QA follow-up) Set required variable for input validation
Built-in validation checks weren't functioning because the variable
required.sex was not getting set. Ensure that variable gets set, and
remove manual validation code that is no longer needed.

Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:47 +00:00
Bo Gustavsson 9d40cdee7f
Bug 38361: Gender field does not include a Required if the field is set to required in PatronSelfRegistrationBorrowerMandatoryField
Solves this bug and enhances the user experiance by replacing the old radio buttons with a less-confusing dropdown menu.

Test plan:

1. Open the setting "PatronSelfRegistrationBorrowerMandatoryField", find Gender(sex) and mark it as not required.
2. Go to the self registration form. No 'Required' label under gender.
3. Go back to the setting  "PatronSelfRegistrationBorrowerMandatoryField", find Gender(sex) and mark it as required.
4. Go to the self registration form. The 'Required' label is there.

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:46 +00:00
Slava Shishkin ac31d8ae25
Bug 41227: Respect SuspendHoldsIntranet on patron holds page
Test plan:
1. Set SuspendHoldsIntranet to "Don't allow"
2. Place a hold for a patron
3. Open the patron holds table
4. Confirm the Suspend button is visible in the Suspend column
5. Apply the patch
6. Reload the patron holds table
7. Confirm the Suspend button is no longer visible
8. Set SuspendHoldsIntranet to "Allow"
9. Reload the patron holds table
10. Confirm the Suspend button is visible again

Signed-off-by: Anneli Österman <anneli.osterman@koha-suomi.fi>
Signed-off-by: Laura_Escamilla <laura.escamilla@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:45 +00:00
Fridolin Somers ea185b6833
Bug 43098: Typo edit_formid in guided_reports_start.tt
Typo in edit SQL when authorised values category is wrong : 'op=edit_formid='

Test plan:

1) Create a new authorised values category WRONG
2) Create a new SQL report with : SELECT * FROM biblio WHERE title LIKE <<val|WRONG>>
3) Save the report
4) Delete authorised values category WRONG
5) Run the SQL report
6) You see message 'The authorized value category (WRONG) you selected does not exist.'
7) Click on 'Edit SQL'
=> You comme back to editing the correct SQL report

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Laura_Escamilla <laura.escamilla@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:44 +00:00
Lari Taskula d0b458af58
Bug 42298: Automatic renewals should only respect the "No automatic renewals before" rule
To test:
1. prove t/db_dependent/Circulation.t
2. Observe success

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Laura_Escamilla <laura.escamilla@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:43 +00:00
Lari Taskula 54ac795b76
Bug 42298: Add a failing test
To test:
1. prove t/db_dependent/Circulation.t
2. Observe the following failures

t/db_dependent/Circulation.t .. 17/87
    #   Failed test 'Bug 42298: No autorenew before rule is respected'
    #   at t/db_dependent/Circulation.t line 1134.
    #          got: '0'
    #     expected: '1'

    #   Failed test 'Bug 42298: No autorenew before rule is respected'
    #   at t/db_dependent/Circulation.t line 1135.
    #          got: 'too_soon'
    #     expected: 'auto_renew'

    #   Failed test 'soonest_renew_date is not returned because this issue can be renewed'
    #   at t/db_dependent/Circulation.t line 1136.
    #          got: '2026-04-21T23:59:00'
    #     expected: undef

    #   Failed test 'No renewal before is 99, patron opted out of auto_renewal so can renew'
    #   at t/db_dependent/Circulation.t line 1141.
    #          got: '0'
    #     expected: '1'
    # Looks like you failed 4 tests of 124.

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Laura_Escamilla <laura.escamilla@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:42 +00:00
Jonathan Druart 34da25f751
Bug 42632: Fix "Manage bundle" after filters are shown/hidden
If "Show/Hide filters" link is clicked, the "Manage bundle" buttons do not longer work.

Test plan:
Edit a record, set leader 7=collection
In the holding list click "Manage bundle"
Click "Show filters"
Click one of the "Manage bundle" button
=> Without this patch nothing happens
=> With this patch applied the table displays the nested table with the
bundle details

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Anni <anni.maki-mantila@turku.fi>
Signed-off-by: Laura_Escamilla <laura.escamilla@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:41 +00:00
Pedro Amorim d7ff63f20b
Bug 41898: DBRev 26.06.00.007
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:39 +00:00
Paul Derscheid 1f45a43f1d
Bug 41898: (QA follow-up) Skip clash detection when collecting a booking
Transitioning a booking to 'issued' no longer skipped clash detection,
and Item::check_booking treats any live checkout on the item as a
clash. Switching an on-site checkout of a booked item to a regular
checkout (SwitchOnSiteCheckouts) therefore crashed AddIssue with an
unhandled Koha::Exceptions::Booking::Clash; collection could fail the
same way whenever the biblio's bookable item pool had shrunk since the
booking was placed.

- short-circuit no-op stores before validation; circulation issues
  redundant status writes that must not re-trigger the checks
- rename _is_final_status_transition to _skip_clash_detection and also
  skip when transitioning to 'issued' from an active status, as long
  as the booked period and item are unchanged
- keep validating period/item changes and reactivations of terminal
  bookings
- cover the on-site switch in Circulation.t and the store() semantics
  in Booking.t

Test plan:
1. Enable OnSiteCheckouts and SwitchOnSiteCheckouts
2. Place a booking starting today for a patron
3. Check the booked item out to that patron from Circulation > Check
   out with 'On-site checkout' ticked. Do NOT use the bookings page's
   'Transform to checkout' button: it submits a fixed field set that
   does not carry the on-site flag (Bug 36789), so the checkout would
   silently be a regular one and step 4 could not reproduce the switch
4. The booking shows 'Issued'. Scan the same barcode for the same
   patron again to switch the on-site checkout to a regular one; this
   crashed with Koha::Exceptions::Booking::Clash before the patch and
   now succeeds with the booking still 'Issued'
5. prove t/db_dependent/Koha/Booking.t t/db_dependent/Circulation.t

Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 15:53:38 +00:00
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