Commit Graph

8880 Commits

Author SHA1 Message Date
Eric Phetteplace 33a8dc5d6f
Bug 42723: Purchase suggestion 500 when EmailPurchaseSuggestions is set to BranchEmailAddress
Testing plan:
- Set EmailPurchaseSuggestions to BranchEmailAddress
- Go to create suggestion on the staff side /suggestion/suggestion.pl?op=add_form
- Create a suggestion with Library: Any
- 500 error: inbound_email_address is not a method of undef
- Apply patch & repeat
- Suggestion created

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-06-08 13:53:24 -06:00
Pedro Amorim 125416a92d
Bug 42733: Add tests
Apply tests patch only, run:
yarn cypress run --spec t/cypress/integration/Tools/ManageMarcImport_spec.ts

Confirm it fails with the same error as Jenkins:
Timed out retrying after 10000ms: `cy.wait()` timed out waiting `10000ms` for the 2nd request to the route: `jobPoll`. No request ever occurred.

Apply fix patch. Run again.

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-06-08 13:53:10 -06:00
Jonathan Druart 147720ca1d
Bug 42606: Add Cypress tests
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-26 08:37:34 -06:00
Jonathan Druart 7cdae514a4
Bug 27826: Turn on WebBasedSelfCheck to fix Cypress tests
On Jenkins, SCO is not setup like it is in ktd after a reset_all
from insert_data.pl
251 sub configure_selfcheckout {

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-26 08:37:26 -06:00
Lucas Gass 4f58ed8953
Bug 27826: (RM follow-up) Attempt to fix failing test
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-22 17:12:22 -06:00
Martin Renvoize 7a7a3ab2c6
Bug 40383: (follow-up) Fix EdifactModal Cypress test reliability
Three fixes to make all 27 EdifactModal Cypress tests reliably pass:

1. insertData.js: fix currency FK constraint violation (use an existing
   currency rather than creating an undefined one).

2. Koha/Edifact/File.pm: add missing use statements for File::Basename
   and File::Temp so the Cypress task can load the module.

3. EdifactModal_spec.ts:
   - Replace arbitrary cy.wait() calls with condition-based assertions
     throughout (expand/collapse animations, Bootstrap modal state).
   - Fix search timing: add cy.intercept + cy.wait("@ediMsg") in the
     Search Functionality beforeEach so initializeSearch is wired before
     input events fire; use invoke("val")+trigger("input") instead of
     type() to bypass the 500ms debounce race.
   - Fix modal close tests: attach a shown.bs.modal listener before
     opening the modal so we can wait for Bootstrap's 300ms show
     animation to complete (_isTransitioning → false) before calling
     hide(). Without this wait, hide() is silently blocked when the Ajax
     response is faster than the animation (common in KTD). Use
     bootstrap.Modal.getInstance().hide() as the programmatic fallback
     since data-bs-dismiss and keyboard events are unreliable in headless
     Cypress/Electron.
   - Fix edifact_interchange.js: remove duplicate &format=json appended
     to URLs that already contain it.

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-22 12:59:11 -06:00
Martin Renvoize 7048a657e7
Bug 40383: (follow-up) Fix failing EdifactModal Cypress tests
Three issues caused the EdifactModal_spec.ts test suite to fail:

1. insertSampleEdifactMessages inserted a vendor with listprice='USD' and
   invoiceprice='USD', but those columns have FK constraints against the
   currency table and USD is not pre-populated in KTD's default install.
   Drop the currency columns from the INSERT so they default to NULL.

2. Koha::Edifact::File::vendor() and basket() called
   Koha::Acquisition::Bookseller and Koha::Acquisition::Basket without
   use-ing them. The edifactmsgs.pl table embeds both on every API call,
   so a missing module caused a 500 and an empty table.

3. showMessage() already appended &format=json before delegating to
   showMessageFromUrl(), which then appended it a second time. Remove the
   redundant copy from showMessage().

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-21 10:49:43 -06:00
Pedro Amorim b5efda989f
Bug 37762: (QA follow-up): Fix insertData.js
cypress run --spec t/cypress/integration/t/insertData.ts

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-21 10:21:40 -06:00
Lucas Gass 1ed156ef40
Bug 42545: (RM follow-up) Tidy test file
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-20 09:04:54 -06:00
Martin Renvoize 0a0dcee152
Bug 42545: (QA follow-up) Add unit tests for holiday subtraction when return time is before due time
Adds a dedicated subtest covering the specific scenario: daily loans have a
due time of 23:59, so a return earlier in the day on a holiday would
previously cause days_between to exit its loop before processing the end
date, silently skipping the holiday subtraction.

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-20 09:04:54 -06:00
Martin Renvoize 6ca7c1118b
Bug 42545: (QA follow-up) Add POD for _init and _holidays and fix incorrect test expectation
The existing 'days: 289 hours, 2 holidays' test asserted 8-1=7 but its own
description named 2 holidays. The expected value matched the buggy behaviour
(loop exiting before the holiday end date) rather than the correct result.
Update to 8-2=6 to reflect the fix.

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-20 09:04:53 -06:00
Kyle M Hall 7dc223b30a
Bug 42547: Fix SIP performance if sip2_resource_last_modified not set in cache
In Koha, the sip2_resource_last_modified key is updated any time the SIP config is edited from the SIP2 editor in the staff interface ( institutions, accounts, system preference overrides ).

If sip2_resource_last_modified is missing from memcached, Koha SIP emits "Couldn't find sip2_resource_last_modified, considering configuration not up to date" in the logs, and fetches the entire configuration from the database, *for each request*.

This causes SIP performance to drop significantly. In my production testing with 83 institutions and 171 accounts the login message response time dropped from 0.283 seconds to 2.899 seconds.

I think the solution is pretty simple. If there is a cache miss on sip2_resource_last_modified, SIP should load the config from database as it does already, but then it should also set sip2_resource_last_modified!

Test Plan:
1) Apply this patch
2) Prove t/db_dependent/Koha/SIP2/SIP2ModuleMigration.t

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-18 09:30:20 -06:00
Jonathan Druart 4da822b6a1
Bug 42578: Prevent Koha/Hold.t to fail on date comparison
We must use t::lib::Dates::compare to compare dates in test.

Test plan:
Confirm that `prove t/db_dependent/Koha/Hold.t` still returns green
after this patch

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-18 09:30:19 -06:00
Pedro Amorim e931157dc7
Bug 42255: Add tests
prove t/db_dependent/Reserves.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-05-18 09:30:15 -06:00
Lucas Gass 11d7450e2a
Bug 37762: (RM follow-up): Tidy t/db_dependent/api/v1/iso18626/request.t
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-15 08:11:13 -06:00
Pedro Amorim 4b525d9e3e
Bug 37762: (QA follow-up): Warn and return 500 on schema validation failure in progress_request
An exception is not implemented as progress_request is called in
different circulation contexts (hold is placed, checkout happens,
etc) and those actions should not be dependent on the outcome of
progress_request, i.e. if progress_request fails due to schema
validation failure, the circulation action should happen as before.

prove t/db_dependent/Koha/ILL/ISO18626/Request.t
prove t/db_dependent/api/v1/iso18626/request.t
cypress run --spec t/cypress/integration/ILL/SupplyingRequests.ts

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-15 08:11:12 -06:00
Pedro Amorim 4cf61f6c73
Bug 37762: (QA follow-up): Fix cypress ILL tests
cypress run --spec t/cypress/integration/ILL/*

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-15 08:11:10 -06:00
Brendan Lawlor 9f8cb217c0
Bug 32773: (follow-up) Unit tests
Signed-off-by: Miranda Nero <mnero@oslri.net>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-08 16:05:37 -06:00
Pedro Amorim 3595d2abe8
Bug 37762: (QA follow-up): Address test files QA complaints
Signed-off-by: Jason Robb <jrobb@sekls.org>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-08 12:25:52 -06:00
Pedro Amorim 23c85e598e
Bug 37762: (QA follow-up): Add loading while action is processing
This also prevents clicking other action buttons while an action is midflight

cypress run --spec t/cypress/integration/ILL/SupplyingRequests.ts

Signed-off-by: Jason Robb <jrobb@sekls.org>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-08 12:25:48 -06:00
Pedro Amorim 86c479edee
Bug 37762: Add additional tests
cypress run --spec t/cypress/integration/ILL/SupplyingRequests.ts
cypress run --spec t/cypress/integration/ILL/RequestingAgencies.ts
prove t/db_dependent/Koha/ILL/ISO18626/Request.t

Signed-off-by: Jason Robb <jrobb@sekls.org>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-08 12:25:47 -06:00
Pedro Amorim f036c92ec0
Bug 37762: Send message back to requesting agency
This commit applies changes to V1.pm to be able to utilize the same XML methods.
We're also updating tests to reflect this new functionality.

Signed-off-by: Jason Robb <jrobb@sekls.org>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-08 12:25:46 -06:00
Pedro Amorim 29c1a17da5
Bug 37762: Add ISO18626 tests
prove t/db_dependent/api/v1/iso18626/request.t
prove t/db_dependent/api/v1/iso18626/requesting_agencies.t

Signed-off-by: Jason Robb <jrobb@sekls.org>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-08 12:25:40 -06:00
Martin Renvoize 6f62a8fe30
Bug 41919: Limit number of current reports a single user can run simultaneously
Building on bug 41918, it would be good to limit the number of reports
a given user can run at a time. This control is config-file based as
it is tied to the server resources provided by the administrator.

Adds a new koha-conf.xml option total_running_reports_per_user_limit
(0 = disabled). When the configured limit is reached, the user is told
"You have too many reports running at the moment. Please wait for some
of them to finish before running this report again."

Note on implementation: the original patch on bz (attachment 198034)
performed a second pre-flight Koha::Reports->running call in
reports/guided_reports.pl. The bug 41918 QA follow-ups already pushed
the duplicate-running guard into Koha::Report::prep_report so it would
fire across the staff UI, svc/report and opac/svc/report. This patch
extends that same model-layer architecture rather than reintroducing
controller-layer enforcement:

- New Koha::Exceptions::Report::TotalRunning subclass of
  Koha::Exceptions::Report.
- prep_report now performs a second check after the duplicate one and
  throws TotalRunning when the user has total_running_reports_per_user_limit
  reports of any kind already in flight.
- All three callers (guided_reports.pl, svc/report, opac/svc/report)
  catch the new exception alongside DuplicateRunning, mapping it to a
  new template error key total_running_reports_limit_exceeded for the
  staff UI and a JSON {error: total_running_reports_limit_exceeded}
  with HTTP 429 for the JSON endpoints.

Test Plan:
1) Set total_running_reports_per_user_limit to 3 in your koha-conf.xml.
2) Create 3 long-running reports like:
   SELECT COUNT(*) FROM items i1 JOIN items i2 JOIN items i3 JOIN items i4 JOIN items i5 JOIN items i6
3) Run each report in turn by opening a new tab, browsing to the
   reports module, and choosing to run that report.
4) Note the first three continue to run, but a fourth attempt gives
   the new "too many reports running" alert in the staff UI.
5) Repeat against /cgi-bin/koha/svc/report?id=N and confirm an HTTP
   429 response with JSON {error: total_running_reports_limit_exceeded}.
6) Set total_running_reports_per_user_limit back to 0 and confirm
   normal behaviour resumes.
7) prove t/db_dependent/Koha/Reports.t -> 13 tests pass, including a
   new subtest for TotalRunning.

Signed-off-by: Alex Carver [Acerock7] <alex@rcls.org>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-07 07:56:21 -06:00
Martin Renvoize 63af0dd4fd
Bug 41918: (QA follow-up) Scope processlist query to the current DB user
Koha::Reports->running queries information_schema.processlist. Without
the PROCESS privilege MariaDB/MySQL already restrict the view to the
caller's own threads, so the previous code worked correctly on stock
deployments. However, if a site has granted broader privileges (eg. a
shared MariaDB server where the Koha DB user happens to also have
PROCESS, or a custom role) the query would happily count threads
belonging to other database users - another Koha instance, a reporting
tool, or any unrelated application sharing the server.

Add an explicit

    user = SUBSTRING_INDEX(CURRENT_USER(), '@', 1)

clause so the query is always scoped to the connection's MySQL user,
regardless of what privileges that user has been granted. The
processlist.user column holds just the username with no host, so we
strip the '@host' off CURRENT_USER() before comparing.

The new clause has no bind parameters - it is a constant per
connection - so the existing parameterised-bind assertions in the
running subtest still hold. A small additional assertion verifies the
clause is present in the SQL text.

Test plan:
1) prove t/db_dependent/Koha/Reports.t (now 12 tests, all pass).
2) Optional: in a multi-tenant MariaDB, grant PROCESS to the Koha DB
   user, start a long query as a different DB user with the marker
   /* { saved_sql.id: 1 } { user_id: 1 } */ in its info, then call
   Koha::Reports->running. Confirm the foreign thread is NOT counted.

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-07 07:56:20 -06:00
Martin Renvoize 7d12dac853
Bug 41918: (QA follow-up) Enforce duplicate-running limit in the model
The previous patches enforced duplicate_running_reports_per_user_limit
only in reports/guided_reports.pl. Two other code paths run saved SQL
reports for an authenticated user and bypassed the guard entirely:

- svc/report (staff JSON endpoint, eg. used for chart widgets and any
  caller hitting /cgi-bin/koha/svc/report?id=N directly)
- opac/svc/report (OPAC JSON endpoint)

A user who hit their staff-UI limit could still spawn additional copies
of the same long-running report via either svc endpoint, defeating the
guard.

Push the check into Koha::Report::prep_report itself, which is the one
function all three callers go through. When the limit is reached
prep_report now throws a new Koha::Exceptions::Report::DuplicateRunning
(carrying report_id, user_id and the configured limit). The guided
reports CGI catches it and re-renders the existing "report is already
running" alert; both svc/report endpoints catch it and return HTTP 429
Too Many Requests with a JSON { error: "duplicate_running_report" }
payload.

Notes:
- The pre-flight Koha::Reports->running call is removed from the
  guided-reports CGI; the model now owns that policy and the
  controller is just rendering. The TOCTOU window remains identical
  in size and is the same intentional trade-off as before.
- Limit=0 (the default) and absent userenv both bypass the check, so
  CLI cronjobs (misc/cronjobs/runreport.pl) and direct DB users are
  unaffected.

Test plan:
1) prove t/db_dependent/Koha/Reports.t - now includes a new subtest
   covering limit=0 (lives), under limit (lives) and at limit (throws
   Koha::Exceptions::Report::DuplicateRunning).
2) Set duplicate_running_reports_per_user_limit=1 in koha-conf.xml,
   restart_all.
3) Open a long-running report in two staff tabs - second tab shows
   the "report is already running" alert (unchanged behaviour).
4) While one run is in flight, hit /cgi-bin/koha/svc/report?id=N as
   the same user - now returns HTTP 429 with the JSON error payload
   (previously: ran a second copy of the query).
5) Same check on /cgi-bin/koha/opac/svc/report?id=N.

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-07 07:56:19 -06:00
Martin Renvoize c8f0f064d6
Bug 41918: (QA follow-up) Add unit tests for Koha::Reports->running
Adds a 'running' subtest covering the new method:

- Returns a Koha::Reports resultset (object identity, not opaque ids).
- Returns an empty resultset when nothing is in flight, and when the
  user_id / report_id filter does not match any process.
- Parses saved_sql.id markers out of synthetic processlist rows and
  resolves them to real Koha::Report objects, ignoring rows without
  the marker.
- Sends parameterised LIKE binds for user_id (proves the WHERE clause
  is built without string interpolation).
- Falls back to an empty resultset and does not propagate the
  exception when the underlying selectall_arrayref call dies, eg.
  when the database user lacks PROCESS privilege.

To make the failure path mockable without trying to mock DBI::db
(which is not loadable as a separate file), the dbh selectall is
extracted into a tiny Koha::Reports::_processlist_rows helper so
Test::MockModule can replace it cleanly. The helper is otherwise
behaviour-preserving.

Test plan:
1) prove t/db_dependent/Koha/Reports.t -> 11 tests, all pass.
2) Existing prep_report subtest still passes.

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-07 07:56:18 -06:00
Martin Renvoize 521ddaacad
Bug 41918: (QA follow-up) Update prep_report tests for new SQL comment
The first patch in this series changed the SQL comment appended by
Koha::Report::prep_report from

    /* saved_sql.id: <id> */

to

    /* { saved_sql.id: <id> } { user_id: <user_id> } */

but did not update the existing assertions in t/db_dependent/Koha/Reports.t,
leaving three subtest failures in the prep_report subtest. Refresh the
expected strings to match the new format and pick up the userenv-derived
user_id (or 0 when no userenv is in scope, matching prep_report's fallback).

Test plan:
1) Apply this follow-up.
2) prove t/db_dependent/Koha/Reports.t
3) Verify all 10 tests pass (previously 3 subtests of prep_report failed).

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-07 07:56:17 -06:00
Matt Blenkinsop 25a2e938fa
Bug 26355: (follow-up) Fix cypress tests
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-07 07:56:15 -06:00
Jonathan Druart 084c07a0fe
Bug 27826: Fix OPAC/SCO_spec.ts
1. The DELETE in afterEach was not needed
2. The test was failing randomly on the UPDATE of the beforeEach
  `Duplicate entry '+42+' for key 'itembarcodeidx'`
I don't explain it, but this patch should fix the failure.

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-07 07:56:14 -06:00
Pedro Amorim 5d716d1e28
Bug 41956: Remove visual_hold_group_id
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-05-06 15:21:00 -06:00
Pedro Amorim 2ed777a854
Bug 41983: (QA follow-up): Rebuild the holds queue when groups are edited
Test plan (ktd)
0) Update the database (ktd, ignore if sandbox):
  $ updatedatabase
1) Enable 'RealTimeHoldsQueue' and 'DisplayAddHoldGroups' system preferences
2) restart_all # This is required or the worker won't pick up the new sys pref values
3) Place at least 2 holds for a patron on different items/biblios, without grouping them:
  <staff_url>/cgi-bin/koha/members/moremember.pl?borrowernumber=19
4) Build the holds queue once to get a baseline:
  $ perl misc/cronjobs/holds/build_holds_queue.pl --force
5) Visit the holds queue and confirm the holds appear without a group indicator:
  <staff_url>/cgi-bin/koha/circ/view_holdsqueue.pl?branchlimit=&itemtypeslimit=&run_report=1
6) Create a hold group for those holds via the patron page
 <staff_url>/cgi-bin/koha/circ/circulation.pl?borrowernumber=<patron_borrowernumber>
7) Without running build_holds_queue.pl, refresh the holds queue, confirm the 'part of a hold group' indicator now appears
8) Ungroup the holds, refresh the holds queue, confirm the 'part of a hold group' is no longer showing
9) Run the unit tests:
  $ prove t/db_dependent/Reserves/HoldGroup.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-05-06 15:20:58 -06:00
Lucas Gass 04a3371ee9
Bug 42103: (follow-up) Fix failing test
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-06 09:52:19 -06:00
Matt Blenkinsop c23b2fb2f7
Bug 26355: (follow-up) Add a fallback value for patronValues in insertSamplePatron
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-06 09:52:18 -06:00
Martin Renvoize dfb875154d
Bug 42188: (follow-up) Update ModBiblio test for new info format
The 'ModBiblio on invalid record' subtest asserted the action log info
column contained 'biblionumber', but Koha::Biblio::_unblessed_for_log
restricts the logged hash to frameworkcode, opac_suppressed, and serial.
The biblionumber lives in action_logs.object, not in the info JSON.

Update the regex to match the new "biblio { ... }" prefix format set by
C4::Log::logaction so the assertion confirms biblio data is logged
without depending on a field that is intentionally excluded.

Test plan:
1. prove t/db_dependent/Biblio.t

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-06 09:52:11 -06:00
Martin Renvoize e7eb27d48e
Bug 29016: (QA follow-up) Expand action_logs API and test coverage
Build on the new ActionLog DBIC relations to expose them as opt-in
embeds, and shore up gaps in the test coverage that QA flagged.

Swagger
  * paths/action_logs.yaml declares an x-koha-embed header parameter
    constrained to the supported relations (librarian, patron).
  * definitions/action_log.yaml adds the corresponding optional
    `librarian` and `patron` object fields.

Tests
  * Convert the single 24-test subtest into focused subtests:
    - authorization (403 vs 200)
    - simple typed filters
    - q-DSL: multi-value module, info LIKE, combined filter
    - embeds: librarian and patron, with and without x-koha-embed
    - pagination + X-Total-Count and order_by
  * Mock AuthSuccessLog/AuthFailureLog/BorrowersLog to 0 to remove the
    flakiness where the API GET itself appended an AUTH log row between
    delete and assertion.
  * Build the test librarian with both `tools` and `borrowers` flags so
    embedded patron fields are not redacted by Koha::Patron->is_accessible.
  * Use a small build_fixtures() helper to avoid copy-pasting setup
    across subtests.

Test plan:
  1. yarn build && restart_all  (refresh swagger bundle)
  2. prove t/db_dependent/api/v1/action_logs.t  (all subtests green)
  3. curl -H 'x-koha-embed: librarian,patron' \
       /api/v1/action_logs?module=MEMBERS  -- patron and librarian
       objects appear in the response.

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-05 13:42:09 -06:00
Kyle M Hall 4edc85cf29
Bug 29016: Add unit tests
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-05 13:42:07 -06:00
Martin Renvoize 4cead7f389
Bug 42032: (follow-up) Eliminate phantom indicator diffs on authority MODIFY
Authority MODIFY action log diffs were emitting bogus entries for every
field with a default ' ' indicator on every save, producing output like:

    _marc.fields.1.040.ind1   ' '   ' '
    _marc.fields.1.040.ind2   ' '   ' '
    _marc.fields.2.150.ind1   ' '   ' '
    _marc.fields.3.667.ind1   ' '   ' '
    ...

even when only a single subfield was edited.

Root cause: Struct::Diff treats a utf8-flagged ' ' as different from a
non-utf8 ' ' SV, even though Perl's eq returns true. ModAuthority was
sourcing the two sides of the diff from different parse paths:

  - $oldrecord = GetAuthority($authid)
      -> MARC::Record->new_from_xml(...) -> indicators arrive utf8-flagged
  - $record (the "after" side) was the in-memory MARC built by the
      editor from CGI parameters -> indicators are bare byte strings

The XML round-trip through AddAuthority normalises the stored copy, but
the in-memory $record we passed to logaction had not been re-parsed, so
every default indicator looked like a change to Struct::Diff.

Fix: after AddAuthority commits the new state, re-read it via
$new_authority->record and pass that to _authority_log_data. Both sides
of the diff now traverse the same new_from_xml path, utf8 flags match,
and Struct::Diff falls silent on unchanged indicators.

This is the symmetric counterpart of how biblios already work in
C4::Biblio: ModBiblio sources both $original_marc and $updated_marc_log
through Koha::Biblio::Metadata::record_strip_nonxml (which also calls
new_from_xml), so biblios don't hit this.

The deeper asymmetry between in-memory editor MARC and stored MARC
(default indicators only get normalised on the XML round-trip) is left
for a separate bug; this patch keeps the action log diff clean without
touching the storage path.

Test plan:
1. prove t/db_dependent/AuthoritiesMarc.t
2. Enable AuthoritiesLog. Edit any authority via the staff UI making a
   single subfield change. Open Tools -> Log viewer for that authority
   and verify the diff column lists only the changed subfield (plus the
   005 timestamp), with no rows for ind1/ind2 of untouched fields.
3. Re-run the same edit a second time changing nothing else; the diff
   should again be limited to 005 + the touched subfield.

Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-05 13:42:06 -06:00
Martin Renvoize 55e2810aac
Bug 42032: (follow-up) Write state JSON to AUTHORITIES info column
Mirrors bug 42188's biblio info-column convention. Generalise the
CATALOGUING-only info-column rewrite branch in C4::Log::logaction to
cover AUTHORITIES as well, using a module-aware prefix map so each
module gets its own prefix ("biblio" / "authority"). For AUTHORITIES
this means MODIFY now stores "authority {pre-change JSON}" and DELETE
stores "authority {final-state JSON}", while ADD stays bare
"authority" (the call site passes the string directly and ADD is
guarded anyway). The biblio behaviour is preserved bit-for-bit.

Test plan:
1. prove t/db_dependent/AuthoritiesMarc.t
2. Enable AuthoritiesLog. Create / modify / delete an authority via
   the staff UI, then check action_logs:
   - ADD    row's info column reads just "authority"
   - MODIFY row's info column reads "authority { ... pre-change JSON ... }"
   - DELETE row's info column reads "authority { ... final-state JSON ... }"
3. prove t/db_dependent/Koha/Biblio.t
4. prove t/db_dependent/Log.t

Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-05 13:42:05 -06:00
Martin Renvoize 3ca746f4fe
Bug 42032: (follow-up) Use MARC-in-JSON for authority action log diffs
Mirrors the bug 42188 main commit for biblios. Replace the custom
_marc_record_to_diffable helper with MARC::Record::MiJ's to_mij_structure
so AUTHORITIES action log diffs use the same MARC-in-JSON shape as
CATALOGUING diffs. Add Koha::Authority::_unblessed_for_log to expose
only the auth_header columns that exist independently of the MARC
record (authtypecode, origincode, linkid), avoiding duplication of
MARC-derived data (heading, authtrees), raw marcxml, and system
timestamps. The explicit whitelist also removes the need for the
post-hoc empty-field stripping added in a previous follow-up.

Test plan:
1. prove t/db_dependent/AuthoritiesMarc.t
2. Enable AuthoritiesLog. Create, edit, and delete an authority via
   the staff UI, then inspect action_logs.diff in the DB:
   - _marc contains MiJ shape { leader: "...", fields: [ { tag: {...} }, ... ] }
   - Changes to heading fields produce diffs on
     _marc.fields[N].TAG.subfields[0].a
   - The diff no longer carries a redundant top-level heading entry
     (it's MARC-derived and now only appears in _marc).
3. prove t/db_dependent/Log.t
4. prove t/db_dependent/Koha/Authority.t
5. prove t/db_dependent/Koha/Biblio.t

Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-05 13:42:04 -06:00
Martin Renvoize 79b42fec90
Bug 42032: Include MARC field changes in AUTHORITIES diff logs
The AUTHORITIES action log entries for authority operations previously
stored minimal data: ADD and DELETE logged just the string "authority",
while MODIFY logged a raw MARC-formatted dump of the pre-change record
as a plain text string ("authority BEFORE=>..."). None of these populated
the diff column.

This patch updates AddAuthority, ModAuthority and DelAuthority in
C4/AuthoritiesMarc.pm to follow the same pattern introduced for biblios
in bug 40135: passing combined hashrefs to logaction so that the diff
column is automatically populated with Struct::Diff JSON.

The combined hashref contains the auth_header table columns (authtypecode,
heading, origincode, etc. — excluding the raw marcxml blob) plus a _marc
key built by the new _marc_record_to_diffable helper, which converts a
MARC::Record into a tag => array-of-formatted-strings structure readable
by the existing diff viewer.

A new _authority_log_data helper packages the authority unblessed data
and MARC record into the combined hashref, keeping the three call sites
concise.

For ADD, the logaction is now emitted after the authority record has been
fully stored (after _after_authority_action_hooks), so the diff captures
the complete saved state. For MODIFY, the before-state is captured from
Koha::Authorities before calling AddAuthority, and the after-state is
read back once AddAuthority has committed the new data. For DELETE, the
MARC is captured before the authority is removed.

The diff column for MODIFY will now show both authority table column
changes (e.g. heading) and MARC field-level changes (e.g. _marc.151.0)
in the existing diff viewer table in the log viewer.

Tests are added to verify that ADD, MODIFY, and DELETE all populate the
diff column and that _marc content is captured correctly.

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-05 13:42:03 -06:00
Martin Renvoize 4ec43ac10d
Bug 42188: (follow-up) Skip JSON payload in info column on ADD
The info column for CATALOGUING action logs is, by convention, the
record state before modification -- MODIFY writes the pre-change JSON
payload, DELETE writes the state at time of deletion, but ADD has no
pre-change state. Writing the new record state into info on ADD
duplicates the state that the subsequent MODIFY (if any) captures as
its "before" snapshot, and conflicts with that convention.

Skip the JSON payload on ADD so info degrades to the bare "biblio"
prefix. The diff column already captures the full creation (every
field "added" against an empty hashref), so no information is lost.

Test plan:
1. prove t/db_dependent/Koha/Biblio.t
2. Observe that the "CataloguingLog MARC-in-JSON diff tests" subtest
   now also asserts:
   - ADD info == "biblio"
   - MODIFY info matches /^biblio \{/
   - DELETE info matches /^biblio \{/
3. Create / modify / delete a record via the staff UI with
   CataloguingLog on, then check action_logs: the ADD row's info
   column should read just "biblio".

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-05 13:42:02 -06:00
Martin Renvoize 3d78340552
Bug 42188: Add/update unit tests for MARC-in-JSON action log diffs
Update the existing _unblessed_for_log() subtest to reflect the new
behaviour: MARC-derived columns (title, author, copyrightdate, etc.)
are now excluded, while frameworkcode is always included even when it
is an empty string (the Default framework).

Add a new subtest 'CataloguingLog MARC-in-JSON diff tests' covering:
- ADD:    diff _marc key is MARC-in-JSON (has leader + fields array),
          indicators and subfield values are correct, and MARC-derived
          biblio columns are absent from the diff.
- MODIFY: diff _marc key present, MARC-derived columns absent.
- DELETE: diff _marc key present with correct MARC-in-JSON structure.

Use from_json() instead of decode_json() when reading diffs back from
the database, as the DBD layer returns decoded Perl Unicode strings
which decode_json (which expects UTF-8 bytes) would reject for any
non-ASCII content.

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-05 13:42:01 -06:00
Nick Clemens 649fbbf452
Bug 40658: Use local-number__sort and not local-number directly
This patch makes sure we inject the __sort form of local-number, rather
than the field directly - this ensurs searching works even if mappings mayu
be different and local-number itself is not numeric

To test:
1 - Edit mappings.yaml and set local-number from 'number' to 'string'
    NOTE: There are two copies of local-number - one in auth and one in biblio - the latter is the important one here
2 - perl misc/search_tools/rebuild_elasticsearch.pl -r -v
3 - Confirm in staff interface Admin -> Search Engine Configuration that local-number is now a string
4 - Search and get an error
    Error: Unable to perform your search. Please try again.
    In the logs:
[illegal_argument_exception] Text fields are not optimised for operations that require per-document field data like aggregations and sorting, so these operations are disabled by default. Please use a keyword field instead. Alternatively, set fielddata=true on [local-number] in order to load field data by uninverting the inverted index. Note that this can use significant memory.
5 - Apply patch, restart all
6 - Search again
7 - Success!

Signed-off-by: Bernard Scaife <bernard.scaife@openfifth.co.uk>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Thomas Klausner <domm@plix.at>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-05 13:41:58 -06:00
Hammat Wele a0b92c0bb6
Bug 41690: (follow-up) Add test
Signed-off-by: David Nind <david@davidnind.com>

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:35 -06:00
Andreas Roussos 2b691bd4dd
Bug 41795: Fix failing test in UNIMARC instances
Previously, t/AuthoritiesMarc_MARC21.t would fail if
you tried to run it from within a UNIMARC instance.

This patch fixes that.

Test plan:

1) Inside a UNIMARC instance, run the test and
   notice the subtest failure:

   $ prove -v t/AuthoritiesMarc_MARC21.t

2) Apply this patch.

3) Re-run the test and notice that it now completes
   without any failures.

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

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:33 -06:00
David Cook ea3b9d9c14
Bug 42406: (QA follow-up) fix little inconsistencies and errors
Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:30 -06:00
Martin Renvoize c484b66fc4
Bug 41751: Allow anonymous_refund permission to access cashups API
The cash register transaction history page (pos/register.pl) allows
access with either cashup OR anonymous_refund permission, but the
API endpoints for fetching cashups only checked for cashup permission.

This caused a 403 error when users with only anonymous_refund permission
tried to view the transaction history page, as the cashups table failed
to load.

This patch updates the API permissions for:
- GET /cash_registers/{id}/cashups
- GET /cashups/{id}

to accept either cashup or anonymous_refund permission, matching the
page access logic.

Test plan:
1. Create a staff user with only cash_management > anonymous_refund
   permission (not cashup)
2. Navigate to Point of Sale > Transaction history for any cash register
3. Verify the cashups table loads successfully (previously returned 403)
4. Run: prove t/db_dependent/api/v1/cashups.t

Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:26 -06:00
Tomás Cohen Arazi 42b2758a76
Bug 42206: (follow-up) Rename to /closed_dates, improve efficiency
Rename /libraries/{id}/holidays to /libraries/{id}/closed_dates
for consistency with existing sub-resource naming (desks,
cash_registers).

Efficiency: instead of calling $calendar->is_holiday() per day
(which re-checks all three data sources each time), access the
pre-loaded data structures directly: weekly_closed_days array,
day_month_closed_days hash, and _holidays hash. The logic properly
handles exceptions (dates marked open despite a weekly/annual
closure).

Additional fixes:
- Add 365-day max range guard
- Add missing 401/403 responses in swagger spec
- Add request_id_header parameter
- Test exception handling (open on normally-closed day)
- Remove fragile 'empty defaults' assertion

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:25 -06:00
Paul Derscheid 2a3376b243
Bug 42206: Add GET /libraries/{id}/holidays endpoint
- Add list_holidays controller to Koha::REST::V1::Libraries
- Returns closed days for a library within a configurable date range
- Uses Koha::Calendar to determine holidays
- Defaults to today + 3 months if no range specified
- Add OpenAPI spec for the new endpoint
- Register route in swagger.yaml
- Add unit tests in t/db_dependent/api/v1/libraries.t

To test:
1. Apply patch
2. prove t/db_dependent/api/v1/libraries.t
3. Verify all tests pass, including the new list_holidays subtest
   which covers:
   - Holidays returned within a specified date range
   - Empty list when no holidays exist in range
   - 400 response when 'to' date precedes 'from' date
   - 404 response for non-existent library
   - Default range (no params) returns successfully

Sponsored-by: Büchereizentrale Schleswig-Holstein <https://www.bz-sh.de/>
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:25 -06:00