Commit Graph

6827 Commits

Author SHA1 Message Date
Lucas Gass 9f50aa9862
Bug 42667: (follow-up) Fix POD coverage
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-10-01 13:32:50 +00:00
Jonathan Druart 36606ea9d6
Bug 41674: Fix review issues - remove unused import, fix POD synopsis, remove unused variable
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-30 09:14:52 +00:00
Owen Leonard 797c1b352e
Bug 41674: Add template plugin for linking to system preferences based on user permission
This patch adds a new template plugin, LinkPref, for displaying system
preference names as links depending on the logged-in user's permission.

Syntax:

  [% "SYSTEM_PREFERENCE_NAME" | html | $LinkPref %]

For a user with 'CAN_user_parameters_manage_sysprefs' permission it
outputs:

  <a class="link_preference" href="admin/preferences.pl?op=search&amp;ok=Search&amp;searchfield=SYSTEM_PREFERENCE_NAME">SYSTEM_PREFERENCE_NAME</a>

For a user without permission it outputs:

  <span class="link_preference">SYSTEM_PREFERENCE_NAME</span>

Sponsored-by: Athens County Public Libraries
Signed-off-by: David Nind <david@davidnind.com>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-30 09:14:45 +00:00
Martin Renvoize c15b42c457
Bug 31925: Don't override 008/11 when the heading's thesaurus wasn't identified
QA (comments 23-30) flagged a regression in the previous patches: the
guard around coding a new authority's 008/11 (and 040$f) from the source
heading's thesaurus was `if ( $heading->{thesaurus} )`, but
C4::Heading::MARC21::_get_subject_thesaurus() never returns a false
value - a heading with a blank/unrecognised indicator 2 (by far the
commonest real-world case) comes back as the placeholder string
"notdefined", and indicator 2 = 4 comes back as "notspecified". Both are
truthy, so the override always fired, unconditionally coding every new
authority's 008/11 as 'z' or '|' instead of respecting the site's
MARCAuthorityControlField008 default (which defaults to 'a'/LCSH) - a
disruptive behaviour change for the majority of headings, not just the
non-LCSH ones this bug is meant to fix.

Adds Koha::Authority::$MARC21_UNDEFINED_THESAURUS_VALUES, the subset of
the existing thesaurus table's keys ('notdefined', 'notspecified') that
mean "no thesaurus could be identified" rather than a genuine one, and
uses it in LinkBibHeadingsToAuthorities()'s guard so those two values now
fall through to the site's own syspref default, unchanged, exactly as
before this bug's patches.

Test plan:
1. prove t/db_dependent/Biblio.t
   Note the two new cases in the "codes new authorities with the source
   thesaurus" subtest: a blank-indicator-2 heading now keeps 008/11 at
   the syspref default ('a') instead of being forced to 'z', and an
   indicator-2=4 heading respects a customised syspref default instead
   of being forced to '|'.
2. Librarian-facing regression check: with AutoLinkBiblios and
   AutoCreateAuthorities on, catalogue a 650 with a blank/space
   indicator 2 (e.g. `650 __ $a Some topic`, no $2) and save. The new
   authority's 008 position 11 should read whatever
   MARCAuthorityControlField008 says (LCSH 'a' by default), not 'z'.
3. Confirm the non-LCSH cases from the earlier patches are unaffected:
   a FAST heading (ind2=7, $2=fast) still codes 008/11='z' with
   040$f=fast, and a MeSH heading (ind2=2) still codes 008/11='c'.

Sponsored-by: Heythrop Library <https://heythroplibrary.co.uk/>
Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-25 12:08:49 +00:00
Martin Renvoize 9186341ddd
Bug 31925: Make QueryBuilder reuse the shared thesaurus/008 lookup
Koha::SearchEngine::Elasticsearch::QueryBuilder kept its own copy of the
thesaurus-name to 008/11-code table used when building "thesaurus" search
queries against authority records. The previous commit moved that same
table to Koha::Authority as a shared lookup. This commit deletes
QueryBuilder's duplicate and points $thesaurus_to_value at
$Koha::Authority::MARC21_THESAURUS_TO_CONTROL_FIELD_008_11 instead, so
there is only one place to update if the code list ever changes.

Pure refactor: $thesaurus_to_value keeps its existing name and contents,
and build_authorities_query_compat() is unchanged, so query behaviour is
identical before and after.

Sponsored-by: Heythrop Library <https://heythroplibrary.co.uk/>
Signed-off-by: Clemens Gresser <cgresser@jesuit.org.uk>
Signed-off-by: Phil Ringnalda <phil@chetcolibrary.org>
Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-25 12:08:47 +00:00
Martin Renvoize 4bfb9abc68
Bug 31925: Add thesaurus/008 lookup and default MARC21 008 builder to Koha::Authority
AutoCreateAuthorities builds a brand-new authority record whenever a bib
subject heading (6XX) has no match, but always lets the record fall back
to C4::AuthoritiesMarc::AddAuthority()'s default 008/040, which hardcodes
008 position 11 ("Subject heading system/thesaurus") to 'a' (LCSH) no
matter what thesaurus the source heading actually uses. This is the first
step towards fixing that.

Adds two things to Koha::Authority:

- $MARC21_THESAURUS_TO_CONTROL_FIELD_008_11: a single-source-of-truth
  lookup mapping a thesaurus name (as already derived by C4::Heading from
  MARC indicator 2 / subfield $2, e.g. 'lcsh', 'mesh', or a raw $2 code
  such as 'fast') to its MARC21 authority 008/11 code, per the LOC ad008
  code list. This table previously only existed once, for a different
  purpose, in Koha::SearchEngine::Elasticsearch::QueryBuilder (used for
  building thesaurus search queries) - it's promoted here so both that
  and the authority-creation fix that follows can read from one place
  instead of two copies that can silently drift apart.

- default_marc21_008($thesaurus): builds the default 34-character body of
  a new MARC21 authority's 008 field, honouring the
  MARCAuthorityControlField008 system preference. When $thesaurus is
  given, position 11 is set from the table above instead of whatever the
  default/syspref value carries, falling back to 'z' (Other) for a
  thesaurus with no dedicated code.

AddAuthority() is refactored to build its own (thesaurus-less, LCSH
default) 008 via this new method too, replacing its previous inline copy
of the same defaulting logic - pure refactor, no behaviour change.
Nothing thesaurus-aware calls it yet.

Sponsored-by: Heythrop Library <https://heythroplibrary.co.uk/>
Signed-off-by: Clemens Gresser <cgresser@jesuit.org.uk>
Signed-off-by: Phil Ringnalda <phil@chetcolibrary.org>
Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-25 12:08:46 +00:00
Tomás Cohen Arazi 9eaeae69e5
Bug 42719: Handle missing CGISESSID in OAuth login (IdP-initiated flow)
When a user initiates login from the IdP (e.g. Okta app tile), they
hit the OAuth endpoint without an existing session cookie. The code
crashed calling ->value on an undefined cookie object.

This patch:
- Removes the premature 'No user session found' guard that blocked
  IdP-initiated flows entirely
- Creates a session on-the-fly for the initial request when no
  cookie exists (IdP-initiated flow)
- On callback, redirects with wrong_csrf_token error instead of
  crashing when the cookie is missing

CSRF protection is preserved: the state token is bound to the
session created in the initial request, and the browser returns
the cookie on callback for validation.

Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-25 12:08:45 +00:00
Martin Renvoize 58d41bfedf
Bug 42608: Stop hold pickup expiration from week-jumping over closures
Koha::Hold::set_waiting calls Koha::Calendar::days_forward to derive the
holds-shelf expiration date when ExcludeHolidaysFromMaxPickUpDelay is on.
days_forward iterated next_open_days($dt, 1) ReservesMaxPickUpDelay times,
and next_open_days consults get_push_amt for every closed day it
encounters. Under useDaysMode=Dayweek, get_push_amt returns 7 for any
closure that is not a permanently closed weekday -- so an intermediate
Bank Holiday Monday inside the pickup window pushed the cursor a whole
week forward instead of one day, inflating the expiration by seven days.

Dayweek is a loan-due-date concept: it exists to keep a Friday-due loan
due on a Friday across closures. A patron does not pick a weekday for a
hold-shelf collection; staff set the expiration when they trap the item,
and the only thing that should drive it is how many open days the patron
should have to come and collect. days_forward is now decoupled from
useDaysMode and always counts forward one open day at a time, ignoring
closures without consuming them from the count.

next_open_days, prev_open_days, get_push_amt and addDays are unchanged --
loan due-date math keeps the existing Dayweek behaviour.

Test plan:
1. Inside KTD, run the updated unit tests:

     prove t/db_dependent/Calendar.t
     prove t/db_dependent/Hold.t

   Confirm both pass, including the new Bug 42608 sub-tests. These cover
   the full matrix of starting weekdays and useDaysMode values; the
   manual steps below are a single confirmatory pass through the UI.

2. Manual reproduction (UI only -- no database or CLI access needed):
   - Set sysprefs: useDaysMode=Dayweek, ReservesMaxPickUpDelay=6,
     ExcludeHolidaysFromMaxPickUpDelay=ON, ExpireReservesOnHolidays=ON.
   - Administration > Calendar: on a test branch, add one single
     (one-time) holiday -- not weekly/repeating -- on any weekday within
     the next 6 days that isn't already closed (e.g. the day after
     tomorrow, as long as it isn't a Sunday or other closed day). This
     one-off closure inside the pickup window is what triggers the bug.
   - Place a hold on an available item, then check the item in at that
     branch and confirm/trap it. This stamps waitingdate with today's
     date automatically -- no manual date entry required.
   - Check the hold's expiration date (Holds awaiting pickup list, or
     the patron's holds tab):
       Without the fix: expiration lands a full 7 days later than it
       should (today + 6 + 7), from the week-jump.
       With the fix: expiration is today + 6 open days, only skipping
       the one closed day.

3. Verify the existing Calendar mode behaviour is unchanged:
   - Switch useDaysMode to Calendar and repeat step 2 -- the same
     expected expiration date should apply.

4. Verify loan due-date behaviour is unchanged:
   - With useDaysMode=Dayweek, check out an item whose loan period would
     place its due date on the same closed day used in step 2. Confirm
     the due date still rolls forward by a whole week (the original
     Dayweek intent), proving this patch did not regress
     addDays/addDuration.

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-24 11:48:37 +00:00
Magnus Enger 6d160313a2
Bug 43336: Remove support for LibrisSpellcheck
The API that this feature was based on was removed by the Swedish
National Library, so we can remove the code and sysprefs from Koha.

Before testing:
- Run this SQL in the database and verify the syspref is empty:
  SELECT value FROM systempreferences WHERE variable = 'OPACdidyoumean';
- Go to Administration > Did you mean? and verify there is a box for
  LibrisSpellcheck
- Move the boxes around and click on "Save configuration"
- Run the SQL again and verify the contents of the syspref are now
  something like this:
  [{ "name": "AuthorityFile"}, { "name": "LibrisSpellcheck"}, { "name": "ExplodedTerms"}]
- Go to Administration > System preferences and search for "libris"
- Verify the two sysprefs LibrisKey and LibrisURL show up

To test:
- Apply this patch and make sure the DB update is run. In KTD you can
  do:
  $ sudo koha-upgrade-schema kohadev
  The output from this should be like this:
	Removed system preference 'LibrisKey'
	Removed system preference 'LibrisURL'
	'LibrisSpellcheck' was removed from the OPACdidyoumean syspref
- Run koha-upgrade-schema again. The output should be:
	'LibrisKey' syspref already removed
	'LibrisURL' syspref already removed
	'OPACdidyoumean' syspref does not contain a setting for 'LibrisSpellcheck'
- Run the same SQL as before and verify that LibrisSpellcheck is now
  gone:
  [{"name":"AuthorityFile"},{"name":"ExplodedTerms"}]
- Go to Administration > Did you mean? and verify there is no box
  for LibrisSpellcheck
- Move the boxes around and click on "Save configuration"
- Run the SQL again and verify that there is still no mention of
  LibrisSpellcheck
- Go to Administration > System preferences and search for "libris"
- Verify the two sysprefs LibrisKey and LibrisURL do NOT show up
- Empty the OPACdidyoumean syspref by running this SQL:
  UPDATE systempreferences SET value = '' WHERE variable = 'OPACdidyoumean';
- Run koha-upgrade-schema again. The output should be:
	'LibrisKey' syspref already removed
	'LibrisURL' syspref already removed
	'OPACdidyoumean' is not set

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-21 16:07:48 +00:00
Chloé Zermatten c96618f064
Bug 10190: (QA follow-up): fixes phase five
1. apply html filter to letters so notice names are escaped (must
    be done in attribute so the JSON format of the letters can
    be parsed).
2. fix a correctness issue where the frontend was pinning the
   logged in library instead of the logged in user's home library,
   which would have led to 403s (correct) when saving rules. Since
   the library dropdown is deliberately disabled unless a user has
   the manage_circ_rules_from_any_libraries permission, the user
   would then have needed to use 'Set library' to match their home
   library to fix this. Prevent the issue by setting the library
   to the user's home library.
   Also rename the variable to user_library_id.
3. list_rules correctly references /api/v1/circulation_rules/kinds
4. make untranslatable strings translatable
5. swap svg for font-awesome icon.
6. avoid css duplication:
   - remove dead / unused scoped css (leftover from an early component
     extraction)
   - import vue.css and only leave the minimal override required.
7. improve set_rules test coverage: add tests for manage_circ_rules
   / manage_circ_triggers separation

Test plan:

1) Administration > Circulation triggers: notice the 'Letter' dropdowns
   list circulation notices as "name (code)" and offer 'No letter'.
   (no regression)
2) Give a staff user 'catalogue' + 'manage_circ_triggers' only, with
   home library A. Log in as them, 'Set library' to B, and open
   Administration > Circulation triggers. Notice the view is pinned to
   A, and that adding and editing a trigger saves without a 403.
   (behaviour is now correct)
3) GET /api/v1/circulation_rules?rules=nosuchrule - notice the error
   points at /api/v1/circulation_rules/kinds.
   (behaviour is now correct)
4) Run 'gulp po:update --lang fr-FR'. Notice 'Add new trigger
   {triggerNumber}', 'No letter' and 'any' now appear in
   misc/translator/po/fr-FR-messages-js.po
   (behaviour is now correct)
5) Edit a trigger. Notice the delay's reset button is an 'x' inside the
   input, left of the chevrons, and clears the delay; the letter's
   reset button does the same for the notice.
   (no regression)
6) Notice no visual regression across the list, add, edit, reset and
   delete screens - select widths, chevrons and modals unchanged.

Also run t/db_dependent/api/v1/circulation_rules.t

Assisted-by: Claude Opus 5 (Anthropic)
Sponsored-by: Glasgow Colleges Library Group <https://library.cityofglasgowcollege.ac.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-21 16:07:39 +00:00
Martin Renvoize 5da8ba1ce9
Bug 42667: (QA follow-up) Fix null guard and exception description
- Add null check around Koha::Patrons->find() in guided_reports.pl
  before calling ->has_permission, matching the defensive pattern
  already used in Koha::Report::check_edit_permission
- Add missing description to Koha::Exceptions::Report base class
  for consistency with its sub-exceptions

Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-21 11:56:37 +00:00
Kyle M Hall 5b5c875df9
Bug 42667: Enforce permission in Koha::Report
Prevents possible template bugs from allowing editing
of reports by others without the proper permissions.

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Brendan Lawlor <blawlor@clamsnet.org>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-21 11:56:36 +00:00
Tomás Cohen Arazi 4f1b98160e
Bug 42385: Use Koha::Policy::Circulation everywhere
Replaces all calls to _GetCircControlBranch with
Koha::Policy::Circulation->circ_control_library in:
- C4::Circulation (10 call sites)
- Koha::Item (1 call site)
- Koha::Recall (1 call site)
- reserve/request.pl (1 call site)
- svc/letters/preview (2 call sites)

Test plan:
1. Apply patch
2. Run:
   $ ktd --shell
   k$ prove t/db_dependent/Koha/Policy/Circulation.t \
          t/db_dependent/Circulation.t \
          t/db_dependent/Koha/Items.t \
          t/db_dependent/Koha/Recall.t
=> SUCCESS: Tests pass!
3. Sign off :-D

Signed-off-by: Alex Carver [Acerock7] <alex@rcls.org>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-21 11:11:30 +00:00
Tomás Cohen Arazi 5c833281ed
Bug 42385: Add Koha::Policy::Circulation
Adds Koha::Policy::Circulation->circ_control_library($item, $patron)
class method that resolves which library controls circulation rules,
based on the CircControl and HomeOrHoldingBranch system preferences.

This follows the same pattern as Koha::Policy::Holds->holds_control_library.

An optional pickup_library_id parameter allows callers in API/daemon
contexts to pass the library explicitly instead of relying on userenv.

Test plan:
1. Apply patch
2. Run:
   $ ktd --shell
   k$ prove t/db_dependent/Koha/Policy/Circulation.t
=> SUCCESS: Tests pass!
3. Sign off :-D

Signed-off-by: Alex Carver [Acerock7] <alex@rcls.org>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-21 11:11:29 +00:00
Tomás Cohen Arazi 8b1e8f80ba
Bug 43375: Use Mojo json() with form-data fallback for userinfo parsing
This patch simplifies the userinfo response parsing by relying on
Mojo::Message::Response->json which decodes JSON regardless of
Content-Type header parameters. If JSON decoding fails (returns undef),
it falls back to URL-encoded form data parsing.

This fixes integration with APIs that return extra Content-Type
parameters (e.g. Microsoft Graph returns odata.metadata,
odata.streaming, and IEEE754Compatible alongside charset) which
previously caused the strict regex to fail and silently misparse
the response as form data.

Test plan:
1. Apply the regression test (previous patch)
2. Run:
   $ ktd --shell
  k$ prove t/db_dependent/Koha/Auth/Client/OAuth.t
=> FAIL: Subtest fails - Content-Type with extra params not recognized
3. Apply this patch
4. Repeat step 2
=> SUCCESS: All tests pass
5. Sign off :-D

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-21 11:11:27 +00:00
Pedro Amorim 1b903bc3c5
Bug 42310: (RM follow-up) Refresh stale DBIC checksum on Koha::Schema::Result::Branch
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:14:17 +00:00
Tomás Cohen Arazi f4268951f0
Bug 42310: (follow-up) Fix POD coverage for Koha::Checkouts::_type
The koha-qa pod_coverage check flags 'sub _type has no pod coverage'
in Koha/Checkouts.pm. The POD block above the sub was headed '=head3
type' while the sub is named '_type', so the documentation was not
associated with the sub. Correct the heading to '=head3 _type' to
match, mirroring the adjacent object_class POD.

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:14:11 +00:00
Martin Renvoize 3ef756f5a0
Bug 42310: (QA follow-up) Refresh in-process closure state on add/delete_weekly_closure and add/delete_repeating_closure
add_weekly_closure/delete_weekly_closure and add_repeating_closure/
delete_repeating_closure returned $self without refreshing
$self->{weekly_closed_days}/{day_month_closed_days}, so a calendar object
would keep reporting the pre-change state to is_holiday (and everything
built on it - days_forward, next_open_days, etc.) until it was
re-instantiated. This is distinct from the _holidays Koha::Caches entry,
which single/exception closures already flush correctly on store/delete.

Call _init to rebuild both structures from the database after each
add/delete, so the same object reflects the change immediately.

Test plan:
1) prove t/db_dependent/Koha/Library/Calendar.t t/db_dependent/Calendar.t
   t/db_dependent/Holidays.t t/db_dependent/HoldsQueue.t
   t/db_dependent/Holds/WaitingReserves.t t/db_dependent/Koha/CurbsidePickups.t
   t/db_dependent/Reserves/CancelExpiredReserves.t
   t/db_dependent/api/v1/libraries.t

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:14:09 +00:00
Martin Renvoize 004fe4de21
Bug 42310: (QA follow-up) Reject in-place date changes on SingleClosure/Exception
store() only flushes the _holidays cache on insert, on the assumption that
updates only ever touch title/description. That assumption held for every
current caller (ModSingleholiday/ModExceptionholiday only ever change those
two fields), but nothing enforced it - a future caller updating date in
place would silently desync the cache for up to its TTL, with no error.

Make the assumption explicit: reject an update that changes date, rather
than silently allowing it. Callers that need to move a closure/exception to
a different date must delete and re-add.

Test plan:
1) prove t/db_dependent/Koha/Library/Calendar.t t/db_dependent/Calendar.t
   t/db_dependent/Holidays.t t/db_dependent/HoldsQueue.t

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:14:08 +00:00
Martin Renvoize 1399f8af42
Bug 42310: (QA follow-up) Restore weekday validation on weekly closures
C4::Calendar::insert_week_day_holiday used to croak "Invalid weekday
$weekday" unless $weekday =~ m/^[0-6]$/. That guard was dropped when the
insert delegated to Koha::Library::Calendar::WeeklyClosure, and nothing
replaced it - an out-of-range or undef weekday now persists silently to
library_weekly_closures instead of failing at the API boundary.

Add the check to WeeklyClosure->store() rather than the legacy wrapper, so
every caller (C4::Calendar, Koha::Library::Calendar->add_weekly_closure,
and any future REST/admin caller) gets it, not just the old entry point.

Test plan:
1) prove t/db_dependent/Koha/Library/Calendar.t t/db_dependent/Calendar.t
   t/db_dependent/Holidays.t t/db_dependent/HoldsQueue.t
   t/db_dependent/Holds/WaitingReserves.t

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:14:06 +00:00
Martin Renvoize 811d8bfd37
Bug 42310: (follow-up) Move Koha::Calendar tree under Koha::Library::Calendar
Koha::Calendar is, by construction, a per-library object — it croaks
without a branchcode argument and every method is branch-scoped. The
top-level namespace hides that. Move the whole tree under
Koha::Library::Calendar to match the actual semantics, the existing
/libraries/{library_id}/calendar REST routes, and the established
Koha::Library::* convention (Group, Hour, FloatLimit, OverDriveInfo).

This commit:
- renames the Koha::Calendar(::*) tree to Koha::Library::Calendar(::*)
- updates package declarations, POD and internal 'use' statements
  within the moved tree
- updates the four DBIC Schema Result overrides (koha_object_class /
  koha_objects_class) to the new namespace
- updates callers inside C4::* modules (Calendar, Circulation,
  Reserves, Overdues, HoldsQueue), Koha::{Patron,Hold,Checkouts,
  CurbsidePickup}, circ/returns.pl, the installer and cronjobs
- updates the 42310-owned tests (Calendar tests, HoldsQueue.t,
  Holds/WaitingReserves.t, Holidays.t, Koha/CurbsidePickups.t,
  Reserves/CancelExpiredReserves.t and the libraries.t CRUD block)

Koha::Library->calendar, the REST controllers and the 42311 tests
are updated in separate follow-up commits under their respective
bugs.

Test plan:
- prove t/db_dependent/Koha/Library/Calendar.t
- prove t/db_dependent/Calendar.t
- prove t/db_dependent/Holidays.t t/db_dependent/HoldsQueue.t
- prove t/db_dependent/Holds/WaitingReserves.t
- prove t/db_dependent/Reserves/CancelExpiredReserves.t

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:14:02 +00:00
Martin Renvoize 7a8a220842
Bug 42310: (QA follow-up) Add closed_dates_in_range to Koha::Calendar
Introduces a public Koha::Calendar->closed_dates_in_range($from_dt,
$to_dt) method that returns an arrayref of ISO date strings representing
every closed day in the inclusive range, honouring weekly, annual
repeating, single and exception (open-override) closures.

This replaces callers that were reaching into Koha::Calendar private
state (_holidays, weekly_closed_days, day_month_closed_days) to
reconstruct the same logic. A follow-up under Bug 42206 converts the
REST controller; further callers can be migrated incrementally.

Test plan:
1. prove t/db_dependent/Koha/Calendar.t
2. Verify the new 'closed_dates_in_range' subtest passes

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:14:01 +00:00
Martin Renvoize 548903ba02
Bug 42310: (QA follow-up) Harden Koha::Calendar->copy_to
Wrap the body in schema->txn_do so a failure partway through does not
leave the target library partially populated. Flush the target's
_holidays cache after the copy, and document the behaviour in POD:
weekly and repeating closures are always copied, but single closures
and exceptions are filtered to today-or-later dates by design.

Test plan:
1) prove t/db_dependent/Koha/Calendar.t
2) Confirm existing copy_to tests still pass.

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:13:58 +00:00
Martin Renvoize c3880a70e6
Bug 42310: (QA follow-up) Fire per-row delete hooks in Koha::Calendar delete_* methods
The four delete_*_closure / delete_exception methods in Koha::Calendar
were calling ResultSet->delete, which issues a single bulk SQL DELETE
and never calls the row-level delete() override. The cache-invalidation
hooks on Koha::Calendar::SingleClosure->delete and
Koha::Calendar::Exception->delete were therefore skipped when callers
went through $library->calendar->delete_single_closure(...) or
delete_exception(...), and the <library>_holidays cache kept reporting
deleted dates as closed until its TTL expired.

The underlying tables enforce UNIQUE constraints on (library_id, weekday),
(library_id, day, month) and (library_id, date), so each search can
match at most one row. Switch the four methods to ->search({...},
{ rows => 1 })->single followed by ->delete on the row, which both
makes the single-row expectation explicit and ensures the per-row
delete override runs.

A regression test is added to t/db_dependent/Koha/Calendar.t asserting
that the _holidays cache is cleared after delete_single_closure and
delete_exception.

Test plan:
1) prove t/db_dependent/Koha/Calendar.t
2) Confirm the new 'delete_*_closure clears _holidays cache' subtest
   passes.

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:13:57 +00:00
Tomás Cohen Arazi d4e4f57072
Bug 42310: (follow-up) Bake cache invalidation into Koha::Object classes
When using the new Koha::Calendar::* classes for writing the CRUD API
endpoints I noticed that in my attempt to keep C4::Calendar similar to what it
was, I ended up with cache invalidation not always done in the right place.

The holidays cache (keyed by {branchcode}_holidays) stores dates and their
open/closed status, not titles or descriptions. This means only inserts and
deletes need to invalidate it — metadata updates (title/description) do not.

Following the pattern established by Koha::AuthorisedValue and Koha::ItemType,
I added store() and delete() overrides to Koha::Calendar::SingleClosure and
Koha::Calendar::Exception:

- store() flushes the cache only on insert (!in_storage), not on updates,
  since the cache doesn't store titles or descriptions.
- delete() always flushes, since removing a closure changes the cached set.

This allowed removing the now-redundant _clear_cache calls from
Koha::Calendar::add_single_closure, add_exception, delete_single_closure,
delete_exception, and from C4::Calendar::ModSingleholiday and
ModExceptionholiday.

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: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:13:55 +00:00
Tomás Cohen Arazi cb7968387a
Bug 42310: (follow-up) Add CRUD methods to Koha::Calendar, delegate C4::Calendar
Add to Koha::Calendar:
  add_weekly_closure, add_repeating_closure, add_single_closure,
  add_exception, delete_weekly_closure, delete_repeating_closure,
  delete_single_closure, delete_exception, copy_to, _clear_cache

C4::Calendar write methods now delegate to Koha::Calendar instead
of using raw DBI. No old table references remain in C4::Calendar.

Tests in t/db_dependent/Koha/Calendar.t cover all CRUD operations:
create, verify via is_holiday, delete, copy_to (including
idempotency).

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: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:13:54 +00:00
Martin Renvoize 166e9831ea
Bug 42310: DBIC schema manual updates
Add koha_object_class/koha_objects_class overrides for the four new
Result classes backing the normalized calendar tables, below the
auto-generated marker.

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:13:51 +00:00
Martin Renvoize c0349ad142
Bug 42310: DBIC schema update
Regenerated via dbic --force after the library_weekly_closures,
library_repeating_closures, library_single_closures, and
library_closure_exceptions tables replaced repeatable_holidays and
special_holidays.

Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:13:49 +00:00
Tomás Cohen Arazi ca487a18e0
Bug 42310: (follow-up) Add missing POD
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: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:13:48 +00:00
Tomás Cohen Arazi 0629de1e54
Bug 42310: Refactor Koha::Calendar to use Koha::Calendar::* classes
Replace raw DBI queries in _init() and _holidays() with
Koha::Calendar::WeeklyClosures, RepeatingClosures, SingleClosures,
and Exceptions searches.

The internal data structures remain identical (weekly_closed_days
array, day_month_closed_days hash, _holidays hash with YYYYMMDD
keys) so all consumers (is_holiday, addDuration, next_open_days)
continue to work unchanged.

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: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:13:46 +00:00
Tomás Cohen Arazi 402938f7cd
Bug 42310: Add Koha::Calendar::* Object classes
Four Koha::Object + Koha::Objects pairs following the Koha::City(ies)
pattern:

- Koha::Calendar::WeeklyClosure(s) -> library_weekly_closures
- Koha::Calendar::RepeatingClosure(s) -> library_repeating_closures
- Koha::Calendar::SingleClosure(s) -> library_single_closures
- Koha::Calendar::Exception(s) -> library_closure_exceptions

DBIC schema files to be generated via the 'dbic' alias in KTD
after the database migration runs.

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: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 16:13:45 +00:00
Martin Renvoize cd30d78c0e
Bug 31391: (QA follow-up) Fix UseRecalls check in Transfers REST controller
UseRecalls was converted from a YesNo syspref to a Choice preference
("off"/"opac"/"staff"/"opac_and_staff") earlier in this bug, and every
call site was updated from a truthy check to `ne "off"` - except this
one. Since even the string "off" is truthy in Perl, the guard in
Koha::REST::V1::Transfers::cancel was silently defeated: cancelling a
transfer via the REST API always attempted to revert an in-transit
recall, even with recalls disabled.

Test plan:
1) prove t/db_dependent/api/v1/transfers.t - new subtest
   'cancel() - UseRecalls disabled' fails against the old code
   (the in-transit recall gets reverted even though UseRecalls is off)
2) Apply this patch
3) Same test now passes

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 14:45:14 +00:00
Pedro Amorim d0a79a677c
Bug 41920: (RM follow-up) Remove dead resultset build in Koha::Reports::running
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 14:45:12 +00:00
Lucas Gass ada73c317c
Bug 41920: Move total_running_reports_per_instance_limit to Koha/Reports.pm
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 14:45:09 +00:00
Kyle M Hall 7ba7bbab7d
Bug 41920: Limit number of concurrent reports that can be run simultaneously for an instance of Koha
As a continuation of bug 41919,  we should limit the total number of simultaneous reports that can be run at a time.

Test Plan:
1) Set total_running_reports_per_instance_limit to 2 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
5) Note the first two continue to run, but the last one gives an error
   that you have too many reports running simultaneously!

Signed-off-by: Alex Carver [Acerock7] <alex@rcls.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 14:45:08 +00:00
Thibaud Guillot e2506598c8
Bug 32748: (QA follow-up) Fix branchcode filtering on itemtypes
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 14:45:04 +00:00
Lisette Scheer 5205fc8bcc
Bug 32748: (QA follow-up) Fix spelling and tidy
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 14:45:02 +00:00
Thibaud Guillot b443b31998
Bug 32748: Fix item edition when authorised values is restricted
When we set an authorised value linked to one library,
users who do not belong to this library do not get the
value and can therefore overwrite it.

This patch add current value to the dropdown list even if it comes from an authorized value of another library.

Test plan :

1) Edit an item with one of these fields has an authorized value from
another library.
2) On 'edit' you will see AV but with '(Not an authorised value)' string
   and a warn
3) Apply this patch
4) Refresh page and edit item again
5) Now if the authorised value belongs to another lib and is the current
   value, you will see it with a tooltip and a warn. But if there are
authorised values restricted to another lib and isn't current value,
normally it will be not present in the option list.

Sponsored-by: BibLibre

Signed-off-by: Mathieu Saby <mathsabypro@gmail.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-15 14:44:51 +00:00
Aleisha Amohia 98c8add185
Bug 31391: Request recalls and convert reserves on staff interface
This enhancement adds the ability to place recalls via the staff
interface by changing the UseRecalls system preference to have choices which
describe where recalls can be placed. Recalls can either be placed via
the OPAC, or the staff interface, or both. To be set to OPAC would be
consistent with current 'on' behaviour. UseRecalls is turned off by default.

This enhancement also adds the ability to convert existing holds to
recalls easily via the staff interface. The hold will be cancelled
and a recall placed using the hold's information.

To test:
1) Update database and restart services
2) Set UseRecalls to OPAC and set the relevant recalls circulation rules
OPAC only by default
3) Check out Item A to Patron B.
4) Log into the OPAC as Patron A. Confirm you can place, view and cancel
recalls as normal.
5) In the staff interface, set UseRecalls to Staff interface only.
6) In the OPAC, confirm you can still view and cancel recalls, but can
no longer place recalls.
7) In the staff interface, search for Item A and go to the Holds tab.
Place a hold for Patron C.
8) Check the checkbox for your hold and confirm the counts in the buttons above the Holds table are incremented as expected.
9) Click the button to Convert selected holds to recalls, a modal should pop up. Confirm all of the information is correct.
10) Confirm the conversion and once the page refreshes, your hold should be correctly cancelled using the new RECALLED reason.
11) Go to the Recalls tab. Confirm your hold has been converted to a
recall, and details like the patron, expiration, and pickup location have
carried across to the recall.
12) On this Recalls page, use the patron search to find Patron D. Test
placing a record-level recall by choosing to recall the next available item.
13) When the page refreshes, confirm the recall was successfully placed
with the correct details stored.
14) Repeat steps 12 and 13 with Patron E, this time place an item-level
recall by choosing an item in the 'place a recall on a specific item'
table. When selecting one of these items, the 'recall next available
item' section should become disabled.
15) Set UseRecalls back to OPAC only. Go back to the record and
view the Recalls tab.
16) Confirm you cannot search for a patron to place a recall, but can
still view and cancel recalls. Confirm you also cannot convert holds
to recalls.
17) Set UseRecalls to both staff interface and OPAC. Confirm you
can place recalls on both interfaces.
18) On the staff interface, test placing recalls that your circulation
rules do not allow. For example, if 'recalls per record' is 1, ensure
you're blocked from placing a second recall on a record for a patron. Another example, testing converting a hold to a recall when there are NO checked out items, as recalls can only be placed if something is checked out.
19) Place a hold for one of the patron's that already has a recall.
Once complete, try to convert this hold into a recall. Confirm you
are blocked and shown a message that the hold cannot be converted to a
recall.
20) Confirm patron messaging preferences for recalls notices can still be configured as expected.
21) Ensure tests pass:

* t/db_dependent/Circulation.t
* t/db_dependent/Circulation/transferbook.t
* t/db_dependent/Holds.t
* t/db_dependent/Koha/Biblio.t
* t/db_dependent/Koha/Item.t
* t/db_dependent/SIP/Transaction.t
* t/db_dependent/Search.t
* t/db_dependent/XSLT.t

Sponsored-by: Auckland University of Technology
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Katrin Fischer <katrin.fischer@bsz-bw.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-14 14:15:54 +00:00
Aleisha Amohia 0f35039974
Bug 31391: Implement UseRecalls choices in modules
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Katrin Fischer <katrin.fischer@bsz-bw.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-14 14:15:48 +00:00
Pedro Amorim c6bf858253
Bug 34632: (QA follow-up) Use Koha::Uploader for patron import files
Large files were failing to enqueue/process because the whole CSV was
embedded in the job's JSON args, hitting DB/broker size limits. This
now uploads the file via Koha::Uploader first (same pattern as MARC
staging) and only passes a small uploaded_file_id through the job,
and fixes the enqueue-failure message to not claim nothing happened
when a job was in fact created.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-11 16:06:05 +00:00
Pedro Amorim ae60b6ebaa
Bug 34632: Add Koha::BackgroundJob::PatronImport
Introduces a new background job class for patron imports. enqueue() reads
the uploaded file content into the job_args and uses line count as job_size
for progress tracking. process() reconstitutes the file handle from the
stored content, delegates to Koha::Patrons::Import with step_callback wired
to $self->step, handles patron list creation, stores the import results, and
clears file_content from the stored data to free space.

Also registers patron_import in Koha::BackgroundJob::core_types_to_classes
so _derived_class can resolve the job correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Brendan Lawlor <blawlor@clamsnet.org>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-11 16:06:01 +00:00
Pedro Amorim 770e5ca6ec
Bug 34632: Add step_callback support to Koha::Patrons::Import
Adds an optional step_callback coderef parameter to import_patrons(). When
provided it is called after every row — whether the row succeeds, is skipped
as a duplicate, or is rejected as invalid — so callers can track progress.

Also removes the 25-error cap that was there to protect browser response
size; that constraint no longer applies when results are stored in a
background job's data field.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Brendan Lawlor <blawlor@clamsnet.org>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-11 16:05:59 +00:00
Tomás Cohen Arazi f7caec076f
Bug 42679: Add GET /patrons/{patron_id}/overdues_count endpoint
Returns the count of overdue checkouts for a patron as a plain integer.
Computed live from the database (date_due < NOW()) to ensure accuracy,
as overdue status is time-based with no discrete event to trigger updates.

This endpoint is intended for async loading in the patron search table,
allowing the main search results to render immediately while overdues
are fetched separately per visible page.

Test plan:
1. Apply patch
2. Run:
   $ ktd --shell
   k$ prove t/db_dependent/api/v1/patrons_overdues_count.t
=> SUCCESS: Tests pass!
3. Sign off :-D

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

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-11 16:05:56 +00:00
Chloé Zermatten 68d3ba779b
Bug 10190: (QA follow-up): fix: only remove restrict if no restricting overdues found
Where a patron had multiple overdues that ought to cause debarment, it was found that removing one removed the restriction.

This is because has_restricting_overdues() was looking up the ItemType object tied to the overdue issue, then passing it down to Koha::CirculationRules->get_effective_rules(). get_effective_rules(), however, expects an itemtype code.
This resulted in false negatives: the patron was treated as having no restricting overdues left where they in fact did.

Amend this by calling effective_itemtype() instead.

Note on side effects: I think this also will activate the previously potentially dead dedup logic in has_restricting_overdues (eq had been used to compare obj instance to obj instance which had no effect (itemtype() runs a find() on each iteration), and is now used to compare itemtype code to itemtype code).

Test plan (based on Pedro's comment number 949):

0) Pick a test opac patron, if using Henry from above remove the permissions
1) http://localhost:8081/cgi-bin/koha/admin/categories.pl, edit the test patron's category, confirm 'Overdue notice required' is set to 'Yes'.
2) Set syspref AutoRemoveOverduesRestrictions to when_no_overdue_causing_debarment.
3) http://localhost:8081/cgi-bin/koha/admin/circulation_triggers.pl, 'Add new trigger'. Leave 'Library' as 'All libraries' and 'Patron category' as 'All categories', set 'Item type' to 'Books'. For 'Letter' pick ODUE, check 'Email' under 'Transport type(s)', set delay to 5 days, for 'Restricts checkouts' select 'Yes', 'Submit'.
4) Check out barcodes 39999000019186 and 39999000007558 to the test patron (confirm both are itemtype 'Books'). Backdate both due dates 6+ days overdue.
5) perl misc/cronjobs/overdue_notices.pl -n — restricts the patron.
6) Check the patron's 'Restrictions' tab: confirm restricted.
7) Return barcode 39999000019186 only, leave 39999000007558
8) Check the patron's 'Restrictions' tab again. Notice they are still restricted (39999000007558 is still overdue).
9) Return barcode 39999000007558 also.
10) Check the patron's 'Restrictions' tab again. Notice the restriction is now removed (a hard refresh may be needed) as no restricting overdues are left.

Assisted-by: Claude Opus 5 (Anthropic)
Sponsored-by: Glasgow Colleges Library Group <https://library.cityofglasgowcollege.ac.uk>
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-10 14:25:29 +00:00
Chloé Zermatten ec22f77b76
Bug 10190: (QA follow-up): fix: permissions
Important note: the following assumes manage_circ_triggers is intended
as a sibling to manage_circ_rules, and that of the two, only
manage_circ_triggers ought to grant access to client-side circulation
trigger configuration actions.

When manage_circ_triggers was introduced, it was not applied client-side,
resulting in the router and menu permission checks incorrectly expecting
manage_circ_rules. Amend that.

Additionally, while the API endpoint blocked a user with only
manage_circ_triggers from editing non-triggers circulation rules,
it did not block a user with only manage_circ_rules from editing
trigger circulation rules. Add a guard clause.

Test plan (based on comment 872):
A) Go to a patron's 'More' > 'Set permissions' tab. Check 'catalogue' (needed for basic staff login), and under circulation-rules sub-permissions, check only 'Manage overdue notice triggers' - leave 'Manage circulation rules' unchecked.
B) Log in as that patron, go to 'Administration' - 'Circulation triggers' link present (admin-home.tt now correctly checks CAN_user_parameters_manage_circ_triggers), 'Circulation and fine rules' is not present.
C) Visit http://localhost:8081/cgi-bin/koha/admin/circulation_triggers directly - you are granted access.
D) Confirm that you can create and edit circulation triggers.
E) Go to a patron's 'More' > 'Set permissions' tab. Check 'catalogue' (needed for basic staff login), and under circulation-rules sub-permissions, check only 'Manage circulation rules' - leave 'Manage overdue notice triggers' unchecked.
F) Log in as that patron, go to 'Administration' - no 'Circulation triggers' link present (as we require manage_circ_triggers), however 'Circulation and fine rules' is present.
G) Visit http://localhost:8081/cgi-bin/koha/admin/circulation_triggers directly - you see "Error: You do not have permission to access this page" and are prompted to log in as a different user (as we require manage_circ_triggers).
H) Go to http://localhost:8081/cgi-bin/koha/admin/smart-rules.pl, and confirm that you can edit circulation rules for your library (no regression).

Assisted-by: Claude Opus 5 (Anthropic)
Sponsored-by: Glasgow Colleges Library Group <https://library.cityofglasgowcollege.ac.uk>
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-10 14:25:25 +00:00
Nick Clemens 8150c532fa
Bug 10190: (QA follow-up) Adjust error message to list both permissions
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-10 14:25:21 +00:00
Victor Grousset/tuxayo 622f0a4742
Bug 10190: (QA follow-up): fix pod of CirculationRules.pm
We have nothing to do with it but since the patches change the file, it
trips the QA script.

Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-10 14:25:16 +00:00
Martin Renvoize e4aecaa519
Bug 10190: Add manage_circ_triggers permission for notice trigger access
Replaces the broad manage_circ_rules permission requirement on the
circulation triggers page with a new dedicated manage_circ_triggers
sub-permission (under parameters, flag 3).

- Add manage_circ_triggers to userpermissions.sql
- Update atomicupdate to add the permission, grant it to existing
  edit_notice_status_triggers users, and remove the old permission
- Update admin/circulation_triggers.pl to require manage_circ_triggers
- Add manage_circ_triggers as an OR option on all circulation_rules
  API endpoints (GET/PUT /circulation_rules, /kinds, /config)
- Restrict manage_circ_triggers users in the API controller to
  overdue_* rule names only on write

Sponsored-by: Glasgow Colleges Library Group <https://library.cityofglasgowcollege.ac.uk>
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-10 14:25:09 +00:00
Martin Renvoize 88b16e6445
Bug 10190: Enforce manage_circ_rules_from_any_libraries permission
- Add OR-type x-koha-authorization to /circulation_rules, /circulation_rules/kinds,
  and /circulation_rules/config endpoints so users with either
  circulate:circulate_remaining_permissions OR parameters:manage_circ_rules
  can access them
- Enforce manage_circ_rules_from_any_libraries in the set_rules controller:
  users without this permission may only set rules for their own library and
  cannot set default (*) rules
- Add explicit use C4::Auth qw(haspermission) import to CirculationRules.pm
- Lock the library selector in the Vue UI for users who lack
  manage_circ_rules_from_any_libraries, defaulting them to their own library
- Update set_rules() tests to cover the new permission restrictions

Signed-off-by: Michaela <michaela.sieber@kit.edu>
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-09-10 14:25:02 +00:00