Commit Graph

61445 Commits

Author SHA1 Message Date
Jonathan Druart d0bd802da6
Bug 42374: Restore require Koha::Plugins (instead of use)
See comment 4 for a detail explanation of the problem.

Test plan:
hit /cgi-bin/koha/installer/install.pl
login with the DB credentials
upgrade
=> Without this patch you get
```
ISBN_RANGE_MESSAGE is set to [] but that file does not exist!
Trying to use the default locations
```
=> With this patch applied: "Everything went okay. Update done."

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:32 -06:00
David Cook bf867d08c2
Bug 42374: Remove buggy ISBN_RANGE_MESSAGE env variable value
This patch removes an ISBN_RANGE_MESSAGE env variable that is
accidentally set by Business::ISBN::Data.

Test plan:
- git checkout v25.11.03-2
- reset_all
- git checkout -b 42374 origin/main
- Apply the patch
- restart_all
- Go to staff interface (e.g. http://localhost:8081/)
- Log into web installer
- Click "Update your database"
- Note it works

- Repeat the process but instead of using the web installer
use updatedatabase.pl directly (say through koha-upgrade-schema)

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:31 -06:00
Lucas Gass a601b14f26
Bug 42406: DBRev 25.12.00.058
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:31 -06:00
David Cook 8dc79ed415
Bug 42406: (QA follow-up) fix QA tool failure
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
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
Nick Clemens 3107cdc368
Bug 42406: Split delete_reports into delete_own_reports and delete_all_reports
This patch splits delete_reports into delete_own_reports and delete_all_reports

While delete_all_reports supercedes delete_own_reports it seems to make more sense than
delete_own_reports and delete_others_reports.

With delete_all_reports we can have a user who can delete all, without specific permission to delete their own
The other way we would have delete others, and you could not delete your own, which seems more odd

To test:
 1 - Have 2 users - 1 with delete_reports, 1 without
 2 - Apply patches, update database
 3 - Confirm user with permission now has 'delete_all_reports' and other user still has no reports deletion permission
 4 - Give the second user 'delete_own_reports' permission
 5 - Create 4 reports as each user
 6 - Confirm view/edit pages show the delete button for each user, and confirm they both work
 7 - You shoudl have 2 reports left for each user
 8 - Confirm each user can delete their own report from the saved SQL list:
     http://localhost:8081/cgi-bin/koha/reports/guided_reports.pl?op=list
 9 - There should be 1 report from each
10 - Give the second user 'delete_all_reports' permission in one tab and reload the saved reports in another tab
11 - Select both reports in the second tab
12 - Remove 'delete_all_reports' from the second user, leaving 'delete_own_reports' in the first tab
13 - Click 'Delete selected' in the reports tab
14 - Confirm you get 'You do not have permission to continue'
15 - Hit back, deselect other users report, confirm you can delete your own with 'Delete selected'
16 - Sign off

Signed-off-by: Judy Poyer <jpoyer@aacpl.net>
Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:29 -06:00
Nick Clemens cdd7438ef6
Bug 38414: (QA follow-up) Adjust templates
Now that we can explicitly list reports with any one of the permissions we need
to adjust some displays to ensure we can get to the list view.

Additionally I change 'Use saved' to 'View saved' since we may not be able to run.

I also fix the action buttons to show 'View' instead of 'Run' if user cannot execute.

I adjust reports home to check if the user can use report plugins before showing that link as well

Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:29 -06:00
David Cook 56d32231b2
Bug 38414: (follow-up) comprehensively cover ops with required permissions
Test plan:
0. Apply the patch and restart_all

1. Using a user with only "create_reports", "execute_reports", or "delete_reports",
try to perform operations not covered by that permission.
That is, if you have "create_reports", try to run, export, and delete reports.
If you have "execute_reports", try to create, modify, and delete reports
If you have "delete_reports", try to create, modify, and run reports

2. Note that a user with each of these permissions can list and view SQL.

Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:28 -06:00
Jonathan Druart e472a7607f
Bug 38414: Fix permission controller-side
This bug refactors the preview sql buttons to pass the permissions granted for each report to
the javascript that clones and updates the modal.

That allows us to check for the appropriate permissions when preparing the buttons for the modal
and to remove any that are not allowed.

This allows for different permissions per report for future devs
(see bugs 42406, 16631)

To test:
1. Create some reports
2. Create a user with only execute_reports permissions
3. Login as that user and go to Reports > use saved
4. In the actions column click the ^ and select 'Preview sql'
5. Notice delete button renders and works to delete
6. Apply patch and restart_all
7. Repeat step 4, now only the Run report button renders and works
8. Add permissions for create_reports
9. Repeat step for, now Edit, Duplicate, Schedule and run buttons render and work
10. Add permissions for delete_reports
11. Repeat step 4, confirm the delete button renders and works
12. Confirm the preview sql button works to preview different reports

We also fix some error in the script, several wrong things there:
* delete was not replaced with cud-delete
* missing op for the creation of new reports
* update and run requires two permssions

Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>

Signed-off-by: Scott Barter <sbarter@aacpl.net>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:28 -06:00
Brendan Lawlor 6a5b68552c
Bug 30303: (follow-up) Copy values from merge results instead of CGI
This patch makes it so the values copied come from the merge results
which fixes the case of copying empty values or - accurately.

To test:
1. Have a patron with some empty fields and some fields with '-'
2. Follow the original test plan and try to copy those fields
3. confirm empty fields and '-' now copy as expected

Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:27 -06:00
Brendan Lawlor 143b01dfa4
Bug 30303: Copy values to keeper record when merging patrons
Signed-off-by: Lari Taskula <lari.taskula@hypernova.fi>
Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:27 -06:00
Brendan Lawlor a906f38203
Bug 30303: Add ui for copying values when merging patrons
When merging two patrons this patch adds a new checkbox to
'Copy values from the record to be deleted'

To test:
- Apply patches and restart_all
1. From select two patrons and click 'Merge selected patrons'
2. Select one of the records
3. Check the new box to 'Copy values from the record to be deleted'
4. A diff of the two patrons is shown with checkboxes for each field
5. Confirm checking and un-checking the boxes updates the diff as expected
6. Confirm changing the patron selection updates the diff as expected.
7. Confirm un-checking and re-checking the Copy values from the record to be deleted
   resets the check boxes for the fields to copy
8. Select some fields to copy values and click merge patrons
9. Confirm the values were copied as expected

Sponsored-by: CLAMS
Signed-off-by: Lari Taskula <lari.taskula@hypernova.fi>
Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:26 -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
Martin Renvoize cdf015b497
Bug 41902: Display additional biblio info on duplicate check
When a duplicate record is suspected during cataloguing, query the
AdditionalContents record_display category for the
StaffDuplicateCheckPage location. If configured, process the content
as a Template Toolkit template with the duplicate biblio object and
display the rendered result in the duplicate warning.

The TT context variable is 'biblio' (a Koha::Biblio object), so
administrators can use expressions like:
  [% biblio.author %] ([% biblio.copyrightdate %])

Content is scrubbed using the record_display scrubber for XSS safety.

Signed-off-by: Jeanne Mauriello <jeanne.mauriello@mainlib.org>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:24 -06:00
Martin Renvoize 470ac28564
Bug 41902: Add StaffDuplicateCheckPage location to record_display
Add a new 'StaffDuplicateCheckPage' location to the record_display
category in the Additional Contents admin UI (Tools > Additional
Contents). This allows administrators to configure custom bibliographic
information to display when a duplicate record is suspected during
cataloguing.

This uses the existing AdditionalContents infrastructure (bug 39860)
rather than a system preference, providing per-branch and multilingual
support.

Signed-off-by: Jeanne Mauriello <jeanne.mauriello@mainlib.org>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:24 -06:00
Martin Renvoize d095599447
Bug 42030: (follow-up) Don't diff undefined fields
This patch updates the diff for ADD/CREATE and DELETE logactions to
exclude undefined fields from the diff.

Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:23 -06:00
Martin Renvoize 1f4320be07
Bug 42030: (follow-up) Fix POD for Koha::Suggestion
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:23 -06:00
Martin Renvoize efe6f26e41
Bug 42030: Populate diff column for suggestion CREATE/MODIFY/DELETE logs
Update suggestion action logging to populate the diff column using the
correct before/after semantics:

- CREATE: empty before-state ({}), new suggestion data as after-state
- MODIFY: pre-change unblessed data as before, post-store object as after
- DELETE: log before the SQL DELETE so the object can still be fetched;
  pass the suggestion as both $infos (info column) and $original (diff
  before-state), logaction sets after-state to {}

Also add tests verifying the diff column is populated for all three actions.

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-01 11:04:22 -06:00
Lucas Gass 7066ac0a93
Bug 23909: DBRev 25.12.00.057
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:21 -06:00
Martin Renvoize 744ac06569
Bug 23909: Preserve SCO behavior on upgrade (value 1 -> 2)
The atomicupdate converts AllowItemsOnHoldCheckoutSCO and
AllowItemsOnHoldCheckoutSIP from YesNo to Choice (0|1|2), where
value 1 now means "Allow pending holds only" and value 2 means
"Allow pending and waiting holds".

For AllowItemsOnHoldCheckoutSCO, pre-upgrade value 1 meant "allow
all on-hold checkouts (pending AND waiting)" at SCO. Post-upgrade,
sco-main.pl reads value 1 as "pending only", silently downgrading
sites that previously allowed waiting-hold checkouts. Remap SCO
value 1 -> 2 at upgrade time to preserve prior behavior.

For AllowItemsOnHoldCheckoutSIP, the SIP checkout code was already
gated on >= 1 for pending and >= 2 for waiting, so value 1 meant
"pending only" in practice even under the old YesNo type. No value
migration is needed -- leaving it at 1 preserves current behavior.

The value remap lives inside the branch that only runs when the
type is not already Choice, so re-running the atomicupdate is safe
and will not double-remap.

Test plan:
  1) On a pre-upgrade DB, set both prefs to YesNo/1:
       UPDATE systempreferences SET type='YesNo', options=NULL,
         value='1'
        WHERE variable IN ('AllowItemsOnHoldCheckoutSCO',
                           'AllowItemsOnHoldCheckoutSIP');
  2) Run installer/data/mysql/updatedatabase.pl.
  3) Verify:
       SELECT variable, type, options, value FROM systempreferences
        WHERE variable IN ('AllowItemsOnHoldCheckoutSCO',
                           'AllowItemsOnHoldCheckoutSIP');
     SCO -> type=Choice, options=0|1|2, value=2.
     SIP -> type=Choice, options=0|1|2, value=1.
  4) Re-run updatedatabase.pl; confirm both prefs report
     "already migrated, skipping" and values are unchanged.
  5) Spot-check other starting values: SCO=0 stays 0, SCO=2
     stays 2, SIP=0 stays 0.

Signed-off-by: Kristi Krueger <kkrueger@cuyahogalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:21 -06:00
Martin Renvoize f43201075a
Bug 23909: Add AllowHoldCheckoutOverride for staff circulation
Introduce a new AllowHoldCheckoutOverride system preference (default 1
to preserve existing behaviour) that lets libraries prevent staff from
overriding held-item blockers at the circulation desk.

When AllowHoldCheckoutOverride is set to 0, CanBookBeIssued still
returns the hold-related keys (RESERVED, RESERVE_WAITING, TRANSFERRED
and PROCESSING) in the confirmation hash, but circ/circulation.pl
promotes them to the impossible hash before rendering the template.
This means no override button appears, and checkout of the held item
is refused outright with a clear message explaining which patron the
hold is for.

Matching entries have been added to the IMPOSSIBLE block of the
circulation template so the blocked-checkout message is meaningful.

This sits alongside the existing Allow*Override family
(AllowTooManyOverride, AllowNotForLoanOverride, AllowHoldPolicyOverride
and AllowRenewalOnHoldOverride) and uses the same staff-only semantics.

Test plan:
1. Set AllowHoldCheckoutOverride=1 (default). Place a waiting hold
   for patron A on an item. At /cgi-bin/koha/circ/circulation.pl
   attempt to check the item out to patron B: a confirmation dialog
   appears with "Check out anyway" override buttons.
2. Set AllowHoldCheckoutOverride=0. Repeat: checkout is refused with
   a message stating the item has been waiting for patron A. No
   override button is offered.
3. Repeat with pending holds (reserves.found IS NULL), items in
   transit and items being processed: each is refused when the
   preference is 0 and offered as an overridable confirmation when
   the preference is 1.

Signed-off-by: Kristi Krueger <kkrueger@cuyahogalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:20 -06:00
Martin Renvoize b80dafc210
Bug 23909: Apply hold policy to OPAC REST self-checkout
The OPAC REST self-checkout path (guarded by OpacTrustedCheckout) is
functionally SCO-over-API, so it should honour the same hold policy
as the SCO web interface. Previously _check_availability unconditionally
promoted RESERVED, RESERVE_WAITING, TRANSFERRED and PROCESSING from
the confirmation hash to the blockers hash for any public caller. This
meant the OPAC REST endpoint always refused held items regardless of
the librarian's AllowItemsOnHoldCheckoutSCO configuration.

With this patch the promotion is gated on AllowItemsOnHoldCheckoutSCO:

* 0 - Promote all four hold confirmations to blockers (previous
      behaviour for pending holds)
* 1 - Promote only waiting/transit/processing; silently allow
      checkout of items with a pending hold
* 2 - Promote none; silently allow checkout of items with any hold

Remaining hold confirmations are stripped from the response so the
caller does not have to resolve a confirmation token for a policy the
admin has already granted.

Test plan:
1. prove t/db_dependent/api/v1/checkouts.t
2. Enable OpacTrustedCheckout. POST an item with a pending hold for
   another patron to /api/v1/public/patrons/X/checkouts:
   - AllowItemsOnHoldCheckoutSCO=0: 403 Checkout not authorized
   - AllowItemsOnHoldCheckoutSCO=1: 201 Created
3. Place a waiting hold for another patron on an item. Repeat:
   - AllowItemsOnHoldCheckoutSCO=1: 403
   - AllowItemsOnHoldCheckoutSCO=2: 201

Signed-off-by: Kristi Krueger <kkrueger@cuyahogalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:20 -06:00
Martin Renvoize cd3b0770c5
Bug 23909: Extend SIP to distinguish pending vs waiting holds
This patch extends the SIP checkout handler to honour the new
three-valued AllowItemsOnHoldCheckoutSIP preference:

* 0 - Don't allow checkout of any held item
* 1 - Allow checkout when the hold is still pending (not attached)
* 2 - Also allow checkout when the hold is waiting, in transit or
      being processed for another patron

Previously the preference was a simple YesNo toggle that, when
enabled, allowed the checkout regardless of whether the hold was
pending or already attached to an item on the hold shelf. Libraries
could not block the more disruptive case (checking out a waiting
hold destined for another patron) without also blocking pending
holds.

The existing post-confirmation filter is reworked into a single
branch per confirmation type that consults the preference value.

Test plan:
1. prove t/db_dependent/SIP/Transaction.t
2. With AllowItemsOnHoldCheckoutSIP=0, place a pending hold for
   patron A on an item, attempt SIP checkout to patron B: refused.
3. Repeat with the hold marked waiting: refused.
4. Set AllowItemsOnHoldCheckoutSIP=1, pending hold: allowed;
   waiting hold: refused.
5. Set AllowItemsOnHoldCheckoutSIP=2, waiting hold: allowed.

Signed-off-by: Kristi Krueger <kkrueger@cuyahogalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:19 -06:00
Martin Renvoize b336cf88d5
Bug 23909: Block waiting holds at SCO checkout
AllowItemsOnHoldCheckoutSCO was previously passed through to
CanBookBeIssued as the ignore_reserves flag, which skipped the entire
holds branch in C4::Circulation and silently allowed checkout of
items already waiting on the hold shelf (reserves.found = 'W') or in
transit/processing for another patron's hold.

With the pref now a 3-value Choice:
  0 = don't allow checkout of any held items
  1 = allow checkout of items with a pending hold only
  2 = allow checkout of items with any hold state

sco-main.pl now always calls CanBookBeIssued with ignore_reserves=0
and filters the needsconfirmation keys according to the pref value,
mirroring the pattern SIP already uses. RESERVE_WAITING, TRANSFERRED
and PROCESSING are added to the error-matching list and to the SCO
template so patrons see a specific "on the hold shelf" / "in transit"
/ "being processed" message instead of falling through to the generic
"please see a member of the library staff" line.

Test plan:
- Enable WebBasedSelfCheck; log in to /cgi-bin/koha/sco/sco-main.pl.
- Create an item with a pending hold for another patron
  (reserves.found IS NULL).
- With AllowItemsOnHoldCheckoutSCO = 0, scan the barcode: checkout is
  refused with "This item is on hold for another patron".
- Switch to 1 ("Allow pending holds only"): checkout succeeds.
- Set the hold to waiting (reserves.found = 'W') and try again with
  the pref still at 1: checkout is refused with "This item is waiting
  on the hold shelf for another patron".
- Switch to 2 ("Allow pending and waiting holds"): checkout succeeds.
- Repeat with found = 'T' and 'P' at pref = 1 -> refused with the
  appropriate transit/processing message; at pref = 2 -> allowed.

Signed-off-by: Kristi Krueger <kkrueger@cuyahogalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:19 -06:00
Martin Renvoize 83155b8ffd
Bug 23909: Add syspref values and atomicupdate
AllowItemsOnHoldCheckoutSCO and AllowItemsOnHoldCheckoutSIP become
Choice (0|1|2) so libraries can distinguish pending from waiting holds:

  0 = Don't allow (unchanged)
  1 = Allow pending holds only (new semantics; previously "Allow")
  2 = Allow pending and waiting holds (preserves old permissive behaviour)

A new AllowHoldCheckoutOverride YesNo preference (default 1) follows
the existing Allow*Override family and lets libraries block the
"check out anyway" override in the staff interface when an item is on
hold for another patron.

Note: installations that previously had either Allow*CheckoutSCO/SIP
set to 1 will migrate to "Allow pending only". Libraries that relied
on the previous behaviour (silently allowing checkout of items already
waiting on the hold shelf) need to manually switch to 2.

Test plan:
- Apply the patch and restart Koha.
- Run the web installer / updatedatabase.pl.
- Confirm no errors and the log mentions the three sysprefs.
- Run it a second time; confirm the "already migrated" / "already
  exists" skip messages appear.
- In Administration > System preferences, search for
  AllowItemsOnHoldCheckoutSCO and AllowItemsOnHoldCheckoutSIP and
  confirm the dropdown now has three options.
- Confirm AllowHoldCheckoutOverride appears in the same section with
  the Allow/Don't allow choices and defaults to Allow.

Signed-off-by: Kristi Krueger <kkrueger@cuyahogalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 11:04:11 -06:00
Lucas Gass dc866af68b
Bug 26355: (RM follow-up) Fix script attribute order
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:08 -06:00
Lucas Gass dc2c4014d0
Bug 26355: (RM follow-up) Fix atomic update
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:07 -06:00
Lucas Gass 7c7258b9da
Bug 26355: (RM follow-up) force DBIC
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:07 -06:00
Lucas Gass 3e7626d4e0
Bug 26355: (RM follow-up) Adjust number of tests
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:06 -06:00
Lucas Gass 3fa81b71b4
Bug 26355: DBRev 25.12.00.056
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:06 -06:00
Martin Renvoize 9487953932
Bug 26355: (QA follow-up) Strip self_renewal_available in EmailPatronRegistrations tests
to_api now returns the derived self_renewal_available field, so
reusing a to_api'd patron as the POST body for /api/v1/patrons fails
with 500 (the value has no matching DB column). Adjacent subtests
already delete this read-only field before POSTing; do the same in
the EmailPatronRegistrations subtest.

Test plan:
1. prove t/db_dependent/api/v1/patrons.t — all tests pass.

Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:05 -06:00
Martin Renvoize e625b68bc1
Bug 26355: (QA follow-up) Bump test plan for new subtest
The "identify_updated_extended_attributes" subtest was added without
bumping the top-level plan, so prove reported a "Bad plan" failure
even though every subtest passed.

Test plan:
1. prove t/db_dependent/Koha/Patron.t — all tests pass, no plan
   mismatch.

Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:05 -06:00
Martin Renvoize 0cdf8a74c8
Bug 26355: (QA follow-up) Add CSP nonce to inline module script
The forbidden_patterns QA check requires <script> elements to have
either a "src" or "nonce" attribute. The inline module script that
hydrates the Vue island in opac-user.tt was missing a nonce.

Test plan:
1. Run the QA tool against opac-user.tt; forbidden_patterns should
   no longer report the inline <script> on line 1024.
2. Load the OPAC user summary page and confirm the Vue island still
   hydrates (no CSP violation in the browser console).

Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:04 -06:00
Matt Blenkinsop bce9aad974
Bug 26355: (follow-up) Fix handling of extended attributes
Extended attributes weren't being identified as changes and so need to be checked to see if any of them have changed

Sponsored-by: Westminster City Council <https://www.westminster.gov.uk/>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:04 -06:00
Andrew Fuerste Henry da92eaa4d3
Bug 26355: (QA follow-up) perl tidy to address qa tool errors
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:03 -06:00
Matt Blenkinsop 029a4f71ec
Bug 26355: (follow-up) Allow the 'Clear' button to remove empty messages
Signed-off-by: Christopher Brannon <cbrannon@cdalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:03 -06:00
Matt Blenkinsop cb9e70f8c2
Bug 26355: (follow-up) Allow the setting of multiple information messages
Signed-off-by: Christopher Brannon <cbrannon@cdalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:02 -06:00
Matt Blenkinsop a76eb18bed
Bug 26355: Add unit tests
Signed-off-by: Christopher Brannon <cbrannon@cdalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:02 -06:00
Matt Blenkinsop d04923b3b9
Bug 26355: Add cypress tests
Adds categories to the db insertion mechanics and allows values to be passed to the patron insertion method

Signed-off-by: Christopher Brannon <cbrannon@cdalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:01 -06:00
Matt Blenkinsop e26310e14b
Bug 26355: Update the OPAC templates to support the Vue island
Signed-off-by: Christopher Brannon <cbrannon@cdalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:01 -06:00
Matt Blenkinsop 01f5214f0f
Bug 26355: Add the class methods and endpoint to handle renewal
This is part of a sponsored piece of development from one of our customers. There was a specification of requirements but I was conscious of the ideas already discussed on this bug and wanted to try and develop a solution that would balance both sets of requirements. The concept works as follows:
- Self-renewal can be enabled and controlled at a category level
    - The window within which it is available is customisable (both pre and post-expiry)
        - Defaults to the NotifyBorrowerDeparture syspref
    - The amount in fines that block self-renewal is customisable
        - Defaults to the category level charges and then the noissescharge preference
    - The message to display if renewal fails is customisable
- Libraries can also add information to display to the patron at the start of the process.
- The self-renewal process will also respect the OPACPatronDetails and AutoApprovePatronProfileSettings sysprefs. If OPACPatronDetails is enabled, the renewal will redirect the patron to the OPAC details form and ask them to confirm their details are still correct. Any changes will be logged as patron modifications and approved in line with the auto approval syspref
- Renewal happens regardless of whether OPACPatronDetails is enabled, only the details changes are logged as modification requests
- Notifications are sent to the patron in line with their messaging preferences

Test plan:
1. Apply all patches and reset_all
2. In the intranet, navigate to patron categories and edit the ‘Patron’ category
3. Scroll to the bottom, there should be a section called ‘Account expiry and self-renewal’
4. Set ‘Allow self-renewal’ to Yes
5. Set Self-renewal availability period to be 10 days
6. Set Self-renewal availability post-expiry’ to also be 10 days and save
7. In system preferences, set the OPACPatronDetails preference to ‘Don’t allow’
8. Choose a patron that has the ‘Patron’ type category
9. Edit that patron so that you can log into the OPAC and set their expiry date to be yesterday’s date
10. Log into the OPAC as that patron (it may be easiest to do this in incognito as we will be swapping between the intranet and OPAC throughout the test plan and it will remove the need to keep logging out and in)
11. You should see a message that says “You are eligible for self-renewal. Please click here to renew your account.”
12. Click that link - you will be presented with a modal that says “Are you sure you want to renew your account?”
13. Click “Close” - we don’t want to complete renewal yet as there is more functionality available to test
14. Navigate to the patron category again, set ‘Self-renewal availability post-expiry’ to be 0
15. Refresh the OPAC, the self-renewal link should not be there as we are now outside the patron self-renewal window
16. Undo that change in the patron category, the link should reappear when refreshing the OPAC
17. Now set the patron’s expiry date to be tomorrow
18. In the category, set ‘Self-renewal availability period’ to be blank
19. Refresh the OPAC, it should still show the link as it is defaulting to NotifyBorrowerDeparture
20. Set NotifyBorrowerDeparture to 0
21. Refresh the OPAC, the link should vanish
22. In the category, restore ‘Self-renewal availability period’ to 10 days
23. Refresh the OPAC, the link should reappear
24. In the intranet, set the patron to be debarred using a manual restriction
25. In the OPAC refresh the page - self-renewal should no longer be available
26. Remove the restriction - the renewal link will be visible again
27. Set a fine on the patron record of 6 USD
28. Refresh the OPAC, the self-renewal link will be gone because we are over the global ‘noissuescharge’ preference value of 5
29. In the category, set ‘Checkout charge limit’ to 7
30. Refresh the OPAC, the link will be visible because we are now under the category charge
31. Add an additional fine of 2 to the patron account
32. Refresh the OPAC, the self-renewal link is gone
33. In the category, set ‘Self-renewal charge limit’ to be 10
34. Refresh the OPAC, the link will be visible again
35. In the category, set ‘Self-renewal failure message’ to be whatever you want
36. In the OPAC, refresh the page and then click into the self-renewal process
37. This time, click “No” - you should see the failure message you have just set
38. Click “Close” and refresh the page to reset this message
39. In the category, set a message in the “Self-renewal information message”
40. In the OPAC, refresh the page and then click the renewal link again
41. You should be met with the modal, this time displaying the information message we just set up in the patron attributes
42. Click “Yes” - you should see the “Are you sure you want to renew your account?” message again
43. This time when prompted with the “Are you sure” message, click “Yes”
44. The page should refresh and display a confirmation with the new expiry date.
45. In the intranet, view the patron’s record - the date should have been updated
46. In the Modification log tab, there should be a log entry for “Membership renewed”
47. Change the patron’s expiry date again, this time to be yesterday’s date
48. In the OPAC, click on “Personal detail”, the new expiry date should show there
49. Now set the OPACPatronDetails syspref to be “Allow”
50. Click on “Summary” again, the self-renewal link should be there again
51. Click into the self-renewal process and click “Yes” on your verification step
52. This time you should be met with a message that says “Confirm your account details”
53. Click “Continue” - you will be redirected to the OPAC details form
54. There should be a message at the top that says “Please verify your details to proceed with your self-renewal.”
55. Update a field somewhere on the patron form
56. Scroll to the bottom - the button should say “Submit renewal request”
57. Click this button
58. You will be shown the renewal confirmation message
59. In the intranet, on the homepage you should now see a modification request
60. Check the request, it should match the fields you changed - don’t approve or deny for now
61. Set ‘AutoApprovePatronProfileSettings’ in sysprefs to “Enable”
62. Reset your patron’s expiry date to today’s date
63. In the OPAC, click into Personal details and then back into Summary again (we need to do this to remove the success parameter from the URL)
64. The renewal link will be visible again - go through the process again
65. On the form, you should see an alert that you have already sent a modification request
66. This time, change a different field to the previous attempt
67. Submit the form
68. In the intranet there should be no modification this time and the change should be visible in the patron record as it has been automatically approved and handled as per the syspref
69. prove t/db_dependent/Koha/Patron.t
70. prove t/db_dependent/api/v1/patrons_self_renewal.t
71. yarn cypress run --spec t/cypress/integration/Islands/SelfRenewal.ts
72. Sign off!

Signed-off-by: Christopher Brannon <cbrannon@cdalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:00 -06:00
Matt Blenkinsop 133946e50c
Bug 26355: Add a Vue island for the self-renewal modal
Signed-off-by: Christopher Brannon <cbrannon@cdalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:30:00 -06:00
Matt Blenkinsop 4979f0aead
Bug 26355: Add the new category fields to the UI
Signed-off-by: Christopher Brannon <cbrannon@cdalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:29:59 -06:00
Matt Blenkinsop 6ef6788406
Bug 26355: Update the API specs
Signed-off-by: Christopher Brannon <cbrannon@cdalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:29:59 -06:00
Matt Blenkinsop a9fda16f38
Bug 26355: DBIC file
Signed-off-by: Christopher Brannon <cbrannon@cdalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:29:58 -06:00
Matt Blenkinsop dfcb16a9ce
Bug 26355: Add new columns to the patron categories table
Signed-off-by: Christopher Brannon <cbrannon@cdalibrary.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-05-01 09:29:52 -06:00
Lucas Gass f0436bd140
Bug 41882: (follow-up) Fix failing xt/tt_kohaTable_tidy.t test
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-30 14:06:22 -06:00
Lucas Gass 482c323144
Bug 41823: (follow-up) Remove redundant table_settings variable
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
2026-04-30 14:06:22 -06:00