3 # Copyright ByWater Solutions 2014
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22 use List::MoreUtils qw( any );
24 use URI::Escape qw( uri_escape_utf8 );
26 use C4::Koha qw( GetNormalizedISBN );
27 use C4::XSLT qw( transformMARCXML4XSLT );
30 use Koha::DateUtils qw( dt_from_string );
32 use base qw(Koha::Object);
34 use Koha::Acquisition::Orders;
35 use Koha::ArticleRequests;
36 use Koha::Biblio::Metadatas;
37 use Koha::Biblioitems;
39 use Koha::CirculationRules;
40 use Koha::Item::Transfer::Limits;
43 use Koha::Old::Checkouts;
44 use Koha::Suggestions;
45 use Koha::Subscriptions;
46 use Koha::SearchEngine;
47 use Koha::SearchEngine::Search;
48 use Koha::SearchEngine::QueryBuilder;
52 Koha::Biblio - Koha Biblio Object class
62 Overloaded I<store> method to set default values
69 $self->datecreated( dt_from_string ) unless $self->datecreated;
71 return $self->SUPER::store;
76 my $metadata = $biblio->metadata();
78 Returns a Koha::Biblio::Metadata object
85 my $metadata = $self->_result->metadata;
86 return Koha::Biblio::Metadata->_new_from_dbic($metadata);
91 my $orders = $biblio->orders();
93 Returns a Koha::Acquisition::Orders object
100 my $orders = $self->_result->orders;
101 return Koha::Acquisition::Orders->_new_from_dbic($orders);
106 my $active_orders = $biblio->active_orders();
108 Returns the active acquisition orders related to this biblio.
109 An order is considered active when it is not cancelled (i.e. when datecancellation
117 return $self->orders->search({ datecancellationprinted => undef });
120 =head3 can_article_request
122 my $bool = $biblio->can_article_request( $borrower );
124 Returns true if article requests can be made for this record
126 $borrower must be a Koha::Patron object
130 sub can_article_request {
131 my ( $self, $borrower ) = @_;
133 my $rule = $self->article_request_type($borrower);
134 return q{} if $rule eq 'item_only' && !$self->items()->count();
135 return 1 if $rule && $rule ne 'no';
140 =head3 can_be_transferred
142 $biblio->can_be_transferred({ to => $to_library, from => $from_library })
144 Checks if at least one item of a biblio can be transferred to given library.
146 This feature is controlled by two system preferences:
147 UseBranchTransferLimits to enable / disable the feature
148 BranchTransferLimitsType to use either an itemnumber or ccode as an identifier
149 for setting the limitations
151 Performance-wise, it is recommended to use this method for a biblio instead of
152 iterating each item of a biblio with Koha::Item->can_be_transferred().
154 Takes HASHref that can have the following parameters:
155 MANDATORY PARAMETERS:
158 $from : Koha::Library # if given, only items from that
159 # holdingbranch are considered
161 Returns 1 if at least one of the item of a biblio can be transferred
162 to $to_library, otherwise 0.
166 sub can_be_transferred {
167 my ($self, $params) = @_;
169 my $to = $params->{to};
170 my $from = $params->{from};
172 return 1 unless C4::Context->preference('UseBranchTransferLimits');
173 my $limittype = C4::Context->preference('BranchTransferLimitsType');
176 foreach my $item_of_bib ($self->items->as_list) {
177 next unless $item_of_bib->holdingbranch;
178 next if $from && $from->branchcode ne $item_of_bib->holdingbranch;
179 return 1 if $item_of_bib->holdingbranch eq $to->branchcode;
180 my $code = $limittype eq 'itemtype'
181 ? $item_of_bib->effective_itemtype
182 : $item_of_bib->ccode;
183 return 1 unless $code;
184 $items->{$code}->{$item_of_bib->holdingbranch} = 1;
187 # At this point we will have a HASHref containing each itemtype/ccode that
188 # this biblio has, inside which are all of the holdingbranches where those
189 # items are located at. Then, we will query Koha::Item::Transfer::Limits to
190 # find out whether a transfer limits for such $limittype from any of the
191 # listed holdingbranches to the given $to library exist. If at least one
192 # holdingbranch for that $limittype does not have a transfer limit to given
193 # $to library, then we know that the transfer is possible.
194 foreach my $code (keys %{$items}) {
195 my @holdingbranches = keys %{$items->{$code}};
196 return 1 if Koha::Item::Transfer::Limits->search({
197 toBranch => $to->branchcode,
198 fromBranch => { 'in' => \@holdingbranches },
201 group_by => [qw/fromBranch/]
202 })->count == scalar(@holdingbranches) ? 0 : 1;
209 =head3 pickup_locations
211 my $pickup_locations = $biblio->pickup_locations( {patron => $patron } );
213 Returns a Koha::Libraries set of possible pickup locations for this biblio's items,
214 according to patron's home library (if patron is defined and holds are allowed
215 only from hold groups) and if item can be transferred to each pickup location.
219 sub pickup_locations {
220 my ( $self, $params ) = @_;
222 my $patron = $params->{patron};
224 my @pickup_locations;
225 foreach my $item_of_bib ( $self->items->as_list ) {
226 push @pickup_locations,
227 $item_of_bib->pickup_locations( { patron => $patron } )
228 ->_resultset->get_column('branchcode')->all;
231 return Koha::Libraries->search(
232 { branchcode => { '-in' => \@pickup_locations } }, { order_by => ['branchname'] } );
235 =head3 hidden_in_opac
237 my $bool = $biblio->hidden_in_opac({ [ rules => $rules ] })
239 Returns true if the biblio matches the hidding criteria defined in $rules.
240 Returns false otherwise. It involves the I<OpacHiddenItems> and
241 I<OpacHiddenItemsHidesRecord> system preferences.
243 Takes HASHref that can have the following parameters:
245 $rules : { <field> => [ value_1, ... ], ... }
247 Note: $rules inherits its structure from the parsed YAML from reading
248 the I<OpacHiddenItems> system preference.
253 my ( $self, $params ) = @_;
255 my $rules = $params->{rules} // {};
257 my @items = $self->items->as_list;
259 return 0 unless @items; # Do not hide if there is no item
261 # Ok, there are items, don't even try the rules unless OpacHiddenItemsHidesRecord
262 return 0 unless C4::Context->preference('OpacHiddenItemsHidesRecord');
264 return !(any { !$_->hidden_in_opac({ rules => $rules }) } @items);
267 =head3 article_request_type
269 my $type = $biblio->article_request_type( $borrower );
271 Returns the article request type based on items, or on the record
272 itself if there are no items.
274 $borrower must be a Koha::Patron object
278 sub article_request_type {
279 my ( $self, $borrower ) = @_;
281 return q{} unless $borrower;
283 my $rule = $self->article_request_type_for_items( $borrower );
284 return $rule if $rule;
286 # If the record has no items that are requestable, go by the record itemtype
287 $rule = $self->article_request_type_for_bib($borrower);
288 return $rule if $rule;
293 =head3 article_request_type_for_bib
295 my $type = $biblio->article_request_type_for_bib
297 Returns the article request type 'yes', 'no', 'item_only', 'bib_only', for the given record
301 sub article_request_type_for_bib {
302 my ( $self, $borrower ) = @_;
304 return q{} unless $borrower;
306 my $borrowertype = $borrower->categorycode;
307 my $itemtype = $self->itemtype();
309 my $rule = Koha::CirculationRules->get_effective_rule(
311 rule_name => 'article_requests',
312 categorycode => $borrowertype,
313 itemtype => $itemtype,
317 return q{} unless $rule;
318 return $rule->rule_value || q{}
321 =head3 article_request_type_for_items
323 my $type = $biblio->article_request_type_for_items
325 Returns the article request type 'yes', 'no', 'item_only', 'bib_only', for the given record's items
327 If there is a conflict where some items are 'bib_only' and some are 'item_only', 'bib_only' will be returned.
331 sub article_request_type_for_items {
332 my ( $self, $borrower ) = @_;
335 foreach my $item ( $self->items()->as_list() ) {
336 my $rule = $item->article_request_type($borrower);
337 return $rule if $rule eq 'bib_only'; # we don't need to go any further
341 return 'item_only' if $counts->{item_only};
342 return 'yes' if $counts->{yes};
343 return 'no' if $counts->{no};
347 =head3 article_requests
349 my $article_requests = $biblio->article_requests
351 Returns the article requests associated with this biblio
355 sub article_requests {
358 return Koha::ArticleRequests->_new_from_dbic( scalar $self->_result->article_requests );
361 =head3 current_checkouts
363 my $current_checkouts = $biblio->current_checkouts
365 Returns the current checkouts associated with this biblio
369 sub current_checkouts {
372 return Koha::Checkouts->search( { "item.biblionumber" => $self->id },
373 { join => 'item' } );
378 my $old_checkouts = $biblio->old_checkouts
380 Returns the past checkouts associated with this biblio
387 return Koha::Old::Checkouts->search( { "item.biblionumber" => $self->id },
388 { join => 'item' } );
393 my $items = $biblio->items();
395 Returns the related Koha::Items object for this biblio
402 my $items_rs = $self->_result->items;
404 return Koha::Items->_new_from_dbic( $items_rs );
409 my $host_items = $biblio->host_items();
411 Return the host items (easy analytical record)
418 return Koha::Items->new->empty
419 unless C4::Context->preference('EasyAnalyticalRecords');
421 my $marcflavour = C4::Context->preference("marcflavour");
422 my $analyticfield = '773';
423 if ( $marcflavour eq 'MARC21' ) {
424 $analyticfield = '773';
426 elsif ( $marcflavour eq 'UNIMARC' ) {
427 $analyticfield = '461';
429 my $marc_record = $self->metadata->record;
431 foreach my $field ( $marc_record->field($analyticfield) ) {
432 push @itemnumbers, $field->subfield('9');
435 return Koha::Items->search( { itemnumber => { -in => \@itemnumbers } } );
440 my $itemtype = $biblio->itemtype();
442 Returns the itemtype for this record.
449 return $self->biblioitem()->itemtype();
454 my $holds = $biblio->holds();
456 return the current holds placed on this record
461 my ( $self, $params, $attributes ) = @_;
462 $attributes->{order_by} = 'priority' unless exists $attributes->{order_by};
463 my $hold_rs = $self->_result->reserves->search( $params, $attributes );
464 return Koha::Holds->_new_from_dbic($hold_rs);
469 my $holds = $biblio->current_holds
471 Return the holds placed on this bibliographic record.
472 It does not include future holds.
478 my $dtf = Koha::Database->new->schema->storage->datetime_parser;
480 { reservedate => { '<=' => $dtf->format_date(dt_from_string) } } );
485 my $field = $self->biblioitem()->itemtype
487 Returns the related Koha::Biblioitem object for this Biblio object
494 $self->{_biblioitem} ||= Koha::Biblioitems->find( { biblionumber => $self->biblionumber() } );
496 return $self->{_biblioitem};
501 my $suggestions = $self->suggestions
503 Returns the related Koha::Suggestions object for this Biblio object
510 my $suggestions_rs = $self->_result->suggestions;
511 return Koha::Suggestions->_new_from_dbic( $suggestions_rs );
514 =head3 get_marc_components
516 my $components = $self->get_marc_components();
518 Returns an array of MARCXML data, which are component parts of
519 this object (MARC21 773$w points to this)
523 sub get_marc_components {
524 my ($self, $max_results) = @_;
526 return [] if (C4::Context->preference('marcflavour') ne 'MARC21');
528 my $searchstr = $self->get_components_query;
531 if (defined($searchstr)) {
532 my $searcher = Koha::SearchEngine::Search->new({index => $Koha::SearchEngine::BIBLIOS_INDEX});
533 my ( $error, $results, $total_hits );
535 ( $error, $results, $total_hits ) = $searcher->simple_search_compat( $searchstr, 0, $max_results );
540 warn "Warning from simple_search_compat: '$error'";
544 message => 'component_search',
549 $components = $results if defined($results) && @$results;
552 return $components // [];
555 =head2 get_components_query
557 Returns a query which can be used to search for all component parts of MARC21 biblios
561 sub get_components_query {
564 my $builder = Koha::SearchEngine::QueryBuilder->new(
565 { index => $Koha::SearchEngine::BIBLIOS_INDEX } );
566 my $marc = $self->metadata->record;
569 if ( C4::Context->preference('UseControlNumber') ) {
570 my $pf001 = $marc->field('001') || undef;
572 if ( defined($pf001) ) {
574 my $pf003 = $marc->field('003') || undef;
576 if ( !defined($pf003) ) {
577 # search for 773$w='Host001'
578 $searchstr .= "rcn:" . $pf001->data();
582 # search for (773$w='Host001' and 003='Host003') or 773$w='(Host003)Host001'
583 $searchstr .= "(rcn:" . $pf001->data() . " AND cni:" . $pf003->data() . ")";
584 $searchstr .= " OR rcn:\"" . $pf003->data() . " " . $pf001->data() . "\"";
588 # limit to monograph and serial component part records
589 $searchstr .= " AND (bib-level:a OR bib-level:b)";
594 my $cleaned_title = $marc->subfield('245', "a");
595 $cleaned_title =~ tr|/||;
596 $cleaned_title = $builder->clean_search_term($cleaned_title);
597 $searchstr = "Host-item:($cleaned_title)";
605 my $subscriptions = $self->subscriptions
607 Returns the related Koha::Subscriptions object for this Biblio object
614 $self->{_subscriptions} ||= Koha::Subscriptions->search( { biblionumber => $self->biblionumber } );
616 return $self->{_subscriptions};
619 =head3 has_items_waiting_or_intransit
621 my $itemsWaitingOrInTransit = $biblio->has_items_waiting_or_intransit
623 Tells if this bibliographic record has items waiting or in transit.
627 sub has_items_waiting_or_intransit {
630 if ( Koha::Holds->search({ biblionumber => $self->id,
631 found => ['W', 'T'] })->count ) {
635 foreach my $item ( $self->items->as_list ) {
636 return 1 if $item->get_transfer;
644 my $coins = $biblio->get_coins;
646 Returns the COinS (a span) which can be included in a biblio record
653 my $record = $self->metadata->record;
655 my $pos7 = substr $record->leader(), 7, 1;
656 my $pos6 = substr $record->leader(), 6, 1;
659 my ( $aulast, $aufirst ) = ( '', '' );
670 # For the purposes of generating COinS metadata, LDR/06-07 can be
671 # considered the same for UNIMARC and MARC21
680 'i' => 'audioRecording',
681 'j' => 'audioRecording',
684 'm' => 'computerProgram',
689 'a' => 'journalArticle',
693 $genre = $fmts6->{$pos6} ? $fmts6->{$pos6} : 'book';
695 if ( $genre eq 'book' ) {
696 $genre = $fmts7->{$pos7} if $fmts7->{$pos7};
699 ##### We must transform mtx to a valable mtx and document type ####
700 if ( $genre eq 'book' ) {
703 } elsif ( $genre eq 'journal' ) {
706 } elsif ( $genre eq 'journalArticle' ) {
714 if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
717 $aulast = $record->subfield( '700', 'a' ) || '';
718 $aufirst = $record->subfield( '700', 'b' ) || '';
719 push @authors, "$aufirst $aulast" if ($aufirst or $aulast);
722 if ( $record->field('200') ) {
723 for my $au ( $record->field('200')->subfield('g') ) {
728 $title = $record->subfield( '200', 'a' );
729 my $subfield_210d = $record->subfield('210', 'd');
730 if ($subfield_210d and $subfield_210d =~ /(\d{4})/) {
733 $publisher = $record->subfield( '210', 'c' ) || '';
734 $isbn = $record->subfield( '010', 'a' ) || '';
735 $issn = $record->subfield( '011', 'a' ) || '';
738 # MARC21 need some improve
741 if ( $record->field('100') ) {
742 push @authors, $record->subfield( '100', 'a' );
746 if ( $record->field('700') ) {
747 for my $au ( $record->field('700')->subfield('a') ) {
751 $title = $record->field('245');
752 $title &&= $title->as_string('ab');
753 if ($titletype eq 'a') {
754 $pubyear = $record->field('008') || '';
755 $pubyear = substr($pubyear->data(), 7, 4) if $pubyear;
756 $isbn = $record->subfield( '773', 'z' ) || '';
757 $issn = $record->subfield( '773', 'x' ) || '';
758 $hosttitle = $record->subfield( '773', 't' ) || $record->subfield( '773', 'a') || q{};
759 my @rels = $record->subfield( '773', 'g' );
760 $pages = join(', ', @rels);
762 $pubyear = $record->subfield( '260', 'c' ) || '';
763 $publisher = $record->subfield( '260', 'b' ) || '';
764 $isbn = $record->subfield( '020', 'a' ) || '';
765 $issn = $record->subfield( '022', 'a' ) || '';
771 [ 'ctx_ver', 'Z39.88-2004' ],
772 [ 'rft_val_fmt', "info:ofi/fmt:kev:mtx:$mtx" ],
773 [ ($mtx eq 'dc' ? 'rft.type' : 'rft.genre'), $genre ],
774 [ "rft.${titletype}title", $title ],
777 # rft.title is authorized only once, so by checking $titletype
778 # we ensure that rft.title is not already in the list.
779 if ($hosttitle and $titletype) {
780 push @params, [ 'rft.title', $hosttitle ];
784 [ 'rft.isbn', $isbn ],
785 [ 'rft.issn', $issn ],
788 # If it's a subscription, these informations have no meaning.
789 if ($genre ne 'journal') {
791 [ 'rft.aulast', $aulast ],
792 [ 'rft.aufirst', $aufirst ],
793 (map { [ 'rft.au', $_ ] } @authors),
794 [ 'rft.pub', $publisher ],
795 [ 'rft.date', $pubyear ],
796 [ 'rft.pages', $pages ],
800 my $coins_value = join( '&',
801 map { $$_[1] ? $$_[0] . '=' . uri_escape_utf8( $$_[1] ) : () } @params );
808 my $url = $biblio->get_openurl;
810 Returns url for OpenURL resolver set in OpenURLResolverURL system preference
817 my $OpenURLResolverURL = C4::Context->preference('OpenURLResolverURL');
819 if ($OpenURLResolverURL) {
820 my $uri = URI->new($OpenURLResolverURL);
822 if (not defined $uri->query) {
823 $OpenURLResolverURL .= '?';
825 $OpenURLResolverURL .= '&';
827 $OpenURLResolverURL .= $self->get_coins;
830 return $OpenURLResolverURL;
835 my $serial = $biblio->is_serial
837 Return boolean true if this bibbliographic record is continuing resource
844 return 1 if $self->serial;
846 my $record = $self->metadata->record;
847 return 1 if substr($record->leader, 7, 1) eq 's';
852 =head3 custom_cover_image_url
854 my $image_url = $biblio->custom_cover_image_url
856 Return the specific url of the cover image for this bibliographic record.
857 It is built regaring the value of the system preference CustomCoverImagesURL
861 sub custom_cover_image_url {
863 my $url = C4::Context->preference('CustomCoverImagesURL');
864 if ( $url =~ m|{isbn}| ) {
865 my $isbn = $self->biblioitem->isbn;
867 $url =~ s|{isbn}|$isbn|g;
869 if ( $url =~ m|{normalized_isbn}| ) {
870 my $normalized_isbn = C4::Koha::GetNormalizedISBN($self->biblioitem->isbn);
871 return unless $normalized_isbn;
872 $url =~ s|{normalized_isbn}|$normalized_isbn|g;
874 if ( $url =~ m|{issn}| ) {
875 my $issn = $self->biblioitem->issn;
877 $url =~ s|{issn}|$issn|g;
880 my $re = qr|{(?<field>\d{3})(\$(?<subfield>.))?}|;
882 my $field = $+{field};
883 my $subfield = $+{subfield};
884 my $marc_record = $self->metadata->record;
887 $value = $marc_record->subfield( $field, $subfield );
889 my $controlfield = $marc_record->field($field);
890 $value = $controlfield->data() if $controlfield;
892 return unless $value;
893 $url =~ s|$re|$value|;
901 Return the cover images associated with this biblio.
908 my $cover_images_rs = $self->_result->cover_images;
909 return unless $cover_images_rs;
910 return Koha::CoverImages->_new_from_dbic($cover_images_rs);
913 =head3 get_marc_notes
915 $marcnotesarray = $biblio->get_marc_notes({ marcflavour => $marcflavour });
917 Get all notes from the MARC record and returns them in an array.
918 The notes are stored in different fields depending on MARC flavour.
919 MARC21 5XX $u subfields receive special attention as they are URIs.
924 my ( $self, $params ) = @_;
926 my $marcflavour = $params->{marcflavour};
927 my $opac = $params->{opac};
929 my $scope = $marcflavour eq "UNIMARC"? '3..': '5..';
932 #MARC21 specs indicate some notes should be private if first indicator 0
933 my %maybe_private = (
941 my %hiddenlist = map { $_ => 1 }
942 split( /,/, C4::Context->preference('NotesToHide'));
943 my $record = $self->metadata->record;
944 $record = transformMARCXML4XSLT( $self->biblionumber, $record, $opac );
946 foreach my $field ( $record->field($scope) ) {
947 my $tag = $field->tag();
948 next if $hiddenlist{ $tag };
949 next if $opac && $maybe_private{$tag} && !$field->indicator(1);
950 if( $marcflavour ne 'UNIMARC' && $field->subfield('u') ) {
951 # Field 5XX$u always contains URI
952 # Examples: 505u, 506u, 510u, 514u, 520u, 530u, 538u, 540u, 542u, 552u, 555u, 561u, 563u, 583u
953 # We first push the other subfields, then all $u's separately
954 # Leave further actions to the template (see e.g. opac-detail)
956 join '', ( 'a' .. 't', 'v' .. 'z', '0' .. '9' ); # excl 'u'
957 push @marcnotes, { marcnote => $field->as_string($othersub) };
958 foreach my $sub ( $field->subfield('u') ) {
959 $sub =~ s/^\s+|\s+$//g; # trim
960 push @marcnotes, { marcnote => $sub };
963 push @marcnotes, { marcnote => $field->as_string() };
969 =head3 get_authors_from_MARC
971 my $authors = $biblio->get_authors_from_MARC;
973 Get all authors from the MARC record and returns them in an array.
974 The authors are stored in different fields depending on MARC flavour
978 sub get_authors_from_MARC {
979 my ( $self, $params ) = @_;
981 my ( $mintag, $maxtag, $fields_filter );
982 my $marcflavour = C4::Context->preference('marcflavour');
984 # tagslib useful only for UNIMARC author responsibilities
986 if ( $marcflavour eq "UNIMARC" ) {
987 # FIXME : we don't have the framework available, we take the default framework. May be buggy on some setups, will be usually correct.
988 $tagslib = C4::Biblio::GetMarcStructure( 1, '', { unsafe => 1 });
991 $fields_filter = '7..';
992 } else { # marc21/normarc
995 $fields_filter = '7..';
999 my $AuthoritySeparator = C4::Context->preference('AuthoritySeparator');
1001 foreach my $field ( $self->metadata->record->field($fields_filter) ) {
1002 next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1005 my @subfields = $field->subfields();
1008 # if there is an authority link, build the link with Koha-Auth-Number: subfield9
1009 my $subfield9 = $field->subfield('9');
1011 my $linkvalue = $subfield9;
1012 $linkvalue =~ s/(\(|\))//g;
1013 @link_loop = ( { 'limit' => 'an', 'link' => $linkvalue } );
1018 for my $authors_subfield (@subfields) {
1019 next if ( $authors_subfield->[0] eq '9' );
1021 # unimarc3 contains the $3 of the author for UNIMARC.
1022 # For french academic libraries, it's the "ppn", and it's required for idref webservice
1023 $unimarc3 = $authors_subfield->[1] if $marcflavour eq 'UNIMARC' and $authors_subfield->[0] =~ /3/;
1025 # don't load unimarc subfields 3, 5
1026 next if ( $marcflavour eq 'UNIMARC' and ( $authors_subfield->[0] =~ /3|5/ ) );
1028 my $code = $authors_subfield->[0];
1029 my $value = $authors_subfield->[1];
1030 my $linkvalue = $value;
1031 $linkvalue =~ s/(\(|\))//g;
1032 # UNIMARC author responsibility
1033 if ( $marcflavour eq 'UNIMARC' and $code eq '4' ) {
1034 $value = C4::Biblio::GetAuthorisedValueDesc( $field->tag(), $code, $value, '', $tagslib );
1035 $linkvalue = "($value)";
1037 # if no authority link, build a search query
1038 unless ($subfield9) {
1041 'link' => $linkvalue,
1042 operator => (scalar @link_loop) ? ' and ' : undef
1045 my @this_link_loop = @link_loop;
1047 unless ( $code eq '0') {
1048 push @subfields_loop, {
1049 tag => $field->tag(),
1052 link_loop => \@this_link_loop,
1053 separator => (scalar @subfields_loop) ? $AuthoritySeparator : ''
1057 push @marcauthors, {
1058 MARCAUTHOR_SUBFIELDS_LOOP => \@subfields_loop,
1059 authoritylink => $subfield9,
1060 unimarc3 => $unimarc3
1063 return \@marcauthors;
1068 my $json = $biblio->to_api;
1070 Overloaded method that returns a JSON representation of the Koha::Biblio object,
1071 suitable for API output. The related Koha::Biblioitem object is merged as expected
1077 my ($self, $args) = @_;
1079 my $response = $self->SUPER::to_api( $args );
1080 my $biblioitem = $self->biblioitem->to_api;
1082 return { %$response, %$biblioitem };
1085 =head3 to_api_mapping
1087 This method returns the mapping for representing a Koha::Biblio object
1092 sub to_api_mapping {
1094 biblionumber => 'biblio_id',
1095 frameworkcode => 'framework_id',
1096 unititle => 'uniform_title',
1097 seriestitle => 'series_title',
1098 copyrightdate => 'copyright_date',
1099 datecreated => 'creation_date'
1103 =head3 get_marc_host
1105 $host = $biblio->get_marc_host;
1107 ( $host, $relatedparts ) = $biblio->get_marc_host;
1109 Returns host biblio record from MARC21 773 (undef if no 773 present).
1110 It looks at the first 773 field with MARCorgCode or only a control
1111 number. Complete $w or numeric part is used to search host record.
1112 The optional parameter no_items triggers a check if $biblio has items.
1113 If there are, the sub returns undef.
1114 Called in list context, it also returns 773$g (related parts).
1119 my ($self, $params) = @_;
1120 my $no_items = $params->{no_items};
1121 return if C4::Context->preference('marcflavour') eq 'UNIMARC'; # TODO
1122 return if $params->{no_items} && $self->items->count > 0;
1125 eval { $record = $self->metadata->record };
1128 # We pick the first $w with your MARCOrgCode or the first $w that has no
1129 # code (between parentheses) at all.
1130 my $orgcode = C4::Context->preference('MARCOrgCode') // q{};
1132 foreach my $f ( $record->field('773') ) {
1133 my $w = $f->subfield('w') or next;
1134 if( $w =~ /^\($orgcode\)\s*(\d+)/i or $w =~ /^\d+/ ) {
1139 return if !$hostfld;
1140 my $rcn = $hostfld->subfield('w');
1142 # Look for control number with/without orgcode
1143 my $engine = Koha::SearchEngine::Search->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
1145 for my $try (1..2) {
1146 my ( $error, $results, $total_hits ) = $engine->simple_search_compat( 'Control-number='.$rcn, 0,1 );
1147 if( !$error and $total_hits == 1 ) {
1148 $bibno = $engine->extract_biblionumber( $results->[0] );
1151 # Add or remove orgcode for second try
1152 if( $try == 1 && $rcn =~ /\)\s*(\d+)/ ) {
1153 $rcn = $1; # number only
1154 } elsif( $try == 1 && $rcn =~ /^\d+/ ) {
1155 $rcn = "($orgcode)$rcn";
1161 my $host = Koha::Biblios->find($bibno) or return;
1162 return wantarray ? ( $host, $hostfld->subfield('g') ) : $host;
1166 =head2 Internal methods
1178 Kyle M Hall <kyle@bywatersolutions.com>