GetNormalizedUPC duplicated logic already available via the
MetadataExtractor (Bug 34828) and exposed as $biblio->normalized_upc.
All callers migrated:
- Scripts with $biblio available: use $biblio->normalized_upc
- C4::Search (only MARC::Record): reuses the extractor instance
already created for OCLC
Assisted-by: Sonnet 4.6 (Anthropic)
Test plan:
1. Apply patch
2. Run:
$ ktd --shell
k$ prove t/db_dependent/Koha.t \
t/db_dependent/Koha/Biblio.t
=> SUCCESS: Tests pass
3. Verify UPC numbers still display in OPAC and staff detail pages
(Syndetics cover images)
4. Sign off :-D
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>
Replaces all calls to GetNormalizedOCLCNumber() and
Koha::Util::MARC::oclc_number() with the extractor pattern.
OPAC scripts pass their already ViewPolicy-filtered $record directly
to the extractor. This preserves the existing filtering behavior
without introducing new abstraction layers:
Koha::Biblio::Metadata::Extractor->new( { metadata => $record } )->get_normalized_oclc
Intranet scripts and the Koha::Biblio convenience method use the
raw cached extractor (035 visibility is not affected by ViewPolicy
in practice, and intranet doesn't filter):
$biblio->normalized_oclc
Test plan:
1. Verify OCLC numbers display on OPAC detail, ISBD, MARC detail
2. Verify staff catalogue detail and search results
3. prove t/db_dependent/Koha/Biblio.t
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Assisted-by: Sonnet 4.6 (Anthropic)
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>
Removed by 6726b1a254 on the assumption store() strips centrally -
but TransformHtmlToMarc never calls store(), so callers like
addbiblio.pl (FindDuplicate, duplicate redisplay) see unstripped
control characters again, the same failure Bug 34549 fixed.
repair_marcxml covers more faults but works on full MARCXML and can
throw - too big a change here; left as a follow-up.
Test plan:
prove t/db_dependent/Biblio/TransformHtmlToMarc.t
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
After the previous patch, $flags->{WAITING} from
C4::Members::patronflags is no longer read anywhere in Koha.
Test Plan:
1) Apply this patch
2) prove t/db_dependent/SIP/ t/db_dependent/Members.t
3) Note all tests pass!
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Aleisha Amohia <aleisha@catalyst.net.nz>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This patch fetches the patron's holds once into a list of
Koha::Hold objects and derives both hold_items and unavail_holds
from that single fetch.
Test Plan:
1) Apply this patch
2) prove t/db_dependent/SIP/
3) Note all tests pass!
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Aleisha Amohia <aleisha@catalyst.net.nz>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Some 3rd party SIP clients calculate the number of holds ready
for pickup as:
holds ready for pickup = hold_items - unavail_holds
Right now this can produce a negative number since hold_items
contains only waiting holds.
This patch makes hold_items contain all of the patron's holds,
so hold_items - unavail_holds correctly gives the count of holds
ready to be picked up.
Test Plan:
1) Apply the first patch only
2) prove t/db_dependent/SIP/Patron.t
3) Note the new "hold_items counts all holds" subtest fails
4) Apply this patch
5) prove t/db_dependent/SIP/Patron.t
6) Note the tests pass!
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Aleisha Amohia <aleisha@catalyst.net.nz>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
ISBNs beginning with 979 do not have an ISBN-10 version, and need to be normalized to an ISBN-13 format. All others, if valid, can safely be normalized to ISBN-10 format (current behaviour).
To set up
1. Download the attached sample records with 979 ISBNs
2. Log into the staff interface and go to Cataloguing
3. Go to Stage MARC records for import and upload the sample MARC file
4. Stage and import the biblio records
5. Once imported, open the detail pages for some of the records
6. In another tab, do a catalogue search and open the detail pages for ISBN-10 records, for example biblionumbers 6 and 54
To test
1. Apply the patch and restart services
2. Log into the staff interface and go to Koha Administration, Global system preferences, Enhanced Content tab
3. Cycle through each cover image service, enabled by the system preferences
4. With each cover image service is enabled, refresh the record detail pages you have open. The ISBN-10 records should CONTINUE to show their cover images. The newly imported ISBN-13 records should NOW show their cover images.
5. Confirm tests pass: t/db_dependent/Koha.t
testing with ISBN 9798885795692 should return this image from Amazon: https://images-na.ssl-images-amazon.com/images/P/9798885795692.01.LZZZZZZZ.jpg
testing with ISBN 9798876536914 should return this image from Amazon: https://images-na.ssl-images-amazon.com/images/P/9798876536914.01.LZZZZZZZ.jpg
Sponsored-by: Horowhenua Libraries Trust
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>
AddBiblio/ModBiblio/ModBiblioMarc gain an optional warnings arrayref
option: any repair messages recorded by Koha::Biblio::Metadata->store()
(see previous commit) are appended to it via object_messages, so a
caller can show the cataloguer what was automatically fixed instead of
the fix happening invisibly.
AddBiblio also now distinguishes Koha::Exceptions::Metadata::Invalid
from other errors in its catch block and rethrows it, so a caller can
tell "this record is unrecoverable" apart from an unexpected internal
error and react accordingly (see following bugs for callers doing so).
The inline StripNonXmlChars calls in TransformHtmlToMarc are removed,
since stripping is now handled centrally and consistently by
Koha::Biblio::Metadata->store() rather than ad-hoc at every call site.
Signed-off-by: Baptiste Bayche <baptiste.bayche@inlibro.com>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Sponsored-by: OpenFifth
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
login_core only cleared leftover session state on the failed-auth
branch. The earlier 'SIP access not authorized' return (unknown
terminal id) left any account/ils/institution/policy/credentials from
a prior connection on a reused prefork worker in place.
Move the reset to the top of login_core so every non-success exit path
leaves clean state, and only a successful api_auth re-establishes the
session. This keeps the no-authenticated-state-until-auth invariant in
a single place rather than duplicating the key list per branch.
Also extend the unit test to cover the unknown-terminal path on a
reused worker.
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
After a SIP terminal logs in successfully, a later connection handled by the
same worker could bypass authentication entirely. login_core() stores the account
in the session before calling api_auth(), and left it in place when authentication
failed. The check in raw_transport() that is meant to drop unauthenticated connections
only looks at $self->{account}->{id}, which is now populated straight from the SIP config,
so the connection is treated as logged in.
Test Plan:
1) Create a SIP user with the username and password "term1"
2) Restart the SIP server
3) Apply the first patch
4) prove t/db_dependent/SIP/MsgType.t
5) Note the new test fails
6) Telnet to the SIP server port ( 6001 for ktd )
5) Send a successful login: 9300CNterm1|COterm1
6) Note you get a 941 back
7) Disconnect, then telnet to localhost 6001 again
8) Send a bad login: 9300CNterm1|COterm2
9) Note you get a 940 back, but the connection stays open
10) Send a patron information request, using a real patron cardnumber:
6300120260615 110310 AOCPL|AA23529001000463
11) Note you get a 64 patron information response with the patron's data back,
even though the login failed!
12) Apply the second patch
13) prove t/db_dependent/SIP/MsgType.t
14) Note the subtest now passes!
15) Restart all the things!
16) Repeat steps 4 to 10
17) Note the the patron info request returns no patron data and the connection is dropped!
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Signed-off-by: Sanjar Tulkinov Anvar o'g'li <sanjarbiy11@gmail.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
The original fix only whitelisted agefield, condition_field, and
substitution_field at the point they're saved in
tools/automatic_item_modification_by_age.pl, leaving the actual
consumer of the stored syspref, C4::Items::ToggleNewStatus, trusting
agefield and substitution field unchecked. agefield is spliced
directly into raw SQL (TO_DAYS($agefield)) and so can't be
parameterized like an ordinary value; substitution field reaches a
dynamic method call ($item->$field($value)). Any future or
alternate writer of the automatic_item_modification_by_age_configuration
syspref would have reopened both holes without touching the form at
all.
This adds the same allow-list validation directly in ToggleNewStatus
(falling back to the safe default agefield, or skipping the
substitution/condition, exactly as before for missing values), shares
the age-field allow-list between the two files via a new
GetAutomaticItemModificationAgeFields() in C4::Items so it can't drift
between them, and logs rejected values via Koha::Logger instead of
silently discarding them so tampering leaves an audit trail.
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
The angle-bracket delimiter notation in the POD prose (e.g.
PATTERN<DELIM>) caused the POD parser to treat the trailing letter of
each word as an unknown formatting code (N<>, T<>). Replace with
C<s/PATTERN/REPLACEMENT/FLAGS> and a parenthetical note that any
non-space delimiter is accepted.
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
When eval { qr/$pattern/ } fails (e.g. the admin-configured s/// contains
an invalid regex), _apply_substitution previously returned silently with no
indication of why the rule was skipped. Add a Koha::Logger warn so the
failure appears in the log and is diagnosable without a debugger.
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Note: While the methodology in Koha::Regex::Replacement may seem
bespoke, it's similar to how Python's re.sub(pattern, repl, string)
regex substitution works.
Unit tests:
prove -v t/Koha/Regex/Replacement.t \
t/SimpleMARC.t \
t/db_dependent/MarcModificationTemplates.t \
t/Labels_split_Regex.t
Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
C4::ClassSplitRoutine::RegEx::split_callnumber ran the admin-configured rule
through eval "\$cn_item =~ $regex", executing arbitrary Perl. Parse the
rule as s<DELIM>PATTERN<DELIM>REPLACEMENT<DELIM>FLAGS, reject the /e flag,
compile the pattern with qr// and expand the replacement as data via
Koha::Regex::Replacement::expand_template, so the rule is applied without
eval.
Signed-off-by: Sanjar Tulkinov Anvar o'g'li <sanjarbiy11@gmail.com>
Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
When an item is checked in via SIP2 at the correct target branch after a
transfer, the system was incorrectly triggering an alert flag. This occurred
because the TransferArrived message was not being filtered out for accounts
with checked_in_ok="1", causing the alert logic to treat successful transfer
completion as a failure condition.
The TransferArrived message indicates successful transfer completion and is
purely informational - it should not trigger SIP alerts. This fix ensures
that TransferArrived messages are filtered out before alert evaluation,
preventing false positive alerts on successful operations.
Test Plan:
1. Set up a SIP2 account with checked_in_ok="1" in SIPconfig.xml
2. Via staff client, check out an item to a patron
3. Return the item to a different branch to trigger a transfer
4. Use misc/sip_cli_emulator.pl to check in the item at the target branch
5. Verify SIP response shows alert flag 'N' (no alert) instead of 'Y'
6. Test wrong branch checkin still shows alert flag 'Y' with CV04 type
Before: Transfer completion at correct branch incorrectly shows alert='Y'
After: Transfer completion at correct branch correctly shows alert='N'
Sponsored-by: OpenFifth <https://openfifth.co.uk>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This patch adds the ability to set an item type as magentic for SIP
response purposes.
Test plan:
1) Run included database updates
2) Navigate to admin > itemtypes
3) Edit/Add an itemtype and scroll to the 'Sip media types' input
4) A new 'Sip magnetic' checkbox should be available
5) Confirm setting/unsetting it works as expected
6) The SIP magnetic flag bit should respect the setting
7) To test SIP functionality:
a) Start the SIP server (e.g., koha-sip service)
b) Create an item with an itemtype that has sip_magnetic checked
c) Use misc/sip_cli_emulator.pl to send a checkout request:
perl misc/sip_cli_emulator.pl -a localhost -p 6001 -su <sip_username> -sp <sip_password> -l <location_code> --patron <patron_cardnumber> --password <patron_password> --item <item_barcode> -m checkout
d) Check the SIP response to ensure it handles magnetic media correctly (e.g., no desensitization flag)
Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Marcus Michel <marcus.michel@rbkc.gov.uk>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
The `magnetic_media` flag was never set, it can be set from
`sip_media_type` if it's present. `sip_media_type` is set via the
Authorized Values system.
Checkins will not be sorted correctly by Biblioteca without this flag.
Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Marcus Michel <marcus.michel@rbkc.gov.uk>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
GetAgeRestriction is no longer used by any caller. This patch removes
it from the exports list and deletes the old test file.
Test plan:
1. Run tests without this patch:
$ ktd --shell
k$ prove t/db_dependent/Circulation.t \
t/db_dependent/Reserves.t \
t/Circulation/AgeRestrictionMarkers.t
=> SUCCESS: Tests pass!
2. Apply this patch
3. Run:
k$ prove t/db_dependent/Circulation.t \
t/db_dependent/Reserves.t \
t/db_dependent/Koha/Policy/Biblio/AgeRestriction.t
=> SUCCESS: Tests pass!
4. Sign off :-D
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Amended-by: Jonathan Druart
Removed this from the commit message:
"The function body is kept for now as it is not harmful and could be
used by third-party code. It is no longer exported."
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This patch updates CanBookBeIssued and CanItemBeReserved to use the
new policy class instead of calling GetAgeRestriction directly.
Test plan:
1. Run tests without this patch:
$ ktd --shell
k$ prove t/db_dependent/Circulation.t \
t/db_dependent/Reserves.t
=> SUCCESS: Tests pass!
2. Apply this patch
3. Repeat step 1
=> SUCCESS: Tests pass!
4. Sign off :-D
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Adds a logscriptaction() function parallel to cronlogaction() that logs
to the SCRIPTS action-log module rather than CRONJOBS, gated behind the
new ScriptLog system preference. Exported from C4::Log alongside the
existing cronlogaction().
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Test plan:
1. Create an order with multiple items and receive the order in full
2. From the vendor record, click Receive shipments
3. You should be in parcels.pl and the table should show your shipments
4. The Items expected column will show 0 even though you ordered multiple items
5. Apply patch and restart_all
6. Refresh the page, the column should now show a value
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
local_holdgroup_match was being hardcoded to 1 in the two LocalHoldsPriority
allocation paths (GiveLibrary and GiveLibraryGroup), regardless of whether the
chosen item actually satisfied the LocalHoldsExclusivity conditions. This
caused staff to be directed to pull an item that would not trigger the hold at
checkin when the two features used different patron/item controls.
The fix removes the premature local_holdgroup_match => 1 from both LHP paths and
lets _flag_local_holdgroup_matches evaluate all allocated items using the correct
LocalHoldsExclusivity* sysprefs.
Also fixes a pre-existing missing bind-variable placeholder in the
INSERT INTO tmp_holdsqueue statement (17 columns, only 16 ?).
Test updates:
- Existing local_holdgroup_match test gains the matching LocalHoldsExclusivity
sysprefs so the flag is set via the correct code path.
- New subtest reproduces the reported bug: LHP (HomeLibrary/homebranch) targets
a non-local item while exclusivity (PickupLibrary/holdingbranch) should not
flag it — confirms local_holdgroup_match is 0 after the fix.
Signed-off-by: Trevor Diamond <trevor.diamond@mainlib.org>
Sponsored-by: Main Library Alliance <https://www.mainlib.org/>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
LocalHoldsExclusivity and LocalHoldsPriority are adjacent but distinct
features: priority alters *which patron's hold* gets filled first, while
exclusivity restricts *which items* may fill a hold during a time window.
Each should be usable independently of the other.
Changes:
- Rename LocalHoldsPriorityExclusivityPeriod to LocalHoldsExclusivityPeriod
- Add LocalHoldsExclusivityPatronControl (PickupLibrary/HomeLibrary)
- Add LocalHoldsExclusivityItemControl (homebranch/holdingbranch)
- HoldsQueue: add _flag_local_holdgroup_matches() that flags
local_holdgroup_match on allocated items using hold-group matching
and the new exclusivity control prefs, regardless of LocalHoldsPriority
- Reserves: CheckReserves exclusivity check now computes its own
"is this item local" using the new prefs and hold-group siblings,
independent of the LocalHoldsPriority matching variables
- Atomicupdate handles both fresh install and rename-from-old-pref paths
- Pref UI note clarifies independence from LocalHoldsPriority
Test plan:
1. Apply patch, run updatedatabase.pl
2. Verify three new sysprefs exist in Administration > System preferences
> Circulation: LocalHoldsExclusivityPeriod, LocalHoldsExclusivityPatronControl,
LocalHoldsExclusivityItemControl
3. Set LocalHoldsPriority to "Don't give" (None)
4. Create a library hold group with at least two libraries (e.g. Lib-A, Lib-B)
5. Create a third library (Lib-C) NOT in the hold group
6. Create an item at Lib-B
7. Place a hold for a patron at Lib-A (pickup at Lib-A)
8. Set LocalHoldsExclusivityPeriod to 7, PatronControl to HomeLibrary,
ItemControl to homebranch
9. Run the holds queue builder (misc/cronjobs/holds/build_holds_queue.pl)
10. Verify hold_fill_targets.local_holdgroup_match = 1 for that hold
(item at Lib-B is in the same hold group as patron at Lib-A)
11. Check in the item at Lib-C — it should NOT fill the hold (exclusivity
blocks the non-local item within the 7-day window)
12. Check in the item at Lib-B — it SHOULD fill the hold (local match)
13. Reset: delete the hold, create a new one with a patron at Lib-C
(pickup at Lib-C). Run the queue again.
14. Verify hold_fill_targets.local_holdgroup_match = 0 for this hold
15. The item at Lib-B should now fill this hold at Lib-C checkin
(non-local patron, no exclusivity protection)
16. Set LocalHoldsExclusivityPeriod to 0 — verify non-local items can
fill holds normally regardless of hold_fill_targets flag
17. prove t/db_dependent/Holds/LocalHoldsPriority.t — passes
18. prove t/db_dependent/HoldsQueue.t — passes
19. prove t/db_dependent/Reserves.t — passes
Signed-off-by: Trevor Diamond <trevor.diamond@mainlib.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
When LocalHoldsPriority is enabled, the holds queue picks a local-group
item for eligible holds. Until now there was nothing to reserve that
priority at checkin time: if a non-local item was returned first, it
would happily fill the hold and the local-priority decision was lost.
This patch adds a time-bounded exclusivity window so that, for a
configurable number of days after a hold is placed, a hold whose queue
target is a local-group item will not be filled by a non-local item.
Changes:
- C4/HoldsQueue.pm: in MapItemsToHoldRequests, flag %item_map entries
that matched on local library (GiveLibrary/GiveLibraryAndGroup) or
local hold group (GiveLibraryGroup/GiveLibraryAndGroup) with
local_holdgroup_match => 1, and propagate that into both the
tmp_holdsqueue and hold_fill_targets INSERTs.
- C4/Reserves.pm: in the CheckReserves loop, when
LocalHoldsPriorityExclusivityPeriod is set, the hold is still within
that window, and the item being evaluated is not itself a local
match, skip the reserve if its hold_fill_targets row has
local_holdgroup_match = 1. Missing hold_fill_targets row means "no
information" -- fall through so holds are never trapped between
queue runs.
- circulation.pref: expose the new LocalHoldsPriorityExclusivityPeriod
preference next to the existing LocalHoldsPriority* entries.
The feature is a no-op unless both LocalHoldsPriority is set to
something other than "Don't give" AND
LocalHoldsPriorityExclusivityPeriod is > 0.
Test plan:
1) Apply the patches and run the atomicupdate.
2) Set up a library group flagged as a local hold group containing
two libraries (call them A and B). Add a third library C that is
NOT in the group.
3) Add one item to each library (itemA, itemB, itemC) on the same
biblio.
4) Administration -> System preferences -> Circulation:
- Set LocalHoldsPriority to "Give library group".
- Set LocalHoldsPriorityPatronControl to "home library".
- Set LocalHoldsPriorityItemControl to "home library".
- Set LocalHoldsPriorityExclusivityPeriod to 7.
5) As a patron whose home library is A, place a title-level hold with
pickup at A.
6) Run the holds queue (misc/cronjobs/holds/build_holds_queue.pl).
-> The queue should target itemA (or itemB) for this hold.
-> Confirm in the database:
SELECT reserve_id, itemnumber, local_holdgroup_match
FROM hold_fill_targets;
The targeted row must have local_holdgroup_match = 1.
-> Holds to pull / holds queue report should also show the flag
(tmp_holdsqueue.local_holdgroup_match = 1).
7) At library C, check in itemC (the non-local item).
-> The hold must NOT be captured -- itemC simply goes back on the
shelf.
8) At library A, check in itemA (the local item).
-> The hold IS captured for the patron.
9) Cancel and re-place the hold, run the queue again. Manually age
the hold past the window:
UPDATE reserves
SET reservedate = DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY)
WHERE reserve_id = <id>;
Now check in itemC again.
-> The hold IS captured (exclusivity window has expired).
10) Re-place the hold but do NOT run the queue. With no
hold_fill_targets row for the reserve, check in itemC.
-> The hold IS captured (we never trap holds between queue runs).
11) Flip LocalHoldsPriorityExclusivityPeriod back to 0 and repeat
step 7.
-> The hold IS captured; the feature is fully disabled.
12) Run:
prove t/db_dependent/Holds/LocalHoldsPriority.t \
t/db_dependent/HoldsQueue.t \
t/db_dependent/Reserves.t \
t/db_dependent/Koha/Hold.t
-> All tests pass.
Sponsored-by: Main Library Alliance <https://www.mainlib.org/>
Signed-off-by: Trevor Diamond <trevor.diamond@mainlib.org>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
caller(1) was introduced to skip past Koha/Script.pm when cronlogaction
is called from the INIT/END blocks, but it breaks any direct caller that
invokes cronlogaction from within a Try::Tiny try/catch closure or a
callback sub - in those contexts caller(1) resolves to Try/Tiny.pm (or
the calling module) rather than the script itself.
\$0 is always the path of the running script regardless of call depth,
INIT/END phase, or enclosing closure, making it the correct choice here.
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Since we are calling from within another module we need more caller information to
log the correct script
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>
This patch implements the library_limit concept for Report/Reports. Staff with the correct permission and superlibrarians can set libaries to which the report should be visible, if the system preference is enabled.
Test plan:
Confirm Initial Settings
1. Log in to Koha staff with username: koha and password: koha and set your library as Centerville if it's not already.
2. Visit the Saved reports page, and create and save three reports: Report 1, Report 2, and Report 3. The SQL query for each report doesn't matter, it should just be something valid.
3. Visit the Patrons page and create two staff users: StaffReportLimitUser and StaffNoLimitPermission. Set their home libraries to Centerville and Fairfield, respectively.
4. Under Allow access to the reports module, ensure both users have report management permissions for Create, Delete, Execute.
Verify Patch Settings
5. Apply the patch and restart services. If using Koha Testing Docker (KTD): from the Koha repo directory, apply the patch (e.g. `git bz apply 16631`, or check out the patched branch with `git fetch && git checkout <branch>`), then run `ktd --shell` to enter the KTD shell and run `restart_all`. If the patched branch is already checked out, just enter the KTD shell and run `restart_all`.
6. Click Patrons in the top navbar to go to the Patrons page.
7. Search for "StaffReportLimitUser" in the patron search field.
8. Click StaffReportLimitUser to edit the patron record for this user.
9. Click More -> Set permissions to access the permissions screens for the StaffReportLimitUser.
10. In the row for Allow access to the reports module (reports), click Show details to view relevant permissions.
11. Confirm Manage Report Limits (manage_report_limits) is an option under Allow access to the reports module (reports).
12. Click the checkbox for Manage Report Limits to give this user this permission, and click Save.
13. Go to the System Preferences page (Click the More dropdown from the top nav bar, click Administration, click System Preferences).
14. Verify Reports is an option in the left side navigation bar, and that when clicked, you see the LimitReportsByBranch option.
15. Enable LimitReportsByBranch as described in step 14.
16. Go back to Saved reports as in Step 2.
17. Click Edit in the row menu for Report 1.
18. At the bottom of the Report 1 edit page, you should see a Library limitation select box.
Verify the Library Limitation Settings are Persisted
19. Choose Centerville from the select box and click Update SQL.
20. Reload the page, or navigate away from the page and go back to the edit screen for Report 1.
21. Verify that Centerville is still in the Library limitation select box.
22. Add Fairfield in the Library limitation select box and click Update and run SQL.
23. Go back to editing Report 1.
24. Confirm the Library limitation select box has both Centerville and Fairfield selected.
25. Duplicate Report 1.
26. Confirm the Library limitation select box has both Centerville and Fairfield selected.
27. Click Save Report.
28. Confirm the Library limitation select box has both Centerville and Fairfield selected.
Verify Reports are Limited Based on Library Limitations
29. Edit Report 1 to add Centerville in the Library limitation select box as the only choice and click Update SQL.
30. Edit Report 2 to add Fairfield in the Library limitation select box as the only choice and click Update SQL.
31. Report 3 should have no library limitations, so it should be visible to all libraries.
32. Go to Saved reports as in Step 2.
33. Confirm you do not see Report 2, and only Report 1 and Report 3.
34. In the upper right corner, click the drop down that says koha Centerville and click Set library to change your library.
35. Set your library to Fairfield.
36. Go to Saved reports as in Step 2.
37. Confirm you do not see Report 1, and only Report 2 and Report 3.
Verify Disabling LimitReportsByBranch Preference Shows All Reports
38. Disable LimitReportsByBranch preference as in steps 13 to 15.
39. Go to Saved reports as in Step 2.
40. Confirm Report 1, Report 2, Report 3 are visible.
41. Click to edit Report 1 and verify there is no Library limitation select box.
Verify Library limitations are Persisted When EnableFilteringReports is Toggled On or Off
42. Enable LimitReportsByBranch preference as in steps 13 to 15.
43. Ensure your library is set to Fairfield as in step 35.
44. Go to Saved reports as in Step 2.
45. Confirm Report 1 is not visible, and Report 2 and Report 3 are visible.
46. Click to edit Report 2 and verify the Library limitation select box is at the bottom of the page and that it has Fairfield selected.
Verify the Show all reports option shows all reports for users with the Manage Report Limits permission and superlibrarians
47. Go to Saved reports as in Step 2 and in the Show reports dropdown, select Show all reports, and click Apply filter.
48. Confirm the page reloads with all reports visible: Report 1, Report 2, and Report 3 should be visible.
49. Select Show my branch reports from the dropdown and click Apply filter.
50. Confirm Report 1 is not visible, and Report 2 and Report 3 are visible (you should only see reports for your branch, Fairfield).
Verify Report filters return the correct results
51. Go to Saved reports as in Step 2.
52. Verify you're viewing only your branch's reports in the filter.
53. Under Filter in the left sidebar, type "2" in the Keyword field and click Apply filter.
54. Confirm only Report 2 is visible.
55. Under Filter in the left sidebar, type "1" in the Keyword field and click Apply filter.
56. Confirm there are no reports visible, and the message "No saved reports match your criteria" is displayed.
Verify Library Limits column is available to superlibrarians and users with the Manage Report Limits permission
57. Go to Saved reports as in Step 2.
58. Click the Columns dropdown and ensure that the Library limits column is available to select for display. Select it.
59. Confirm that the Library limits column is now visible in the list of reports and that it shows the correct library limitations for each report (e.g., Report 1 should show 1 library limitation and Centerville (CPL) on hover, Report 2 should show 1 library limitation and Fairfield (FFL) on hover, and Report 3 should show no limitation).
Verify the Show all reports and Limit reports options are available to users that are not superlibrarian but have the Manage Report Limits permission
60. For the StaffReportLimitUser, add the permission for Manage Report Limits as described in steps 9 to 12. This will allow this user to see the Show all reports and Limit reports options.
61. Log out of the koha admin account. In the upper right corner, click the drop down that says koha Fairfield and click Log out.
62. Log in as StaffReportLimitUser, the previously created staff user with report permissions.
63. Confirm the staff user's library is Centerville in the upper right corner of the screen.
64. Go to Saved reports as in Step 2.
65. Confirm the filter option for Show reports is visible.
66. Click to edit Report 1.
67. Verify there is a Library limitation select box and that it can be interacted with.
Verify the Show all reports and Limit reports options are not available to users that are not superlibrarian and do not have the Manage Report Limits permission
68. Log out of the StaffReportLimitUser account.
69. Log in as StaffNoLimitPermission, the previously created staff user without the Manage Report Limits permission.
70. Confirm the staff user's library is Fairfield in the upper right corner of the screen.
71. Go to Saved reports as in Step 2.
72. Click the Columns dropdown and ensure that the Library limits column is not in the list of available columns to select for display.
73. Confirm the filter option for Show reports is not available.
74. Click to edit Report 2.
75. Verify there is no Library limitation select box at the bottom of the page.
[NEW] Verify library limits are auto-assigned and preserved correctly for non-permissioned users
76. Still logged in as StaffNoLimitPermission (Fairfield) from the previous section. Go to Saved reports and click New report. Enter "Limit Auto-Assign Test" as the report name and SELECT 1 as the SQL. Confirm there is no Library limitations select box visible on the form. Click Save.
77. Go back to Saved reports. Click Edit for Report 2 (limited to Fairfield). Add `-- test` as a trailing SQL comment and click Update SQL. This simulates a non-permissioned user saving an existing report that already has limits.
78. Go back to Saved reports. Click Edit for Report 3 (no library limits). Add `-- test` as a trailing SQL comment and click Update SQL. This simulates a non-permissioned user saving an existing report that has no limits.
79. Log out and log back in as StaffReportLimitUser (Centerville, has manage_report_limits).
80. Go to Saved reports. Use Show all reports if needed. Find "Limit Auto-Assign Test" and click Edit. Confirm the Library limitations select box shows Fairfield as the only selected library — the report was automatically limited to the creator's home library on create.
81. Find Report 2 and click Edit. Confirm the Library limitations select box still shows only Fairfield — the existing limit was preserved when the non-permissioned user saved the report.
82. Find Report 3 and click Edit. Confirm the Library limitations select box is empty — no limits were introduced when the non-permissioned user saved a report that had no existing limits.
Verify limited reports are not visible to users without permissions
83. While still logged in as StaffReportLimitUser from step 79, copy the URL for the edit page of Report 1.
84. Log out of the StaffReportLimitUser account.
85. Log in as StaffNoLimitPermission, the user without the Manage Report Limits permission.
86. Paste the copied URL for the edit page of Report 1 into the browser and navigate to it.
87. Confirm that you see an error message that says "This report is restricted and cannot be accessed by the current user." and that the page does not load the report details.
Run Unit Tests
88. Using Koha Testing Docker, in the Koha home directory run the command: ktd --shell to start the ktd shell.
89. Run command: reset_all to drop and recreate the koha database.
90. Run command prove t/db_dependent/Koha/Reports.t to run the test.
91. This file contains the unit tests for the reports_branches table and the new subtest `library limits enforcement on save and update`, which covers auto-assignment of branch limits on create and preservation of limits on update for non-permissioned users.
92. All tests should pass.
93. Run command prove t/Koha/Auth/Permissions.t
94. Permissions unit tests in this file were updated.
95. All tests should pass.
96. Run command prove t/db_dependent/Reports/Guided.t
97. The Guided report tests were updated to account for the new library limitations and permissions.
98. All tests should pass.
Signoff
99. Sign off if the steps above check out.
Sponsored-by: Ramapo Catskill Library System
Co-authored-by: Casey Conlin <casey.conlin@gmail.com>
Signed-off-by: Emmi Takkinen <emmi.takkinen@koha-suomi.fi>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Implements a 4-state booking lifecycle: new → issued (on checkout) →
completed (on return), plus cancelled at any point.
Backend changes:
- AddIssue sets booking status to 'issued' (was incorrectly 'completed')
- AddReturn transitions 'issued' → 'completed' via booking_id on the issue
- filter_by_active updated to status-based: status IN ('new', 'issued');
previously it filtered by date range — active/inactive now reflect booking
lifecycle rather than the current date relative to start/end dates
- _is_final_status_transition reverted to only skip clash detection for
terminal statuses 'cancelled' and 'completed'; 'issued' is active and does
not bypass clash detection
- GET /api/v1/bookings no longer pre-filters to active bookings server-side;
callers must supply an explicit status filter if they need active-only
Display changes:
- Patron bookings tab: replaces the date-based expired/active toggle with
per-status filters (completed, cancelled) using the AdditionalFilters
framework; active bookings (new/issued) are shown by default
- Biblio bookings list: same status-based filters; removes the date-based
expired filter that was still present
- Status column is always visible on both views (was previously hidden on
the patron tab unless expired bookings were shown)
- Status badges: new=bg-success, issued=bg-info, completed/cancelled=bg-secondary
- Timeline isActive check corrected to ['new','issued']
- patron-detail-tabs.inc: rename expired_bookings_count → closed_bookings_count
to reflect status-based (not date-based) counting
- Filter button colour fixed to use $link-color so both views are consistent
Test plan:
1. Enable the EnableBookings system preference.
2. Create a biblio with at least one bookable item.
3. Place a booking for a patron via the biblio's Bookings tab.
- The booking should appear with status badge "New" (green).
4. Check out the booked item to that patron (Circulation → Check out).
- Reload the Bookings tab; the booking should now show "Issued" (blue).
5. Return the item (Circulation → Check in).
- Reload the Bookings tab; the booking should now show "Completed" (grey).
6. Place a second booking and cancel it from the Bookings tab Actions menu.
- The booking should show "Cancelled" (grey), with the reason if provided.
7. On the biblio Bookings tab:
- Completed and cancelled bookings are hidden by default.
- Clicking "Include completed" shows completed bookings.
- Clicking "Include cancelled" shows cancelled bookings.
- Both can be toggled independently.
- The Status column is always visible.
8. Navigate to the patron's detail page → Bookings tab.
- Active bookings (new/issued) are shown by default.
- Completed and cancelled can be toggled independently using the same
filter buttons.
- The Status column is always visible.
9. Confirm via the REST API that GET /api/v1/bookings (no filters) returns
bookings of all statuses, not just active ones.
Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Sponsored-by: Westminster City Council <https://www.westminster.gov.uk/>
Signed-off-by: Hannah Dunne-Howrie <hdunne-howrie@westminster.gov.uk>
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Always include the plural version, not the singular one (the plural
imports the singular)
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Laura_Escamilla <laura.escamilla@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This patch replaces all calls to C4::Stats::UpdateStats with the wrapped
function from the Koha namespace. As this is the last function in C4::Stats, the
module is also deleted.
Several tests depended on transitive includes to Koha::Statistics through
C4::Stats. I've identified uses of that module by searching, and added the
necessary use in each case. I've run `prove -r t xt` with this patch applied and
didn't hit any relevant errors.
Test plan:
1. Inspect the patch, and verify each changed call maintains the same parameters.
2. Apply patch.
3. Prove the following files:
- t/db_dependent/Accounts.t
- t/db_dependent/Circulation.t
- t/db_dependent/Koha/Account.t
- t/db_dependent/Koha/ILL/Request.t
- t/db_dependent/Koha/Item.t
- t/db_dependent/Koha/Pseudonymization.t
- t/db_dependent/Koha/Recalls.t
Sponsored-by: Open Fifth
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Laura_Escamilla <laura.escamilla@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
If a session has expired, the login form will get an empty CSRF token
and the login form will return 403
To recreate:
hit /cgi-bin/koha/sco/sco-main.pl
then hit /cgi-bin/koha/opac-main.pl
login
=> 403
Apply this patch, try again
=> Success
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Bug 40866 introduced JSON logging for HOLDS CREATE but stored null for
'confirmations' and 'forced' when no overrides were present. This patch
mirrors the earlier change to CIRCULATION ISSUE by defaulting them to
empty arrays, keeping the two log streams structurally identical and
simplifying reporting queries.
Changes:
C4/Reserves.pm:
- Default confirmations/forced to [] in the AddReserve log payload.
t/db_dependent/Reserves.t:
- Update the Bug 40866 subtest to assert the empty-array defaults on
normal holds, and fix a 'my $log_data' redeclaration.
tools/viewlog.pl:
- Extend the CSV export JSON-decode block to also cover HOLDS logs,
guarding the item lookup on itemnumber being present (HOLDS payloads
may lack one) and filling the CSV item columns from the decoded
payload. The legacy bare-itemnumber fallback is restricted to
CIRCULATION so a legacy HOLDS hold_id is not mistaken for an
itemnumber.
koha-tmpl/intranet-tmpl/prog/js/viewlog.js:
- Extend the info column override rendering (renderCircInfo) to HOLDS
entries so forced/confirmations arrays display translated, exactly as
they do for CIRCULATION. The log viewer moved to an ajax datatable in
bug 29016, so this replaces the earlier json_found change to
viewlog.pl/viewlog.tt.
misc/maintenance/migrate_action_logs_to_json.pl:
- Extended to handle legacy pre-Bug 40866 HOLDS CREATE logs where info
is a bare hold_id, recovering branchcode / biblionumber / itemnumber
from old_reserves / reserves.
- Uses the same keyset-pagination pattern as the circulation loop so
rows are not skipped under --commit.
Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Previously, CIRCULATION ISSUE logs would store just the itemnumber when
there were no confirmations or forced overrides, but would store a full
JSON structure when overrides were present. This inconsistency makes
reporting difficult as queries need to handle both formats.
This patch changes AddIssue to always store logs in JSON format,
matching the pattern already used in AddRenewal. The JSON structure
now consistently includes:
- issue: issue_id
- branchcode: branchcode
- itemnumber: itemnumber
- confirmations: array of confirmation codes (empty array if none)
- forced: array of forced override codes (empty array if none)
Benefits:
- Consistent data format simplifies reporting and analysis
- Matches existing RENEWAL logging pattern
- Display code (tools/viewlog.pl) already handles both formats,
so backward compatibility is maintained
Test plan:
1. prove t/db_dependent/Circulation.t
2. Perform a normal checkout - verify action log contains JSON
3. Perform a checkout with overrides - verify confirmations/forced
are populated in the JSON
4. Check Tools > Log viewer - both types should display correctly
Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
So we protect other calls
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
The previous two commits on this bug fixed two ordering assumptions in
the RETRY loop of _allocateWithTransportCostMatrix(): resetting $inf at
the start of each attempt, and splicing unallocated task indices out of
@requests in descending order (removing a lower index first would shift
the position of any higher index still waiting to be removed).
This is a third instance of the same underlying problem, a few lines
earlier in the loop that compacts the working cost matrix $m before
that splice runs. @unallocated is built by iterating over agents (item
rows), pushing the column index Munkres assigned to each row that
turned out infeasible - and Munkres' row-to-column assignment is a
permutation, not a monotonic function of the row index, so
@unallocated is no more reliably sorted here than it was for the
splice.
That compaction loop scans matrix columns in ascending order and
advances a single pointer into @unallocated, expecting to meet each
entry in that same ascending order. When @unallocated arrives out of
order, the pointer never catches up: columns that should have been
dropped are left in place, and the columns after them are never
shifted left to fill the gap. From that point on, $m no longer lines
up with @requests, which *is* correctly reduced by the splice - so on
the next retry pass a hold's transport cost gets evaluated using a
different hold's matrix row. Depending on the exact misalignment, this
can silently drop a fillable hold from the queue while a genuinely
available item sits unused, or fill a hold via the wrong-cost item.
It didn't show up in the "many holds" regression test added earlier on
this bug, because that test's transport cost matrix is uniform (every
disabled pair costs the same); this defect only surfaces once the
columns competing for a scarce item have distinct costs.
Sort @unallocated once, ascending, and use that copy for the
compaction pass; reverse it for the splice, which needs descending
order.
Test plan:
1. Run the new t/db_dependent/HoldsQueue/RetryAllocationOrdering.t -
it calls _allocateWithTransportCostMatrix() directly with a small
hand-built scarce transport cost matrix and a pinned srand() (the
specific item-to-branch permutation needed to trigger the bug
depends on the shuffle() inside the routine). Reverting just this
patch makes 3 of its 6 subtests fail: the L2 pickup hold is dropped
even though an available item could have filled it.
2. Run t/db_dependent/HoldsQueue/TransportCostOptimizations.t to
confirm no regression in the existing retry-loop coverage.
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
* The representation of the value "infinity" must be reset to -1 at the
start of each allocation attempt.
* Unallocated items must be spliced out from the candidate arrays in
reverse index order.
Test plan:
1. Run the unit tests in
t/db_dependent/HoldsQueue/TransportCostOptimizations.t
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Skips the empty results; only reports the 'real failures'.
Includes adding a grand total of hits to the form.
Some preliminary work to remove the hardcoded 20 results per target
in the code.
This is not yet a complete solution, but an improvement. The code in
C4/Breeding needs a larger overhaul. (And there might be an issue on
the Folio server side at LOC.)
Test plan:
Without this patch:
Use the LOC SRU server (since if frequently generates errcode 61).
Try the same search several times in a row (with Go). You probably will
come across the "LOC SRU record x: 0" messages. (Random behavior.)
Apply this patch.
Repeat the above. Notice that you will sometimes see the error 61 (and the
tip to refresh) but not the code 0 messages.
Run t/db../Breeding.t
Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
As with guided reports through the admin interface, this patch switches
EmailReport to use the same function for processing templates. This
gives patron emails sent via cronjobs access to the same variables as
other report locations.
Test plan:
1. Under Tools > Notices and Slips, create a new notice in the Reports
module. Choose a code and a name, and under Email assign a subject
and the following body:
Date: [% today %]
2. Under Reports, create a new report from SQL with a name and the
following query:
SELECT 1 AS borrowernumber, 'to@example.com' AS email
3. Run the following command from the base of Koha, replacing CODE with
the notice code created earlier, and ID with the report ID created
earlier:
misc/cronjobs/patron_emailer.pl -module report -from from@example.com -notice CODE -report ID
4. Verify that the report runs successfully, and the console output
includes "Date:" on a line with nothing following it.
5. Apply patch.
6. Run the following command from the base of Koha, replacing CODE with
the notice code created earlier, and ID with the report ID created
earlier:
misc/cronjobs/patron_emailer.pl -module report -from from@example.com -notice CODE -report ID
7. Verify that the report runs successfully, and the console output
includes "Date:" on a line with the current time following it.
8. Run `prove t/db_dependent/Reports/Guided.t`, and verify it passes.
Sponsored-by: Open Fifth
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
The FallbackToSMSIfNoEmail transport selection in ModSuggestion() was
checking $patron->email (the raw borrowers.email column) rather than
$patron->notice_email_address, which respects the EmailFieldPrimary and
EmailFieldPrecedence system preferences. Patrons with only emailpro or
B_email set were incorrectly receiving suggestion status-change notices
via SMS even when a valid email address was available.
Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
SIP2 accounts/institutions are stored in the database and the SIP server is supposed to reload its configuration when they change.
In practice, after adding or editing a SIP2 account, most SIP worker processes keep serving the old configuration (e.g. new accounts get "SIP access not authorized") until the SIP server is restarted ( I tested multiple times over 30 minutes ).
There seem to be two issues:
* L1 cache flushed too late. process_request() calls _config_up_to_date() before Koha::Caches->flush_L1_caches(). The SIP parent reads sip2_resource_last_modified into its own L1 cache at startup and never flushes it. Every worker inherits that stale L1, so a worker's first connection compares against the startup-era value instead of the current shared value.
* sip2_config_read_timestamp is stored in the shared (memcached) cache, but the configuration it tracks is per-process. When one worker reloads the config from the database, it bumps the shared timestamp, so every other worker then evaluates as "up to date" and never reloads its own stale in-memory config.
Test Plan:
1) Configure the SIP server with several worker processes (e.g. set
min_servers and max_servers to 5) and confirm an existing SIP2
account works
2) Add a new SIP2 account in the staff interface
3) Without restarting the SIP server, test the new account with misc/sip_cli_emulator.pl
4) Note the login fails
5) Apply this patch
6) Restart all the things!
7) Add another new SIP2 account
8) Without restarting the SIP server test this new account with sip_cli_emulator.pl
9) Note that the new account works!
10) prove t/db_dependent/SIP/SIPServer.t
Signed-off-by: Brendan Lawlor <blawlor@clamsnet.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
To test:
Test the cronjob purge_suggestions.pl
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>
ConnectDuplicate does not appear to be used anywhere.
No test plan to provide here.
git grep connectDuplicate should return nothing
Since there is no cud-, it would at least have been broken for a year
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>
MARC21 4xx/5xx $w is a control subfield with up to four positions;
only position 0 encodes the hierarchical relationship. The previous
regex required the whole subfield to equal g or h, so values such as
"g " or "gnna" would fall into the middle bucket and lose the
broader/narrower distinction.
This case is hypothetical and likely rare in practice (most catalogs
store a single character in $w), but the correctness improvement still
makes sense and aligns with how BuildSummary reads the same subfield
elsewhere via substr($w, 0, 1).
To test:
- prove -v t/db_dependent/AuthoritiesMarc.t
- Verify BuildSummary/_marc21_sort_hierarchy_alpha subtest passes,
including the new multi-position $w case.
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>