Main Koha release repository https://koha-community.org
25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 
 
 

852 satır
21 KiB

  1. package Koha::Biblio;
  2. # Copyright ByWater Solutions 2014
  3. #
  4. # This file is part of Koha.
  5. #
  6. # Koha is free software; you can redistribute it and/or modify it
  7. # under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # Koha is distributed in the hope that it will be useful, but
  12. # WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with Koha; if not, see <http://www.gnu.org/licenses>.
  18. use Modern::Perl;
  19. use Carp;
  20. use List::MoreUtils qw(any);
  21. use URI;
  22. use URI::Escape;
  23. use C4::Koha;
  24. use C4::Biblio qw();
  25. use Koha::Database;
  26. use Koha::DateUtils qw( dt_from_string );
  27. use base qw(Koha::Object);
  28. use Koha::Acquisition::Orders;
  29. use Koha::ArticleRequest::Status;
  30. use Koha::ArticleRequests;
  31. use Koha::Biblio::Metadatas;
  32. use Koha::Biblioitems;
  33. use Koha::CirculationRules;
  34. use Koha::Item::Transfer::Limits;
  35. use Koha::Items;
  36. use Koha::Libraries;
  37. use Koha::Suggestions;
  38. use Koha::Subscriptions;
  39. =head1 NAME
  40. Koha::Biblio - Koha Biblio Object class
  41. =head1 API
  42. =head2 Class Methods
  43. =cut
  44. =head3 store
  45. Overloaded I<store> method to set default values
  46. =cut
  47. sub store {
  48. my ( $self ) = @_;
  49. $self->datecreated( dt_from_string ) unless $self->datecreated;
  50. return $self->SUPER::store;
  51. }
  52. =head3 metadata
  53. my $metadata = $biblio->metadata();
  54. Returns a Koha::Biblio::Metadata object
  55. =cut
  56. sub metadata {
  57. my ( $self ) = @_;
  58. my $metadata = $self->_result->metadata;
  59. return Koha::Biblio::Metadata->_new_from_dbic($metadata);
  60. }
  61. =head3 orders
  62. my $orders = $biblio->orders();
  63. Returns a Koha::Acquisition::Orders object
  64. =cut
  65. sub orders {
  66. my ( $self ) = @_;
  67. my $orders = $self->_result->orders;
  68. return Koha::Acquisition::Orders->_new_from_dbic($orders);
  69. }
  70. =head3 active_orders
  71. my $active_orders = $biblio->active_orders();
  72. Returns the active acquisition orders related to this biblio.
  73. An order is considered active when it is not cancelled (i.e. when datecancellation
  74. is not undef).
  75. =cut
  76. sub active_orders {
  77. my ( $self ) = @_;
  78. return $self->orders->search({ datecancellationprinted => undef });
  79. }
  80. =head3 can_article_request
  81. my $bool = $biblio->can_article_request( $borrower );
  82. Returns true if article requests can be made for this record
  83. $borrower must be a Koha::Patron object
  84. =cut
  85. sub can_article_request {
  86. my ( $self, $borrower ) = @_;
  87. my $rule = $self->article_request_type($borrower);
  88. return q{} if $rule eq 'item_only' && !$self->items()->count();
  89. return 1 if $rule && $rule ne 'no';
  90. return q{};
  91. }
  92. =head3 can_be_transferred
  93. $biblio->can_be_transferred({ to => $to_library, from => $from_library })
  94. Checks if at least one item of a biblio can be transferred to given library.
  95. This feature is controlled by two system preferences:
  96. UseBranchTransferLimits to enable / disable the feature
  97. BranchTransferLimitsType to use either an itemnumber or ccode as an identifier
  98. for setting the limitations
  99. Performance-wise, it is recommended to use this method for a biblio instead of
  100. iterating each item of a biblio with Koha::Item->can_be_transferred().
  101. Takes HASHref that can have the following parameters:
  102. MANDATORY PARAMETERS:
  103. $to : Koha::Library
  104. OPTIONAL PARAMETERS:
  105. $from : Koha::Library # if given, only items from that
  106. # holdingbranch are considered
  107. Returns 1 if at least one of the item of a biblio can be transferred
  108. to $to_library, otherwise 0.
  109. =cut
  110. sub can_be_transferred {
  111. my ($self, $params) = @_;
  112. my $to = $params->{to};
  113. my $from = $params->{from};
  114. return 1 unless C4::Context->preference('UseBranchTransferLimits');
  115. my $limittype = C4::Context->preference('BranchTransferLimitsType');
  116. my $items;
  117. foreach my $item_of_bib ($self->items->as_list) {
  118. next unless $item_of_bib->holdingbranch;
  119. next if $from && $from->branchcode ne $item_of_bib->holdingbranch;
  120. return 1 if $item_of_bib->holdingbranch eq $to->branchcode;
  121. my $code = $limittype eq 'itemtype'
  122. ? $item_of_bib->effective_itemtype
  123. : $item_of_bib->ccode;
  124. return 1 unless $code;
  125. $items->{$code}->{$item_of_bib->holdingbranch} = 1;
  126. }
  127. # At this point we will have a HASHref containing each itemtype/ccode that
  128. # this biblio has, inside which are all of the holdingbranches where those
  129. # items are located at. Then, we will query Koha::Item::Transfer::Limits to
  130. # find out whether a transfer limits for such $limittype from any of the
  131. # listed holdingbranches to the given $to library exist. If at least one
  132. # holdingbranch for that $limittype does not have a transfer limit to given
  133. # $to library, then we know that the transfer is possible.
  134. foreach my $code (keys %{$items}) {
  135. my @holdingbranches = keys %{$items->{$code}};
  136. return 1 if Koha::Item::Transfer::Limits->search({
  137. toBranch => $to->branchcode,
  138. fromBranch => { 'in' => \@holdingbranches },
  139. $limittype => $code
  140. }, {
  141. group_by => [qw/fromBranch/]
  142. })->count == scalar(@holdingbranches) ? 0 : 1;
  143. }
  144. return 0;
  145. }
  146. =head3 pickup_locations
  147. my $pickup_locations = $biblio->pickup_locations( {patron => $patron } );
  148. Returns an I<arrayref> of possible pickup locations for this biblio's items,
  149. according to patron's home library (if patron is defined and holds are allowed
  150. only from hold groups) and if item can be transferred to each pickup location.
  151. =cut
  152. sub pickup_locations {
  153. my ($self, $params) = @_;
  154. my $patron = $params->{patron};
  155. my @pickup_locations;
  156. foreach my $item_of_bib ($self->items->as_list) {
  157. push @pickup_locations, @{ $item_of_bib->pickup_locations( {patron => $patron} )->as_list() };
  158. }
  159. my %seen;
  160. @pickup_locations =
  161. grep { !$seen{ $_->branchcode }++ } @pickup_locations;
  162. return \@pickup_locations;
  163. }
  164. =head3 hidden_in_opac
  165. my $bool = $biblio->hidden_in_opac({ [ rules => $rules ] })
  166. Returns true if the biblio matches the hidding criteria defined in $rules.
  167. Returns false otherwise.
  168. Takes HASHref that can have the following parameters:
  169. OPTIONAL PARAMETERS:
  170. $rules : { <field> => [ value_1, ... ], ... }
  171. Note: $rules inherits its structure from the parsed YAML from reading
  172. the I<OpacHiddenItems> system preference.
  173. =cut
  174. sub hidden_in_opac {
  175. my ( $self, $params ) = @_;
  176. my $rules = $params->{rules} // {};
  177. my @items = $self->items->as_list;
  178. return 0 unless @items; # Do not hide if there is no item
  179. return !(any { !$_->hidden_in_opac({ rules => $rules }) } @items);
  180. }
  181. =head3 article_request_type
  182. my $type = $biblio->article_request_type( $borrower );
  183. Returns the article request type based on items, or on the record
  184. itself if there are no items.
  185. $borrower must be a Koha::Patron object
  186. =cut
  187. sub article_request_type {
  188. my ( $self, $borrower ) = @_;
  189. return q{} unless $borrower;
  190. my $rule = $self->article_request_type_for_items( $borrower );
  191. return $rule if $rule;
  192. # If the record has no items that are requestable, go by the record itemtype
  193. $rule = $self->article_request_type_for_bib($borrower);
  194. return $rule if $rule;
  195. return q{};
  196. }
  197. =head3 article_request_type_for_bib
  198. my $type = $biblio->article_request_type_for_bib
  199. Returns the article request type 'yes', 'no', 'item_only', 'bib_only', for the given record
  200. =cut
  201. sub article_request_type_for_bib {
  202. my ( $self, $borrower ) = @_;
  203. return q{} unless $borrower;
  204. my $borrowertype = $borrower->categorycode;
  205. my $itemtype = $self->itemtype();
  206. my $rule = Koha::CirculationRules->get_effective_rule(
  207. {
  208. rule_name => 'article_requests',
  209. categorycode => $borrowertype,
  210. itemtype => $itemtype,
  211. }
  212. );
  213. return q{} unless $rule;
  214. return $rule->rule_value || q{}
  215. }
  216. =head3 article_request_type_for_items
  217. my $type = $biblio->article_request_type_for_items
  218. Returns the article request type 'yes', 'no', 'item_only', 'bib_only', for the given record's items
  219. If there is a conflict where some items are 'bib_only' and some are 'item_only', 'bib_only' will be returned.
  220. =cut
  221. sub article_request_type_for_items {
  222. my ( $self, $borrower ) = @_;
  223. my $counts;
  224. foreach my $item ( $self->items()->as_list() ) {
  225. my $rule = $item->article_request_type($borrower);
  226. return $rule if $rule eq 'bib_only'; # we don't need to go any further
  227. $counts->{$rule}++;
  228. }
  229. return 'item_only' if $counts->{item_only};
  230. return 'yes' if $counts->{yes};
  231. return 'no' if $counts->{no};
  232. return q{};
  233. }
  234. =head3 article_requests
  235. my @requests = $biblio->article_requests
  236. Returns the article requests associated with this Biblio
  237. =cut
  238. sub article_requests {
  239. my ( $self, $borrower ) = @_;
  240. $self->{_article_requests} ||= Koha::ArticleRequests->search( { biblionumber => $self->biblionumber() } );
  241. return wantarray ? $self->{_article_requests}->as_list : $self->{_article_requests};
  242. }
  243. =head3 article_requests_current
  244. my @requests = $biblio->article_requests_current
  245. Returns the article requests associated with this Biblio that are incomplete
  246. =cut
  247. sub article_requests_current {
  248. my ( $self, $borrower ) = @_;
  249. $self->{_article_requests_current} ||= Koha::ArticleRequests->search(
  250. {
  251. biblionumber => $self->biblionumber(),
  252. -or => [
  253. { status => Koha::ArticleRequest::Status::Pending },
  254. { status => Koha::ArticleRequest::Status::Processing }
  255. ]
  256. }
  257. );
  258. return wantarray ? $self->{_article_requests_current}->as_list : $self->{_article_requests_current};
  259. }
  260. =head3 article_requests_finished
  261. my @requests = $biblio->article_requests_finished
  262. Returns the article requests associated with this Biblio that are completed
  263. =cut
  264. sub article_requests_finished {
  265. my ( $self, $borrower ) = @_;
  266. $self->{_article_requests_finished} ||= Koha::ArticleRequests->search(
  267. {
  268. biblionumber => $self->biblionumber(),
  269. -or => [
  270. { status => Koha::ArticleRequest::Status::Completed },
  271. { status => Koha::ArticleRequest::Status::Canceled }
  272. ]
  273. }
  274. );
  275. return wantarray ? $self->{_article_requests_finished}->as_list : $self->{_article_requests_finished};
  276. }
  277. =head3 items
  278. my $items = $biblio->items();
  279. Returns the related Koha::Items object for this biblio
  280. =cut
  281. sub items {
  282. my ($self) = @_;
  283. my $items_rs = $self->_result->items;
  284. return Koha::Items->_new_from_dbic( $items_rs );
  285. }
  286. =head3 itemtype
  287. my $itemtype = $biblio->itemtype();
  288. Returns the itemtype for this record.
  289. =cut
  290. sub itemtype {
  291. my ( $self ) = @_;
  292. return $self->biblioitem()->itemtype();
  293. }
  294. =head3 holds
  295. my $holds = $biblio->holds();
  296. return the current holds placed on this record
  297. =cut
  298. sub holds {
  299. my ( $self, $params, $attributes ) = @_;
  300. $attributes->{order_by} = 'priority' unless exists $attributes->{order_by};
  301. my $hold_rs = $self->_result->reserves->search( $params, $attributes );
  302. return Koha::Holds->_new_from_dbic($hold_rs);
  303. }
  304. =head3 current_holds
  305. my $holds = $biblio->current_holds
  306. Return the holds placed on this bibliographic record.
  307. It does not include future holds.
  308. =cut
  309. sub current_holds {
  310. my ($self) = @_;
  311. my $dtf = Koha::Database->new->schema->storage->datetime_parser;
  312. return $self->holds(
  313. { reservedate => { '<=' => $dtf->format_date(dt_from_string) } } );
  314. }
  315. =head3 biblioitem
  316. my $field = $self->biblioitem()->itemtype
  317. Returns the related Koha::Biblioitem object for this Biblio object
  318. =cut
  319. sub biblioitem {
  320. my ($self) = @_;
  321. $self->{_biblioitem} ||= Koha::Biblioitems->find( { biblionumber => $self->biblionumber() } );
  322. return $self->{_biblioitem};
  323. }
  324. =head3 suggestions
  325. my $suggestions = $self->suggestions
  326. Returns the related Koha::Suggestions object for this Biblio object
  327. =cut
  328. sub suggestions {
  329. my ($self) = @_;
  330. my $suggestions_rs = $self->_result->suggestions;
  331. return Koha::Suggestions->_new_from_dbic( $suggestions_rs );
  332. }
  333. =head3 subscriptions
  334. my $subscriptions = $self->subscriptions
  335. Returns the related Koha::Subscriptions object for this Biblio object
  336. =cut
  337. sub subscriptions {
  338. my ($self) = @_;
  339. $self->{_subscriptions} ||= Koha::Subscriptions->search( { biblionumber => $self->biblionumber } );
  340. return $self->{_subscriptions};
  341. }
  342. =head3 has_items_waiting_or_intransit
  343. my $itemsWaitingOrInTransit = $biblio->has_items_waiting_or_intransit
  344. Tells if this bibliographic record has items waiting or in transit.
  345. =cut
  346. sub has_items_waiting_or_intransit {
  347. my ( $self ) = @_;
  348. if ( Koha::Holds->search({ biblionumber => $self->id,
  349. found => ['W', 'T'] })->count ) {
  350. return 1;
  351. }
  352. foreach my $item ( $self->items->as_list ) {
  353. return 1 if $item->get_transfer;
  354. }
  355. return 0;
  356. }
  357. =head2 get_coins
  358. my $coins = $biblio->get_coins;
  359. Returns the COinS (a span) which can be included in a biblio record
  360. =cut
  361. sub get_coins {
  362. my ( $self ) = @_;
  363. my $record = $self->metadata->record;
  364. my $pos7 = substr $record->leader(), 7, 1;
  365. my $pos6 = substr $record->leader(), 6, 1;
  366. my $mtx;
  367. my $genre;
  368. my ( $aulast, $aufirst ) = ( '', '' );
  369. my @authors;
  370. my $title;
  371. my $hosttitle;
  372. my $pubyear = '';
  373. my $isbn = '';
  374. my $issn = '';
  375. my $publisher = '';
  376. my $pages = '';
  377. my $titletype = '';
  378. # For the purposes of generating COinS metadata, LDR/06-07 can be
  379. # considered the same for UNIMARC and MARC21
  380. my $fmts6 = {
  381. 'a' => 'book',
  382. 'b' => 'manuscript',
  383. 'c' => 'book',
  384. 'd' => 'manuscript',
  385. 'e' => 'map',
  386. 'f' => 'map',
  387. 'g' => 'film',
  388. 'i' => 'audioRecording',
  389. 'j' => 'audioRecording',
  390. 'k' => 'artwork',
  391. 'l' => 'document',
  392. 'm' => 'computerProgram',
  393. 'o' => 'document',
  394. 'r' => 'document',
  395. };
  396. my $fmts7 = {
  397. 'a' => 'journalArticle',
  398. 's' => 'journal',
  399. };
  400. $genre = $fmts6->{$pos6} ? $fmts6->{$pos6} : 'book';
  401. if ( $genre eq 'book' ) {
  402. $genre = $fmts7->{$pos7} if $fmts7->{$pos7};
  403. }
  404. ##### We must transform mtx to a valable mtx and document type ####
  405. if ( $genre eq 'book' ) {
  406. $mtx = 'book';
  407. $titletype = 'b';
  408. } elsif ( $genre eq 'journal' ) {
  409. $mtx = 'journal';
  410. $titletype = 'j';
  411. } elsif ( $genre eq 'journalArticle' ) {
  412. $mtx = 'journal';
  413. $genre = 'article';
  414. $titletype = 'a';
  415. } else {
  416. $mtx = 'dc';
  417. }
  418. if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
  419. # Setting datas
  420. $aulast = $record->subfield( '700', 'a' ) || '';
  421. $aufirst = $record->subfield( '700', 'b' ) || '';
  422. push @authors, "$aufirst $aulast" if ($aufirst or $aulast);
  423. # others authors
  424. if ( $record->field('200') ) {
  425. for my $au ( $record->field('200')->subfield('g') ) {
  426. push @authors, $au;
  427. }
  428. }
  429. $title = $record->subfield( '200', 'a' );
  430. my $subfield_210d = $record->subfield('210', 'd');
  431. if ($subfield_210d and $subfield_210d =~ /(\d{4})/) {
  432. $pubyear = $1;
  433. }
  434. $publisher = $record->subfield( '210', 'c' ) || '';
  435. $isbn = $record->subfield( '010', 'a' ) || '';
  436. $issn = $record->subfield( '011', 'a' ) || '';
  437. } else {
  438. # MARC21 need some improve
  439. # Setting datas
  440. if ( $record->field('100') ) {
  441. push @authors, $record->subfield( '100', 'a' );
  442. }
  443. # others authors
  444. if ( $record->field('700') ) {
  445. for my $au ( $record->field('700')->subfield('a') ) {
  446. push @authors, $au;
  447. }
  448. }
  449. $title = $record->field('245');
  450. $title &&= $title->as_string('ab');
  451. if ($titletype eq 'a') {
  452. $pubyear = $record->field('008') || '';
  453. $pubyear = substr($pubyear->data(), 7, 4) if $pubyear;
  454. $isbn = $record->subfield( '773', 'z' ) || '';
  455. $issn = $record->subfield( '773', 'x' ) || '';
  456. $hosttitle = $record->subfield( '773', 't' ) || $record->subfield( '773', 'a') || q{};
  457. my @rels = $record->subfield( '773', 'g' );
  458. $pages = join(', ', @rels);
  459. } else {
  460. $pubyear = $record->subfield( '260', 'c' ) || '';
  461. $publisher = $record->subfield( '260', 'b' ) || '';
  462. $isbn = $record->subfield( '020', 'a' ) || '';
  463. $issn = $record->subfield( '022', 'a' ) || '';
  464. }
  465. }
  466. my @params = (
  467. [ 'ctx_ver', 'Z39.88-2004' ],
  468. [ 'rft_val_fmt', "info:ofi/fmt:kev:mtx:$mtx" ],
  469. [ ($mtx eq 'dc' ? 'rft.type' : 'rft.genre'), $genre ],
  470. [ "rft.${titletype}title", $title ],
  471. );
  472. # rft.title is authorized only once, so by checking $titletype
  473. # we ensure that rft.title is not already in the list.
  474. if ($hosttitle and $titletype) {
  475. push @params, [ 'rft.title', $hosttitle ];
  476. }
  477. push @params, (
  478. [ 'rft.isbn', $isbn ],
  479. [ 'rft.issn', $issn ],
  480. );
  481. # If it's a subscription, these informations have no meaning.
  482. if ($genre ne 'journal') {
  483. push @params, (
  484. [ 'rft.aulast', $aulast ],
  485. [ 'rft.aufirst', $aufirst ],
  486. (map { [ 'rft.au', $_ ] } @authors),
  487. [ 'rft.pub', $publisher ],
  488. [ 'rft.date', $pubyear ],
  489. [ 'rft.pages', $pages ],
  490. );
  491. }
  492. my $coins_value = join( '&amp;',
  493. map { $$_[1] ? $$_[0] . '=' . uri_escape_utf8( $$_[1] ) : () } @params );
  494. return $coins_value;
  495. }
  496. =head2 get_openurl
  497. my $url = $biblio->get_openurl;
  498. Returns url for OpenURL resolver set in OpenURLResolverURL system preference
  499. =cut
  500. sub get_openurl {
  501. my ( $self ) = @_;
  502. my $OpenURLResolverURL = C4::Context->preference('OpenURLResolverURL');
  503. if ($OpenURLResolverURL) {
  504. my $uri = URI->new($OpenURLResolverURL);
  505. if (not defined $uri->query) {
  506. $OpenURLResolverURL .= '?';
  507. } else {
  508. $OpenURLResolverURL .= '&amp;';
  509. }
  510. $OpenURLResolverURL .= $self->get_coins;
  511. }
  512. return $OpenURLResolverURL;
  513. }
  514. =head3 is_serial
  515. my $serial = $biblio->is_serial
  516. Return boolean true if this bibbliographic record is continuing resource
  517. =cut
  518. sub is_serial {
  519. my ( $self ) = @_;
  520. return 1 if $self->serial;
  521. my $record = $self->metadata->record;
  522. return 1 if substr($record->leader, 7, 1) eq 's';
  523. return 0;
  524. }
  525. =head3 custom_cover_image_url
  526. my $image_url = $biblio->custom_cover_image_url
  527. Return the specific url of the cover image for this bibliographic record.
  528. It is built regaring the value of the system preference CustomCoverImagesURL
  529. =cut
  530. sub custom_cover_image_url {
  531. my ( $self ) = @_;
  532. my $url = C4::Context->preference('CustomCoverImagesURL');
  533. if ( $url =~ m|{isbn}| ) {
  534. my $isbn = $self->biblioitem->isbn;
  535. $url =~ s|{isbn}|$isbn|g;
  536. }
  537. if ( $url =~ m|{normalized_isbn}| ) {
  538. my $normalized_isbn = C4::Koha::GetNormalizedISBN($self->biblioitem->isbn);
  539. $url =~ s|{normalized_isbn}|$normalized_isbn|g;
  540. }
  541. if ( $url =~ m|{issn}| ) {
  542. my $issn = $self->biblioitem->issn;
  543. $url =~ s|{issn}|$issn|g;
  544. }
  545. my $re = qr|{(?<field>\d{3})\$(?<subfield>.)}|;
  546. if ( $url =~ $re ) {
  547. my $field = $+{field};
  548. my $subfield = $+{subfield};
  549. my $marc_record = $self->metadata->record;
  550. my $value = $marc_record->subfield($field, $subfield);
  551. $url =~ s|$re|$value|;
  552. }
  553. return $url;
  554. }
  555. =head3 cover_images
  556. Return the cover images associated with this biblio.
  557. =cut
  558. sub cover_images {
  559. my ( $self ) = @_;
  560. my $cover_images_rs = $self->_result->cover_images;
  561. return unless $cover_images_rs;
  562. return Koha::CoverImages->_new_from_dbic($cover_images_rs);
  563. }
  564. =head3 to_api
  565. my $json = $biblio->to_api;
  566. Overloaded method that returns a JSON representation of the Koha::Biblio object,
  567. suitable for API output. The related Koha::Biblioitem object is merged as expected
  568. on the API.
  569. =cut
  570. sub to_api {
  571. my ($self, $args) = @_;
  572. my $response = $self->SUPER::to_api( $args );
  573. my $biblioitem = $self->biblioitem->to_api;
  574. return { %$response, %$biblioitem };
  575. }
  576. =head3 to_api_mapping
  577. This method returns the mapping for representing a Koha::Biblio object
  578. on the API.
  579. =cut
  580. sub to_api_mapping {
  581. return {
  582. biblionumber => 'biblio_id',
  583. frameworkcode => 'framework_id',
  584. unititle => 'uniform_title',
  585. seriestitle => 'series_title',
  586. copyrightdate => 'copyright_date',
  587. datecreated => 'creation_date'
  588. };
  589. }
  590. =head2 Internal methods
  591. =head3 type
  592. =cut
  593. sub _type {
  594. return 'Biblio';
  595. }
  596. =head1 AUTHOR
  597. Kyle M Hall <kyle@bywatersolutions.com>
  598. =cut
  599. 1;