commit 00b8c7bd7397f5b07ecc274cf96c7336aea4bfdb Author: Martin Renvoize Date: Thu Aug 13 13:38:08 2026 +0100 Bug 43288: Fix change amount missing from printed fee receipts pay.tt, paycollect.tt and boraccount.tt all open printfeercpt.pl with a change_given= query parameter after a payment, but printfeercpt.pl only ever reads a 'change' param into its GetPreparedLetter substitute hash - matching the ACCOUNT_CREDIT/CREDIT_* default notice templates, which use [% change | $Price %], not [% change_given | $Price %]. As a result the change amount is silently blank on every printed fee receipt that shows it, regardless of FinePaymentAutoPopup. Rather than touch printfeercpt.pl or the notice content (both already agree on 'change', matching a similar migration Koha did once before in installer/data/mysql/db_revs/220600072.pl going the same direction), fix the three query strings that build the printfeercpt.pl URL to send change= instead of change_given=. The change_given variable name is kept everywhere else (form fields, JS locals, Perl params) since this is purely about the query key at the printfeercpt.pl boundary. Test plan: 1) Set FinePaymentAutoPopup to 'show' 2) Take a payment for a patron via members/pay.pl or members/paycollect.pl that generates change (fine amount not evenly covered by cash tendered) 3) Note the auto-popped-up receipt shows the change amount (before this patch, the Change line was always blank) 4) Repeat via the boraccount.pl > printfeercpt.pl print receipt route commit 481e170da1484a70bd281d6c4299adf8f2134751 Author: Pedro Amorim Date: Tue Sep 1 15:49:40 2026 +0000 Bug 42968: Revert "Bug 42968: Add fallback label for 505 contents notes" This reverts commit 3bdecfb4e6f21aef10281157e1dae945349594b9. Signed-off-by: Pedro Amorim commit 3bdecfb4e6f21aef10281157e1dae945349594b9 Author: Laura Escamilla Date: Wed Jul 1 15:30:34 2026 +0000 Bug 42968: Add fallback label for 505 contents notes To test: 1. Create or locate a bibliographic record with a MARC 505 field. 2. Leave the first indicator blank. 3. Add text to the 505 field. 4. View the record detail page. 5. Notice that the 505 contents display without a label. 6. Apply the patch 7. Refresh the record detail page. Notice that the 505 content displays a label now. 8. Sign off and have a lovely day! :D Signed-off-by: David Nind Signed-off-by: Andrew Fuerste Henry Signed-off-by: Pedro Amorim commit 9d0b71511b9bf62552346b226c4b56ba15486cbe Author: Pedro Amorim Date: Wed May 20 14:39:32 2026 +0000 Bug 42653: Add tests prove t/db_dependent/Koha/ILL/Request.t prove t/db_dependent/Koha/Plugins/ILL/Backends.t NOTE: These must be run without having any ILL backend plugin installed Signed-off-by: Hannah Prince Signed-off-by: Andrew Fuerste Henry Signed-off-by: Pedro Amorim commit 0e959003351a1dc941fe9dc42b6878e8c618f173 Author: Pedro Amorim Date: Wed May 20 14:38:13 2026 +0000 Bug 42653: get_backend_plugin should cache results per backend name The plugin cache used a single slot, so the first backend looked up poisoned it for all subsequent calls. This caused 500 errors when viewing ILL requests that had been migrated between backends. Test plan: 1) Download and install 2 additional plugin backends that implement their own templates: https://github.com/openfifth/koha-ill-libkey-lending-tool/releases/download/v2.6.1/koha-ill-libkey-lending-tool-v2.6.1.kpz https://github.com/openfifth/koha-ill-reprintsdesk/releases/download/v4.2.0/koha-ill-reprintsdesk-v4.2.0.kpz 2) Enable ILLModule and run the following SQL: INSERT INTO illrequests (borrowernumber, branchcode, status, backend, placed) SELECT b.borrowernumber, br.branchcode, 'COMP', 'ReprintsDesk', NOW() FROM borrowers b, branches br LIMIT 1; INSERT INTO action_logs (timestamp, user, module, action, object, info, interface) VALUES (NOW() - INTERVAL 2 HOUR, 1, 'ILL', 'INCDOCS_REQUEST_PLACED', 1, '{"log_origin":"IncDocs","response":1}', 'intranet'), (NOW() - INTERVAL 1 HOUR, 1, 'ILL', 'REPRINTS_DESK_REQUEST_ORDER_UPDATED', 1, '{"log_origin":"ReprintsDesk","response":null}', 'commandline'); 3) Attempt to open the 'Manage request' page for this newly created request: http://localhost:8081/cgi-bin/koha/ill/ill-requests.pl?op=illview&illrequest_id=1 4) Notice you get a 500 error: Template process failed: file error - : not found 5) Apply all patches, restart plack, refresh. Notice the 'Manage request' page now renders correctly. 6) Click the 'ILL request log' toolbar button. Confirm that both the IncDocs and ReprintsDesk entries are shown. Signed-off-by: Hannah Prince Signed-off-by: Andrew Fuerste Henry Signed-off-by: Pedro Amorim commit 6d8ea60630a89063d71ebccba9e4afcefc0f150e Author: Martin Renvoize Date: Fri Jul 3 22:22:22 2026 +0100 Bug 42993: EDI: log transport failures via Koha::Logger instead of carp Koha::Edifact::Transport reported every transport failure (no transport configured, connect failure, directory-change failure, file-listing failure and per-file download/upload failure) with carp. Under edi_cron.pl these go to STDERR, so on a normal cron run the messages are lost and EDI simply goes quiet with nothing in editrace.log to explain why. Route these failures through Koha::Logger on the 'edi' interface (the same interface edi_cron.pl already uses) so they are recorded in the EDI log. The two ingest()-level data warnings (duplicate filename, unreadable file) are intentionally left as carp: one is asserted by the existing unit test and neither is a transport failure. No change to the transfer logic. Test plan: 1) prove t/db_dependent/Koha/Edifact/Transport.t 2) Point an EDI account at an unreachable transport and run edi_cron.pl; confirm the failure is now written to the edi log rather than only to STDERR. Signed-off-by: David Nind Signed-off-by: Andrew Fuerste Henry Signed-off-by: Pedro Amorim commit 02491b08d36a746a470679f9766b39bd132e33b9 Author: Jonathan Druart Date: Thu Aug 27 16:45:08 2026 +0200 Bug 43394: DT - Cast pageLength to Integer In "Edit patron card batch" the pageLength is coming from the syspref, the value is a string. But DataTable's needs an integer. Let's fix this in datatables.js, in the kohaTable's constructor Test plan: Got to cgi-bin/koha/patroncards/edit-batch.pl Enter more than 20 borrowernumbers and "Add patron(s)" Click "next" and notice that with this patch the pagination works. Without this patch the JS error is "Uncaught TypeError: can't access property "nTr", p is undefined" Signed-off-by: David Nind Signed-off-by: Nick Clemens Signed-off-by: Pedro Amorim commit eb81e1cc5e224cf8715f1aa17370419158215a6c Author: Pedro Amorim Date: Tue Sep 1 14:05:16 2026 +0000 Bug 43072: DBRev 26.06.00.018 Signed-off-by: Pedro Amorim commit dcba10c45db9015d5276bd05a36a4671eec2d89f Author: Emily Lamancusa Date: Fri Aug 28 13:57:35 2026 +0000 Bug 43072: (QA follow-up) Use say_info in dbrev Signed-off-by: Emily Lamancusa Signed-off-by: Pedro Amorim commit 17c1c87583439e045a6deed707c10a336bc8cddb Author: Andrew Fuerste-Henry Date: Tue Aug 4 12:48:58 2026 +0000 Bug 43072: standardize atomicupdate formatting Signed-off-by: Lawrence O'Regan-Lloyd Signed-off-by: Emily Lamancusa Signed-off-by: Pedro Amorim commit 241abf5545248493a48f2c2e55cf11afd417bbdc Author: Nick Clemens Date: Sun Aug 2 19:40:46 2026 +0000 Bug 43072: (Bug 41267 follow-up) Add default rules when not set Bug 41267 defined the new rule, but didn't set the default. For consistencies sake we should create these in the DB To test: 1 - Create 'Default holds and bookings policies by item type' for two itemtypes Set 'Fill other record level holds on record at checkout' to yes for one and no for the other 2 - Apply this patch 3 - updatedatabase 4 - Verify the rules are not altered 5 - Chekc the DB: SELECT * FROM circulation_rules WHERE branchcode IS NULL and categorycode IS NULL; 6 - Note the number of rules/ids 7 - Run the DB update and confirm nothing changes 8 - DELETE from circulation_rules WHERE rule_name='fill_other_biblio_holds_policy'; 9 - Run the DB update 10 - Confirm rules are replaced, both as yes 11 - Delete a single one of the rules now: DELETE from circulation_rules WHERE rule_name='fill_other_biblio_holds_policy' AND id=#; 12 - Run the DB update 13 - Confirm only that rule is replaced 14 - DELETE from circulation_rules WHERE branchcode IS NULL and categorycode IS NULL; 15 - Run the DB update 16 - No new rules are created follo# Koha/.Patrons.pm.swp Signed-off-by: Lawrence O'Regan-Lloyd Signed-off-by: Emily Lamancusa Signed-off-by: Pedro Amorim commit 0c86df4755536ed97007069e486acdbccea0b4db Author: Andrew Fuerste-Henry Date: Tue Jul 14 16:04:41 2026 +0000 Bug 43072: Show Yes when no rule exists for fill_other_biblio_holds_policy To test 1 - have three test biblios. Bib A has just one item, a Book. Bib B has two items, a Book and a Computer File. Bib C has one item, a Computer File 2 - have KTD default circ rules (holds allowed for all itemtypes in the circ rules table, no values defined in "Default checkout, hold and return policy" or "Default holds and bookings policies by item type ") 3 - set "Default holds and bookings policies by item type" for Computer Files to Hold Policy = "No holds allowed." 4 - observe that "Fill other record level holds on record at checkout" defaults to "Yes" when a value is set to Hold Policy 5 - confirm you can place a holds on Bib A and Bib B but not Bib C 6 - confirm your hold on Bib B is filled when checking out the Computer File item from Bib B 7 - update "Default holds and bookings policies by item type" so Computer Files has Hold Policy = "No holds allowed" and "Fill other record level holds on record at checkout" = No 8 - confirm you're still able to place a hold on Bib B 9 - confirm your hold on Bib B is now *not* filled by checking out the Computer Files item 10 - open the commandline SQL interface (sudo koha-mysql kohadev on KTD) 11 - run query "select * from circulation_rules where rule_name = 'fill_other_biblio_holds_policy'"; confirm an entry for Computer Files with rule_value=0 12 - run command "delete from circulation_rules where rule_name = 'fill_other_biblio_holds_policy'" 13 - reload your circ rules page, confirm the interface still shows "Fill other record level holds on record at checkout" = No for comoputer files 14 - confirm your hold on Bib B can again be filled by checking out the Computer File item APPLY PATCH, restart_all 15 - reload circ rule page, confirm it now shows "Fill other record level holds on record at checkout" = Yes for Computer Files 16 - run query "select * from circulation_rules where rule_name = 'fill_other_biblio_holds_policy'"; confirm there is still no entry for Computer Files 17 - place a new hold on Bib B, confirm it fills by checking out the Computer File (not having any rule defined is enforced the same as a "Yes" and displays a "Yes") 18 - in circ rules, set "Fill other record level holds on record at checkout" = Yes for computer files 19 - run query "select * from circulation_rules where rule_name = 'fill_other_biblio_holds_policy'"; confirm there is an entry for Computer Files with rule_value=1 20 - place a new hold on Bib B, confirm it fills by checking out the Computer File (having an explicit Yes rule defined is enforced correctly and displays correctly) 21 - in circ rules, set "Fill other record level holds on record at checkout" = No for computer files 22 - run query "select * from circulation_rules where rule_name = 'fill_other_biblio_holds_policy'"; confirm there is an entry for Computer Files with rule_value=0 23 - place a new hold on Bib B, confirm it does not fill by checking out the Computer File (having an explicit No rule defined is enforced correctly and displays correctly) Signed-off-by: Lawrence O'Regan-Lloyd Signed-off-by: Emily Lamancusa Signed-off-by: Pedro Amorim commit ddd790e4c5d2f867787111e49d688c5f80728bae Author: David Cook Date: Mon Jun 29 06:56:25 2026 +0000 Bug 42924: Restore default MaxAge for CSRF token This patch changes the default MaxAge from 8 hours to 168 hours or 7 days due to usability issues encountered from using a short-lived token. See OWASP for more information on why timestamps with expiry are not necessary for CSRF tokens. The MaxAge is a requirement of the WWW::CSRF token used by Koha, so let's just use a longer expiry in lieu of no expiry. Test plan: 0. apply patch 1. prove t/Token.t Signed-off-by: Jonathan Druart Signed-off-by: Marcel de Rooy Signed-off-by: Pedro Amorim commit bfc68662003ab27deb48108116c219e7828e0eb1 Author: Martin Renvoize Date: Tue Aug 4 12:08:21 2026 +0100 Bug 39589: Auto-skip to the next page and note hidden results when a page is fully filtered When every result on a page is hidden (OpacHiddenItemsHidesRecord), rather than leaving the patron on a dead-end "not available" page, look ahead a bounded number of pages (3) for one with visible results and redirect there directly. The lookahead is capped so a library with many consecutive hidden pages doesn't turn a single search into a long chain of extra search-engine queries; RSS/atom/opensearchdescription requests are left alone since redirecting a feed reader makes no sense. Whenever a hidden page was skipped over (whether we found a visible page to land on, or exhausted the lookahead and fell back to the "not available" message), show a caveat next to the results count so the "Your search returned N results" figure doesn't read as contradicting what the patron can actually see - this was raised as a sign-off concern on the original patch (comment 4). Signed-off-by: Marcel de Rooy Signed-off-by: Pedro Amorim commit a8523740b057b12ee99fa7baf31b22909a9f6859 Author: Martin Renvoize Date: Tue Aug 4 10:46:14 2026 +0100 Bug 39589: (QA follow-up) Revert unrelated star rating markup change The read-only star rating widget in search results was changed from to elements, unrelated to this bug's fix. That activated a pre-existing but previously dead click handler ($(".br-readonly a") in this same template) and turned every star icon into a focusable, non-functional link, adding up to 5 dead tab stops per search result. Restore the markup used everywhere else this read-only widget appears. Signed-off-by: Marcel de Rooy Signed-off-by: Pedro Amorim commit 84831855d24ce640b3cb783a88b4ec805f8944e4 Author: Martin Renvoize Date: Tue Aug 4 10:46:04 2026 +0100 Bug 39589: (QA follow-up) Don't zero hits when a page has no visible results Zeroing $hits alongside the no_page_results flag also zeroed $total (since $total accumulates $hits, and the final template 'total' param is set from $hits/$total). Because the top "No results found!" banner and the entire search-results section are both gated on 'total' being true, this meant the new no_page_results branch could never actually render: whenever it was true, 'total' was false, so the classic "No results found!" page won instead - recreating the exact bug this patch is meant to fix. Verified against the bug's own test plan (OPACnumSearchResults=5, OpacHiddenItemsHidesRecord, OpacHiddenItems: withdrawn:[1], 6 bibs with the first 5 withdrawn/hidden): before this change, page 1 still showed "No results found!" with no pagination; after this change, it shows "Your search returned 6 results" / "Search results on this page are not available", with a working pagination link to the page containing the visible record. Signed-off-by: Marcel de Rooy Signed-off-by: Pedro Amorim commit ff26e80dc8516f898ecf4d940ff390a84394a839 Author: Chris Mathevet Date: Mon Aug 3 11:52:04 2026 -0400 Bug 39589: (follow-up) Applied suggested changes Signed-off-by: Martin Renvoize Signed-off-by: Marcel de Rooy Signed-off-by: Pedro Amorim commit 38e0b734c986c5025300008651e179def02d38ee Author: Hammat Wele Date: Tue Aug 12 00:56:00 2025 +0000 Bug 39589: OPAC search returns 'No results found' despite existing matching records This issue occurs when the records on the first page of results are not displayed. The message "No results found" is shown, and it is not possible to access records on other pages. To test 1. Set the following system preferences: OPACnumSearchResults to 5 OpacHiddenItemsHidesRecord to Hide OpacHiddenItems to withdrawn: [1] 2. Create 6 bibliographic records with the following titles: "notice test 1" "notice test 2" "notice test 3" "notice test 4" "notice test 5" "notice test 6" 3. For the first 5 records, add an item with Withdrawn status. 4. For "notice test 6", add an item with Available status. 5. In the OPAC interface, search for "notice test". 6. Apply the patch 7. Repeat step 4 ---> The results table is now shown with the message: "Search results on this page are not available." ---> You can now access the second page and see "notice test 6" Signed-off-by: David Nind Signed-off-by: Martin Renvoize Signed-off-by: Marcel de Rooy Signed-off-by: Pedro Amorim commit 3934a2bcc070f28f8e0f24a27f35aed9c8e2ffba Author: Jonathan Druart Date: Tue Jul 21 09:14:05 2026 +0200 Bug 43117: Restore use Test::NoWarnings From commit 6cc683b518223479f0c427a8305071fbd9581b5e Bug 34776: Unit tests -use Test::NoWarnings; -use Test::More tests => 2; +use Test::More tests => 1; +use Test::MockModule; +#use Test::NoWarnings; Test plan: Apply the first patch prove xt/use-test-no-warnings.t => FAIL Apply this patch prove xt/use-test-no-warnings.t => PASS prove t/Form_MessagingPreferences.t => PASS Signed-off-by: David Nind Signed-off-by: Lisette Scheer Signed-off-by: Pedro Amorim commit e2c90b57502916fc0b3645e1ebe94dd230cf74f0 Author: Jonathan Druart Date: Tue Jul 21 09:13:50 2026 +0200 Bug 43117: Adjust xt test to catch commented use Test::NoWarnings Signed-off-by: David Nind Signed-off-by: Lisette Scheer Signed-off-by: Pedro Amorim commit fd6a616428d0c5444d6a2f82082d58d7990df30e Author: Andrew Nugged Date: Fri Aug 21 08:26:56 2026 +0300 Bug 43354: Fix ILL request table search Bug 33544 combined the ILL request detail fields into one column. The combined column kept an empty data property without the related-object search metadata. A global table search therefore generated a query against "me.", which the REST endpoint rejected with a 500 error. Restore the extended attribute search mapping for the fields displayed in the combined Request details column. Add Cypress coverage for both the REST query and the matching result. Test plan: Before applying the patch: 1. Make sure the ILLModule system preference is enabled. 2. Open a patron record. 3. Click "ILL requests history" in the menu on the left. 4. Type any text in the Search box above the table. 5. Confirm that Koha shows a 500 Internal Server Error message. Apply the patch. After applying the patch: 1. Repeat steps 2 to 4 above. 2. Confirm that the table refreshes without an error. It may show no matching requests. 3. If the patron has an ILL request, search for part of a title or author shown under Request details. 4. Confirm that the matching request remains in the table. Co-Authored-By: Loontik Signed-off-by: David Nind Signed-off-by: Lisette Scheer Signed-off-by: Pedro Amorim commit 274432cd688858e022f9a436bb5a556b8a3a689c Author: Jonathan Druart Date: Mon Aug 24 16:05:56 2026 +0200 Bug 43259: Fix add/remove basket to/from group Caused by commit e9b8004d197876dea8c38374c08ec00451e37020 Bug 38255: Replace dataTable constructor with kohaTable - Staff grouped and ungrouped should be DataTable objects. Test plan: Create a basket, close it Create a basket group and test the "Add to group" and "Remove" button on the basket group edit view Without this patch there was a JS error: "Uncaught TypeError: grouped.row is not a function" Signed-off-by: David Nind Signed-off-by: Nick Clemens Signed-off-by: Pedro Amorim commit 8870a456dc4f4800c52eb5729c3903cc850d14eb Author: Pedro Amorim Date: Fri Aug 28 16:32:19 2026 +0000 Bug 30144: dbic Signed-off-by: Pedro Amorim commit e42b4df37b1f7d00c9117c15046a1acdeb2f4839 Author: Jonathan Druart Date: Tue Jul 7 13:30:01 2026 +0200 Bug 42661: Remove redundant library's name if HidePatronName If HidePatronName is turned on and a hold is being placed on a hold for a patron with a different library, there is an alert the duplicate the library's name: Pickup library is different. Patron: A patron from library Springfield Patron's home library: (Springfield / SPL) With this patch: Pickup library is different. Patron's home library: Springfield / SPL Pickup library is different. Patron: Keith Hendrix Patron's home library: Springfield / SPL Test plan: Go to /cgi-bin/koha/reserve/request.pl?biblionumber=117&borrowernumber=26 And confirm that the changes make sense Signed-off-by: Anneli Österman Signed-off-by: Nick Clemens Signed-off-by: Pedro Amorim commit 6e162e4c1042657579ca70f95aaa4033f86e2aa0 Author: Jonathan Druart Date: Thu Jun 18 12:07:26 2026 +0200 Bug 42727: Sort using the order from the staff syspref The main goal of the previous patch was to prevent the randomness, the languages were displayed by code. The languages in the footer are the languages enabled for the staff interface. This patch suggests to use the order of the syspref, and append the languages not enabled at the end of the list. Signed-off-by: David Nind Signed-off-by: Nick Clemens Signed-off-by: Pedro Amorim commit a098d97b5a8dc62ccc2eb2e04a8e722440cd7122 Author: Jonathan Druart Date: Mon Jun 1 14:28:17 2026 +0200 Bug 42727: List languages in a determinist order The list of languages are displayed in a random order in: * language picker at the bottom of the page * language tab when editing a notice template or an additional content This patch fixes it to display them in a determinist order Test plan: Have several languages installed, and some of them enabled, change the order of the languages in the syspref Reload the mainpage several times and notice that the languages are always displayed in the same order. Same when editing a notice template or an additional content. Note that I have not investigated yet why on the notice template and additinal content edit views they are not listed in the same order. Signed-off-by: David Nind Signed-off-by: Nick Clemens Signed-off-by: Pedro Amorim commit 0f7145e4afcf2da16d5dc4077c56deefc73debc0 Author: Jonathan Druart Date: Mon Jun 1 14:25:45 2026 +0200 Bug 42727: Add tests Signed-off-by: David Nind Signed-off-by: Nick Clemens Signed-off-by: Pedro Amorim commit 0a5919f108fa4140b3e82c3c49b70088a48b01b3 Author: Owen Leonard Date: Mon Aug 10 09:56:14 2026 -0400 Bug 43257: Remove event attributes from circulation.tt This patch updates circulation.tt in order to remove event attributes in favor of defining event handlers in the relevant JS file. To test, apply the patch and clear your cache if necessary. - If necessary, enable the SpecifyDueDate system preference. - Check out to a patron. - Click the checkout settings icon inside the barcode field. - Click the "Specify due date" field's "Remember for session" checkbox - The focus should move to the barcode field. - Enable recalls if necessary - Locate a checked out item which can be recalled. - Log in to the OPAC and recall the item. - In the staff interface, try to check that item out to another patron. - You should get a "Please confirm checkout" message. - Click the "Don't check out and print slip" button. - A popup should open for printing a recall slip. - The checkout page should reload without checking out the item. - If necessary, place a hold on an item for delivery to your logged-in library, check in the item, and confirm the hold. - Try to check out the item to another patron. - You should get a "Please confirm checkout" message. - Click "Don't check out, confirm hold, and print slip" - A popup should open for printing a hold slip. - The checkout page should reload without checking out the item. - If necessary, place a hold on an item but do not check it in. - Try to check out the item to a patron. - You should get a "Please confirm checkout" message. - Click "Cancel checkout and place hold for..." - You should be redirected to the holds page, placing a hold for that patron and that item. - The item should not have gotten checked out in the process. Sponsored-by: Athens County Public Libraries Signed-off-by: Olivia Reynolds Signed-off-by: Jonathan Druart Signed-off-by: Pedro Amorim commit b1905338eb6f64a24db4be159c304fafd772b576 Author: Jonathan Druart Date: Tue Aug 25 10:50:27 2026 +0200 Bug 43373: Holdings table - hide the shelving location column if empty The shelving location cell is always built with data: an empty HTML tag is added even if there is no location. So the "Shelving location" column is always displayed, it should only be displayed if there are items with data. Test plan: Have a biblio with all items without shelving location (or without several pages and at least one page without data). If all rows have empty shelving loc, the column should be hidden Signed-off-by: David Nind Signed-off-by: Nick Clemens Signed-off-by: Pedro Amorim commit d80ca5f4e844ec85621ce8865bcb686dcf6549aa Author: Jonathan Druart Date: Tue Jul 7 10:14:35 2026 +0200 Bug 42875: Correct failure message when adding an item to a bundle If bc_1 is checked out and I try to add bc_2 I get: "Failure: Item 'bc_2' belongs to another bundle" It should be: "Failure: Bundle is currently checked out" Test plan (using KTD): 1. Pick a record with items, for example: The definitive guide to Catalyst (146) 2. Edit the record and change 000 (the leader): - Click the tag editor - Change "7- Bibliographic level" to "C- Collection" 3. In the holdings table there is now a "Manage bundle (0|0)" option in the far right column for each item. 4. Check an item out for the record out, for example I checked out 39999000006117 to Mary Burton. 5. In the holdings table for the record, click on "Manage bundle" for the item you checked out (39999000006117). 6. In the expanded text area below the item row, click "+ Add to bundle". 7. In pop-up window, enter 39999000006131 "Item barcode" field (this is one of the other items for the record that is not checked out). 8. Click Submit. 9. You get an error: Failure: Item '39999000006131' belongs to another bundle 10. Apply the patch. 11. Restart everything: restart all 12. Hard refresh the page for the record. 13. Repeat steps 5 to 8. 14. You now get the correct error: "Failure: Bundle is currently checked out" Signed-off-by: David Nind Signed-off-by: Nick Clemens Signed-off-by: Pedro Amorim commit 0c952e9e09a20ddecb6c46a2983151252c40bfbd Author: Jonathan Druart Date: Tue Aug 25 11:56:30 2026 +0200 Bug 43241: Add tests for timezone support in RFC3339 q= queries Co-Authored-By: Mistral Vibe Signed-off-by: Jonathan Druart Signed-off-by: Pedro Amorim commit fd87c5f1958c0aa4689871031eae59bdd6efba8c Author: Tomás Cohen Arazi Date: Thu Aug 6 15:28:18 2026 -0300 Bug 43241: (follow-up) Use form => for q= params in tests The + character in RFC3339 timezone offsets (e.g. +00:00) is decoded as a space when interpolated directly into a URL query string. Use Test::Mojo form => { q => ... } which properly URL-encodes the parameter value. Signed-off-by: Tomás Cohen Arazi Signed-off-by: David Nind Signed-off-by: Jonathan Druart Signed-off-by: Pedro Amorim commit 450e6bf178e938a116d733df3da9d93940cb95d8 Author: Tomás Cohen Arazi Date: Thu Aug 6 10:45:31 2026 -0300 Bug 43241: Fix datetime/boolean conversion in q= queries with DBIC operators _parse_dbic_query maps field names but did not convert values. When datetime or boolean filters are nested inside DBIC operators (-and, -or) in the q= parameter, attributes_from_api (which normally handles the conversion via _recursive_fixup) cannot reach them because it only iterates top-level keys. This patch fixes the issue in two places: 1. _parse_dbic_query (Query.pm): When a key maps to a known column (on the primary table or a related object), call _recursive_fixup to convert RFC3339 timestamps to MySQL format and booleans to 0/1. Related object instances are cached on the result_set to avoid repeated prefetch_whitelist lookups during recursive parsing. 2. Objects.pm: Move attributes_from_api to process named query params before merging with q= params. This avoids double-processing since q= values are already fixed up by _parse_dbic_query. Test plan: 1. Apply the regression tests from the previous commit 2. Run: $ prove t/Koha/REST/Plugin/Query.t $ prove t/db_dependent/Koha/REST/Plugin/Objects.t => FAIL: Tests fail! 3. Apply this patch 4. Repeat step 2 => SUCCESS: Tests pass! 5. Sign off :-D Signed-off-by: Tomás Cohen Arazi Signed-off-by: David Nind Signed-off-by: Jonathan Druart Signed-off-by: Pedro Amorim commit 811e57c44165adc16152c756e14d0300d8db1104 Author: Tomás Cohen Arazi Date: Thu Aug 6 10:45:13 2026 -0300 Bug 43241: Add tests for datetime/boolean fixup inside DBIC operators This patch adds regression tests proving that RFC3339 datetime values and boolean values nested inside DBIC operators (-and, -or, -between) in q= queries are not converted to their DB-native format. Unit tests (Query.t): - Datetime: >=, <=, range, -or, -between, nested -and/-or, me. prefix - Boolean: true/false inside -and, -or, nested -and within -or - Non-datetime values pass through unchanged Integration tests (Objects.t): - Full API stack tests using action_logs with controlled data - Same operator/nesting coverage as unit tests Test plan: 1. Apply patch 2. Run: $ prove t/Koha/REST/Plugin/Query.t $ prove t/db_dependent/Koha/REST/Plugin/Objects.t => FAIL: Tests fail! Values inside -and are not converted 3. Sign off :-D Signed-off-by: Tomás Cohen Arazi Signed-off-by: David Nind Signed-off-by: Jonathan Druart Signed-off-by: Pedro Amorim commit 537a64a01b8a94f2157ffff1c9a3b29ae612aa45 Author: Martin Renvoize Date: Mon May 18 16:13:53 2026 +0100 Bug 42634: Force JWT cookie deletion on self-checkout logout The web-based self-checkout (opac/sco/sco-main.pl) builds the response JWT cookie with -expires => $jwt ? '+1d' : ''. On logout $jwt is undef, so the cookie is sent with an empty value and no Expires attribute - i.e. a session cookie with an empty value rather than a deletion. Some browsers/proxies retain the previous valid JWT in this scenario, which presents as the Finish/close button "doing nothing" or working only intermittently: the patron appears to remain logged in despite clicking Finish. Use a past Expires ('-1d') when no JWT is present so the browser deletes the cookie entirely. The subsequent ?op=logout render cannot then rediscover the patron from a stale cookie. Test plan: 1. Enable web-based self-checkout (SelfCheckoutByLogin or SelfCheckAllowByIPRanges). 2. Log in as a patron at /cgi-bin/koha/sco/sco-main.pl. 3. Open DevTools -> Application -> Cookies and note the JWT cookie. 4. Click the green "Finish" button. 5. Without the patch: Set-Cookie on the ?op=logout response shows JWT= with no Expires; the cookie persists as an empty session cookie. 6. With the patch: Set-Cookie shows JWT=; expires=; the browser removes the cookie entirely. 7. Confirm the kiosk reliably returns to the login screen and subsequent page loads do not re-authenticate from a stale JWT. Sponsored-by: OpenFifth Signed-off-by: David Nind Signed-off-by: Emmi Takkinen Signed-off-by: David Cook Signed-off-by: Pedro Amorim commit b719532f24551ce0039f41baab9933b0441c7415 Author: Andrew Nugged Date: Wed Aug 19 23:47:12 2026 +0300 Bug 42605: Add automated tests Store scalar preference values when saving the acquisition preferences and clean up the invoice explicitly. Exercise the rendered receiving behavior for a basket that inherits AcqCreateItem, one that explicitly overrides it with "ordering", and a basket that creates items on receiving. Keep the explicit "ordering" case as a control for behavior that already works without the production fix. This changes tests only. Automated tests: yarn cypress run --spec \ t/cypress/integration/Acquisitions/OrderReceivePreferences_spec.ts Co-Authored-By: Loontik Signed-off-by: David Nind Signed-off-by: Jonathan Druart Signed-off-by: Pedro Amorim commit 757b347bed2873b8851756f9c0444f66cd8dcddb Author: Andrew Nugged Date: Tue Aug 18 11:47:28 2026 +0300 Bug 42605: Preserve acquisition preference string values Bug 41563 converted AcqCreateItem and UniqueItemFields to booleans when adding them to Koha.prefs. Both preferences are strings: AcqCreateItem controls where items are created, while UniqueItemFields contains a pipe-separated list. When a basket inherits AcqCreateItem=ordering, the boolean value matches none of the receiving page branches and existing items are hidden. When items are created on receiving, the numeric UniqueItemFields value also fails when the page calls split(). Preserve both string values in Koha.prefs. Add a Cypress regression test which renders orderreceive.pl and verifies the values exposed to JavaScript. Test plan: Before applying the patch: 1. Set AcqCreateItem to "placing an order" and UniqueItemFields to "barcode". 2. Create a basket that inherits the system preference. Add an order with one item, close the basket, and start receiving it. 3. Confirm that the Items table has no existing item row and no Receive checkbox. 4. Set AcqCreateItem to "receiving an order" and open the same order for receiving. 5. Confirm that the Items section is empty: no item-entry fields and no Add item button are shown. Apply the patch. After applying the patch: 1. Set AcqCreateItem back to "placing an order" and open the order for receiving again. 2. Confirm that its existing item is listed and has a Receive checkbox. 3. Set AcqCreateItem to "receiving an order" and open the same order again. 4. Confirm that the Items section contains item-entry fields and an Add item button. Co-Authored-By: Loontik Signed-off-by: David Nind Signed-off-by: Jonathan Druart Signed-off-by: Pedro Amorim commit 3dc90004ca324e54340888356df5c88aa01c7122 Author: Tomás Cohen Arazi Date: Thu May 21 23:45:06 2026 -0300 Bug 39769: Flush L1 cache in es_indexer_daemon.pl before each batch The daemon runs as a long-lived process and never clears its L1 cache. System preference changes (e.g. IncludeSeeFromInSearches) are not picked up until the daemon is manually restarted. Flush L1 caches at the start of each commit() call so that preference changes take effect on the next indexing batch without requiring a daemon restart. Signed-off-by: Jonathan Druart Signed-off-by: Nick Clemens Signed-off-by: Pedro Amorim commit 279448539c4a2003f531cab955067acb9ef679c2 Author: Pedro Amorim Date: Fri Aug 28 16:22:47 2026 +0000 Bug 30144: DBRev 26.06.00.017 Signed-off-by: Pedro Amorim commit 08a2e0475b330e4459198f96a3876c458d14fd98 Author: Martin Renvoize Date: Tue Aug 18 16:57:38 2026 +0100 Bug 30144: (QA follow-up) Exclude servicing_instruction from order API representation The servicing_instruction column was not mapped in Koha::Acquisition::Order->to_api_mapping, so it was exposed verbatim in the REST API order representation. Since api/v1/swagger/definitions/order.yaml does not declare this property and sets additionalProperties: false, any request touching the acquisitions/orders endpoint (e.g. the "Receive shipments" page) failed with a 500 error: "Properties not allowed: servicing_instruction". Exclude it from the API representation, consistent with the other EDIFACT-only columns (line_item_id, suppliers_reference_number, etc.) that are already excluded the same way. Test plan: 1) Go to a vendor page, create a basket, add an order line, and close the basket. 2) From the vendor page, click "Receive shipments". 3) Without this patch, the orders table fails to load with a 500 error mentioning "Properties not allowed: servicing_instruction". 4) With this patch applied, the orders table loads correctly. 5) prove t/db_dependent/api/v1/acquisitions_orders.t Signed-off-by: Pedro Amorim commit 2e1f8b86607e42f56fb1ecb6055083e3f6362c1e Author: Martin Renvoize Date: Wed Aug 5 14:17:08 2026 +0100 Bug 30144: (QA follow-up) Make servicing instruction UI strings translatable The servicing instructions widget added new user-facing strings (alerts, button labels, hints) built entirely in JS via string concatenation, none of which were wrapped for translation - unlike the pre-existing JS in the same file, which already uses _() throughout. Wrap them the same way, using _("...").format(...) for the strings that interpolate a value, matching the existing pattern used elsewhere in this file. Signed-off-by: Pedro Amorim commit bbb7f406d2ec6ddf4a78bf923842ac99ec8b1326 Author: Martin Renvoize Date: Wed Aug 5 13:07:57 2026 +0100 Bug 30144: (QA follow-up) Fix stored XSS in servicing instructions The RM's review found two stored XSS vectors in the servicing instruction free-text (LVT) handling: 1. neworderempty.tt embedded the servicing instruction JSON (and the EDIFACT_SI authorised values list) directly into an inline