Commit Graph

6531 Commits

Author SHA1 Message Date
Lucas Gass 3fd93025ec
Bug 40255: (follow-up) Fix failure in add_debit tests
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-20 13:04:58 -06:00
Lucas Gass 5fc7fbbb21
Bug 40255: Allow scrubbed HTML to be used
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-17 09:18:14 -06:00
Lucas Gass 6b93327e6c
Bug 40255: Fixes for translations and updated unit tests
Signed-off-by: Scott Aylett <saylett@spanishfork.gov>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-17 09:18:14 -06:00
Lucas Gass 1f1e0c677d
Bug 40255: Allow for custom debit descriptions
To test:

1. Apply patch, restart all
2. Go to debit types and click "Show all debit types" and pick a system defined debit type to test with. I worked mainly with ACCOUNT ( Account creation fee ) but we should test with as many debit types as possible
3. Note the account_debit_types.code you are working with
4. Now go to Tools > Notices and Slips and create a new notice
5. Choose the "Debit description (custom)" module as your module.
6. Use the code from step 2 and 3.
7. In the "email" part of the noctice add something like:
    $[% accountline.amount %] (includes $[% accountline.amount * 0.1 %] tax)

8. Now go to  Administration > Patron categories and configure a patron category to have a "Enrollment fee". ( I set mine to 100 )
9. Now create a patron in that category.
10. Once created, visit the accounting tab for that patron.
11. You should see a description like "$100.00 (includes $10.00 tax)"
12. Try with other debit types to ensure this works with all.

Signed-off-by: Scott Aylett <saylett@spanishfork.gov>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-17 09:18:12 -06:00
Martin Renvoize 6f96128f5b
Bug 35267: (follow-up) Add POD coverage for private subroutines
Add POD documentation for private subroutines in C4/Letters.pm and
fix the POD heading for _type in Koha/Notice/Message.pm to satisfy
the pod_coverage QA check.

Also fixes a typo in the existing _add_attachments POD heading
(_add_attachements → _add_attachments).

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-17 09:18:10 -06:00
Martin Renvoize 49651ddaf0
Bug 35267: Apply preferences to preview and mailing
This patch applies the new style preferences to the preview display and
email sending code.

Sponsored-by: Open Fifth <https://openfifth.co.uk>
Signed-off-by: Marion Durand <marion.durand@biblibre.com>
Signed-off-by: Thomas Klausner <domm@plix.at>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-17 09:18:06 -06:00
David Cook 70ff8dd1b4
Bug 37713: Do not show record metadata if OPAC suppressed
This change checks the 942$n if OpacSuppression is enabled,
and does not return the record metadata if 942$n is true. See BZ 31161.

Test plan:
0. Apply the patch
1. koha-plack --restart kohadev
2. Enable syspref "OAI-PMH"
3. Set syspref "OpacSuppression" to "Hide"
4. Go to http://localhost:8080/cgi-bin/koha/oai.pl?verb=ListRecords&metadataPrefix=oai_dc
5. Note KOHA-OAI-TEST:2 metadata visible
6. Go to http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=2
7. Set 942$n to "Yes"
8. Go to http://localhost:8080/cgi-bin/koha/oai.pl?verb=ListRecords&metadataPrefix=oai_dc
9. Note KOHA-OAI-TEST:2 metadata no longer visible and instead "Record Status: deleted" shows

Signed-off-by: Roman Dolny <roman.dolny@jezuici.pl>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-17 09:18:03 -06:00
Jonathan Druart 49271d8d19
Bug 42394: Fallback to {} if no userenv set
It fixes the following warnings:
  Can't use an undefined value as a HASH reference at /kohadevbox/koha/Koha/BackgroundJob.pm line 128

I started to add mock_userenv to all our tests that enqueue but finally
found a test in t/db_dependent/Koha/BackgroundJob.t that expect an
userenv not set
102     is( $job->borrowernumber, undef, 'No userenv, borrowernumber undef' );

So falling back to {} seems safe for now.

Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-17 09:18:00 -06:00
Jonathan Druart 98ac202ddc
Bug 42394: Shallow-copy userenv in BackgroundJob->enqueue
C4::Context->userenv returns a direct reference to the singleton
userenv hash. enqueue() was mutating this reference — deleting
session_id and adding an interface key — which corrupted the
session state for the remainder of the request.

See bug 40943 comment 32 for a detailed explanation of the issue.

Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-17 09:17:59 -06:00
Pedro Amorim 7d574453ee
Bug 41954: Preparation: No longer delete 'empty' hold groups
Hold groups are now only deleted if no holds or old_holds are attached at the moment of cleanup

Signed-off-by: Bob Bennhoff <bbennhoff@clicweb.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-16 15:54:12 -06:00
Martin Renvoize fc6e5839d4
Bug 42221: Fix EAN-13 autoBarcode not incrementing
The EAN-13 barcode query used max(abs(barcode)) which allowed non-EAN-13
barcodes (e.g. 14-digit KTD sample barcodes) to dominate the result.
The saved 13-digit EAN-13 barcodes were always smaller, so $nextnum
never advanced and the same barcode was generated on every page load.

Fix by extracting EAN-13 logic into C4::Barcodes::ValueBuilder::EAN13
(consistent with other barcode type packages) and scoping the query to
WHERE length(barcode) = 13 so only valid-length barcodes contribute to
the max. Both barcode.pl and Koha::REST::V1::Biblios now call the shared
function, eliminating the duplicated inline code.

Tests: adds EAN-13 test cases to t/db_dependent/Barcodes_ValueBuilder.t
Signed-off-by: Caroline Cyr La Rose <caroline.cyr-la-rose@inlibro.com>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-16 15:54:09 -06:00
Martin Renvoize 757526cbde
Bug 42085: Slim down members/member-flags.pl cud-newflags block
Replace the raw-DBI permission write block (~62 lines) with a call to
$patron->set_permissions(\%granted). Logging and the superlibrarian
guard are now handled inside the object method.

The CGI layer now only parses the flag params into a granted hashref
and delegates — keeping the controller thin and the logic testable.

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-16 15:54:08 -06:00
Martin Renvoize d8062a1fe6
Bug 42085: Log patron permissions in MEMBERS/DELETE action log
When BorrowersLog is enabled, Koha::Patron::delete() now captures the
patron's granted permissions before deletion and includes them in the
MEMBERS/DELETE log entry.

Patrons with no granted permissions produce no permissions key in the
log. Patrons with permissions will show them as removed in the diff
viewer, consistent with all other fields in the DELETE diff.

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-16 15:54:08 -06:00
Martin Renvoize e32a108427
Bug 42085: Add Koha::Patron::set_permissions method
This new method moves the permissions write logic out of the CGI
controller and into Koha::Patron, making it testable and reusable.

Behaviour:
- Accepts a hashref of granted permissions only (matches the non-zero
  subset of permissions() return value)
- Computes the borrowers.flags bitmask from the userflags table
- Replaces user_permissions rows atomically inside a txn_do block
- Applies a session-aware superlibrarian guard when
  ProtectSuperlibrarianPrivileges is enabled
- Logs a MEMBERS/MODIFY action when permissions change and BorrowersLog
  is enabled, diffing before/after states

Tests added to t/db_dependent/Koha/Patrons.t.

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-16 15:54:07 -06:00
Mark Hofstetter 07ee120d50
Bug 39164: Add max_statement_time to SQL report queries
Reports may consume too much resources of the db. To avoid that a new config param
'report_sql_max_statement_time_seconds' was created, which sets the maximum time a report statement may
take to execute in seconds. If it takes longer it will be aborted.

reflowed the code, so that is is executed after is_sql_valid, and put
the code in a subroutine

Test plan:
0. if you run this test in ktd you should check if you are running
   mysql or mariadb, and then be able to switch database
1. create a new report by going to
More > Reports > New SQL Report
(/cgi-bin/koha/reports/guided_reports.pl?op=add_form_sql)

enter the following SQL (which only purpose is to run long)

select ExtractValue(bm1.metadata,'//controlfield[@tag="001"]')
  from biblio_metadata bm1
  join biblio_metadata bm2 on
    ExtractValue(bm1.metadata,'//controlfield[@tag="001"]') =
    ExtractValue(bm2.metadata,'//datafield[@tag="773"]/subfield[@code="w"]')

give the Report a name eg "extract_long"
2. run the report
3. report should be executed and return several 1000 lines on ktd
4. apply patch
5. add a the parameter to koha-conf.xml to the <config> </config> part
<report_sql_max_statement_time_seconds>0.001</report_sql_max_statement_time_seconds>

OR use the following statemtn
sed -i '/<biblioserver>/a <report_sql_max_statement_time_seconds>0.001<\/report_sql_max_statement_time_seconds>' /etc/koha/sites/kohadev/koha-conf.xml
6. restart koha
    koha-plack --restart kohadev
7. go back to the report page and rerun report which now should lead to an error
The following error was encountered:
The database returned the following error:
Query execution was interrupted, maximum statement execution time exceeded
Please check the log for further details.
8. in koha-conf.xml set
<report_sql_max_statement_time_seconds>20</report_sql_max_statement_time_seconds>
9. restart koha
10. rerun query, which now should execute as before
11. stop ktd, and restart with the "other" database

DB_IMAGE=mysql:8.0 ktd up
DB_IMAGE=mariadb:10.5 ktd up

12. repeat from step 1 (omit applying the patch)
13. Sign off
14. Thx

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:33 -06:00
Jacob O'Mara b6eeffb79f
Bug 17387: (follow-up) fix embed items on get deleted biblio
Add x-koha-embed parameter to GET /deleted/biblios/{biblio_id} and use
$c->objects->to_api() to properly handle embedding items.

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:28 -06:00
Martin Renvoize 06f4abbf9a
Bug 17387: (follow-up) address review feedback
- Fix permission description to explicitly mention bibliographic records
  and items (not just 'records')
- Fix date filter to use correct DB column per table: deletedbiblio uses
  'timestamp', deleteditems uses 'deleted_on'
- Remove leftover CAN_user_editcatalogue_edit_catalogue condition from
  the Batch editing section in cat-menu.inc; a user with only
  edit_catalogue would see an empty section heading
- Wrap biblio and item restore operations in DB transactions so that a
  failed store cannot leave a record inserted without being removed from
  the deleted table (or vice-versa)
- Catch Koha::Exceptions::Object::DuplicateID in the item restore
  controller and return a human-readable 409 instead of a generic 500
- When 'Restore bibliographic record' is clicked in the deleted-bib
  warning modal, fetch the biblio and open the full restore modal with
  the originally clicked item pre-selected, rather than restoring
  immediately without item selection

Signed-off-by: Michaela <michaela.sieber@kit.edu>
Sponsored-by: ByWater Solutions
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:27 -06:00
Jacob O'Mara c07639777d
Bug 17387: (follow-up) take library groups in to account for permissions check
Signed-off-by: Michaela <michaela.sieber@kit.edu>
Sponsored-by: ByWater Solutions
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:26 -06:00
Jacob O'Mara b1df5271f3
Bug 17387: add logging for biblio/item restore
This adds RESTORE action for biblios and items to the action log

Signed-off-by: Michaela <michaela.sieber@kit.edu>
Sponsored-by: ByWater Solutions
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:25 -06:00
Jacob O'Mara 4c3cf66681
Bug 17387: add items embed to deleted biblios API
Signed-off-by: Michaela <michaela.sieber@kit.edu>
Sponsored-by: ByWater Solutions
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:23 -06:00
Jacob O'Mara 4285de5deb
Bug 17387: add biblio embed to deleteditems return
Signed-off-by: Michaela <michaela.sieber@kit.edu>
Sponsored-by: ByWater Solutions
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:22 -06:00
Jacob O'Mara 321e9647ea
Bug 17387: add pagination headers and support comma-delimited Accept headers
Signed-off-by: Michaela <michaela.sieber@kit.edu>
Sponsored-by: ByWater Solutions
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:21 -06:00
Jacob O'Mara 98d74ff6bc
Bug 17387: item cannot be restored if associated biblio does not exist
Signed-off-by: Michaela <michaela.sieber@kit.edu>
Sponsored-by: ByWater Solutions
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:20 -06:00
Jacob O'Mara 1baf34b1c4
Bug 17387: reindex biblio/item on restore
Signed-off-by: Michaela <michaela.sieber@kit.edu>
Sponsored-by: ByWater Solutions
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:20 -06:00
Jacob O'Mara 6272f438d5
Bug 17387: add deleted biblios restore api
Signed-off-by: Michaela <michaela.sieber@kit.edu>
Sponsored-by: ByWater Solutions
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:18 -06:00
Jacob O'Mara d7ce637ed4
Bug 17387: add deleted items restore api
Signed-off-by: Michaela <michaela.sieber@kit.edu>
Sponsored-by: ByWater Solutions
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:18 -06:00
Jacob O'Mara 291261678b
Bug 17387: add restore functionality to Old::Biblio/Item
TEST PLAN: Item/Biblio Undelete Feature

  SETUP:
  - Apply patches and update database
  - Give test user "Restore deleted records" permission under Cataloging
permissions
  - Delete at least one bibliographic record with items attached
  - Delete at least one standalone item

  TESTING:

  Navigate to Cataloging:
  - Go to Cataloging home page
  - Click "Restore deleted records" link (should be visible with
permission)

  Test Restore Records Page:
  - Verify two tables appear: "Deleted bibliographic records" and
"Deleted items"
  - Verify deleted records appear in tables with biblio ID, title,
barcode, deleted date

  Test Date Filtering:
  - In left sidebar, enter date range in "Deleted from" and "Deleted to"
fields
  - Click "Apply" button - tables should filter by date range
  - Click "Clear" button - filter should reset

  Test Item Restore:
  - Find a deleted item in the items table (with existing biblio)
  - Click "Restore" button in Actions column
  - Verify success message appears and item disappears from table
  - Search catalog for the restored item - confirm it's back

  Test Biblio Warning Modal:
  - Find a deleted item whose biblio is also deleted
  - Click "Restore" button on the item
  - Modal appears warning "bibliographic record is deleted"
  - Click "Restore biblio" - biblio restores, modal closes
  - Click "Restore" on the item again - item now restores successfully
  - Verify both records are back in catalog

  Test Biblio with Items Restore:
  - Find deleted biblio with multiple deleted items
  - Click "Restore" button for the biblio
  - Modal shows checkboxes for each deleted item
  - Select some items (not all), click "Restore with selected items"
  - Verify biblio and selected items restore
  - Unselected items remain in deleted items table

  Permission Testing:
  - Remove "Restore deleted records" permission from user
  - Verify "Restore deleted records" link no longer appears in
Cataloging home

Signed-off-by: Michaela <michaela.sieber@kit.edu>
Sponsored-by: ByWater Solutions
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:17 -06:00
Owen Leonard 61c84f9e6a
Bug 8088: (follow-up) Tidy
Signed-off-by: Owen Leonard <oleonard@myacpl.org>
Signed-off-by: Laura_Escamilla <laura.escamilla@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:15 -06:00
Hammat Wele 22a13ba009
Bug 8088: Png-images of covers lost transparency
Images of covers must preserve transparency of png-images. This is
useful for example for the covers of CD/DVD disks.

Plan test :
1. Apply the patch.
2. In 'Koha administration > System preferences', set
   'LocalCoverImages' to 'show' and  OPACLocalCoverImages to 'show'.
3. Create a new bibligraphical record
4. In the record details select tab Images and click on upload
5. Click on « Drop files here » and select the image file to upload then
   click «Process images»
6. Search for the record created on step 3
7. Click on the record to view detail page (detail.pl)
8. Click on the image to view it fully
   ---> Notice the background is transparent
9. Save the image and open it
   ---> Notice the background is transparent

Signed-off-by: Owen Leonard <oleonard@myacpl.org>
Signed-off-by: Laura_Escamilla <laura.escamilla@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:15 -06:00
Baptiste 3af877e78a
Bug 39027: Order news on opac-main.pl by publication date instead of ID
On the OPAC, news should rather be ordered by the publication date than
by the creation date (~ id): a user want to see last published news
first and not last created news, regardless of their publication date

TEST PLAN:
1 - Create 3 news, have the 3 news published in an order different from
  their id (for example news 1 => 2020, news 2 => 2025, news 3 => 2023)
2 - Go on http://youropac/cgi-bin/koha/opac-main.pl -> news are ordered
  by ID
APPLY PATCH
3 - Repeat 2 -> news are ordered by Date

Signed-off-by: Mercury <mercury@coffee.geek.nz>
Signed-off-by: Laura_Escamilla <laura.escamilla@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 14:10:14 -06:00
Thomas Klausner 55308aa365
Bug 21820: Add ElasticsearchEnableZebraQueue syspref to control Zebra indexing when using Elasticsearch
Previously, Koha always added items to the Zebra queue when indexing via Elasticsearch. This adds a new system preference ElasticsearchEnableZebraQueue that allows disabling this behavior.

Unit Test:

* run the unit test t/db_dependent/Koha/SearchEngine/Elasticsearch/Indexer.t
* there should be a new subtest like this:

    1..5
    ok 1 - We start with an empty zebra queue
    ok 2 - new entry in zebraqueue for auth
    ok 3 - new entry in zebraqueue for biblio
    ok 4 - no new entry in zebraqueue for auth
    ok 5 - no new entry in zebraqueue for biblio
ok 5 - syspref ElasticsearchEnableZebraQueue

Manual Test Plan:

* Start ktd with Elasticsearch enabled
* As there is no interface to the zebraqueue, you need to connect to the DB and use SQL directly
  * ktd --dbshell
  * `select count(*) from zebraqueue;`
  * remember that number
* Edit any biblio or auth
* Check the zebraqueue again via SQL
* There should be one more entry

* Now apply the patch
* Then got to Sysprefs and set ElasticsearchEnableZebraQueue to `Disable`

* Edit another biblio or auth
  * has to be a different one, because Koha will prevent the same ID to be added again to zebraqueue
* Again check  zebraqueue via SQL
* There should be *no* new entry

QA Note:

the qa tool reports
[FAIL] Koha/SearchEngine/Elasticsearch/Indexer.pm
   FAIL	 pod_coverage
		sub _get_record has no pod coverage.

But this patch does not touch this code, so I guess this fail is caused by a stricter qa tool

Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 11:37:18 -06:00
Tomás Cohen Arazi caa52ddf91
Bug 22639: Add GET /config/search_mappings endpoint
This patch adds a new API endpoint to retrieve the search field
mappings configuration (search fields and their MARC mappings, grouped
by index).

The endpoint supports content negotiation:
- application/json (default)
- application/yaml (for export/import compatibility)

An optional 'type' query parameter allows filtering mappings by MARC
type (marc21 or unimarc).

Implementation notes:
- New controller: Koha::REST::V1::Config::SearchMappings
- Reuses Koha::SearchEngine::Elasticsearch::raw_elasticsearch_mappings
- Registers 'application/yaml' MIME type in Koha::REST::V1
- Permission: manage_search_engine_config

To test:
1. Apply this patch
2. Rebuild the API bundle:
   $ yarn api:bundle
3. Restart Plack
4. Run:
   $ prove t/db_dependent/api/v1/config_search_mappings.t
=> SUCCESS: Tests pass
5. Test manually:
   $ curl -u user:pass /api/v1/config/search_mappings
=> SUCCESS: Returns JSON with search field mappings
   $ curl -u user:pass -H 'Accept: application/yaml' \
     /api/v1/config/search_mappings
=> SUCCESS: Returns YAML output
   $ curl -u user:pass /api/v1/config/search_mappings?type=marc21
=> SUCCESS: Only marc21 mappings are returned
6. Sign off :-D

Signed-off-by: Laurence <laurence.rault@biblibre.com>
Signed-off-by: Thomas Klausner <domm@plix.at>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 11:37:17 -06:00
Thomas Klausner 45a9cd081d
Bug 39158: (QA follow-up) Fixing POD
Signed-off-by: Thomas Klausner <domm@plix.at>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 11:37:16 -06:00
David Gustafsson 37aca2b36c
Bug 39158: Reduce code duplication in marc_records_to_documents
Refactor marc_record_to_documents to avoid code duplicaiton.

To test:
1) Verify tests in t/db_dependant/Koha/SearchEngine/Elasticsearch.t all
   pass
2) Apply patch
3) Veriy tests in t/db_dependant/Koha/SearchEngine/Elasticsearch.t
   still pass

Signed-off-by: Thomas Klausner <domm@plix.at>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 11:37:16 -06:00
Lucas Gass bf2cffdba6
Bug 38136: DBIC
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 11:37:15 -06:00
Julian Maurice e7f483def5
Bug 38136: Refactor database translations (alternative)
This has the same goals of bug 24975 but with a different approach.

Bug 24975 tried the "POT/PO files as database tables" approach but
it appeared that is not what best fits our needs.

Instead of a "source text / translated text" mapping, we need
translations to be attached to a specific "object" (or table row), which
has the added benefit of keeping translations unaffected when the
original string changes. It may or may not be what you want but at least
this avoids the problem where fixing an error in the original string
forces users to re-translate everything.

We also need to be able to translate all translatable properties of a
particular entity.

The solution proposed here is to add a DBIC component that will add
several DBIC relations to the `localization` table:
* a relation named `localizations` that will return all translations for
  all translatable properties for all languages.
* for each translatable property:
  * a relation named `{$property}_localizations` that will return all
    translations for a given property, for all languages
  * a relation named `{$property}_localization` that will return the
    translation for a given property, for the current language

To enable translations for a particular entity, we will need to:
* add two method calls in the "result class" corresponding to the
  localizable object type
* in the admin interface, add a link to the the translation popup.
  Unlike bug 24975 there is not a page where you can translate
  everything. I don't know if it is really useful (probably better to
  translate itemtypes when you are on the itemtype page). It can be
  added later if needed.

To give an example, this is what needs to be added manually in
Koha/Schema/Result/Itemtype.pm:

    __PACKAGE__->load_components('+Koha::DBIx::Class::Localization');
    __PACKAGE__->localization_add_relationships(
        'itemtype', # the PK column
        'description' # a list of translatable columns
    );

(see POD in Koha/Schema/Component/Localization.pm for more information)

Final notes:
* I wanted this patch to have as few changes as possible so I kept
  methods like `search_with_localization`, `children_with_localization`
  even if I believe they can be removed (replaced by search/children)
  Localizations do not need to be prefetched/joined unless you want to
  sort on them.
  For the same reason, $itemtype->unblessed now always return a
  `translated_description` key.
  This can be fixed in followup patches
* There is cache involved (see
  Koha::Schema::Component::Localization::localization).
  Localizations in cache are grouped by object type and language (keys
  look like 'KOHA:localization:Itemtype:en')
* By keeping a single table for all translations of all entities, we
  cannot have data integrity at the RDBMS level, but automatic deletion
  of translations is handled by the DBIC `cascade_delete` feature, so
  integrity should be fine as long as no raw SQL update/delete operation
  is involved.

Signed-off-by: Caroline Cyr La Rose <caroline.cyr-la-rose@inlibro.com>
Signed-off-by: Katrin Fischer <katrin.fischer@bsz-bw.de>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 11:37:14 -06:00
Nick Clemens 99eb693cd9
Bug 41733: Honor EmailPatronRegistrations in the API
This patch adds new overrides opac_reg_yes and opac_reg_no to the patrons api
and will send a notice to the library when a patron is registered via the API if the pref is
active and the NO override is not sent, or the YES override is sent

To test:
1 - Apply patches
2 - yarn build
3 - restart_all
4 - run unit tests

Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 11:37:13 -06:00
Nick Clemens 925d5f50be
Bug 42107: Add search scores to staff results page
This patch ensures the scores from ES are passed through and added to the
search results in the staff side.

Initially hidden, the scores can be shown by entering the line below into the
JS console in developer tools in the browser, or added to IntranetUserJS to
make them always show:
$(".search_score").toggle();

To test:
1 - Have Koha using ES
2 - Do some searches, note results
3 - Apply patch, restart all
4 - Confirm results don't change
5 - Enter code from description into IntranetUserJS
6 - Do more searches
7 - Note the score is visible

To really note scores, you will need to add some weights to the ES config:
Set weights like:
title 32
author 16
subject 8

Signed-off-by: Andrew Auld <andrew.auld@openfifth.co.uk>
Signed-off-by: Thomas Klausner <domm@plix.at>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 11:37:12 -06:00
Jonathan Druart 6eed79ccf8
Bug 41994: Implement /sysprefs route to list system preferences
This new route will be used in a follow-up bug to auto-complete
sysprefs when using the header to search for sysprefs.

Note that this is based on top of bug 41834 that NULL options,
explanation and type in DB, but a future enhancement will populate the
response to return the values present in the yaml files.

Test plan:
1. Bundle the swagger specs and reload plack:
   yarn api:bundle && koha-plack --reload kohadev
2. Fetch the sysprefs using the new route:
 curl -u koha:koha --request GET 'http://${INSTANCE}-intra.localhost/api/v1/sysprefs' --header "Content-Type: application/json" | jq
3. Confirm that the tests pass
   prove t/db_dependent/api/v1/sysprefs.t

Signed-off-by: Owen Leonard <oleonard@myacpl.org>
Signed-off-by: Jan Kissig <bibliothek@th-wildau.de>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 11:37:05 -06:00
Lucas Gass 2186fa2798
Bug 41884: Make barcode a link to the item
Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 09:14:48 -06:00
Lucas Gass c42f58d196
Bug 41884: Add better reporting when item fails to modify via batch mod
To test:
1. Search for the system preference, PreventWithDrawingItemsStatus
2. Select both in transit and checked out, then save
3. Find an item and put it in transit.
4. Try to batch modify the item, making it withdrawen.
5. View the details of job, you should see 1 item could not be modified.
6. APPLY PATCH, restart_all
7. Try the steps again, this time when you get to the details of the job you should see more information on the item that failed:

    1 item could not be modified.
        39999000001457 : Item is in transit

8. Repeat this steps with a a checkout out item.
9. Also try with multiple items that will fail, ensure the display looks good.

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-15 09:14:48 -06:00
Lucas Gass c9c8ec5693
DBIC changes to Category.pm
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-14 16:11:42 -06:00
Janusz Kaczmarek 00c4c9a599
Bug 41863: Facets generated from Authorized values sometimes show empty labels
With facets generated from Authorized values, it may happen that the
list of authorized values lacks an entry for a code used in
a record. In this case, an empty entry will appear in the
facet list, with the number of uses but without any label.

It would be desirable to show at least the raw code, rather
than an empty label. This makes it easier to see which code has no
entry in the respective AV and to either add it or correct an
erroneous code.

Test plan:
==========

1. Have a standard KTD installation with Elasticsearch.
2. Add AV COUNTRY_CODES category and add two pairs (Authorized
   value, Description):
   * enk - England
   * xxu - United States

3. In the admin/searchengine/elasticsearch/mappings.yaml file,
   correct the definition of the pl search field as follows:

   pl:
-    label: pl
+    facet_order: 24
+    label: Publication place
     mandatory: ~
+    authorised_value_category: COUNTRY_CODES
     mappings:
-    - facet: ''
+    - facet: 1
       marc_field: 008_/15-17
       marc_type: marc21
       sort: 1

4. Reindex ES with:
   koha-elasticsearch --rebuild -r -d -b kohadev

5. Set the displayFacetCount systempreference to 'Show'
6. Perform a general search in the OPAC or staff interface: *
   View the 'Publication place' facet and notice a dozen empty
   entries, with only the number of occurrences.
7. Apply the patch ; restart_all
8. Repeat p. 6. You will see country codes that you can now add to AV
   or correct.

BTW, note that the tiny correction to Search.pm is analogous to
setting the label for 'special' types (i.e. itype, location, ccode,
holdingbranch, homebranch) two lines above.

Sponsored-by: Ignatianum University in Cracow
Signed-off-by: Roman Dolny <roman.dolny@jezuici.pl>

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-10 09:48:12 -06:00
Nick Clemens e83f14c5fe
Bug 41843: Gracefully handle XML errors during authority deletion
This patch updates move_to_deleted in Koha::Authorities to attempt to
handle encoding errors and delete a record with invalid marcxml.

If recoverable, the leader of the deletd record will be updated to
reflect deletion, but in the case it cannot the XML will be saved
unaltered.

To test:
1 - Find an authority record that is not used in your Koha
    More->Authorities, search for 'a'
    Record #1541 for example
2 - Make the record have errors:
    sudo koha-mysql kohadev
    UPDATE auth_header SET marcxml = UpdateXML(marcxml, '//datafield[@tag="040"]/subfield[@code="c"]',CONCAT('<subfield code',CHAR(27),'="c">OSt</subfield>')) WHERE auth_header.authid=1541;
3 - In the search results, click Actions->Delete
4 - Error 500!
5 - Apply patch, restart all
6 - Delete the record
7 - Success!

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

Amended-by: Jonathan Druart
tidy

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-10 09:48:12 -06:00
Jonathan Druart ebd8e4f169
Bug 42126: Make 00-strict.t run strict on all Perl files
The current implementation is wrong:
* we list explicitly the directories to process and it's not kept up-to-date (eg. booking and Koha are missing).
* we should process the files that are in the git repository, and use Koha::Devel::Files to list them.

This patch suggests to use git ls-files (through Koha::Devel::Files) and
process them one by one. Also only call strict_ok instead of
all_perl_files_ok.
all_perl_files_ok was doing strict_ok and syntax_ok, but syntax_ok is
doing a `perl -c` that is done in xt/pl_valid.t already

We can ignore xt and t: the tests are... run by the test suite.

On main:
Files=1, Tests=1360, 287 wallclock secs ( 0.05 usr  0.02 sys + 1677.26 cusr 268.46 csys = 1945.79 CPU)
real    4m46.741

all_perl_files_ok with t and xt:
Files=1, Tests=3488, 884 wallclock secs ( 0.10 usr  0.01 sys + 3820.09 cusr 720.63 csys = 4540.83 CPU)
real    14m44.086s

all_perl_files_ok without t and xt:
Files=1, Tests=2666, 586 wallclock secs ( 0.08 usr  0.03 sys + 2261.22 cusr 447.02 csys = 2708.35 CPU)
9m46.519s

With this patch (syntax_ok without t and xt):
Files=1, Tests=2663, 337 wallclock secs ( 0.09 usr  0.01 sys + 76.18 cusr 70.32 csys = 146.60 CPU)
real    5m36.870s

Note that Koha/Account/Credit.pm, Koha/Account/Debit.pm and Koha/Old/Hold.pm are failing strict_ok, we ignore them for now:
Inconsistent hierarchy during C3 merge of class 'Koha::Account::Credit':
        current merge results [
                Koha::Account::Credit,
        ]
        merging failed on 'Koha::Account::Line' at /usr/lib/x86_64-linux-gnu/perl-base/base.pm line 186.
BEGIN failed--compilation aborted at Koha/Account/Credit.pm line 22.

Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-10 09:48:11 -06:00
Martin Renvoize dee69bb7bf
Bug 41977: Hold fee not charged for title-level holds when all items are on order
Koha's convention is that notforloan <= 0 means "holdable" and
notforloan > 0 means "not holdable". This is documented in both
IsAvailableForItemLevelRequest() and CheckReserves():

  "item with negative or zero notforloan value is holdable"

_calculate_title_hold_fee() was only searching for items with
notforloan = 0 or IS NULL, silently excluding items with negative
notforloan values (e.g. -1 = "Ordered"). When all items on a title
had a negative notforloan status, no items were found, @fees was
empty, and the method returned 0 — causing no fee to be charged.

Fix the filter to use <= 0 so it matches the same holdability rule
used consistently elsewhere in the codebase.

Sponsored-by: OpenFifth <https://openfifth.co.uk>
Signed-off-by: Anneli Österman <anneli.osterman@koha-suomi.fi>
Signed-off-by: Roger fredricks <roger.fredricks@westsussex.gov.uk>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-10 09:48:05 -06:00
Pedro Amorim 5ae5ac3330
Bug 41849: Add cancel hold_group REST API endpoint
Signed-off-by: Bob Bennhoff <bbennhoff@clicweb.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-10 09:48:04 -06:00
Pedro Amorim 7dfc0db168
Bug 41878: Add Hold logging when grouping/ungrouping
Test plan:
-Enable DisplayAddHoldGroups and HoldsLog sys prefs
-Place 2 or more holds for a patron (regular, not grouped)
-Find the reserve_id for one of the holds, and view it in Tools > Logs. There will be an entry for the hold's creation.
-From the patron's account, select the holds and click 'Group selected'
-Run the log for the reserve_id, note that there is an entry generated by putting the hold into a group
-Suspend the hold, then view the log for that reserve_id. Note that the log entry for the suspension includes the hold_group_id.
-From the patron's hold tab, click on the number in the 'Hold group' column, then select 'Ungroup hold'
-Run the log for the reserve_id, and note that there is a new entry for ungrouping the hold

Signed-off-by: Bob Bennhoff <bbennhoff@clicweb.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-10 09:47:59 -06:00
Saiful Amin 7cdba6ba02
Bug 41383: SIP2 server does not search patrons by unique patron attributes (alternate IDs unusable in SIP2)
Currently, Koha::Patrons->find_by_identifier only searches for patrons by userid and cardnumber.

This patch extends this method to also search by unique borrower attributes (where unique_id=1).
This enhancement allows modules like SIP2 to identify patrons using alternative unique identifiers
defined in the system.

Test Plan:
1. Test current behaviour:
   - Go to Administration -> Patron attribute types
   - Create a new Patron attribute with:
      - Patron attribute type code = "AltID"
      - Description = "Alternate ID"
      - Unique identifier checkbox selected
      - Save
   - Edit any patron record and go to Additional attributes and identifiers
      - Add a value "altid123" to "Alternate ID"
      - Save
   - Use any SIP2 client to search for the patron using this attribute value. e.g.,
     `perl misc/sip_cli_emulator.pl -su term1 -sp term1 -l CPL --host localhost --patron altid123 -m patron_information`
   - Verify that the patron is NOT found with this message:
     'AFInvalid cardnumber'
     e.g.:
     READ: 64YYYY          00120260304    104611000000000000000000000000AOCPL|AAaltid123|AE|BLN|AFInvalid cardnumber|

2. Apply the patch.

3. Run the tests:
   prove t/db_dependent/Koha/Patrons.t
   - Verify that all tests pass, including the new cases for unique attribute lookup.

4. Test with patch applied:
   - Use any SIP2 client to search for the patron using this attribute value. e.g.,
     `perl misc/sip_cli_emulator.pl -su term1 -sp term1 -l CPL --host localhost --patron altid123 -m patron_information`
   - Verify that the patron is found with this message:
     'AFGreetings from Koha.'
     e.g.:
     READ: 64              00120260304    104328000000000000000000000000AOCPL|AA23529001000463|AEEdna Acosta|BLY|BHUSD|BV0|CC5|BD7896 Library Rd. Portland, OR 44240|BF(212) 555-1212|PB19800424|PCPT|PIY|AFGreetings from Koha. |

5. Test non-unique attribute:
   - Go to Administration -> Patron attribute types
   - Edit "Alternate ID" attribute type
      - Unique identifier checkbox NOT selected
      - Save
   - Use any SIP2 client to search for the patron using this attribute value. e.g.,
     `perl misc/sip_cli_emulator.pl -su term1 -sp term1 -l CPL --host localhost --patron altid123 -m patron_information`
   - Verify that the patron is NOT found with this message:
     'AFInvalid cardnumber'
     e.g.:
     READ: 64YYYY          00120260304    104611000000000000000000000000AOCPL|AAaltid123|AE|BLN|AFInvalid cardnumber|

6. Sign-off

Signed-off-by: Syed Faheemuddin <faheem@nexuslib.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-09 08:27:37 -06:00
Lucas Gass 8357a8f074
Bug 41880: Keep original copy of hold for proper logging
To test:
0. APPLY PATCH, restart_all.
1. Make sure HoldsLog is enabled
2. Place a record/bib level hold on bib A.
3. Move the hold to bib B.
4. Look at the diff column in the holds log. You should see the Old (0) biblionumber and the New (N) biblionumber. You should see nothing about itemnumber because this is a record level hold.
5. Ensure that the New and Old biblionumbers are the correct ones.
6. Place an item level hold on item A.
7. Mobe the hold to item B
8. Look at the diff column in the holds log. You should see the Old (0) biblionumber and the New (N) biblionumber. You should also see the New and Old itemnumbers since this is an item level hold.
8. Ensure that the New and Old biblionumbers AND the New and Old itemnumbers are correct.

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-09 08:27:31 -06:00