commit 6a12116dd2e9a4d241651795ba3d5ab43cfdca91 Author: Nick Clemens Date: Mon Dec 1 20:11:38 2025 +0000 Bug 41343: Further improvements Remove unused patron attribute values sent to the template Send preferred name Use SQL to do date comparison to see if an item is overdue Signed-off-by: David Nind Signed-off-by: Marcel de Rooy Signed-off-by: Lucas Gass commit e3862dc81b67792615208f8513d87d6dccde0e23 Author: Nick Clemens Date: Mon Dec 1 19:51:52 2025 +0000 Bug 41343: Don't fetch patron objects per overdue We use SQL here to get all the needed info - the query says it is slow, but isn't so bad. Worse is fetching the patron for each borrower with overdues (and more than once if they have several) It appears we started grabbing the patron so we could use patron title, but we can just pass the info already fetched. I additionally fetch the patron category types and update patron-title to allow for hide_patron_infos_if_needed to work without a patron object - we can just use the branchcode as ultimately that's all we need To test: 1 - Add many items to your catalog (can use my handy repo randitems.pl) https://github.com/kidclamp/handy-koha-script 2 - Add many issues (can use randissues.pl from above) 3 - I tested with about 1000 4 - Browse to Circulation->Overdues 5 - Note load time 6 - Apply patch 7 - Restart all 8 - Note load time is faster Signed-off-by: David Nind Signed-off-by: Marcel de Rooy Signed-off-by: Lucas Gass commit 950c9f8188e4d7073a81d1ef2e95681f3740b5ac Author: Bernard Scaife Date: Mon Nov 3 15:12:13 2025 +0000 Bug 41073: Fix default expiry date which is being ignored Fixes regression in which default expiry date is ignored due to regression introduced by fix for bug 34483. If default expiry date is completed in patron import tool, the value is ignored and expiry date unaltered. This patch fixes this error. Test plan: 1. User should exist and have expiry date in future (say 2099-12-31) 2. Save above as file 3. Tools > Import users 4. Choose file saved in 2 5. Field to use for record matching: cardnumber 6. In default values set expiration date to a date in past (say 2025-10-19) using date picker 7. Select overwrite the existing one with this 8. Leave renew existing patrons as "use the expiration date in the file if present" 9. Run 10. Observe user expiry date is still 2099-12-31 11. Apply patch / restart_all 12. Repeat 4 - 9 above 13. Observe user expiry date is now 2025-10-19 Signed-off-by: Eric Phetteplace Signed-off-by: Marcel de Rooy Signed-off-by: Lucas Gass commit a0b92c0bb6b48957c5b15c076ecbfba682518b0b Author: Hammat Wele Date: Fri Mar 6 20:46:47 2026 +0000 Bug 41690: (follow-up) Add test Signed-off-by: David Nind Signed-off-by: Marcel de Rooy Signed-off-by: Lucas Gass commit 412e4e4d95eed7577b2768221848e663bd4821d5 Author: Hammat Wele Date: Wed Mar 4 21:23:42 2026 +0000 Bug 41690: Add MARC21 245 (subtitle) to Cite option The "Cite" option in the OPAC is missing the subtitle (245) in MARC21. To recreate: 1. Add a record or find a record with a subtitle (in KTD, biblionumber 58 has a 245 http://localhost:8081/cgi-bin/koha/catalogue/MARCdetail.pl?biblionumber=58 2. In the OPAC, go to the record http://localhost:8080/cgi-bin/koha/opac-detail.pl?biblionumber=58 3. Click "Cite" --> The subtitle is not in any of the citations 4. Apply the patch 5. Repeat step 2 and 3 --> The subtitle is now shown Signed-off-by: Marcel de Rooy Signed-off-by: Lucas Gass commit f5a38efff1ab4165414e18e022fb8566bdb7d49c Author: Marcel de Rooy Date: Mon Mar 2 11:00:12 2026 +0100 Bug 41962: Add comment to SearchAuthorities, change POD Test plan: Read the patch. Search for both parameters to verify that they are not used. Signed-off-by: Marcel de Rooy Signed-off-by: Lucas Gass commit db6287aa2190a827d62fa75b25303eaefaf39927 Author: Marcel de Rooy Date: Fri Apr 24 09:07:25 2026 +0200 Bug 41795: (QA follow-up) Improve comment Signed-off-by: Marcel de Rooy Signed-off-by: Lucas Gass commit 2b691bd4ddccb463816c52fbfc54c0346f2be1dd Author: Andreas Roussos Date: Mon Feb 9 01:52:30 2026 +0000 Bug 41795: Fix failing test in UNIMARC instances Previously, t/AuthoritiesMarc_MARC21.t would fail if you tried to run it from within a UNIMARC instance. This patch fixes that. Test plan: 1) Inside a UNIMARC instance, run the test and notice the subtest failure: $ prove -v t/AuthoritiesMarc_MARC21.t 2) Apply this patch. 3) Re-run the test and notice that it now completes without any failures. Signed-off-by: David Nind Signed-off-by: Marcel de Rooy Signed-off-by: Lucas Gass commit e43d32a1231ca2a1cf75113c984641bdd8961a58 Author: Andreas Roussos Date: Mon Feb 9 00:17:08 2026 +0000 Bug 41795: Include the completeness attribute when matching on authtype UNIMARC instances include the following two authority types by default (among others): Corporate Body Name (code: 'CO') and Collective Title (code: 'CO_UNI_TI'). The problem is that when searching for Corporate Body Name authorities (in the OPAC or in the Staff interface), the results will include Collective Title authorities as well. It has to do with Corporate Body Name authorities using an authtypecode (CO) that happens to be the prefix of the authtypecode (CO_UNI_TI) used by Collective Titles. NOTE: Elasticsearch is not affected by this. The fix is to additionally pass the completeness attribute to the RPN query sent to Zebra so that it will try to match on what is literally found in the entire field's phrase index (i.e. the value of UNIMARC 152$b), instead of performing a partial match. More information can be found at the official documentation: https://software.indexdata.com/zebra/doc/querymodel-rpn.html#querymodel-bib1-completeness Test plan (tailored for KTD): 0) Launch a UNIMARC Koha instance using Zebra: ktd --marcflavour unimarc --search-engine zebra up 1) Import the Sample MARC record attached to this Bug, containing a single Collective Title authority (adding one manually would take a long time due to the many mandatory subfields). 2) Perform a generic authority search (without entering anything in the search box) for all 'Corporate Body Name' authorities. Note the number of results returned (83), and also notice that in page 4 of the results you get a stray 'Collective Title' authority ("Selected works", the one you imported in Step 1). 3) Apply this patch (and `restart_all`). 4) Repeat the same search as in Step 2. This time, the number of results returned should be one less than before (82), and you should only see 'Corporate Body Name' authorities in the results. Signed-off-by: David Nind Signed-off-by: Marcel de Rooy Signed-off-by: Lucas Gass commit 3d8e069a4dbe9925c8ae99900f96b33b8776725a Author: Fridolin Somers Date: Thu Feb 26 11:03:49 2026 +0100 Bug 41942: Fix lang hidding in opac patron modification Bug 34438 added lang in opac registration/modification form. But it is linked to primary contact method hidden or not. Looks like this is not on purpose. This patch removes this link. Test plan: 1) Verify system preference 'PatronSelfModificationBorrowerUnwantedField' is empty 2) Go to OPAC user account > Personal details 3) You see 'Preferred language for notices' 4) In system preference 'PatronSelfModificationBorrowerUnwantedField' check 'primary_contact_method' 5) Go to OPAC user account > Personal details => Without patch lang is also hidden => With patch lang is not hidden Signed-off-by: Owen Leonard Signed-off-by: Marcel de Rooy Signed-off-by: Lucas Gass commit d0bd802da61818a15bb8adfcc720bb693d587544 Author: Jonathan Druart Date: Thu Apr 16 09:15:56 2026 +0200 Bug 42374: Restore require Koha::Plugins (instead of use) See comment 4 for a detail explanation of the problem. Test plan: hit /cgi-bin/koha/installer/install.pl login with the DB credentials upgrade => Without this patch you get ``` ISBN_RANGE_MESSAGE is set to [] but that file does not exist! Trying to use the default locations ``` => With this patch applied: "Everything went okay. Update done." Signed-off-by: Jonathan Druart Signed-off-by: Emily Lamancusa Signed-off-by: Lucas Gass commit bf867d08c20e42436b574ba8df74f44b647629c6 Author: David Cook Date: Thu Apr 16 04:31:09 2026 +0000 Bug 42374: Remove buggy ISBN_RANGE_MESSAGE env variable value This patch removes an ISBN_RANGE_MESSAGE env variable that is accidentally set by Business::ISBN::Data. Test plan: - git checkout v25.11.03-2 - reset_all - git checkout -b 42374 origin/main - Apply the patch - restart_all - Go to staff interface (e.g. http://localhost:8081/) - Log into web installer - Click "Update your database" - Note it works - Repeat the process but instead of using the web installer use updatedatabase.pl directly (say through koha-upgrade-schema) Signed-off-by: Jonathan Druart Signed-off-by: Emily Lamancusa Signed-off-by: Lucas Gass commit a601b14f26a56819fbea94ee893c2d5107bf6c36 Author: Lucas Gass Date: Fri May 1 16:33:02 2026 +0000 Bug 42406: DBRev 25.12.00.058 Signed-off-by: Lucas Gass commit 8dc79ed415df9dc5705550aaf6119529ea2ac837 Author: David Cook Date: Fri Apr 24 04:24:14 2026 +0000 Bug 42406: (QA follow-up) fix QA tool failure Signed-off-by: David Cook Signed-off-by: Lucas Gass commit ea3b9d9c14ba055acd66ebc98352000264bc390e Author: David Cook Date: Fri Apr 24 04:14:44 2026 +0000 Bug 42406: (QA follow-up) fix little inconsistencies and errors Signed-off-by: David Cook Signed-off-by: Lucas Gass commit 3107cdc3683218ef56a1ac49c1614a09df044d14 Author: Nick Clemens Date: Fri Apr 17 12:33:34 2026 +0000 Bug 42406: Split delete_reports into delete_own_reports and delete_all_reports This patch splits delete_reports into delete_own_reports and delete_all_reports While delete_all_reports supercedes delete_own_reports it seems to make more sense than delete_own_reports and delete_others_reports. With delete_all_reports we can have a user who can delete all, without specific permission to delete their own The other way we would have delete others, and you could not delete your own, which seems more odd To test: 1 - Have 2 users - 1 with delete_reports, 1 without 2 - Apply patches, update database 3 - Confirm user with permission now has 'delete_all_reports' and other user still has no reports deletion permission 4 - Give the second user 'delete_own_reports' permission 5 - Create 4 reports as each user 6 - Confirm view/edit pages show the delete button for each user, and confirm they both work 7 - You shoudl have 2 reports left for each user 8 - Confirm each user can delete their own report from the saved SQL list: http://localhost:8081/cgi-bin/koha/reports/guided_reports.pl?op=list 9 - There should be 1 report from each 10 - Give the second user 'delete_all_reports' permission in one tab and reload the saved reports in another tab 11 - Select both reports in the second tab 12 - Remove 'delete_all_reports' from the second user, leaving 'delete_own_reports' in the first tab 13 - Click 'Delete selected' in the reports tab 14 - Confirm you get 'You do not have permission to continue' 15 - Hit back, deselect other users report, confirm you can delete your own with 'Delete selected' 16 - Sign off Signed-off-by: Judy Poyer Signed-off-by: David Cook Signed-off-by: Lucas Gass commit cdd7438ef671adf87ced33dec8d0b05aa3cde236 Author: Nick Clemens Date: Thu Apr 23 12:56:17 2026 +0000 Bug 38414: (QA follow-up) Adjust templates Now that we can explicitly list reports with any one of the permissions we need to adjust some displays to ensure we can get to the list view. Additionally I change 'Use saved' to 'View saved' since we may not be able to run. I also fix the action buttons to show 'View' instead of 'Run' if user cannot execute. I adjust reports home to check if the user can use report plugins before showing that link as well Signed-off-by: Nick Clemens Signed-off-by: Lucas Gass commit 56d32231b284a0603b18c240a31b81a97923b9b9 Author: David Cook Date: Thu Apr 23 02:20:36 2026 +0000 Bug 38414: (follow-up) comprehensively cover ops with required permissions Test plan: 0. Apply the patch and restart_all 1. Using a user with only "create_reports", "execute_reports", or "delete_reports", try to perform operations not covered by that permission. That is, if you have "create_reports", try to run, export, and delete reports. If you have "execute_reports", try to create, modify, and delete reports If you have "delete_reports", try to create, modify, and run reports 2. Note that a user with each of these permissions can list and view SQL. Signed-off-by: David Cook Signed-off-by: Nick Clemens Signed-off-by: Lucas Gass commit e472a7607f0672bb8cf497d656398abb34557078 Author: Jonathan Druart Date: Fri Oct 17 05:46:13 2025 +0200 Bug 38414: Fix permission controller-side This bug refactors the preview sql buttons to pass the permissions granted for each report to the javascript that clones and updates the modal. That allows us to check for the appropriate permissions when preparing the buttons for the modal and to remove any that are not allowed. This allows for different permissions per report for future devs (see bugs 42406, 16631) To test: 1. Create some reports 2. Create a user with only execute_reports permissions 3. Login as that user and go to Reports > use saved 4. In the actions column click the ^ and select 'Preview sql' 5. Notice delete button renders and works to delete 6. Apply patch and restart_all 7. Repeat step 4, now only the Run report button renders and works 8. Add permissions for create_reports 9. Repeat step for, now Edit, Duplicate, Schedule and run buttons render and work 10. Add permissions for delete_reports 11. Repeat step 4, confirm the delete button renders and works 12. Confirm the preview sql button works to preview different reports We also fix some error in the script, several wrong things there: * delete was not replaced with cud-delete * missing op for the creation of new reports * update and run requires two permssions Signed-off-by: David Cook Signed-off-by: Nick Clemens Signed-off-by: Scott Barter Signed-off-by: Lucas Gass commit 6a5b68552c3ab7c29a99fdaa8704781cfb93695f Author: Brendan Lawlor Date: Sat Apr 11 15:37:32 2026 +0000 Bug 30303: (follow-up) Copy values from merge results instead of CGI This patch makes it so the values copied come from the merge results which fixes the case of copying empty values or - accurately. To test: 1. Have a patron with some empty fields and some fields with '-' 2. Follow the original test plan and try to copy those fields 3. confirm empty fields and '-' now copy as expected Signed-off-by: Emily Lamancusa Signed-off-by: Lucas Gass commit 143b01dfa4771a63b9047f8dc294330064da7e2b Author: Brendan Lawlor Date: Fri Sep 12 12:20:59 2025 +0000 Bug 30303: Copy values to keeper record when merging patrons Signed-off-by: Lari Taskula Signed-off-by: Emily Lamancusa Signed-off-by: Lucas Gass commit a906f3820300722f5f92391deb6638fdafd37d09 Author: Brendan Lawlor Date: Thu Sep 11 15:58:35 2025 +0000 Bug 30303: Add ui for copying values when merging patrons When merging two patrons this patch adds a new checkbox to 'Copy values from the record to be deleted' To test: - Apply patches and restart_all 1. From select two patrons and click 'Merge selected patrons' 2. Select one of the records 3. Check the new box to 'Copy values from the record to be deleted' 4. A diff of the two patrons is shown with checkboxes for each field 5. Confirm checking and un-checking the boxes updates the diff as expected 6. Confirm changing the patron selection updates the diff as expected. 7. Confirm un-checking and re-checking the Copy values from the record to be deleted resets the check boxes for the fields to copy 8. Select some fields to copy values and click merge patrons 9. Confirm the values were copied as expected Sponsored-by: CLAMS Signed-off-by: Lari Taskula Signed-off-by: Emily Lamancusa Signed-off-by: Lucas Gass commit c484b66fc43a355b8b5a4ab9b9e6991c76527116 Author: Martin Renvoize Date: Mon Feb 2 13:58:31 2026 +0000 Bug 41751: Allow anonymous_refund permission to access cashups API The cash register transaction history page (pos/register.pl) allows access with either cashup OR anonymous_refund permission, but the API endpoints for fetching cashups only checked for cashup permission. This caused a 403 error when users with only anonymous_refund permission tried to view the transaction history page, as the cashups table failed to load. This patch updates the API permissions for: - GET /cash_registers/{id}/cashups - GET /cashups/{id} to accept either cashup or anonymous_refund permission, matching the page access logic. Test plan: 1. Create a staff user with only cash_management > anonymous_refund permission (not cashup) 2. Navigate to Point of Sale > Transaction history for any cash register 3. Verify the cashups table loads successfully (previously returned 403) 4. Run: prove t/db_dependent/api/v1/cashups.t Signed-off-by: Jackie Usher Sponsored-by: OpenFifth Signed-off-by: Andrew Fuerste Henry Signed-off-by: Lucas Gass commit 42b2758a76c7b98384d10381a8265435965ac47f Author: Tomás Cohen Arazi Date: Wed Apr 8 12:10:23 2026 -0300 Bug 42206: (follow-up) Rename to /closed_dates, improve efficiency Rename /libraries/{id}/holidays to /libraries/{id}/closed_dates for consistency with existing sub-resource naming (desks, cash_registers). Efficiency: instead of calling $calendar->is_holiday() per day (which re-checks all three data sources each time), access the pre-loaded data structures directly: weekly_closed_days array, day_month_closed_days hash, and _holidays hash. The logic properly handles exceptions (dates marked open despite a weekly/annual closure). Additional fixes: - Add 365-day max range guard - Add missing 401/403 responses in swagger spec - Add request_id_header parameter - Test exception handling (open on normally-closed day) - Remove fragile 'empty defaults' assertion Signed-off-by: Tomás Cohen Arazi Signed-off-by: Paul Derscheid Signed-off-by: David Nind Signed-off-by: Martin Renvoize Signed-off-by: Lucas Gass commit 2a3376b2437cb4b4daadba580b3798f26c9e570f Author: Paul Derscheid Date: Fri Mar 27 09:38:45 2026 +0100 Bug 42206: Add GET /libraries/{id}/holidays endpoint - Add list_holidays controller to Koha::REST::V1::Libraries - Returns closed days for a library within a configurable date range - Uses Koha::Calendar to determine holidays - Defaults to today + 3 months if no range specified - Add OpenAPI spec for the new endpoint - Register route in swagger.yaml - Add unit tests in t/db_dependent/api/v1/libraries.t To test: 1. Apply patch 2. prove t/db_dependent/api/v1/libraries.t 3. Verify all tests pass, including the new list_holidays subtest which covers: - Holidays returned within a specified date range - Empty list when no holidays exist in range - 400 response when 'to' date precedes 'from' date - 404 response for non-existent library - Default range (no params) returns successfully Sponsored-by: Büchereizentrale Schleswig-Holstein Signed-off-by: Tomás Cohen Arazi Signed-off-by: David Nind Signed-off-by: Martin Renvoize Signed-off-by: Lucas Gass commit cdf015b49790b847a2f27c9c9ff0d22bb19c32e1 Author: Martin Renvoize Date: Thu Apr 16 09:07:13 2026 +0100 Bug 41902: Display additional biblio info on duplicate check When a duplicate record is suspected during cataloguing, query the AdditionalContents record_display category for the StaffDuplicateCheckPage location. If configured, process the content as a Template Toolkit template with the duplicate biblio object and display the rendered result in the duplicate warning. The TT context variable is 'biblio' (a Koha::Biblio object), so administrators can use expressions like: [% biblio.author %] ([% biblio.copyrightdate %]) Content is scrubbed using the record_display scrubber for XSS safety. Signed-off-by: Jeanne Mauriello Signed-off-by: Lucas Gass commit 470ac28564b85eb9419f6fbd89c9c6297a5fe88e Author: Martin Renvoize Date: Thu Apr 16 09:07:00 2026 +0100 Bug 41902: Add StaffDuplicateCheckPage location to record_display Add a new 'StaffDuplicateCheckPage' location to the record_display category in the Additional Contents admin UI (Tools > Additional Contents). This allows administrators to configure custom bibliographic information to display when a duplicate record is suspected during cataloguing. This uses the existing AdditionalContents infrastructure (bug 39860) rather than a system preference, providing per-branch and multilingual support. Signed-off-by: Jeanne Mauriello Signed-off-by: Lucas Gass commit d0955994472b7d7ce0585fd9320668103396cd5f Author: Martin Renvoize Date: Thu Mar 26 16:13:42 2026 +0100 Bug 42030: (follow-up) Don't diff undefined fields This patch updates the diff for ADD/CREATE and DELETE logactions to exclude undefined fields from the diff. Signed-off-by: Lisette Scheer Signed-off-by: Lucas Gass commit 1f4320be0732bc22cdadd5e074321a156396c64c Author: Martin Renvoize Date: Thu Mar 26 15:57:45 2026 +0100 Bug 42030: (follow-up) Fix POD for Koha::Suggestion Signed-off-by: Lisette Scheer Signed-off-by: Lucas Gass commit efe6f26e4154e9f2d81183f6084bb3759a45cbd4 Author: Martin Renvoize Date: Mon Mar 9 19:59:08 2026 +0000 Bug 42030: Populate diff column for suggestion CREATE/MODIFY/DELETE logs Update suggestion action logging to populate the diff column using the correct before/after semantics: - CREATE: empty before-state ({}), new suggestion data as after-state - MODIFY: pre-change unblessed data as before, post-store object as after - DELETE: log before the SQL DELETE so the object can still be fetched; pass the suggestion as both $infos (info column) and $original (diff before-state), logaction sets after-state to {} Also add tests verifying the diff column is populated for all three actions. Signed-off-by: David Nind Signed-off-by: Lisette Scheer Signed-off-by: Lucas Gass commit 7066ac0a935b3c959558232cf41bcf22450c6ec1 Author: Lucas Gass Date: Fri May 1 16:06:25 2026 +0000 Bug 23909: DBRev 25.12.00.057 Signed-off-by: Lucas Gass commit 744ac0656932d569c9f3c411758e21c0fabdc653 Author: Martin Renvoize Date: Tue Apr 21 16:53:56 2026 +0100 Bug 23909: Preserve SCO behavior on upgrade (value 1 -> 2) The atomicupdate converts AllowItemsOnHoldCheckoutSCO and AllowItemsOnHoldCheckoutSIP from YesNo to Choice (0|1|2), where value 1 now means "Allow pending holds only" and value 2 means "Allow pending and waiting holds". For AllowItemsOnHoldCheckoutSCO, pre-upgrade value 1 meant "allow all on-hold checkouts (pending AND waiting)" at SCO. Post-upgrade, sco-main.pl reads value 1 as "pending only", silently downgrading sites that previously allowed waiting-hold checkouts. Remap SCO value 1 -> 2 at upgrade time to preserve prior behavior. For AllowItemsOnHoldCheckoutSIP, the SIP checkout code was already gated on >= 1 for pending and >= 2 for waiting, so value 1 meant "pending only" in practice even under the old YesNo type. No value migration is needed -- leaving it at 1 preserves current behavior. The value remap lives inside the branch that only runs when the type is not already Choice, so re-running the atomicupdate is safe and will not double-remap. Test plan: 1) On a pre-upgrade DB, set both prefs to YesNo/1: UPDATE systempreferences SET type='YesNo', options=NULL, value='1' WHERE variable IN ('AllowItemsOnHoldCheckoutSCO', 'AllowItemsOnHoldCheckoutSIP'); 2) Run installer/data/mysql/updatedatabase.pl. 3) Verify: SELECT variable, type, options, value FROM systempreferences WHERE variable IN ('AllowItemsOnHoldCheckoutSCO', 'AllowItemsOnHoldCheckoutSIP'); SCO -> type=Choice, options=0|1|2, value=2. SIP -> type=Choice, options=0|1|2, value=1. 4) Re-run updatedatabase.pl; confirm both prefs report "already migrated, skipping" and values are unchanged. 5) Spot-check other starting values: SCO=0 stays 0, SCO=2 stays 2, SIP=0 stays 0. Signed-off-by: Kristi Krueger Signed-off-by: Andrew Fuerste Henry Signed-off-by: Lucas Gass commit f43201075a0999930ebd1c138725d67b3b3deab7 Author: Martin Renvoize Date: Mon Apr 20 17:53:06 2026 +0100 Bug 23909: Add AllowHoldCheckoutOverride for staff circulation Introduce a new AllowHoldCheckoutOverride system preference (default 1 to preserve existing behaviour) that lets libraries prevent staff from overriding held-item blockers at the circulation desk. When AllowHoldCheckoutOverride is set to 0, CanBookBeIssued still returns the hold-related keys (RESERVED, RESERVE_WAITING, TRANSFERRED and PROCESSING) in the confirmation hash, but circ/circulation.pl promotes them to the impossible hash before rendering the template. This means no override button appears, and checkout of the held item is refused outright with a clear message explaining which patron the hold is for. Matching entries have been added to the IMPOSSIBLE block of the circulation template so the blocked-checkout message is meaningful. This sits alongside the existing Allow*Override family (AllowTooManyOverride, AllowNotForLoanOverride, AllowHoldPolicyOverride and AllowRenewalOnHoldOverride) and uses the same staff-only semantics. Test plan: 1. Set AllowHoldCheckoutOverride=1 (default). Place a waiting hold for patron A on an item. At /cgi-bin/koha/circ/circulation.pl attempt to check the item out to patron B: a confirmation dialog appears with "Check out anyway" override buttons. 2. Set AllowHoldCheckoutOverride=0. Repeat: checkout is refused with a message stating the item has been waiting for patron A. No override button is offered. 3. Repeat with pending holds (reserves.found IS NULL), items in transit and items being processed: each is refused when the preference is 0 and offered as an overridable confirmation when the preference is 1. Signed-off-by: Kristi Krueger Signed-off-by: Andrew Fuerste Henry Signed-off-by: Lucas Gass commit b80dafc210fa4798d81adbb751ae2b8eeddecfa4 Author: Martin Renvoize Date: Mon Apr 20 17:50:43 2026 +0100 Bug 23909: Apply hold policy to OPAC REST self-checkout The OPAC REST self-checkout path (guarded by OpacTrustedCheckout) is functionally SCO-over-API, so it should honour the same hold policy as the SCO web interface. Previously _check_availability unconditionally promoted RESERVED, RESERVE_WAITING, TRANSFERRED and PROCESSING from the confirmation hash to the blockers hash for any public caller. This meant the OPAC REST endpoint always refused held items regardless of the librarian's AllowItemsOnHoldCheckoutSCO configuration. With this patch the promotion is gated on AllowItemsOnHoldCheckoutSCO: * 0 - Promote all four hold confirmations to blockers (previous behaviour for pending holds) * 1 - Promote only waiting/transit/processing; silently allow checkout of items with a pending hold * 2 - Promote none; silently allow checkout of items with any hold Remaining hold confirmations are stripped from the response so the caller does not have to resolve a confirmation token for a policy the admin has already granted. Test plan: 1. prove t/db_dependent/api/v1/checkouts.t 2. Enable OpacTrustedCheckout. POST an item with a pending hold for another patron to /api/v1/public/patrons/X/checkouts: - AllowItemsOnHoldCheckoutSCO=0: 403 Checkout not authorized - AllowItemsOnHoldCheckoutSCO=1: 201 Created 3. Place a waiting hold for another patron on an item. Repeat: - AllowItemsOnHoldCheckoutSCO=1: 403 - AllowItemsOnHoldCheckoutSCO=2: 201 Signed-off-by: Kristi Krueger Signed-off-by: Andrew Fuerste Henry Signed-off-by: Lucas Gass commit cd3b0770c50905e36bef55badb2b136e08f40aad Author: Martin Renvoize Date: Mon Apr 20 17:48:56 2026 +0100 Bug 23909: Extend SIP to distinguish pending vs waiting holds This patch extends the SIP checkout handler to honour the new three-valued AllowItemsOnHoldCheckoutSIP preference: * 0 - Don't allow checkout of any held item * 1 - Allow checkout when the hold is still pending (not attached) * 2 - Also allow checkout when the hold is waiting, in transit or being processed for another patron Previously the preference was a simple YesNo toggle that, when enabled, allowed the checkout regardless of whether the hold was pending or already attached to an item on the hold shelf. Libraries could not block the more disruptive case (checking out a waiting hold destined for another patron) without also blocking pending holds. The existing post-confirmation filter is reworked into a single branch per confirmation type that consults the preference value. Test plan: 1. prove t/db_dependent/SIP/Transaction.t 2. With AllowItemsOnHoldCheckoutSIP=0, place a pending hold for patron A on an item, attempt SIP checkout to patron B: refused. 3. Repeat with the hold marked waiting: refused. 4. Set AllowItemsOnHoldCheckoutSIP=1, pending hold: allowed; waiting hold: refused. 5. Set AllowItemsOnHoldCheckoutSIP=2, waiting hold: allowed. Signed-off-by: Kristi Krueger Signed-off-by: Andrew Fuerste Henry Signed-off-by: Lucas Gass commit b336cf88d5a5eb2ae071a20844006f035d20a10c Author: Martin Renvoize Date: Mon Apr 20 17:45:05 2026 +0100 Bug 23909: Block waiting holds at SCO checkout AllowItemsOnHoldCheckoutSCO was previously passed through to CanBookBeIssued as the ignore_reserves flag, which skipped the entire holds branch in C4::Circulation and silently allowed checkout of items already waiting on the hold shelf (reserves.found = 'W') or in transit/processing for another patron's hold. With the pref now a 3-value Choice: 0 = don't allow checkout of any held items 1 = allow checkout of items with a pending hold only 2 = allow checkout of items with any hold state sco-main.pl now always calls CanBookBeIssued with ignore_reserves=0 and filters the needsconfirmation keys according to the pref value, mirroring the pattern SIP already uses. RESERVE_WAITING, TRANSFERRED and PROCESSING are added to the error-matching list and to the SCO template so patrons see a specific "on the hold shelf" / "in transit" / "being processed" message instead of falling through to the generic "please see a member of the library staff" line. Test plan: - Enable WebBasedSelfCheck; log in to /cgi-bin/koha/sco/sco-main.pl. - Create an item with a pending hold for another patron (reserves.found IS NULL). - With AllowItemsOnHoldCheckoutSCO = 0, scan the barcode: checkout is refused with "This item is on hold for another patron". - Switch to 1 ("Allow pending holds only"): checkout succeeds. - Set the hold to waiting (reserves.found = 'W') and try again with the pref still at 1: checkout is refused with "This item is waiting on the hold shelf for another patron". - Switch to 2 ("Allow pending and waiting holds"): checkout succeeds. - Repeat with found = 'T' and 'P' at pref = 1 -> refused with the appropriate transit/processing message; at pref = 2 -> allowed. Signed-off-by: Kristi Krueger Signed-off-by: Andrew Fuerste Henry Signed-off-by: Lucas Gass commit 83155b8ffd6eea8cc6191b81de61a10ccb441908 Author: Martin Renvoize Date: Mon Apr 20 17:42:43 2026 +0100 Bug 23909: Add syspref values and atomicupdate AllowItemsOnHoldCheckoutSCO and AllowItemsOnHoldCheckoutSIP become Choice (0|1|2) so libraries can distinguish pending from waiting holds: 0 = Don't allow (unchanged) 1 = Allow pending holds only (new semantics; previously "Allow") 2 = Allow pending and waiting holds (preserves old permissive behaviour) A new AllowHoldCheckoutOverride YesNo preference (default 1) follows the existing Allow*Override family and lets libraries block the "check out anyway" override in the staff interface when an item is on hold for another patron. Note: installations that previously had either Allow*CheckoutSCO/SIP set to 1 will migrate to "Allow pending only". Libraries that relied on the previous behaviour (silently allowing checkout of items already waiting on the hold shelf) need to manually switch to 2. Test plan: - Apply the patch and restart Koha. - Run the web installer / updatedatabase.pl. - Confirm no errors and the log mentions the three sysprefs. - Run it a second time; confirm the "already migrated" / "already exists" skip messages appear. - In Administration > System preferences, search for AllowItemsOnHoldCheckoutSCO and AllowItemsOnHoldCheckoutSIP and confirm the dropdown now has three options. - Confirm AllowHoldCheckoutOverride appears in the same section with the Allow/Don't allow choices and defaults to Allow. Signed-off-by: Kristi Krueger Signed-off-by: Andrew Fuerste Henry Signed-off-by: Lucas Gass commit dc866af68b8e70486b672a189f9efb4a0c0ee50a Author: Lucas Gass Date: Fri May 1 15:26:13 2026 +0000 Bug 26355: (RM follow-up) Fix script attribute order Signed-off-by: Lucas Gass commit dc2c4014d0335b445ac49889d5d764d091ea1e63 Author: Lucas Gass Date: Fri May 1 15:01:13 2026 +0000 Bug 26355: (RM follow-up) Fix atomic update Signed-off-by: Lucas Gass commit 7c7258b9da4469bce0be8b4bcc99de14b243aebc Author: Lucas Gass Date: Fri May 1 14:48:35 2026 +0000 Bug 26355: (RM follow-up) force DBIC Signed-off-by: Lucas Gass commit 3e7626d4e089d9b5be5699ba9818c8b18812f486 Author: Lucas Gass Date: Fri May 1 14:40:31 2026 +0000 Bug 26355: (RM follow-up) Adjust number of tests Signed-off-by: Lucas Gass commit 3fa81b71b474cd708aa492f6cc147991dcfe5850 Author: Lucas Gass Date: Fri May 1 14:37:17 2026 +0000 Bug 26355: DBRev 25.12.00.056 Signed-off-by: Lucas Gass commit 9487953932f040ade9ec1dcb9e584f6179b3550e Author: Martin Renvoize Date: Tue Apr 21 16:12:10 2026 +0100 Bug 26355: (QA follow-up) Strip self_renewal_available in EmailPatronRegistrations tests to_api now returns the derived self_renewal_available field, so reusing a to_api'd patron as the POST body for /api/v1/patrons fails with 500 (the value has no matching DB column). Adjacent subtests already delete this read-only field before POSTing; do the same in the EmailPatronRegistrations subtest. Test plan: 1. prove t/db_dependent/api/v1/patrons.t — all tests pass. Signed-off-by: Martin Renvoize Signed-off-by: Lucas Gass commit e625b68bc11c08cfb7970e3f0726fb0c0fd1e326 Author: Martin Renvoize Date: Tue Apr 21 15:55:45 2026 +0100 Bug 26355: (QA follow-up) Bump test plan for new subtest The "identify_updated_extended_attributes" subtest was added without bumping the top-level plan, so prove reported a "Bad plan" failure even though every subtest passed. Test plan: 1. prove t/db_dependent/Koha/Patron.t — all tests pass, no plan mismatch. Signed-off-by: Martin Renvoize Signed-off-by: Lucas Gass commit 0cdf8a74c8388bbc1eac9eea13b65682cc5839b5 Author: Martin Renvoize Date: Tue Apr 21 15:11:42 2026 +0100 Bug 26355: (QA follow-up) Add CSP nonce to inline module script The forbidden_patterns QA check requires