From 28047e46e439f27af1bbe7a349263c63ebb71250 Mon Sep 17 00:00:00 2001 From: Jake Deery Date: Mon, 1 Dec 2025 13:06:40 +0000 Subject: [PATCH] Bug 14962: On Display code for existing Koha modules This patch adds code for the On Display module, found in the existing Koha modules. Please see the test files commit for instructions on how to test this bundle of patches. Sponsored-by: ByWater Solutions Signed-of-by: Martin Renvoize --- C4/Circulation.pm | 88 +++++++++++- C4/Search.pm | 187 ++++++++++++++++++++++--- C4/XSLT.pm | 17 ++- Koha/Item.pm | 343 +++++++++++++++++++++++++++++++++++++++++++++- Koha/Patron.pm | 24 ++++ 5 files changed, 624 insertions(+), 35 deletions(-) diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 1223afceca4..6a634cf1bfb 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -118,6 +118,7 @@ use Koha::Config::SysPrefs; use Koha::Charges::Fees; use Koha::Config::SysPref; use Koha::Checkouts::ReturnClaims; +use Koha::DisplayItems; use Koha::SearchEngine::Indexer; use Koha::Exceptions::Checkout; use Koha::Plugins; @@ -2784,6 +2785,27 @@ sub AddReturn { my $indexer = Koha::SearchEngine::Indexer->new( { index => $Koha::SearchEngine::BIBLIOS_INDEX } ); $indexer->index_records( $item->biblionumber, "specialUpdate", "biblioserver" ); + if ( my $display_item = $item->active_display_item ) { + my $display = $display_item->display; + my $return_over = $display->display_return_over; + my $should_remove = 0; + + if ( $return_over eq 'yes - any library' ) { + $should_remove = 1; + } elsif ( $return_over eq 'yes - except at home library' ) { + my $homebranch = $item->homebranch; + $should_remove = 1 if $branch ne $homebranch; + } + + if ($should_remove) { + $display_item->delete; + $messages->{RemovedFromDisplay} = { + display_id => $display->display_id, + display_name => $display->display_name, + }; + } + } + if ( $doreturn and $issue ) { my $checkin = Koha::Old::Checkouts->find( $issue->id ); @@ -4838,8 +4860,10 @@ sub GetAgeRestriction { sub GetPendingOnSiteCheckouts { my $dbh = C4::Context->dbh; - return $dbh->selectall_arrayref( - q| + + my $use_display_module = C4::Context->preference('UseDisplayModule'); + + my $sql = q| SELECT items.barcode, items.biblionumber, @@ -4855,14 +4879,64 @@ sub GetPendingOnSiteCheckouts { borrowers.firstname, borrowers.surname, borrowers.cardnumber, - borrowers.borrowernumber + borrowers.borrowernumber|; + + if ($use_display_module) { + $sql .= q|, + COALESCE(active_display.display_location, items.location) AS effective_location, + active_display.display_name, + active_display.display_location AS raw_display_location|; + } else { + $sql .= q|, + items.location AS effective_location, + NULL AS display_name, + NULL AS raw_display_location|; + } + + $sql .= q| FROM items LEFT JOIN issues ON items.itemnumber = issues.itemnumber LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber - LEFT JOIN borrowers ON issues.borrowernumber = borrowers.borrowernumber - WHERE issues.onsite_checkout = 1 - |, { Slice => {} } - ); + LEFT JOIN borrowers ON issues.borrowernumber = borrowers.borrowernumber|; + + if ($use_display_module) { + $sql .= q| + LEFT JOIN ( + SELECT + di.itemnumber, + d.display_location, + d.display_name, + ROW_NUMBER() OVER (PARTITION BY di.itemnumber ORDER BY di.date_added DESC) as rn + FROM display_items di + JOIN displays d ON di.display_id = d.display_id + WHERE d.enabled = 1 + AND (d.start_date IS NULL OR d.start_date <= CURDATE()) + AND (d.end_date IS NULL OR d.end_date >= CURDATE()) + AND (di.date_remove IS NULL OR di.date_remove >= CURDATE()) + ) active_display ON items.itemnumber = active_display.itemnumber AND active_display.rn = 1|; + } + + $sql .= q| + WHERE issues.onsite_checkout = 1|; + + my $results = $dbh->selectall_arrayref( $sql, { Slice => {} } ); + + # Add effective location description for each item + foreach my $checkout (@$results) { + if ( $checkout->{display_name} && !$checkout->{raw_display_location} ) { + + # Item is on display with no specific display location + $checkout->{effective_location_description} = "DISPLAY: " . $checkout->{display_name}; + } else { + + # Use AuthorisedValues to get effective location description + my $av = Koha::AuthorisedValues->get_description_by_koha_field( + { kohafield => 'items.location', authorised_value => $checkout->{effective_location} } ); + $checkout->{effective_location_description} = $av->{lib} || ''; + } + } + + return $results; } =head2 GetTopIssues diff --git a/C4/Search.pm b/C4/Search.pm index de8162cb721..ffe49a7a6b6 100644 --- a/C4/Search.pm +++ b/C4/Search.pm @@ -46,6 +46,8 @@ use C4::XSLT qw( XSLTParse4Display ); use C4::Reserves qw( GetReserveStatus ); use C4::Charset qw( SetUTF8Flag ); use Koha::AuthorisedValues; +use Koha::Displays; +use Koha::Items; use Koha::ItemTypes; use Koha::Libraries; use Koha::Logger; @@ -1683,11 +1685,147 @@ sub searchResults { # FIXME - We build an authorised values hash here, using the default framework # though it is possible to have different authvals for different fws. + ## get metadatas + my $collections = { + map { $_->{authorised_value} => $_->{lib} } Koha::AuthorisedValues->get_descriptions_by_koha_field( + { frameworkcode => '', kohafield => 'items.ccode' } + ) + }; my $shelflocations = { map { $_->{authorised_value} => $_->{lib} } Koha::AuthorisedValues->get_descriptions_by_koha_field( { frameworkcode => '', kohafield => 'items.location' } ) }; + my $itemtypes = Koha::ItemTypes->search_with_localization; + my %itemtypes = map { $_->{itemtype} => $_ } @{ $itemtypes->unblessed }; + + # Cache for display lookups to avoid repeated queries + my $display_cache = {}; + + # Helper function to get effective location for search results + my $get_effective_collection_code = sub { + my ($item) = @_; + return $collections->{ $item->{ccode} } unless C4::Context->preference('UseDisplayModule'); + + my $item_obj = Koha::Items->find( $item->{itemnumber} ); + return $collections->{ $item->{ccode} } unless $item_obj; + + my $effective_collection_code = $item_obj->effective_collection_code; + + # If it starts with "DISPLAY:", validate against displays table + if ( $effective_collection_code =~ /^DISPLAY:\s*(.+)$/ ) { + my $display_name = $1; + + # Use cache to avoid repeated database queries + if ( !exists $display_cache->{$display_name} ) { + my $display = Koha::Displays->search( { display_name => $display_name } )->next; + $display_cache->{$display_name} = $display ? $display->display_name : undef; + } + return $display_cache->{$display_name} + ? "DISPLAY: " . $display_cache->{$display_name} + : $effective_collection_code; + } else { + return $collections->{$effective_collection_code}; + } + }; + my $get_effective_itemtype = sub { + my ($item) = @_; + return $itemtypes{ $item->{itype} }{translated_description} unless C4::Context->preference('UseDisplayModule'); + + my $item_obj = Koha::Items->find( $item->{itemnumber} ); + return $itemtypes{ $item->{itype} }{translated_description} unless $item_obj; + + my $effective_itemtype = $item_obj->effective_itemtype; + + # If it starts with "DISPLAY:", validate against displays table + if ( $effective_itemtype =~ /^DISPLAY:\s*(.+)$/ ) { + my $display_name = $1; + + # Use cache to avoid repeated database queries + if ( !exists $display_cache->{$display_name} ) { + my $display = Koha::Displays->search( { display_name => $display_name } )->next; + $display_cache->{$display_name} = $display ? $display->display_name : undef; + } + return $display_cache->{$display_name} + ? "DISPLAY: " . $display_cache->{$display_name} + : $effective_itemtype; + } else { + return $itemtypes{$effective_itemtype}{translated_description}; + } + }; + my $get_effective_location = sub { + my ($item) = @_; + return $shelflocations->{ $item->{location} } unless C4::Context->preference('UseDisplayModule'); + + my $item_obj = Koha::Items->find( $item->{itemnumber} ); + return $shelflocations->{ $item->{location} } unless $item_obj; + + my $effective_loc = $shelflocations->{ $item_obj->effective_location }; + + # If it starts with "DISPLAY:", validate against displays table + if ( $effective_loc =~ /^DISPLAY:\s*(.+)$/ ) { + my $display_name = $1; + + # Use cache to avoid repeated database queries + if ( !exists $display_cache->{$display_name} ) { + my $display = Koha::Displays->search( { display_name => $display_name } )->next; + $display_cache->{$display_name} = $display ? $display->display_name : undef; + } + return $display_cache->{$display_name} ? "DISPLAY: " . $display_cache->{$display_name} : $effective_loc; + } else { + return $effective_loc; + } + }; + my $get_effective_homebranch = sub { + my ($item) = @_; + return $item->{homebranch} unless C4::Context->preference('UseDisplayModule'); + + my $item_obj = Koha::Items->find( $item->{itemnumber} ); + return $item->{homebranch} unless $item_obj; + + my $effective_homebranch = $item_obj->effective_homebranch->branchcode; + + # If it starts with "DISPLAY:", validate against displays table + if ( $effective_homebranch =~ /^DISPLAY:\s*(.+)$/ ) { + my $display_name = $1; + + # Use cache to avoid repeated database queries + if ( !exists $display_cache->{$display_name} ) { + my $display = Koha::Displays->search( { display_name => $display_name } )->next; + $display_cache->{$display_name} = $display ? $display->display_name : undef; + } + return $display_cache->{$display_name} + ? "DISPLAY: " . $display_cache->{$display_name} + : $effective_homebranch; + } else { + return $effective_homebranch; + } + }; + my $get_effective_holdingbranch = sub { + my ($item) = @_; + return $item->{holdingbranch} unless C4::Context->preference('UseDisplayModule'); + + my $item_obj = Koha::Items->find( $item->{itemnumber} ); + return $item->{holdingbranch} unless $item_obj; + + my $effective_holdingbranch = $item_obj->effective_holdingbranch->branchcode; + + # If it starts with "DISPLAY:", validate against displays table + if ( $effective_holdingbranch =~ /^DISPLAY:\s*(.+)$/ ) { + my $display_name = $1; + + # Use cache to avoid repeated database queries + if ( !exists $display_cache->{$display_name} ) { + my $display = Koha::Displays->search( { display_name => $display_name } )->next; + $display_cache->{$display_name} = $display ? $display->display_name : undef; + } + return $display_cache->{$display_name} + ? "DISPLAY: " . $display_cache->{$display_name} + : $effective_holdingbranch; + } else { + return $effective_holdingbranch; + } + }; # get notforloan authorised value list (see $shelflocations FIXME) my $av = Koha::MarcSubfieldStructures->search( @@ -1698,10 +1836,6 @@ sub searchResults { ); my $notforloan_authorised_value = $av->count ? $av->next->authorised_value : undef; - #Get itemtype hash - my $itemtypes = Koha::ItemTypes->search_with_localization; - my %itemtypes = map { $_->{itemtype} => $_ } @{ $itemtypes->unblessed }; - #search item field code my ( $itemtag, undef ) = &GetMarcFromKohaField("items.itemnumber"); @@ -1866,7 +2000,12 @@ sub searchResults { next; } - $item->{description} = $itemtypes{ $item->{itype} }{translated_description} if $item->{itype}; + $item->{collectioncode} = $get_effective_collection_code->($item) if $item->{ccode}; + $item->{description} = $get_effective_itemtype->($item) if $item->{itype}; + $item->{location} = $get_effective_location->($item) if $item->{location}; + + $item->{homebranch} = $get_effective_homebranch->($item) if $item->{homebranch}; + $item->{holdingbranch} = $get_effective_holdingbranch->($item) if $item->{holdingbranch}; # OPAC hidden items if ($is_opac) { @@ -1893,6 +2032,23 @@ sub searchResults { $item->{'branchcode'} = $item->{$otherbranch}; } + if ( C4::Context->preference('UseDisplayModule') ) { + my @displays; + my @display_items = Koha::DisplayItems->search( + { itemnumber => $item->{itemnumber} }, + { + order_by => { '-desc' => 'date_added' }, + } + )->as_list; + + foreach my $display_item (@display_items) { + push @displays, $display_item->display; + } + + $item->{display_items} = \@display_items if @display_items; + $item->{displays} = \@displays if @displays; + } + my $prefix = ( $item->{$hbranch} ? $item->{$hbranch} . '--' : q{} ) . ( $item->{location} ? $item->{location} : q{} ) @@ -1914,15 +2070,15 @@ sub searchResults { $onloan_items->{$key}->{count}++ if $item->{$hbranch}; $onloan_items->{$key}->{branchname} = $item->{branchname}; $onloan_items->{$key}->{branchcode} = $item->{branchcode}; - $onloan_items->{$key}->{location} = $shelflocations->{ $item->{location} } if $item->{location}; + $onloan_items->{$key}->{location} = $item->{location}; $onloan_items->{$key}->{itemcallnumber} = $item->{itemcallnumber}; $onloan_items->{$key}->{description} = $item->{description}; $onloan_items->{$key}->{imageurl} = getitemtypeimagelocation( $search_context->{'interface'}, $itemtypes{ $item->{itype} }->{imageurl} ); - $onloan_items->{$key}->{collectioncode} = - GetAuthorisedValueDesc( '', '', $item->{ccode}, '', '', 'CCODE' ); + $onloan_items->{$key}->{collectioncode} = $item->{collectioncode}; + $onloan_items->{$key}->{displays} = $item->{displays}; # if something's checked out and lost, mark it as 'long overdue' if ( $item->{itemlost} ) { @@ -2032,14 +2188,14 @@ sub searchResults { GetAuthorisedValueDesc( '', '', $item->{notforloan}, '', '', $notforloan_authorised_value ) if $notforloan_authorised_value and $item->{notforloan}; $other_items->{$key}->{count}++ if $item->{$hbranch}; - $other_items->{$key}->{location} = $shelflocations->{ $item->{location} } if $item->{location}; + $other_items->{$key}->{location} = $item->{location}; $other_items->{$key}->{description} = $item->{description}; $other_items->{$key}->{imageurl} = getitemtypeimagelocation( $search_context->{'interface'}, $itemtypes{ $item->{itype} // q{} }->{imageurl} ); - $other_items->{$key}->{collectioncode} = - GetAuthorisedValueDesc( '', '', $item->{ccode}, '', '', 'CCODE' ); + $other_items->{$key}->{collectioncode} = $item->{collectioncode}; + $other_items->{$key}->{displays} = $item->{displays}; } # item is available @@ -2053,14 +2209,13 @@ sub searchResults { } $available_items->{$prefix}->{branchavailablecount} = $branch_available_count; $available_items->{$prefix}->{branchcode} = $item->{branchcode}; - $available_items->{$prefix}->{location} = $shelflocations->{ $item->{location} } - if $item->{location}; - $available_items->{$prefix}->{imageurl} = getitemtypeimagelocation( + $available_items->{$prefix}->{location} = $item->{location}; + $available_items->{$prefix}->{imageurl} = getitemtypeimagelocation( $search_context->{'interface'}, $itemtypes{ $item->{itype} // q{} }->{imageurl} ); - $available_items->{$prefix}->{collectioncode} = - GetAuthorisedValueDesc( '', '', $item->{ccode}, '', '', 'CCODE' ); + $available_items->{$prefix}->{collectioncode} = $item->{collectioncode}; + $available_items->{$prefix}->{displays} = $item->{displays}; } } } # notforloan, item level and biblioitem level diff --git a/C4/XSLT.pm b/C4/XSLT.pm index be3ac7d41f5..14ae2d01ee3 100644 --- a/C4/XSLT.pm +++ b/C4/XSLT.pm @@ -414,16 +414,19 @@ sub buildKohaItemsNamespace { } else { $status = "available"; } - my $homebranch = C4::Koha::xml_escape( $branches{ $item->homebranch } ); - my $holdingbranch = C4::Koha::xml_escape( $branches{ $item->holdingbranch } ); + my $homebranch = C4::Koha::xml_escape( $branches{ $item->effective_homebranch } ); + my $holdingbranch = C4::Koha::xml_escape( $branches{ $item->effective_holdingbranch } ); my $resultbranch = C4::Context->preference('OPACResultsLibrary') eq 'homebranch' ? $homebranch : $holdingbranch; my $location = C4::Koha::xml_escape( - $item->location && exists $shelflocations->{ $item->location } - ? $shelflocations->{ $item->location } - : $item->location + $item->effective_location && exists $shelflocations->{ $item->effective_location } + ? $shelflocations->{ $item->effective_location } + : $item->effective_location + ); + my $ccode = C4::Koha::xml_escape( + $item->effective_collection_code && exists $ccodes->{ $item->effective_collection_code } + ? $ccodes->{ $item->effective_collection_code } + : $item->effective_collection_code ); - my $ccode = C4::Koha::xml_escape( $item->ccode - && exists $ccodes->{ $item->ccode } ? $ccodes->{ $item->ccode } : $item->ccode ); my $itemcallnumber = C4::Koha::xml_escape( $item->itemcallnumber ); my $stocknumber = C4::Koha::xml_escape( $item->stocknumber ); $xml .= diff --git a/Koha/Item.pm b/Koha/Item.pm index ed175d3ced5..a5620dc2971 100644 --- a/Koha/Item.pm +++ b/Koha/Item.pm @@ -31,6 +31,7 @@ use C4::Reserves; use C4::ClassSource qw( GetClassSort ); use C4::Log qw( logaction ); +use Koha::AuthorisedValues; use Koha::BackgroundJob::BatchUpdateBiblioHoldsQueue; use Koha::Biblio::ItemGroups; use Koha::Checkouts; @@ -58,6 +59,7 @@ use Koha::StockRotationItem; use Koha::StockRotationRotas; use Koha::TrackedLinks; use Koha::Policy::Holds; +use Koha::DisplayItems; use base qw(Koha::Object); @@ -417,6 +419,10 @@ Returns the itemtype for the item based on whether item level itemtypes are set sub effective_itemtype { my ($self) = @_; + if ( my $display = $self->active_display ) { + return $display->get_column('display_itype') || $self->_result()->effective_itemtype(); + } + return $self->_result()->effective_itemtype(); } @@ -783,6 +789,44 @@ sub check_booking { return $bookings_count ? 0 : 1; } +=head3 active_display_item + + my $display_item = $item->active_display_item; + +Returns the Koha::DisplayItem object if this item is part of an active display, undef otherwise. +An active display is one where enabled = 1 and the current date falls within the start_date and end_date range (if set). + +=cut + +sub active_display_item { + my ($self) = @_; + + return unless C4::Context->preference('UseDisplayModule'); + + my $dtf = Koha::Database->new->schema->storage->datetime_parser; + my $today = dt_from_string(); + + my $display_item = Koha::DisplayItems->search( + { + itemnumber => $self->itemnumber, + 'display.enabled' => 1, + -and => [ + -or => [ + { 'display.start_date' => undef }, + { 'display.start_date' => { '<=' => $dtf->format_date($today) } } + ], + -or => [ + { 'display.end_date' => undef }, + { 'display.end_date' => { '>=' => $dtf->format_date($today) } } + ] + ] + }, + { join => 'display' } + )->next; + + return $display_item; +} + =head3 request_transfer my $transfer = $item->request_transfer( @@ -2058,6 +2102,10 @@ sub to_api { $overrides->{effective_item_type_id} = $self->effective_itemtype; $overrides->{effective_not_for_loan_status} = $self->effective_not_for_loan_status; $overrides->{effective_bookable} = $self->effective_bookable; + $overrides->{effective_location} = $self->effective_location; + $overrides->{effective_collection_code} = $self->effective_collection_code; + $overrides->{effective_home_library_id} = $self->effective_homebranch; + $overrides->{effective_holding_library_id} = $self->effective_holdingbranch; return { %$response, %$overrides }; } @@ -2178,6 +2226,191 @@ sub effective_bookable { return $self->bookable // $self->itemtype->bookable; } +=head3 effective_location + + my $location = $item->effective_location; + +Returns the effective location of the item. If the item is on an active display +with a display_location, returns the display_location. Otherwise, returns +the item's location. + +=cut + +sub effective_location { + my ($self) = @_; + + if ( my $display = $self->active_display ) { + return $display->display_location + if ( $display->display_location ); + + return "DISPLAY: " . $display->display_name; + } + + return $self->location; +} + +=head3 effective_collection_code + + my $ccode = $item->effective_collection_code; + +Returns the effective collection code for the item. If the item is part of an active display +with a display_code set, returns that code. Otherwise returns the item's regular ccode. + +=cut + +sub effective_collection_code { + my ($self) = @_; + + if ( my $display = $self->active_display ) { + return $display->display_code; + } + + return $self->ccode; +} + +=head3 effective_home_library + +=cut + +sub effective_home_library { + my ($self) = @_; + + return Koha::Libraries->find( $self->effective_homebranch ); +} + +=head3 effective_home_branch + +=cut + +sub effective_home_branch { + return shift->effective_home_library(@_); +} + +=head3 effective_homebranch + + my $homebranch = $item->effective_homebranch; + +Returns the effective home branch for the item. If the item is part of an active display +with a display_branch set, returns that branch. Otherwise returns the item's regular homebranch. + +=cut + +sub effective_homebranch { + my ($self) = @_; + + if ( my $display = $self->active_display ) { + return $display->get_column('display_branch'); + } + + return $self->get_column('homebranch'); +} + +=head3 effective_holding_library + +=cut + +sub effective_holding_library { + my ($self) = @_; + + return Koha::Libraries->find( $self->effective_holdingbranch ); +} + +=head3 effective_holding_branch + +=cut + +sub effective_holding_branch { + return shift->effective_holding_library(@_); +} + +=head3 effective_holdingbranch + + my $holdingbranch = $item->effective_holdingbranch; + +Returns the effective holding branch for the item. If the item is part of an active display +with a display_holding_branch set, returns that branch. Otherwise returns the item's regular holdingbranch. + +=cut + +sub effective_holdingbranch { + my ($self) = @_; + + if ( my $display = $self->active_display ) { + return $display->get_column('display_holding_branch'); + } + + return $self->get_column('holdingbranch'); +} + +=head3 active_display + + my $display = $item->active_display; + +Returns the active display for this item, if any. Returns undef if the item is not +currently in an active display. + +An active display is one that: +- Is enabled +- Has a start_date that is today or in the past (or no start_date) +- Has an end_date that is today or in the future (or no end_date) +- Has a date_remove on the display_item that is today or in the future (or no date_remove) + +=cut + +sub active_display { + my ($self) = @_; + + return unless C4::Context->preference('UseDisplayModule'); + + my $display_item_rs = $self->_result->display_items( + { + 'display.enabled' => 1, + '-or' => [ + 'display.start_date' => { '<=', \'CURDATE()' }, + 'display.start_date' => undef + ], + '-and' => [ + '-or' => [ + 'display.end_date' => { '>=', \'CURDATE()' }, + 'display.end_date' => undef + ], + '-or' => [ + 'date_remove' => { '>=', \'CURDATE()' }, + 'date_remove' => undef + ] + ] + }, + { + join => 'display', + order_by => { -desc => 'date_added' } + } + ); + + if ( my $display_item = $display_item_rs->first ) { + return $display_item->display; + } + + return; +} + +=head3 location_description + + my $description = $item->location_description(); + +Returns the human-readable description for the item's location from authorised values. + +=cut + +sub location_description { + my ($self) = @_; + + return '' unless $self->location; + + my $av = Koha::AuthorisedValues->get_description_by_koha_field( + { kohafield => 'items.location', authorised_value => $self->location } ); + return $av->{lib} || $self->location; +} + =head3 orders my $orders = $item->orders(); @@ -2830,10 +3063,8 @@ sub strings_map { # Handle not null and default values for integers and dates my $strings = {}; - foreach my $col ( @{ $self->_columns } ) { - - # By now, we are done with known columns, now check the framework for mappings - my $field = $self->_result->result_source->name . '.' . $col; + my $get_av_desc = sub { + my ( $col, $field ) = @_; # Check there's an entry in the MARC subfield structure for the field if ( exists $mss->{$field} @@ -2848,13 +3079,29 @@ sub strings_map { undef, $params->{public} ); my $type = exists $code_to_type->{$code} ? $code_to_type->{$code} : 'av'; - $strings->{$col} = { + + return { str => $str, type => $type, ( $type eq 'av' ? ( category => $code ) : () ), }; } + return; + }; + + my @cols = @{ $self->_columns }; + push @cols, 'effective_collection_code'; + push @cols, 'effective_holding_library_id'; + push @cols, 'effective_home_library_id'; + push @cols, 'effective_item_type_id'; + push @cols, 'effective_location'; + + foreach my $col (@cols) { + + # By now, we are done with known columns, now check the framework for mappings + my $field = $self->_result->result_source->name . '.' . $col; + if ( $col eq 'booksellerid' ) { my $booksellerid = $self->booksellerid; if ($booksellerid) { @@ -2865,7 +3112,93 @@ sub strings_map { type => 'acquisition_source' } if $bookseller; } + + next; + } + + if ( $col eq 'effective_collection_code' ) { + my $ccode = $self->effective_collection_code; + + if ($ccode) { + my $av = Koha::AuthorisedValues->get_description_by_koha_field( + { kohafield => 'items.ccode', authorised_value => $ccode } ); + my $description = $av->{lib} || $ccode; + + $strings->{$col} = { + category => 'CCODE', + str => $description, + type => 'av', + } if $description; + } + + next; + } + + if ( $col eq 'effective_holding_library_id' ) { + my $holding_library = $self->effective_holding_library; + + if ($holding_library) { + my $description = $holding_library->branchname ? $holding_library->branchname : $holding_library; + + $strings->{$col} = { + str => $description, + type => 'library', + } if $description; + } + + next; } + + if ( $col eq 'effective_home_library_id' ) { + my $home_library = $self->effective_home_library; + + if ($home_library) { + my $description = $home_library->branchname ? $home_library->branchname : $home_library; + + $strings->{$col} = { + str => $description, + type => 'library', + } if $description; + } + + next; + } + + if ( $col eq 'effective_item_type_id' ) { + my $itemtype = Koha::ItemTypes->find( $self->effective_itemtype ); + + if ($itemtype) { + my $description = $itemtype->translated_description ? $itemtype->translated_description : $itemtype; + + $strings->{$col} = { + str => $description, + type => 'item_type', + } if $description; + } + + next; + } + + if ( $col eq 'effective_location' ) { + my $location = $self->effective_location; + + if ($location) { + my $av = Koha::AuthorisedValues->get_description_by_koha_field( + { kohafield => 'items.location', authorised_value => $location } ); + my $description = $av->{lib} || $location; + + $strings->{$col} = { + category => 'LOC', + str => $description, + type => 'av', + } if $description; + } + + next; + } + + my $av_desc = $get_av_desc->( $col, $field ); + $strings->{$col} = $av_desc if $av_desc; } return $strings; diff --git a/Koha/Patron.pm b/Koha/Patron.pm index c0ef6e292c5..85f90dd8806 100644 --- a/Koha/Patron.pm +++ b/Koha/Patron.pm @@ -2246,6 +2246,30 @@ sub libraries_where_can_see_patrons { ); } +=head3 libraries_where_can_edit_displays + + my $libraries = $patron->libraries_where_can_edit_displays; + +Return the list of branchcodes(!) of libraries the patron is allowed to edit +displays for. The branchcodes are arbitrarily returned sorted. + +If the patron has the 'add_items_to_display_from_any_libraries' permission, +an empty list is returned as a signal they have access to all libraries. + +=cut + +sub libraries_where_can_edit_displays { + my ($self) = @_; + + return $self->libraries_where_can_see_things( + { + permission => 'displays', + subpermission => 'add_items_to_display_from_any_libraries', + group_feature => 'ft_display_group', + } + ); +} + =head3 can_see_things_from my $can_see = $patron->can_see_things_from( -- 2.53.0