From b63c7f7af99c597d0d3450eef41d489d3df78995 Mon Sep 17 00:00:00 2001 From: Paul Derscheid Date: Tue, 24 Feb 2026 15:07:30 +0100 Subject: [PATCH] Bug 41917: Add parent itemtype fallback to Item::effective_bookable - Check parent itemtype bookable flag when child itemtype is not bookable - Item-level and child-level bookable values still take priority To test: - Run t/db_dependent/Koha/Item.t - Verify effective_bookable falls back to parent itemtype when child is not bookable - Verify child-level and item-level bookable take priority - Verify orphan itemtypes without a parent return 0 --- Koha/Item.pm | 18 +++++++++------- t/db_dependent/Koha/Item.t | 43 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/Koha/Item.pm b/Koha/Item.pm index ed175d3ced5..530f1d07d25 100644 --- a/Koha/Item.pm +++ b/Koha/Item.pm @@ -179,7 +179,7 @@ sub store { # If the field has changed otherwise, we much update # the 'on' field - elsif ( exists $updated_columns{$field} + elsif (exists $updated_columns{$field} && $updated_columns{$field} && !$pre_mod_item->$field ) { @@ -381,7 +381,7 @@ sub safe_to_delete { { itemnumber => undef, } - )->count; + )->count; if ($error) { return Koha::Result::Boolean->new(0)->add_message( { message => $error } ); @@ -1563,8 +1563,8 @@ sub columns_to_str { $subfield ? $subfield->{authorised_value} ? C4::Biblio::GetAuthorisedValueDesc( - $itemtagfield, - $subfield->{tagsubfield}, $value, '', $tagslib + $itemtagfield, + $subfield->{tagsubfield}, $value, '', $tagslib ) : $value : $value; @@ -1788,8 +1788,7 @@ sub _set_found_trigger { if ($lost_fee_payment) { my $today = dt_from_string(); my $payment_age_in_days = - dt_from_string( $lost_fee_payment->created_on ) - ->delta_days($today) + dt_from_string( $lost_fee_payment->created_on )->delta_days($today) ->in_units('days'); if ( $payment_age_in_days > $no_refund_if_lost_fee_paid_age ) { $self->add_message( @@ -2175,7 +2174,12 @@ Returns the effective bookability of the current item, be that item or itemtype sub effective_bookable { my ($self) = @_; - return $self->bookable // $self->itemtype->bookable; + my $bookable = $self->bookable // $self->itemtype->bookable; + if ( !$bookable && $self->itemtype && $self->itemtype->parent ) { + $bookable = $self->itemtype->parent->bookable; + } + + return $bookable; } =head3 orders diff --git a/t/db_dependent/Koha/Item.t b/t/db_dependent/Koha/Item.t index e0405a6a265..506f4303313 100755 --- a/t/db_dependent/Koha/Item.t +++ b/t/db_dependent/Koha/Item.t @@ -2886,7 +2886,7 @@ subtest 'store() tests' => sub { { borrowernumber => $patron->id, date => '1970-01-01 14:00:01', - amountoutstanding => 0, + amountoutstanding => 0, amount => -5, interface => 'commandline', credit_type_code => 'PAYMENT' @@ -3869,7 +3869,7 @@ subtest 'effective_not_for_loan_status() tests' => sub { subtest 'effective_bookable() tests' => sub { - plan tests => 5; + plan tests => 9; $schema->storage->txn_begin; @@ -3920,6 +3920,45 @@ subtest 'effective_bookable() tests' => sub { '->effective_bookable returns item specific bookable value when item bookable is defined' ); + note("Parent itemtype fallback tests"); + + my $parent_itype = $builder->build_object( { class => 'Koha::ItemTypes', value => { bookable => 1 } } ); + my $child_itype = $builder->build_object( + { class => 'Koha::ItemTypes', value => { bookable => 0, parent_type => $parent_itype->itemtype } } ); + + my $child_item = $builder->build_sample_item( + { biblionumber => $biblio->biblionumber, itype => $child_itype->itemtype, bookable => undef } ); + + is( + $child_item->effective_bookable, $parent_itype->bookable, + '->effective_bookable falls back to parent itemtype when child is not bookable' + ); + + $child_itype->bookable(1)->store(); + $child_itype->discard_changes; + is( + $child_item->effective_bookable, 1, + '->effective_bookable uses child itemtype bookable when set' + ); + + $child_itype->bookable(0)->store(); + $child_itype->discard_changes; + $child_item->bookable(1)->store(); + $child_item->discard_changes; + is( + $child_item->effective_bookable, 1, + '->effective_bookable uses item-level bookable over parent fallback' + ); + + my $orphan_itype = + $builder->build_object( { class => 'Koha::ItemTypes', value => { bookable => 0, parent_type => undef } } ); + my $orphan_item = $builder->build_sample_item( + { biblionumber => $biblio->biblionumber, itype => $orphan_itype->itemtype, bookable => undef } ); + is( + $orphan_item->effective_bookable, 0, + '->effective_bookable returns 0 for orphan itemtype with no parent' + ); + $schema->storage->txn_rollback; }; -- 2.39.5