commit c748b51c09708d94df3ba3274304bb5948352757 Author: Olivia Reynolds Date: Tue Jul 21 12:13:00 2026 +0100 Bug 43141: Add cronjob to cancel expired disclaimer prompt requests This cronjob cancels requests that: 1. Have associated type disclaimer prompts. 2. All of which have expired without reply. Test plan (following on from previous patches): 1. Delete all existing ILL requests. 2. Create an ILL request "A" without sending any prompts. 3. Create an ILL request "B", send a prompt and fill it in. 5. Create an ILL request "C", send a prompt and don't fill it in. 6. In the database console, execute the following SQL to force all prompts to expire: UPDATE ill_type_disclaimer_prompts SET valid_until='2000-01-01 11:11:11'; 7. Run: perl misc/cronjobs/cancel_expired_ill_disclaimers.pl -v -t 8. Verify it identifies one request "C" to cancel. 9. Run: perl misc/cronjobs/cancel_expired_ill_disclaimers.pl -v -c 10. Verify request "C" has been cancelled. Sponsored-by: NHS England commit 04fd7ec21a70bf4866eb8f9576c4f58ad7813c69 Author: Olivia Reynolds Date: Tue Jul 21 12:53:49 2026 +0000 Bug 43141: Add ILL table column for type disclaimer prompt replies This patch adds a (default-hidden) column to the ILL requests table indicating whether each request has been replied to via a type disclaimer prompt. Test plan (following on from previous patch): 1. Enable the "Type disclaimer prompt replied" column in the ILL requests table. 2. Verify the value in this column is "1" for the ILL request used in the previous test plan, which has been replied to via a prompt. 3. Create a new ILL request through the staff interface. 4. Verify the value in the "Type disclaimer prompt replied" column is 0 for this new request. 5. Send a type disclaimer prompt for this new request. 6. Verify the value in the "Type disclaimer prompt replied" column is still 0 for this new request. Sponsored-by: NHS England commit 8deb6b1fa9cd945b0486479903083e8361441187 Author: Olivia Reynolds Date: Mon Jul 20 10:12:10 2026 +0000 Bug 43141: Allow sending type disclaimer prompts This patch allows sending a notice to the patron associated with an ILL request, giving them a unique link to fill in a type disclaimer. Only one link is created for each request and patron, but the notice may be resent. A new messaging preference is used for this notice type. Test plan: 1. Reset KTD to pick up the database updates and new sample notice. 2. Enable the ILLModule syspref. 3. Set the ILLModuleDisclaimerByType syspref to the following: all: text: Test text av_category_code: YES_NO interface: opac 4. Create an ILL request through the staff UI, assigned to a patron. 5. View the new ILL request. Verify there is a "Prompt type disclaimer to user" option in the "Send notice to patron" dropdown. 6. Click "Prompt type disclaimer to user" and verify sending the notice is successful. 7. Verify the patron has received a notice containing a link to fill in the type disclaimer. 8. Visit this unique link in a logged-out session. Verify the type disclaimer form is displayed. 9. Fill out and submit the type disclaimer form. Verify there is a confirmation screen. 10. Visit the unique link again in a logged-out session. Verify the confirmation screen is displayed instead of the form. 11. Return to the ILL request view in the staff interface. Verify the type disclaimer information has been filled out correctly. 12. Prove t/db_dependent/Koha/ILL/Requests.t Sponsored-by: NHS England commit 50c2b03c0fe9d43fe77d9f705658ba5a240489dd Author: Olivia Reynolds Date: Mon Jul 20 11:20:51 2026 +0000 Bug 43141: Add public page for filling out type disclaimer prompts To allow patrons to fill out type disclaimer prompts without authenticating, a new unauthenticating controller is introduced to look up the type disclaimer prompt based on a UUID. This functionality is included in a later test plan. Sponsored-by: NHS England commit 988470c26ff1c55fb0b805b8c5dc22dd6c9ca779 Author: Olivia Reynolds Date: Mon Jul 20 09:29:01 2026 +0000 Bug 43141: DBIC updates (DO NOT PUSH) Sponsored-by: NHS England commit 1f89e835d2bd8c234cc40277ffe50c09f5cefe85 Author: Olivia Reynolds Date: Mon Jul 20 09:28:09 2026 +0000 Bug 43141: DB updates Adds a table for storing prompts to patrons for filling out type disclaimers. As these disclaimers will be sent via notices, a new messaging preference is also introduced and enabled by default. Sponsored-by: NHS England commit 81069fd8ddcc5393a57c4209eea4b5572c59aeee Author: Olivia Reynolds Date: Wed Jul 22 17:26:55 2026 +0100 Bug 43141: Add type disclaimer modal to request views This patch includes the new type_disclaimer_text field in the ILL request views. As there is now more information associated with the disclaimer, it is all displayed together in a modal. Furthermore, on the staff side it is possible to clear and fill out the disclaimer on behalf of a patron. Test plan: 1. Enable the ILLModule syspref. 2. Set the ILLModuleDisclaimerByType syspref to the following: all: text: Test text av_category_code: YES_NO interface: opac 3. Create an ILL request through the staff UI. Verify you are not prompted for a type disclaimer during creation. 4. View the new ILL request in the staff UI. Verify there is a "Type disclaimer" row showing "Fill in disclaimer for patron". 5. Click "Fill in disclaimer for patron", fill in the type declaration, and verify you return to viewing the ILL request. 6. View the same ILL request in the OPAC, and verify there is a type disclaimer row with a link to "View details". 7. Click "View details", and verify appropriate type disclaimer information is displayed in a modal. 8. In the staff view of the ILL request, verify the "Type disclaimer" row also now has a link to "View details". 9. Click "View details", and verify appropriate type disclaimer information is displayed in a modal. 10. In the modal, click "Clear disclaimer information". 11. Verify the disclaimer information is deleted, and the row once again shows "Fill in disclaimer for patron". Sponsored-by: NHS England commit beecd76bf31db7d6bcdad492b91bcbd8306f3ac5 Author: Olivia Reynolds Date: Fri Jul 17 14:29:25 2026 +0000 Bug 43141: Allow bypassing type disclaimer per-interface This patch adds an "interface" option to "ILLModuleDisclaimerByType", similar to "bypass". It can take "both", "none", "staff", or "opac" to control which interfaces show the type disclaimer screen. Test plan: 1. Prove t/db_dependent/Koha/ILL/Request/Workflow/TypeDisclaimer.t Sponsored-by: NHS England commit 1875e71ba35304bce245453e96533b10fa564d84 Author: Olivia Reynolds Date: Fri Jul 17 14:21:25 2026 +0000 Bug 43141: Save text of type disclaimer when submitted This patch saves the displayed text of a completed type disclaimer, so that it can be referenced later. This functionality is tested further in a later patch. Test plan: 1. Prove t/db_dependent/Koha/ILL/Request.t Sponsored-by: NHS England commit d6d95f7d7cad03367e199a062baada296fc30343 Author: Matt Blenkinsop Date: Thu Jun 11 16:33:57 2026 +0100 Bug 42827: Restore values to the Items expected column in parcels.pl Test plan: 1. Create an order with multiple items and receive the order in full 2. From the vendor record, click Receive shipments 3. You should be in parcels.pl and the table should show your shipments 4. The Items expected column will show 0 even though you ordered multiple items 5. Apply patch and restart_all 6. Refresh the page, the column should now show a value Signed-off-by: Andrew Fuerste Henry Signed-off-by: Katrin Fischer Signed-off-by: Pedro Amorim commit 57991590343dbdb955921875460a9cc6c715e885 Author: Martin Renvoize Date: Wed Jul 15 13:46:19 2026 +0100 Bug 43082: Fix flaky object filter test in action_logs.t The default `_match=contains` matching builds a LIKE query even on integer columns, so a plain `?object=42` also matches object values like 420 or 142. The test's dynamically-assigned patron borrowernumber (auto_increment, never reset by transaction rollback) could drift into a value containing "42" as a substring, intermittently inflating the match count in CI. Add `_match=exact` since the test's intent is an exact object id match. Signed-off-by: David Nind Signed-off-by: Katrin Fischer Signed-off-by: Pedro Amorim commit bbc0124ede0c66135e4fe7faf785bc35f5700743 Author: David Cook Date: Fri Jul 24 06:04:19 2026 +0000 Bug 43159: Restore localisation of PERL5LIB and KOHA_HOME This change localises PERL5LIB and KOHA_HOME, which means that any changes made to PERL5LIB or KOHA_HOME within _do_instance() in koha-plack are confined to the specific instance being worked on in that function. See Bugzilla for steps on how to reproduce the problem. Test plan: - Apply the patch - sudo vi debian/scripts/koha-plack Within _do_instance after PERL5LIB is set, add the following: echo $PERL5LIB - sudo koha-create --request-db test1 - sudo ./debian/scripts/koha-plack --restart test1 test1 test1 test1 - Note that now PERL5LIB stays constant: /usr/share/koha/lib:/usr/share/koha/installer:/usr/share/koha/lib/installer - git restore debian/scripts/koha-plack Signed-off-by: David Nind Signed-off-by: Laura_Escamilla Signed-off-by: Pedro Amorim commit 6d30ead414e24c0d05d8750d0ec9ca292859ff37 Author: Kyle M Hall Date: Thu Jul 16 14:23:44 2026 -0400 Bug 43103: Switch OR chains to INs for batch item modification and patron deletion Three searches pass large ID lists: * Koha::BackgroundJob::BatchUpdateItem passes every itemnumber of the batch, and batch item modifications can cover tens of thousands of items * Koha::Items::batch_update does the same with the modified itemnumbers when collecting biblionumbers for reindexing * Koha::Patrons::filter_by_safe_to_delete does it with the borrowernumbers found safe to delete ( unlikely to approach the same numbers as items/bibs, but still good to fix ) This patch switches all three to IN. The lists are still passed as values, but the query stays a flat IN list the optimizer handles well instead of a chain of ORs that can't be optimized. Test Plan: 1) Apply this patch 2) prove t/db_dependent/Koha/Items/BatchUpdate.t 3) prove t/db_dependent/Koha/Patrons.t 4) Note all tests pass! Signed-off-by: David Nind Signed-off-by: Emily Lamancusa Signed-off-by: Pedro Amorim commit 53524b4824197dd0c972124e4a161243db7c4e6a Author: Paul Derscheid Date: Thu Jul 30 08:53:01 2026 +0000 Bug 43174: (QA follow-up) Restore user choices on redraw independent of search state To test: - cypress run --spec t/cypress/integration/KohaTable/PatronSearch_spec.ts - cypress run --spec t/cypress/integration/KohaTable/KohaTable_spec.ts Signed-off-by: Paul Derscheid Signed-off-by: Pedro Amorim commit 684c6cb96ad098f95bc09d55f7785419f42b09ac Author: Jonathan Druart Date: Mon Jul 27 10:18:32 2026 +0200 Bug 43174: Restore defer loading for patron searches On the hold request and the patron search pages, the search is not deferred when a state exists. Coming from commit 2a344136014250b205ff74d8ea2dcb679ebb7105 Bug 41604: Save user decision per column when visibility changes - if (default_save_state_search) { + if (default_save_state || default_save_state_search) { $("#" + settings.nTable.id).data("loaded_from_state", true); - } else { + } + if (!default_save_state_search) { delete state.search; state.columns.forEach(c => delete c.search); I think this was a leftover from another try to fix bug 41604. I don't see how it could help in the context of saving the user decision. In the different patches from bug 41604 we don't test loaded_from_state. Test plan: Go to /cgi-bin/koha/members/members-home.pl => The table is not displayed Search for patrons => The table is displayed Reload the page => Without this patch the table is displayed => With this patch the table is not displayed If you have "Save search state on page change" enabled for this table, the table should be displayed if a previous search was made. Signed-off-by: David Nind Signed-off-by: Paul Derscheid Signed-off-by: Pedro Amorim commit 96f1906fcc439e60f6231b4d6db66a6a3dd7e3b6 Author: Jonathan Druart Date: Mon Jul 27 10:17:38 2026 +0200 Bug 43174: Add a Cypress test Signed-off-by: David Nind Signed-off-by: Paul Derscheid Signed-off-by: Pedro Amorim commit 21e34086caf5e3e7ad257a30b0cd18f18355bbcd Author: Jonathan Druart Date: Fri Jul 31 10:45:38 2026 +0200 Bug 43053: Simplify code, only 1 return statement Signed-off-by: Jonathan Druart Signed-off-by: Pedro Amorim commit 2ad5ba09cb689ac3b1627b0b1c8f3a26cd42c045 Author: Matt Blenkinsop Date: Fri Jul 10 16:50:18 2026 +0100 Bug 43053: Fix display of vendor payment methods Test plan: 1) Add multiple vendor payment method authorised values 2) Create a vendor and add multiple payment methods 3) Click to edit, the payment method will be displaying one option of a "|" concatenated string 4) Apply patch and run yarn js:build 5) Hard refresh the browser, the payment methods should now display correctly 6) Edit the payment methods and save, the saved changes should be correctly displayed on the vendor details page 7) Add a new vendor with multiple payment methods, it should save correctly Signed-off-by: David Nind Signed-off-by: Jonathan Druart Signed-off-by: Pedro Amorim commit 29e34ba1c2a30d21df64393918bb14ac530a052e Author: Jonathan Druart Date: Fri Jul 31 10:45:11 2026 +0200 Bug 43053: Add a Cypress test Signed-off-by: Jonathan Druart Signed-off-by: Pedro Amorim commit 4b7da59db5f1941782f612d762421514e34ce40f Author: David Nind Date: Sat Jul 25 10:11:21 2026 +0000 Bug 43173: Update the terminology for the log viewer - "cardnumber" to "card number" Update the log viewer to change "Modify cardnumber" to "Modify card number", as per the terminology guidelines https://wiki.koha-community.org/wiki/Terminology#C Test plan: 1. Edit a patron and change their card number under the library management section. 2. Go to Tools > Additional tools > Log viewer. 3. Note under the actions section that there is an action: Modify cardnumber 4. Click "Submit" to see the log viewer results. 5. Note that there is an entry for the patron card number change with the action: "Modify cardnumber". 6. Apply the patch. 7. Refresh the log viewer page. 8. Note that "Modify cardnumber" is now "Modify card number" in both places. Signed-off-by: David Nind Signed-off-by: Gretchen Signed-off-by: Emily Lamancusa Signed-off-by: Pedro Amorim commit 3573aa2492236c716fe13757679cd17a4c4a503b Author: Jonathan Druart Date: Fri Jul 31 10:37:29 2026 +0200 Bug 43168: Add a test Signed-off-by: Jonathan Druart Signed-off-by: Pedro Amorim commit c13096326fd74a28adedad07a7a603abee60ee29 Author: Nick Clemens Date: Thu Jul 23 19:16:28 2026 +0000 Bug 43168: Check if there are errors, not if variable defined $errors is an array ref returned from a blank array variable if( $errors ){ #this will be true if( scalar @$errors ){ #only true when array has members To test: 1 - tail -f /var/log/koha/kohadev/intranet-error.log 2 - Stage a marc file 3 - Note the error: ==> /var/log/koha/kohadev/intranet-error.log <== [2026/07/23 19:11:37] [WARN] The following error(s) occurred during ISO2709 record import: ERROR: Koha::ImportBatch::try {...} /kohadevbox/koha/Koha/ImportBatch.pm (105) 4 - Apply patch 5 - Restart all 6 - Stage a file 7 - No error! Signed-off-by: Jonathan Druart Signed-off-by: Pedro Amorim commit d5715ed7531b544ca8539fa738d6fc5c64d27401 Author: Pedro Amorim Date: Fri Jul 31 09:58:33 2026 +0000 Bug 42988: DBRev 26.06.00.010 Signed-off-by: Pedro Amorim commit d9dda87fff74f8c0155463aa20f58122b1cd5bb9 Author: Pedro Amorim Date: Fri Jul 31 09:52:24 2026 +0000 Bug 42988: dbic Signed-off-by: Pedro Amorim commit 73380b410c3dd9f85aea419e959c673268e31a31 Author: Kyle M Hall Date: Thu Jul 2 13:00:39 2026 -0400 Bug 42988: Default holds_get_captured to yes for new SIP accounts The account editor preselects "No" for "Holds get captured" when creating a new SIP account, but the SIP checkin default is to capture holds. This patch makes the editor default the option to "Yes" so a new account behaves the same as one that never set the option. Test Plan: 1) Apply this patch 2) yarn js:build 3) Restart all the things! 4) Create a new account in the SIP2 administration module 5) Note "Holds get captured" defaults to "Yes"! Signed-off-by: David Nind Signed-off-by: Emily Lamancusa Signed-off-by: Pedro Amorim commit 48c54b04b628e472ad61d80c8630044496efc725 Author: Kyle M Hall Date: Thu Jul 2 13:00:39 2026 -0400 Bug 42988: Update database, set holds_get_captured default to 1 The migration from SIPconfig.xml to the database ( bug 37893 ) leaves holds_get_captured NULL for any account that didn't define it in the config file. At SIP checkin an unset holds_get_captured defaults to on, but the REST API returns NULL boolean columns as false, so the SIP account editor shows "No" selected. Saving the account without touching the option then stores 0 and holds are no longer captured at SIP checkin. This patch changes the column default to 1 and sets existing NULL values to 1 so the stored value matches the actual checkin behavior. Test Plan: 1) If you have no SIP accounts, create one 2) Simulate an account migrated from a SIPconfig.xml that doesn't define holds_get_captured: UPDATE sip_accounts SET holds_get_captured = NULL; 3) Edit the account, note "Holds get captured" shows "No" even though holds *are* captured at SIP checkin while the value is unset 4) Apply this patch 5) Run updatedatabase.pl 6) SELECT login_id, holds_get_captured FROM sip_accounts; 7) Note holds_get_captured is now 1! 8) Edit the account again, note "Yes" is now selected for "Holds get captured"! Signed-off-by: David Nind Signed-off-by: Emily Lamancusa Signed-off-by: Pedro Amorim commit ad1d40f0b724be4a916f9ba703c5052cbb14b210 Author: Laura Escamilla Date: Mon Jul 27 18:31:39 2026 +0000 Bug 43134: Add missing colons to OPAC advanced search section headings To test : 1. Apply the patch 2. Go to the OPAC advanced search page http://kohadev.mydnsname.org:8080/cgi-bin/koha/opac-search.pl 3. Click More options 4. Notice that all section titles now use colons - "Search for:" = colon - "Limit to any of the following:" = colon - "Location and availability:" = colon - "Sort by:" = colon - "Publication range" = colon - "Language" = colon - "Audience" = colon - "Content" = colon - "Format" = colon - "Additional content types..." = colon 5. Sign off and have an incredible day! :) Signed-off-by: David Nind Signed-off-by: Emily Lamancusa Signed-off-by: Pedro Amorim commit 32e16ca89a1f5cd79f767d9d0c0ebd83ff97195f Author: Owen Leonard Date: Wed Jul 29 07:41:31 2026 -0400 Bug 43192: Cast escape_str(obj) arg to string to use escapeHtml func The escape_str function in the OPAC requires the argument to be a string, and will throw a fatal error if passed a Number. This is because String.prototype.escapeHtml is created by Koha and is not a JS standard. This patch converts the object to a string before applying escapeHtml. To test, apply the patch and log in to the OPAC. - If necessary, add a list. - Search for records in the catalog and add one or more of the results to your new list. - Go to Lists -> Your lists. - In the list of your lists, click the "Delete" link for the list you added records to. - You should see a confirmation modal, "Are you sure you want to delete this list?" showing the name of the list and the number of items on it. - Clicking "Yes, delete" should delete the list. Sponsored-by: Athens County Public Libraries Signed-off-by: Roman Dolny Signed-off-by: David Cook Signed-off-by: Pedro Amorim commit 0f894cb6b1ff6012a6f40bb8feefb6f38c6643ee Author: Martin Renvoize Date: Wed Apr 15 17:13:00 2026 +0100 Bug 41410: DBIx::Class schema update Regenerated via `dbic --force` after adding the local_holdgroup_match column to hold_fill_targets and tmp_holdsqueue in the previous patch. Test plan: 1. Inspect Koha/Schema/Result/HoldFillTarget.pm and TmpHoldsqueue.pm and confirm the new local_holdgroup_match accessor is defined above the `DO NOT MODIFY` marker. 2. Confirm custom content below the marker (koha_object_class, +item_level_request is_boolean) is preserved. Sponsored-by: Main Library Alliance Signed-off-by: Trevor Diamond Signed-off-by: Andrew Fuerste Henry Signed-off-by: Pedro Amorim commit cf852cdd10b594fbbc391f46625a738c2036cd89 Author: Pedro Amorim Date: Wed Jul 29 15:49:18 2026 +0000 Bug 41410: dbic Signed-off-by: Pedro Amorim commit 471571a17d33f177c489a8e7686ae78ab9ac19f9 Author: Pedro Amorim Date: Wed Jul 29 15:43:52 2026 +0000 Bug 41410: DBRev 26.06.009 Signed-off-by: Pedro Amorim commit 9b27b4d03d28f13c965a31105229fa2d1db20b4d Author: Martin Renvoize Date: Thu Jun 11 14:45:17 2026 +0100 Bug 41410: (follow-up) Fix local_holdgroup_match set incorrectly by LocalHoldsPriority local_holdgroup_match was being hardcoded to 1 in the two LocalHoldsPriority allocation paths (GiveLibrary and GiveLibraryGroup), regardless of whether the chosen item actually satisfied the LocalHoldsExclusivity conditions. This caused staff to be directed to pull an item that would not trigger the hold at checkin when the two features used different patron/item controls. The fix removes the premature local_holdgroup_match => 1 from both LHP paths and lets _flag_local_holdgroup_matches evaluate all allocated items using the correct LocalHoldsExclusivity* sysprefs. Also fixes a pre-existing missing bind-variable placeholder in the INSERT INTO tmp_holdsqueue statement (17 columns, only 16 ?). Test updates: - Existing local_holdgroup_match test gains the matching LocalHoldsExclusivity sysprefs so the flag is set via the correct code path. - New subtest reproduces the reported bug: LHP (HomeLibrary/homebranch) targets a non-local item while exclusivity (PickupLibrary/holdingbranch) should not flag it — confirms local_holdgroup_match is 0 after the fix. Signed-off-by: Trevor Diamond Sponsored-by: Main Library Alliance Signed-off-by: Andrew Fuerste Henry Signed-off-by: Pedro Amorim commit 4b23e5c5d1802593e4b68e1fc872ea329aecb11f Author: Martin Renvoize Date: Thu Apr 16 09:54:18 2026 +0100 Bug 41410: (follow-up) Decouple exclusivity from LocalHoldsPriority LocalHoldsExclusivity and LocalHoldsPriority are adjacent but distinct features: priority alters *which patron's hold* gets filled first, while exclusivity restricts *which items* may fill a hold during a time window. Each should be usable independently of the other. Changes: - Rename LocalHoldsPriorityExclusivityPeriod to LocalHoldsExclusivityPeriod - Add LocalHoldsExclusivityPatronControl (PickupLibrary/HomeLibrary) - Add LocalHoldsExclusivityItemControl (homebranch/holdingbranch) - HoldsQueue: add _flag_local_holdgroup_matches() that flags local_holdgroup_match on allocated items using hold-group matching and the new exclusivity control prefs, regardless of LocalHoldsPriority - Reserves: CheckReserves exclusivity check now computes its own "is this item local" using the new prefs and hold-group siblings, independent of the LocalHoldsPriority matching variables - Atomicupdate handles both fresh install and rename-from-old-pref paths - Pref UI note clarifies independence from LocalHoldsPriority Test plan: 1. Apply patch, run updatedatabase.pl 2. Verify three new sysprefs exist in Administration > System preferences > Circulation: LocalHoldsExclusivityPeriod, LocalHoldsExclusivityPatronControl, LocalHoldsExclusivityItemControl 3. Set LocalHoldsPriority to "Don't give" (None) 4. Create a library hold group with at least two libraries (e.g. Lib-A, Lib-B) 5. Create a third library (Lib-C) NOT in the hold group 6. Create an item at Lib-B 7. Place a hold for a patron at Lib-A (pickup at Lib-A) 8. Set LocalHoldsExclusivityPeriod to 7, PatronControl to HomeLibrary, ItemControl to homebranch 9. Run the holds queue builder (misc/cronjobs/holds/build_holds_queue.pl) 10. Verify hold_fill_targets.local_holdgroup_match = 1 for that hold (item at Lib-B is in the same hold group as patron at Lib-A) 11. Check in the item at Lib-C — it should NOT fill the hold (exclusivity blocks the non-local item within the 7-day window) 12. Check in the item at Lib-B — it SHOULD fill the hold (local match) 13. Reset: delete the hold, create a new one with a patron at Lib-C (pickup at Lib-C). Run the queue again. 14. Verify hold_fill_targets.local_holdgroup_match = 0 for this hold 15. The item at Lib-B should now fill this hold at Lib-C checkin (non-local patron, no exclusivity protection) 16. Set LocalHoldsExclusivityPeriod to 0 — verify non-local items can fill holds normally regardless of hold_fill_targets flag 17. prove t/db_dependent/Holds/LocalHoldsPriority.t — passes 18. prove t/db_dependent/HoldsQueue.t — passes 19. prove t/db_dependent/Reserves.t — passes Signed-off-by: Trevor Diamond Signed-off-by: Andrew Fuerste Henry Signed-off-by: Pedro Amorim commit bec5c357b4ab8f51292a1167774873f4e63b945f Author: Martin Renvoize Date: Wed Apr 15 17:59:58 2026 +0100 Bug 41410: Unit tests Covers the new LocalHoldsPriorityExclusivityPeriod behaviour and keeps existing tests working with the schema change: - t/db_dependent/Holds/LocalHoldsPriority.t: new subtest exercising all four cases -- pref disabled, pref active on a fresh hold, pref active past the window, and pref active with no hold_fill_targets row (queue hasn't run). - t/db_dependent/HoldsQueue.t: assert that after CreateQueue() with a local-group match, both tmp_holdsqueue and hold_fill_targets rows carry local_holdgroup_match = 1. - t/db_dependent/Reserves.t, t/db_dependent/Koha/Hold.t: qualify the two direct "INSERT INTO hold_fill_targets VALUES (...)" statements with explicit column names so they continue to work now that hold_fill_targets has one extra column. Test plan: prove t/db_dependent/Holds/LocalHoldsPriority.t \ t/db_dependent/HoldsQueue.t \ t/db_dependent/Reserves.t \ t/db_dependent/Koha/Hold.t All tests should pass. Sponsored-by: Main Library Alliance Signed-off-by: Trevor Diamond Signed-off-by: Andrew Fuerste Henry Signed-off-by: Pedro Amorim commit a2ef73fe957e2f17b001ace61c0ff7c553d4dbdd Author: Martin Renvoize Date: Wed Apr 15 17:59:43 2026 +0100 Bug 41410: Add local holds priority exclusivity period When LocalHoldsPriority is enabled, the holds queue picks a local-group item for eligible holds. Until now there was nothing to reserve that priority at checkin time: if a non-local item was returned first, it would happily fill the hold and the local-priority decision was lost. This patch adds a time-bounded exclusivity window so that, for a configurable number of days after a hold is placed, a hold whose queue target is a local-group item will not be filled by a non-local item. Changes: - C4/HoldsQueue.pm: in MapItemsToHoldRequests, flag %item_map entries that matched on local library (GiveLibrary/GiveLibraryAndGroup) or local hold group (GiveLibraryGroup/GiveLibraryAndGroup) with local_holdgroup_match => 1, and propagate that into both the tmp_holdsqueue and hold_fill_targets INSERTs. - C4/Reserves.pm: in the CheckReserves loop, when LocalHoldsPriorityExclusivityPeriod is set, the hold is still within that window, and the item being evaluated is not itself a local match, skip the reserve if its hold_fill_targets row has local_holdgroup_match = 1. Missing hold_fill_targets row means "no information" -- fall through so holds are never trapped between queue runs. - circulation.pref: expose the new LocalHoldsPriorityExclusivityPeriod preference next to the existing LocalHoldsPriority* entries. The feature is a no-op unless both LocalHoldsPriority is set to something other than "Don't give" AND LocalHoldsPriorityExclusivityPeriod is > 0. Test plan: 1) Apply the patches and run the atomicupdate. 2) Set up a library group flagged as a local hold group containing two libraries (call them A and B). Add a third library C that is NOT in the group. 3) Add one item to each library (itemA, itemB, itemC) on the same biblio. 4) Administration -> System preferences -> Circulation: - Set LocalHoldsPriority to "Give library group". - Set LocalHoldsPriorityPatronControl to "home library". - Set LocalHoldsPriorityItemControl to "home library". - Set LocalHoldsPriorityExclusivityPeriod to 7. 5) As a patron whose home library is A, place a title-level hold with pickup at A. 6) Run the holds queue (misc/cronjobs/holds/build_holds_queue.pl). -> The queue should target itemA (or itemB) for this hold. -> Confirm in the database: SELECT reserve_id, itemnumber, local_holdgroup_match FROM hold_fill_targets; The targeted row must have local_holdgroup_match = 1. -> Holds to pull / holds queue report should also show the flag (tmp_holdsqueue.local_holdgroup_match = 1). 7) At library C, check in itemC (the non-local item). -> The hold must NOT be captured -- itemC simply goes back on the shelf. 8) At library A, check in itemA (the local item). -> The hold IS captured for the patron. 9) Cancel and re-place the hold, run the queue again. Manually age the hold past the window: UPDATE reserves SET reservedate = DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY) WHERE reserve_id = ; Now check in itemC again. -> The hold IS captured (exclusivity window has expired). 10) Re-place the hold but do NOT run the queue. With no hold_fill_targets row for the reserve, check in itemC. -> The hold IS captured (we never trap holds between queue runs). 11) Flip LocalHoldsPriorityExclusivityPeriod back to 0 and repeat step 7. -> The hold IS captured; the feature is fully disabled. 12) Run: prove t/db_dependent/Holds/LocalHoldsPriority.t \ t/db_dependent/HoldsQueue.t \ t/db_dependent/Reserves.t \ t/db_dependent/Koha/Hold.t -> All tests pass. Sponsored-by: Main Library Alliance Signed-off-by: Trevor Diamond Signed-off-by: Andrew Fuerste Henry Signed-off-by: Pedro Amorim commit 753c5fe349cbca24e34e611d218c9bf9fc269a70 Author: Martin Renvoize Date: Wed Apr 15 17:12:50 2026 +0100 Bug 41410: Add local_holdgroup_match column and exclusivity period syspref Adds groundwork for a time-bounded local holds exclusivity feature: - Adds a local_holdgroup_match flag (tinyint, default 0) to both hold_fill_targets (consulted by CheckReserves/_Findgroupreserve at checkin) and tmp_holdsqueue (so queue/pull-list reports can display it). The flag will be set by the queue builder when a local-group match is made. - Adds a new system preference LocalHoldsPriorityExclusivityPeriod (Integer, default 0 = disabled) specifying the number of days from hold creation during which only locally-targeted items may fill a hold with a local queue target. - Provides an atomicupdate that guards both ALTERs with column_exists and INSERT IGNOREs the new syspref. Test plan: 1. Apply atomicupdate: perl installer/data/mysql/updatedatabase.pl 2. Verify both columns exist: SHOW COLUMNS FROM hold_fill_targets LIKE 'local%'; SHOW COLUMNS FROM tmp_holdsqueue LIKE 'local%'; 3. Verify syspref row: SELECT variable, value FROM systempreferences WHERE variable = 'LocalHoldsPriorityExclusivityPeriod'; 4. Confirm default value is 0 and type is Integer. Sponsored-by: Main Library Alliance Signed-off-by: Trevor Diamond Signed-off-by: Andrew Fuerste Henry Signed-off-by: Pedro Amorim commit 2fe11906a7bfd786bf8d0727f8b1d5c2efa83c04 Author: Jan Kissig Date: Wed Jul 29 13:22:53 2026 +0000 Bug 42292: (QA follow-up) replace remaining OD_password_required with Koha.prefs.OverDrivePasswordRequired As in opac-user.tt OD_password_required was replaced by Koha.prefs.OverDrivePasswordRequired. The script overdrive.js checks already for the Koha.prefs variable. 1) Set sysprefs: OPACOverDrive, OverDriveCirculation, OverDrivePasswordRequired = Enable/Yes; OverDriveClientKey, OverDriveClientSecret, OverDriveLibraryID = any non-empty value (e.g. 123). 2) Go to /cgi-bin/koha/opac-user.pl, click "Log in to your OverDrive account" - password modal appears (correct). 3) Go to /cgi-bin/koha/opac-overdrive-search.pl?q=music, click the same link, it fails with "No password provided" instead, no modal. 4) apply patch and repeat 3) The modal appears now. Signed-off-by: Pedro Amorim commit c2514018b498b71ba9b165ac805b09ffe9d94eee Author: Jan Kissig Date: Wed Jun 24 11:34:25 2026 +0000 Bug 42292: (QA follow-up) remove unused reference OverDriveEnabled in opac-shelves As OverDriveEnabled is declared via SET in the template and not a system preference and after all not used in that script it is removed. Signed-off-by: Pedro Amorim commit 87d4eac98ea9c36dffc84cf13e3f61161d28874a Author: Jan Kissig Date: Wed Jun 24 09:14:42 2026 +0000 Bug 42292: (QA follow-up) Fix evaluation of OpacHighlightedWords to be numeric in opac-results OpacHighlightedWords was set in quotation marks and resulted in either "1" or "0". As "0" and "1" always evaluates to true in JS the following error was thrown when OpacHighlightedWords was set to Don't highlight: Uncaught TypeError: $(...).highlight is not a function To test: a) Set system preference OpacHighlightedWords to Don't highlight b) search a title in the opac with open dev console and check that no error (like above) is thrown. Signed-off-by: Pedro Amorim commit 71b121bb6f42dd3f7fa66c2e9525e518f7c26287 Author: Paul Derscheid Date: Fri Apr 17 16:00:22 2026 +0200 Bug 42292: (follow-up) Fix OverDrive/OpenLibrary references in opac-results.tt Three issues introduced by the OPAC port of opac-results.tt: - The tt-allowed block sets OverDriveEnabled via `Koha.Preference("OverDriveEnabled")`, but OverDriveEnabled is a TT-side SET variable (computed from OverDriveLibraryID + OverDriveClientKey + OverDriveClientSecret), not a system preference, so Koha.prefs.OverDriveEnabled is always "". The code at line 621 then reads an unprefixed `OverDriveEnabled` identifier which is not declared -- the OverDrive result block never fires. - The neighbouring block `if (OpenLibrarySearch) { ... }` reads an unprefixed identifier for a value that is only exposed on Koha.prefs, so the OpenLibrary result block also never fires. - Both blocks build a throbber with `src=\`${interface}/${theme}/images/spinner-small.gif\`` inside a single-quoted JS string; the backticks are literal characters there, so the rendered src ends up as that literal template text, not a usable URL. Replace the OverDriveEnabled addPrefs entry with a plain `const OverDriveEnabled = [% OverDriveEnabled ? 1 : 0 | html %];` that reads the TT SET variable, prefix OpenLibrarySearch with Koha.prefs, and rebuild the throbber src via normal string concatenation. To test: - With OverDrive configured (OPACOverDrive, OverDriveLibraryID, OverDriveClientKey, OverDriveClientSecret), run a search in the OPAC: the OverDrive results panel shows the spinner and then the API results. - With OpenLibrarySearch enabled, the OpenLibrary panel does the same. - In both cases the spinner image loads (no broken-image icon). - Disable both prefs -> neither panel is rendered, no console errors. Signed-off-by: Paul Derscheid Signed-off-by: Jan Kissig Signed-off-by: Pedro Amorim commit b4035c5a0c11c0671b4f26337fd41187f1d5cead Author: Paul Derscheid Date: Fri Apr 17 16:00:22 2026 +0200 Bug 42292: (follow-up) Fix duplicate NovelistSelectProfile declaration In the new tt-allowed block, the NovelistSelect fallback declarations redeclare `NovelistSelectProfile` instead of also exposing `NovelistSelectPassword`: var NovelistSelectProfile = "[% NovelistSelectProfile | html %]"; var NovelistSelectProfile = "[% NovelistSelectPassword | html %]"; So `NovelistSelectProfile` ends up holding the password, and `NovelistSelectPassword` is never declared. The later `novSelect.loadContentForQuery(..., NovelistSelectProfile, NovelistSelectPassword, ...)` call then throws ReferenceError and NovelistSelect content never loads on biblio detail pages. Rename the second declaration to `NovelistSelectPassword` to match the original template behaviour. To test: - Configure the NovelistSelect sysprefs (NovelistSelectEnabled, NovelistSelectProfile, NovelistSelectPassword) in the OPAC. Real NovelistSelect content only loads with valid EBSCO subscription credentials -- with dummy values the JS path still runs but the tab stays empty; that is still sufficient to verify this fix. - Open an OPAC biblio detail page with a known ISBN. - In the browser console, run console.log(NovelistSelectProfile, NovelistSelectPassword) and confirm both values are printed (before the fix, NovelistSelectPassword is undefined). - With valid subscription credentials, NovelistSelect content loads in its tab/panel instead of throwing a ReferenceError in the console. Signed-off-by: Paul Derscheid Signed-off-by: Jan Kissig Signed-off-by: Pedro Amorim commit a8da9a5f14db4ac14f099f418e3f1d5a65862c95 Author: Paul Derscheid Date: Fri Apr 17 15:21:04 2026 +0200 Bug 42292: (follow-up) Rename opac-shelves print flag to avoid shadowing window.print The port declares `var print = [% print ? 1 : 0 | html %];` at the top of the tt-allowed block, then later calls `window.print()` inside `if (print) { ... }`. Declaring `var print` at top-level overwrites the built-in `window.print` with 0 or 1, so the subsequent `window.print()` call throws "print is not a function" and the print view never fires its actual print dialog. Rename the flag to `should_print` and update the single `if (print)` check accordingly. To test: - Open a public list with items in the OPAC. - Click "Print this list" (or load the page with op=print). - The browser print dialog opens and the window closes afterwards. - No "print is not a function" error in the console. Signed-off-by: Paul Derscheid Signed-off-by: Jan Kissig Signed-off-by: Pedro Amorim commit ed7fef7dd7ab2f3e7965029f073365a97394093d Author: Paul Derscheid Date: Fri Apr 17 15:15:15 2026 +0200 Bug 42292: (follow-up) Replace removed dateformat_pref var in OPAC calendar.inc The OPAC port moved the dateformat pref into Koha.addPrefs and dropped the local `var dateformat_pref = ...;` declaration, but four usages of `dateformat_pref` remain in the same file (the debug alert, two `switch` statements, and the get_dateformat_str call in validate_date). At runtime the switches throw ReferenceError, so flatpickr ends up with the default 'Y-m-d' format and date validation via validate_date crashes before showing the invalid-date alert. Replace all remaining occurrences with Koha.prefs.dateformat, which is populated by the new addPrefs block at the top of the include. To test: - Set dateformat syspref to each of us / metric / dmydot / iso. - Open any OPAC page using a flatpickr input (e.g. hold date, patron dob on the registration form). - Confirm the widget renders dates in the expected format and entering an invalid string triggers the validate_date alert. - No ReferenceError in the browser console on page load. Signed-off-by: Paul Derscheid Signed-off-by: Jan Kissig Signed-off-by: Pedro Amorim commit e53b7346c989dd871bd5d13d97ff50cff76076e3 Author: Jonathan Druart Date: Fri Apr 3 11:46:10 2026 +0200 Bug 42292: Tidy all script tags - OPAC (auto tidy) Signed-off-by: Paul Derscheid Signed-off-by: Jan Kissig Signed-off-by: Pedro Amorim commit 5e822ff61301c8c7145dc86c89e12394ae068cb0 Author: Jonathan Druart Date: Tue Mar 31 12:04:35 2026 +0200 Bug 42292: Tidy all script tags - OPAC In order to tidy all the script tags of the .tt files we isolate the interpolation of Template::Toolkit variables in a separate script tag. This patch focuses on the OPAC module. Non-trivial changes, we modify the whole OPAC in a single patch. Testing this patch should focus on: * date formatting * the cover images on the search result and shelves views Signed-off-by: Paul Derscheid Signed-off-by: Jan Kissig Signed-off-by: Pedro Amorim commit 8812de46fe32fee0b7919d802d90d6e31146b72b Author: Jan Kissig Date: Wed Jun 3 09:47:52 2026 +0000 Bug 42279: (QA follow-up) Fix straying TT variable, auto tidy Removes a left over TT variable from a JS script block without data-tt-allowed="true" attribute. Auto tidy Signed-off-by: Pedro Amorim commit 3dbe00ccbe1221c483d46c65329897f3c5dbfd98 Author: Paul Derscheid Date: Fri Apr 17 10:00:55 2026 +0200 Bug 42279: (follow-up) Restore numeric compare of quantityrec The port quoted [% quantityrec | html %] into a string const, but the value is used as `$(this).val() < quantityrec` where both sides are now strings and JS does a lexicographic compare ("10" < "5" is true), giving the wrong branch when the user enters a multi-digit quantity. Render quantityrec as a JS number (with a 0 fallback) so the comparison stays numeric, matching the original template output. To test: - Modify an existing order with quantityrec, e.g. 5 items received. - Change quantity to 10 (higher than received): "can't add new item" alert fires, value reverts. - Change quantity to 2 (lower): "don't forget to delete" alert fires. Signed-off-by: Paul Derscheid Signed-off-by: Jan Kissig Signed-off-by: Pedro Amorim commit af445bdebabdc0acd3b953d7e67073c0921770bd Author: Jonathan Druart Date: Mon Apr 13 11:31:34 2026 +0200 Bug 42279: Tidy all script tags - acqui (auto tidy) Signed-off-by: Paul Derscheid Signed-off-by: Jan Kissig Signed-off-by: Pedro Amorim commit 3113ca088e9c9a1b390f82416d1338ed8956134e Author: Jonathan Druart Date: Mon Mar 30 11:09:19 2026 +0200 Bug 42279: Tidy all script tags - acqui In order to tidy all the script tags of the .tt files we isolate the interpolation of Template::Toolkit variables in a separate script tag. This patch focuses on the acquisition module. The 2 main changes are in the following files: * basket.tt It's mostly about table settings and the visibility of columns depending on syspref: - "supplier report" / EDIFACT - "modify" / if basket is not closed - "cancel" / CancelOrdersInClosedBaskets * neworderempty.tt trivial changes, no error or warning in the console should appear. Confirm than createing a new order works as expected. Signed-off-by: Paul Derscheid Signed-off-by: Jan Kissig Signed-off-by: Pedro Amorim commit 707878312baf52d3bc1e645074ad760ccae074b1 Author: Owen Leonard Date: Mon Mar 2 13:53:51 2026 -0500 Bug 41116: (follow-up) Fix template variable filter Signed-off-by: David Nind Signed-off-by: Laura_Escamilla Signed-off-by: Pedro Amorim