Bug 41254: Allow filtering by all authorised values for multi-valued statuses

Some item statuses can correspond to multiple authorised values: Not
for loan, Withdrawn, Lost and Damaged. Until now, only a single entry
could be selected, and filtering applied to all values at once.

This change introduces finer-grained filtering by exposing each
authorised value individually in the filter.

Test plan:
Assign multiple authorised values to items on a bibliographic record
for the four affected attributes: Not for loan, Withdrawn, Lost and
Damaged. Use the "Status" filter and verify that each value behaves as
expected.

Display note:
The current dropdown presentation is not ideal.
Suggestions for improvement are welcome, although avoiding optgroup
would be preferable to keep the scope small.

Signed-off-by: Owen Leonard <oleonard@myacpl.org>
Signed-off-by: Baptiste Wojtkowski <baptiste.wojtkowski@biblibre.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
Jonathan Druart 2025-11-25 15:45:13 +01:00 committed by Pedro Amorim
parent 391ef08e30
commit fd935fa7d7
No known key found for this signature in database
GPG Key ID: 06B75BA7F3894D55
3 changed files with 89 additions and 1 deletions

View File

@ -678,17 +678,32 @@ sub search {
if ( $status eq 'lost' ) {
$self = $self->search( { itemlost => { '!=' => 0 } } );
}
if ( $status =~ '^lost-(.*)' ) {
$self = $self->search( { itemlost => $1 } );
}
if ( $status eq 'withdrawn' ) {
$self = $self->search( { withdrawn => { '!=' => 0 } } );
}
if ( $status =~ '^withdrawn-(.*)' ) {
$self = $self->search( { withdrawn => $1 } );
}
if ( $status eq 'damaged' ) {
$self = $self->search( { damaged => { '!=' => 0 } } );
}
if ( $status =~ '^damaged-(.*)' ) {
$self = $self->search( { damaged => $1 } );
}
if ( $status eq 'not_for_loan' ) {
my @item_types_notforloan =
Koha::ItemTypes->search( { notforloan => { '!=' => 0 } } )->get_column('itemtype');
$self = $self->search( [ { notforloan => { '!=' => 0 } }, { 'me.itype' => \@item_types_notforloan } ] );
}
if ( $status =~ '^not_for_loan-(.*)' ) {
my $av_not_for_loan = $1;
my @item_types_notforloan =
Koha::ItemTypes->search( { notforloan => $av_not_for_loan } )->get_column('itemtype');
$self = $self->search( [ { notforloan => $av_not_for_loan }, { 'me.itype' => \@item_types_notforloan } ] );
}
if ( $status eq 'on_hold' ) {
$self = $self->filter_by_has_holds;
}

View File

@ -274,9 +274,13 @@
local_use: _("On-site checkout"),
in_transit: _("In transit"),
lost: _("Lost"),
...Object.fromEntries([...av_lost].map(([k, v]) => [`lost-${k}`, `* ${v}`])),
withdrawn: _("Withdrawn"),
...Object.fromEntries([...av_withdrawn].map(([k, v]) => [`withdrawn-${k}`, `* ${v}`])),
damaged: _("Damaged"),
...Object.fromEntries([...av_damaged].map(([k, v]) => [`damaged-${k}`, `* ${v}`])),
not_for_loan: _("Not for loan"),
...Object.fromEntries([...av_not_for_loan].map(([k, v]) => [`not_for_loan-${k}`, `* ${v}`])),
on_hold: _("On hold"),
recalled: _("Recalled"),
available: _("Available"),

View File

@ -75,7 +75,7 @@ is( $retrieved_item_1->barcode, $new_item_1->barcode, 'Find a item by id should
subtest 'search' => sub {
plan tests => 9;
plan tests => 15;
$schema->storage->txn_begin;
my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
@ -160,6 +160,75 @@ subtest 'search' => sub {
is( $withdrawn_items->count, 1, "Filtered to 1 withdrawn item" );
is( $notforloan_items->count, 1, "Filtered to 1 notforloan item" );
$item_6->notforloan(-1)->store;
is_deeply(
[
Koha::Items->search(
{
_status => 'not_for_loan',
biblionumber => $biblio->biblionumber,
}
)->get_column('itemnumber')
],
[ $item_6->itemnumber ]
);
is_deeply(
[
Koha::Items->search(
{
_status => 'not_for_loan-1',
biblionumber => $biblio->biblionumber,
}
)->get_column('itemnumber')
],
[]
);
is_deeply(
[
Koha::Items->search(
{
_status => 'not_for_loan--1',
biblionumber => $biblio->biblionumber,
}
)->get_column('itemnumber')
],
[ $item_6->itemnumber ]
);
$item_6->notforloan(2)->store;
is_deeply(
[
Koha::Items->search(
{
_status => 'not_for_loan',
biblionumber => $biblio->biblionumber,
}
)->get_column('itemnumber')
],
[ $item_6->itemnumber ]
);
is_deeply(
[
Koha::Items->search(
{
_status => 'not_for_loan-1',
biblionumber => $biblio->biblionumber,
}
)->get_column('itemnumber')
],
[]
);
is_deeply(
[
Koha::Items->search(
{
_status => 'not_for_loan-2',
biblionumber => $biblio->biblionumber,
}
)->get_column('itemnumber')
],
[ $item_6->itemnumber ]
);
C4::Circulation::AddIssue( $patron, $item_1->barcode );
my $checked_out_items = Koha::Items->search(