3 # Copyright 2000-2002 Katipo Communications
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA 02111-1307 USA
26 use MARC::File::USMARC;
31 use C4::Dates qw/format_date/;
32 use C4::Log; # logaction
34 use vars qw($VERSION @ISA @EXPORT);
39 @ISA = qw( Exporter );
43 # to add biblios or items
44 push @EXPORT, qw( &AddBiblio &AddItem &AddBiblioAndItems );
52 &GetBiblioItemByBiblioNumber
53 &GetBiblioFromItemNumber
73 &GetItemsByBiblioitemnumber
74 &GetItemnumberFromBarcode
78 &GetAuthorisedValueDesc
82 &GetPublisherNameFromIsbn
94 &ModItemInMarconefield
105 # those functions are exported but should not be used
106 # they are usefull is few circumstances, so are exported.
107 # but don't use them unless you're a core developer ;-)
116 &TransformHtmlToMarc2
119 &PrepareItemrecordDisplay
126 C4::Biblio - cataloging management functions
130 Biblio.pm contains functions for managing storage and editing of bibliographic data within Koha. Most of the functions in this module are used for cataloging records: adding, editing, or removing biblios, biblioitems, or items. Koha's stores bibliographic information in three places:
134 =item 1. in the biblio,biblioitems,items, etc tables, which are limited to a one-to-one mapping to underlying MARC data
136 =item 2. as raw MARC in the Zebra index and storage engine
138 =item 3. as raw MARC the biblioitems.marc and biblioitems.marcxml
142 In the 3.0 version of Koha, the authoritative record-level information is in biblioitems.marcxml
144 Because the data isn't completely normalized there's a chance for information to get out of sync. The design choice to go with a un-normalized schema was driven by performance and stability concerns. However, if this occur, it can be considered as a bug : The API is (or should be) complete & the only entry point for all biblio/items managements.
148 =item 1. Compared with MySQL, Zebra is slow to update an index for small data changes -- especially for proc-intensive operations like circulation
150 =item 2. Zebra's index has been known to crash and a backup of the data is necessary to rebuild it in such cases
154 Because of this design choice, the process of managing storage and editing is a bit convoluted. Historically, Biblio.pm's grown to an unmanagable size and as a result we have several types of functions currently:
158 =item 1. Add*/Mod*/Del*/ - high-level external functions suitable for being called from external scripts to manage the collection
160 =item 2. _koha_* - low-level internal functions for managing the koha tables
162 =item 3. Marc management function : as the MARC record is stored in biblioitems.marc(xml), some subs dedicated to it's management are in this package. They should be used only internally by Biblio.pm, the only official entry points being AddBiblio, AddItem, ModBiblio, ModItem.
164 =item 4. Zebra functions used to update the Zebra index
166 =item 5. internal helper functions such as char_decode, checkitems, etc. Some of these probably belong in Koha.pm
170 The MARC record (in biblioitems.marcxml) contains the complete marc record, including items. It also contains the biblionumber. That is the reason why it is not stored directly by AddBiblio, with all other fields . To save a biblio, we need to :
174 =item 1. save datas in biblio and biblioitems table, that gives us a biblionumber and a biblioitemnumber
176 =item 2. add the biblionumber and biblioitemnumber into the MARC records
178 =item 3. save the marc record
182 When dealing with items, we must :
186 =item 1. save the item in items table, that gives us an itemnumber
188 =item 2. add the itemnumber to the item MARC field
190 =item 3. overwrite the MARC record (with the added item) into biblioitems.marc(xml)
192 When modifying a biblio or an item, the behaviour is quite similar.
196 =head1 EXPORTED FUNCTIONS
202 ($biblionumber,$biblioitemnumber) = AddBiblio($record,$frameworkcode);
203 Exported function (core API) for adding a new biblio to koha.
210 my ( $record, $frameworkcode ) = @_;
211 my ($biblionumber,$biblioitemnumber,$error);
212 my $dbh = C4::Context->dbh;
213 # transform the data into koha-table style data
214 my $olddata = TransformMarcToKoha( $dbh, $record, $frameworkcode );
215 ($biblionumber,$error) = _koha_add_biblio( $dbh, $olddata, $frameworkcode );
216 $olddata->{'biblionumber'} = $biblionumber;
217 ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $olddata );
219 _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
222 $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
224 &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$biblionumber,"biblio")
225 if C4::Context->preference("CataloguingLog");
227 return ( $biblionumber, $biblioitemnumber );
230 =head2 AddBiblioAndItems
234 ($biblionumber,$biblioitemnumber, $itemnumber_ref, $error_ref) = AddBiblioAndItems($record, $frameworkcode);
238 Efficiently add a biblio record and create item records from its
239 embedded item fields. This routine is suitable for batch jobs.
241 The goal of this API is to have a similar effect to using AddBiblio
242 and AddItems in succession, but without inefficient repeated
243 parsing of the MARC XML bib record.
245 One functional difference is that the duplicate item barcode
246 check is implemented in this API, instead of relying on
247 the caller to do it, like AddItem does.
249 This function returns the biblionumber and biblioitemnumber of the
250 new bib, an arrayref of new itemsnumbers, and an arrayref of item
251 errors encountered during the processing. Each entry in the errors
252 list is a hashref containing the following keys:
258 Sequence number of original item tag in the MARC record.
262 Item barcode, provide to assist in the construction of
263 useful error messages.
265 =item error_condition
267 Code representing the error condition. Can be 'duplicate_barcode',
268 'invalid_homebranch', or 'invalid_holdingbranch'.
270 =item error_information
272 Additional information appropriate to the error condition.
278 sub AddBiblioAndItems {
279 my ( $record, $frameworkcode ) = @_;
280 my ($biblionumber,$biblioitemnumber,$error);
281 my @itemnumbers = ();
283 my $dbh = C4::Context->dbh;
285 # transform the data into koha-table style data
286 # FIXME - this paragraph copied from AddBiblio
287 my $olddata = TransformMarcToKoha( $dbh, $record, $frameworkcode );
288 ($biblionumber,$error) = _koha_add_biblio( $dbh, $olddata, $frameworkcode );
289 $olddata->{'biblionumber'} = $biblionumber;
290 ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $olddata );
292 # FIXME - this paragraph copied from AddBiblio
293 _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
295 # now we loop through the item tags and start creating items
296 my @bad_item_fields = ();
297 my ($itemtag, $itemsubfield) = &GetMarcFromKohaField("items.itemnumber",'');
298 my $item_sequence_num = 0;
299 ITEMFIELD: foreach my $item_field ($record->field($itemtag)) {
300 $item_sequence_num++;
301 # we take the item field and stick it into a new
302 # MARC record -- this is required so far because (FIXME)
303 # TransformMarcToKoha requires a MARC::Record, not a MARC::Field
304 # and there is no TransformMarcFieldToKoha
305 my $temp_item_marc = MARC::Record->new();
306 $temp_item_marc->append_fields($item_field);
308 # add biblionumber and biblioitemnumber
309 my $item = TransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' );
310 $item->{'biblionumber'} = $biblionumber;
311 $item->{'biblioitemnumber'} = $biblioitemnumber;
313 # check for duplicate barcode
314 my %item_errors = CheckItemPreSave($item);
316 push @errors, _repack_item_errors($item_sequence_num, $item, \%item_errors);
317 push @bad_item_fields, $item_field;
320 my $duplicate_barcode = exists($item->{'barcode'}) && GetItemnumberFromBarcode($item->{'barcode'});
321 if ($duplicate_barcode) {
322 warn "ERROR: cannot add item $item->{'barcode'} for biblio $biblionumber: duplicate barcode\n";
325 # Make sure item statuses are set to 0 if empty or NULL in both the item and the MARC
326 for ('notforloan', 'damaged','itemlost','wthdrawn') {
327 if (!$item->{$_} or $item->{$_} eq "") {
329 &MARCitemchange( $temp_item_marc, "items.$_", 0 );
333 # FIXME - dateaccessioned stuff copied from AddItem
334 if ( !$item->{'dateaccessioned'} || $item->{'dateaccessioned'} eq '' ) {
337 my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
342 "$year-" . sprintf( "%0.2d", $mon ) . "-" . sprintf( "%0.2d", $mday );
343 $item->{'dateaccessioned'} = $date;
344 &MARCitemchange( $temp_item_marc, "items.dateaccessioned", $date );
347 my ( $itemnumber, $error ) = &_koha_new_items( $dbh, $item, $item->{barcode} );
348 warn $error if $error;
349 push @itemnumbers, $itemnumber; # FIXME not checking error
351 # FIXME - not copied from AddItem
352 # FIXME - AddItems equiv code about passing $sth to TransformKohaToMarcOneField is stupid
353 &MARCitemchange( $temp_item_marc, "items.itemnumber", $itemnumber );
355 &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item")
356 if C4::Context->preference("CataloguingLog");
358 $item_field->replace_with($temp_item_marc->field($itemtag));
361 # remove any MARC item fields for rejected items
362 foreach my $item_field (@bad_item_fields) {
363 $record->delete_field($item_field);
367 # FIXME - this paragraph copied from AddBiblio -- however, moved since
368 # since we need to create the items row and plug in the itemnumbers in the
370 $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
372 # FIXME - when using this API, do we log both bib and item add, or just
374 &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$biblionumber,"biblio")
375 if C4::Context->preference("CataloguingLog");
377 return ( $biblionumber, $biblioitemnumber, \@itemnumbers, \@errors);
381 sub _repack_item_errors {
382 my $item_sequence_num = shift;
383 my $item_ref = shift;
384 my $error_ref = shift;
386 my @repacked_errors = ();
388 foreach my $error_code (sort keys %{ $error_ref }) {
389 my $repacked_error = {};
390 $repacked_error->{'item_sequence'} = $item_sequence_num;
391 $repacked_error->{'item_barcode'} = exists($item_ref->{'barcode'}) ? $item_ref->{'barcode'} : '';
392 $repacked_error->{'error_code'} = $error_code;
393 $repacked_error->{'error_information'} = $error_ref->{$error_code};
394 push @repacked_errors, $repacked_error;
397 return @repacked_errors;
404 $biblionumber = AddItem( $record, $biblionumber)
405 Exported function (core API) for adding a new item to Koha
412 my ( $record, $biblionumber ) = @_;
413 my $dbh = C4::Context->dbh;
415 my $frameworkcode = GetFrameworkCode( $biblionumber );
416 my $item = &TransformMarcToKoha( $dbh, $record, $frameworkcode );
418 # needs old biblionumber and biblioitemnumber
419 $item->{'biblionumber'} = $biblionumber;
422 "SELECT biblioitemnumber,itemtype FROM biblioitems WHERE biblionumber=?"
424 $sth->execute( $item->{'biblionumber'} );
426 ( $item->{'biblioitemnumber'}, $itemtype ) = $sth->fetchrow;
429 "SELECT notforloan FROM itemtypes WHERE itemtype=?");
430 $sth->execute( C4::Context->preference('item-level_itypes') ? $item->{'itype'} : $itemtype );
431 my $notforloan = $sth->fetchrow;
432 ##Change the notforloan field if $notforloan found
433 if ( $notforloan > 0 ) {
434 $item->{'notforloan'} = $notforloan;
435 &MARCitemchange( $record, "items.notforloan", $notforloan );
437 if ( !$item->{'dateaccessioned'} || $item->{'dateaccessioned'} eq '' ) {
440 my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
445 "$year-" . sprintf( "%0.2d", $mon ) . "-" . sprintf( "%0.2d", $mday );
446 $item->{'dateaccessioned'} = $date;
447 &MARCitemchange( $record, "items.dateaccessioned", $date );
449 my ( $itemnumber, $error ) = &_koha_new_items( $dbh, $item, $item->{barcode} );
450 # add itemnumber to MARC::Record before adding the item.
451 $sth = $dbh->prepare(
452 "SELECT tagfield,tagsubfield
453 FROM marc_subfield_structure
454 WHERE frameworkcode=?
457 &TransformKohaToMarcOneField( $sth, $record, "items.itemnumber", $itemnumber,
461 &AddItemInMarc( $record, $item->{'biblionumber'},$frameworkcode );
463 &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item")
464 if C4::Context->preference("CataloguingLog");
466 return ($item->{biblionumber}, $item->{biblioitemnumber},$itemnumber);
471 ModBiblio( $record,$biblionumber,$frameworkcode);
472 Exported function (core API) to modify a biblio
477 my ( $record, $biblionumber, $frameworkcode ) = @_;
478 if (C4::Context->preference("CataloguingLog")) {
479 my $newrecord = GetMarcBiblio($biblionumber);
480 &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$biblionumber,"BEFORE=>".$newrecord->as_formatted);
483 my $dbh = C4::Context->dbh;
485 $frameworkcode = "" unless $frameworkcode;
487 # get the items before and append them to the biblio before updating the record, atm we just have the biblio
488 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
489 my $oldRecord = GetMarcBiblio( $biblionumber );
491 # parse each item, and, for an unknown reason, re-encode each subfield
492 # if you don't do that, the record will have encoding mixed
493 # and the biblio will be re-encoded.
494 # strange, I (Paul P.) searched more than 1 day to understand what happends
495 # but could only solve the problem this way...
496 my @fields = $oldRecord->field( $itemtag );
497 foreach my $fielditem ( @fields ){
499 foreach ($fielditem->subfields()) {
501 $field->add_subfields(Encode::encode('utf-8',$_->[0]) => Encode::encode('utf-8',$_->[1]));
503 $field = MARC::Field->new("$itemtag",'','',Encode::encode('utf-8',$_->[0]) => Encode::encode('utf-8',$_->[1]));
506 $record->append_fields($field);
509 # update biblionumber and biblioitemnumber in MARC
510 # FIXME - this is assuming a 1 to 1 relationship between
511 # biblios and biblioitems
512 my $sth = $dbh->prepare("select biblioitemnumber from biblioitems where biblionumber=?");
513 $sth->execute($biblionumber);
514 my ($biblioitemnumber) = $sth->fetchrow;
516 _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
518 # update the MARC record (that now contains biblio and items) with the new record data
519 &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
521 # load the koha-table data object
522 my $oldbiblio = TransformMarcToKoha( $dbh, $record, $frameworkcode );
524 # modify the other koha tables
525 _koha_modify_biblio( $dbh, $oldbiblio, $frameworkcode );
526 _koha_modify_biblioitem_nonmarc( $dbh, $oldbiblio );
534 Exported function (core API) for modifying an item in Koha.
541 my ( $record, $biblionumber, $itemnumber, $delete, $new_item_hashref )
545 &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$itemnumber,$record->as_formatted)
546 if C4::Context->preference("CataloguingLog");
548 my $dbh = C4::Context->dbh;
550 # if we have a MARC record, we're coming from cataloging and so
551 # we do the whole routine: update the MARC and zebra, then update the koha
554 my $frameworkcode = GetFrameworkCode( $biblionumber );
555 ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode );
556 my $olditem = TransformMarcToKoha( $dbh, $record, $frameworkcode,'items');
557 $olditem->{'biblionumber'} = $biblionumber;
558 my $sth = $dbh->prepare("select biblioitemnumber from biblioitems where biblionumber=?");
559 $sth->execute($biblionumber);
560 my ($biblioitemnumber) = $sth->fetchrow;
562 $olditem->{'biblioitemnumber'} = $biblioitemnumber;
563 _koha_modify_item( $dbh, $olditem );
564 return $biblionumber;
567 # otherwise, we're just looking to modify something quickly
568 # (like a status) so we just update the koha tables
569 elsif ($new_item_hashref) {
570 _koha_modify_item( $dbh, $new_item_hashref );
574 sub ModItemTransfer {
575 my ( $itemnumber, $frombranch, $tobranch ) = @_;
577 my $dbh = C4::Context->dbh;
579 #new entry in branchtransfers....
580 my $sth = $dbh->prepare(
581 "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
582 VALUES (?, ?, NOW(), ?)");
583 $sth->execute($itemnumber, $frombranch, $tobranch);
584 #update holdingbranch in items .....
586 "UPDATE items SET holdingbranch = ? WHERE items.itemnumber = ?");
587 $sth->execute($tobranch,$itemnumber);
588 &ModDateLastSeen($itemnumber);
589 $sth = $dbh->prepare(
590 "SELECT biblionumber FROM items WHERE itemnumber=?"
592 $sth->execute($itemnumber);
593 while ( my ( $biblionumber ) = $sth->fetchrow ) {
594 &ModItemInMarconefield( $biblionumber, $itemnumber,
595 'items.holdingbranch', $tobranch );
600 =head2 ModBiblioframework
602 ModBiblioframework($biblionumber,$frameworkcode);
603 Exported function to modify a biblio framework
607 sub ModBiblioframework {
608 my ( $biblionumber, $frameworkcode ) = @_;
609 my $dbh = C4::Context->dbh;
610 my $sth = $dbh->prepare(
611 "UPDATE biblio SET frameworkcode=? WHERE biblionumber=?"
613 $sth->execute($frameworkcode, $biblionumber);
617 =head2 ModItemInMarconefield
621 modify only 1 field in a MARC item (mainly used for holdingbranch, but could also be used for status modif - moving a book to "lost" on a long overdu for example)
622 &ModItemInMarconefield( $biblionumber, $itemnumber, $itemfield, $newvalue )
628 sub ModItemInMarconefield {
629 my ( $biblionumber, $itemnumber, $itemfield, $newvalue ) = @_;
630 my $dbh = C4::Context->dbh;
631 if ( !defined $newvalue ) {
635 my $record = GetMarcItem( $biblionumber, $itemnumber );
636 my ($tagfield, $tagsubfield) = GetMarcFromKohaField( $itemfield,'');
637 # FIXME - the condition is done this way because GetMarcFromKohaField
638 # returns (0, 0) if it can't field a MARC tag for the kohafield. However,
639 # some fields like items.wthdrawn are mapped to subfield $0, making the
640 # customary test of "if ($tagfield && $tagsubfield)" incorrect.
641 # GetMarcFromKohaField should probably be returning (undef, undef), making
642 # the correct test "if (defined $tagfield && defined $tagsubfield)", but
643 # this would be a large change and consequently deferred for after 3.0.
644 if (not(int($tagfield) == 0 && int($tagsubfield) == 0)) {
645 my $tag = $record->field($tagfield);
647 # my $tagsubs = $record->field($tagfield)->subfield($tagsubfield);
648 $tag->update( $tagsubfield => $newvalue );
649 $record->delete_field($tag);
650 $record->insert_fields_ordered($tag);
651 my $frameworkcode = GetFrameworkCode( $biblionumber );
652 &ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode );
661 &ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode )
668 my ( $ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
669 my $dbh = C4::Context->dbh;
671 # get complete MARC record & replace the item field by the new one
672 my $completeRecord = GetMarcBiblio($biblionumber);
673 my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
674 my $itemField = $ItemRecord->field($itemtag);
675 my @items = $completeRecord->field($itemtag);
677 if ($_->subfield($itemsubfield) eq $itemnumber) {
678 # $completeRecord->delete_field($_);
679 $_->replace_with($itemField);
683 my $sth = $dbh->prepare("UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
684 $sth->execute( $completeRecord->as_usmarc(), $completeRecord->as_xml_record(),$biblionumber );
686 ModZebra($biblionumber,"specialUpdate","biblioserver",$completeRecord);
689 =head2 ModDateLastSeen
691 &ModDateLastSeen($itemnum)
692 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking
693 C<$itemnum> is the item number
697 sub ModDateLastSeen {
699 my $dbh = C4::Context->dbh;
702 "UPDATE items SET itemlost=0,datelastseen = NOW() WHERE items.itemnumber = ?"
704 $sth->execute($itemnum);
711 my $error = &DelBiblio($dbh,$biblionumber);
712 Exported function (core API) for deleting a biblio in koha.
713 Deletes biblio record from Zebra and Koha tables (biblio,biblioitems,items)
714 Also backs it up to deleted* tables
715 Checks to make sure there are not issues on any of the items
717 C<$error> : undef unless an error occurs
724 my ( $biblionumber ) = @_;
725 my $dbh = C4::Context->dbh;
726 my $error; # for error handling
728 # First make sure this biblio has no items attached
729 my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber=?");
730 $sth->execute($biblionumber);
731 if (my $itemnumber = $sth->fetchrow){
732 # Fix this to use a status the template can understand
733 $error .= "This Biblio has items attached, please delete them first before deleting this biblio ";
736 return $error if $error;
738 # Delete in Zebra. Be careful NOT to move this line after _koha_delete_biblio
739 # for at least 2 reasons :
740 # - we need to read the biblio if NoZebra is set (to remove it from the indexes
741 # - if something goes wrong, the biblio may be deleted from Koha but not from zebra
742 # and we would have no way to remove it (except manually in zebra, but I bet it would be very hard to handle the problem)
743 ModZebra($biblionumber, "recordDelete", "biblioserver", undef);
745 # delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
748 "SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
749 $sth->execute($biblionumber);
750 while ( my $biblioitemnumber = $sth->fetchrow ) {
752 # delete this biblioitem
753 $error = _koha_delete_biblioitems( $dbh, $biblioitemnumber );
754 return $error if $error;
757 # delete biblio from Koha tables and save in deletedbiblio
758 # must do this *after* _koha_delete_biblioitems, otherwise
759 # delete cascade will prevent deletedbiblioitems rows
760 # from being generated by _koha_delete_biblioitems
761 $error = _koha_delete_biblio( $dbh, $biblionumber );
763 &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$biblionumber,"")
764 if C4::Context->preference("CataloguingLog");
772 DelItem( $biblionumber, $itemnumber );
773 Exported function (core API) for deleting an item record in Koha.
780 my ( $dbh, $biblionumber, $itemnumber ) = @_;
782 # check the item has no current issues
785 &_koha_delete_item( $dbh, $itemnumber );
787 # get the MARC record
788 my $record = GetMarcBiblio($biblionumber);
789 my $frameworkcode = GetFrameworkCode($biblionumber);
792 my $copy2deleted = $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
793 $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
795 #search item field code
796 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
797 my @fields = $record->field($itemtag);
799 # delete the item specified
800 foreach my $field (@fields) {
801 if ( $field->subfield($itemsubfield) eq $itemnumber ) {
802 $record->delete_field($field);
805 &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
806 &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$itemnumber,"item")
807 if C4::Context->preference("CataloguingLog");
810 =head2 CheckItemPreSave
814 my $item_ref = TransformMarcToKoha($marc, 'items');
816 my %errors = CheckItemPreSave($item_ref);
817 if (exists $errors{'duplicate_barcode'}) {
818 print "item has duplicate barcode: ", $errors{'duplicate_barcode'}, "\n";
819 } elsif (exists $errors{'invalid_homebranch'}) {
820 print "item has invalid home branch: ", $errors{'invalid_homebranch'}, "\n";
821 } elsif (exists $errors{'invalid_holdingbranch'}) {
822 print "item has invalid holding branch: ", $errors{'invalid_holdingbranch'}, "\n";
829 Given a hashref containing item fields, determine if it can be
830 inserted or updated in the database. Specifically, checks for
831 database integrity issues, and returns a hash containing any
832 of the following keys, if applicable.
836 =item duplicate_barcode
838 Barcode, if it duplicates one already found in the database.
840 =item invalid_homebranch
842 Home branch, if not defined in branches table.
844 =item invalid_holdingbranch
846 Holding branch, if not defined in branches table.
850 This function does NOT implement any policy-related checks,
851 e.g., whether current operator is allowed to save an
852 item that has a given branch code.
856 sub CheckItemPreSave {
857 my $item_ref = shift;
861 # check for duplicate barcode
862 if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) {
863 my $existing_itemnumber = GetItemnumberFromBarcode($item_ref->{'barcode'});
864 if ($existing_itemnumber) {
865 if (!exists $item_ref->{'itemnumber'} # new item
866 or $item_ref->{'itemnumber'} != $existing_itemnumber) { # existing item
867 $errors{'duplicate_barcode'} = $item_ref->{'barcode'};
872 # check for valid home branch
873 if (exists $item_ref->{'homebranch'} and defined $item_ref->{'homebranch'}) {
874 my $branch_name = GetBranchName($item_ref->{'homebranch'});
875 unless (defined $branch_name) {
876 # relies on fact that branches.branchname is a non-NULL column,
877 # so GetBranchName returns undef only if branch does not exist
878 $errors{'invalid_homebranch'} = $item_ref->{'homebranch'};
882 # check for valid holding branch
883 if (exists $item_ref->{'holdingbranch'} and defined $item_ref->{'holdingbranch'}) {
884 my $branch_name = GetBranchName($item_ref->{'holdingbranch'});
885 unless (defined $branch_name) {
886 # relies on fact that branches.branchname is a non-NULL column,
887 # so GetBranchName returns undef only if branch does not exist
888 $errors{'invalid_holdingbranch'} = $item_ref->{'holdingbranch'};
900 $data = &GetBiblioData($biblionumber);
901 Returns information about the book with the given biblionumber.
902 C<&GetBiblioData> returns a reference-to-hash. The keys are the fields in
903 the C<biblio> and C<biblioitems> tables in the
905 In addition, C<$data-E<gt>{subject}> is the list of the book's
906 subjects, separated by C<" , "> (space, comma, space).
907 If there are multiple biblioitems with the given biblionumber, only
908 the first one is considered.
916 my $dbh = C4::Context->dbh;
918 # my $query = C4::Context->preference('item-level_itypes') ?
919 # " SELECT * , biblioitems.notes AS bnotes, biblio.notes
921 # LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
922 # WHERE biblio.biblionumber = ?
923 # AND biblioitems.biblionumber = biblio.biblionumber
926 my $query = " SELECT * , biblioitems.notes AS bnotes, itemtypes.notforloan as bi_notforloan, biblio.notes
928 LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
929 LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
930 WHERE biblio.biblionumber = ?
931 AND biblioitems.biblionumber = biblio.biblionumber ";
933 my $sth = $dbh->prepare($query);
934 $sth->execute($bibnum);
936 $data = $sth->fetchrow_hashref;
940 } # sub GetBiblioData
947 @results = &GetItemsInfo($biblionumber, $type);
949 Returns information about books with the given biblionumber.
951 C<$type> may be either C<intra> or anything else. If it is not set to
952 C<intra>, then the search will exclude lost, very overdue, and
955 C<&GetItemsInfo> returns a list of references-to-hash. Each element
956 contains a number of keys. Most of them are table items from the
957 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
958 Koha database. Other keys include:
962 =item C<$data-E<gt>{branchname}>
964 The name (not the code) of the branch to which the book belongs.
966 =item C<$data-E<gt>{datelastseen}>
968 This is simply C<items.datelastseen>, except that while the date is
969 stored in YYYY-MM-DD format in the database, here it is converted to
970 DD/MM/YYYY format. A NULL date is returned as C<//>.
972 =item C<$data-E<gt>{datedue}>
974 =item C<$data-E<gt>{class}>
976 This is the concatenation of C<biblioitems.classification>, the book's
977 Dewey code, and C<biblioitems.subclass>.
979 =item C<$data-E<gt>{ocount}>
981 I think this is the number of copies of the book available.
983 =item C<$data-E<gt>{order}>
985 If this is set, it is set to C<One Order>.
994 my ( $biblionumber, $type ) = @_;
995 my $dbh = C4::Context->dbh;
996 my $query = "SELECT *,items.notforloan as itemnotforloan
998 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
999 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber";
1000 $query .= (C4::Context->preference('item-level_itypes')) ?
1001 " LEFT JOIN itemtypes on items.itype = itemtypes.itemtype "
1002 : " LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype ";
1003 $query .= "WHERE items.biblionumber = ? ORDER BY items.dateaccessioned desc" ;
1004 my $sth = $dbh->prepare($query);
1005 $sth->execute($biblionumber);
1008 my ( $date_due, $count_reserves );
1010 my $isth = $dbh->prepare(
1011 "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
1012 FROM issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
1013 WHERE itemnumber = ?
1014 AND returndate IS NULL"
1016 while ( my $data = $sth->fetchrow_hashref ) {
1018 $isth->execute( $data->{'itemnumber'} );
1019 if ( my $idata = $isth->fetchrow_hashref ) {
1020 $data->{borrowernumber} = $idata->{borrowernumber};
1021 $data->{cardnumber} = $idata->{cardnumber};
1022 $data->{surname} = $idata->{surname};
1023 $data->{firstname} = $idata->{firstname};
1024 $datedue = $idata->{'date_due'};
1025 if (C4::Context->preference("IndependantBranches")){
1026 my $userenv = C4::Context->userenv;
1027 if ( ($userenv) && ( $userenv->{flags} != 1 ) ) {
1028 $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
1032 if ( $datedue eq '' ) {
1033 my ( $restype, $reserves ) =
1034 C4::Reserves::CheckReserves( $data->{'itemnumber'} );
1036 $count_reserves = $restype;
1041 #get branch information.....
1042 my $bsth = $dbh->prepare(
1043 "SELECT * FROM branches WHERE branchcode = ?
1046 $bsth->execute( $data->{'holdingbranch'} );
1047 if ( my $bdata = $bsth->fetchrow_hashref ) {
1048 $data->{'branchname'} = $bdata->{'branchname'};
1050 $data->{'datedue'} = $datedue;
1051 $data->{'count_reserves'} = $count_reserves;
1053 # get notforloan complete status if applicable
1054 my $sthnflstatus = $dbh->prepare(
1055 'SELECT authorised_value
1056 FROM marc_subfield_structure
1057 WHERE kohafield="items.notforloan"
1061 $sthnflstatus->execute;
1062 my ($authorised_valuecode) = $sthnflstatus->fetchrow;
1063 if ($authorised_valuecode) {
1064 $sthnflstatus = $dbh->prepare(
1065 "SELECT lib FROM authorised_values
1067 AND authorised_value=?"
1069 $sthnflstatus->execute( $authorised_valuecode,
1070 $data->{itemnotforloan} );
1071 my ($lib) = $sthnflstatus->fetchrow;
1072 $data->{notforloan} = $lib;
1075 # my stack procedures
1076 my $stackstatus = $dbh->prepare(
1077 'SELECT authorised_value
1078 FROM marc_subfield_structure
1079 WHERE kohafield="items.stack"
1082 $stackstatus->execute;
1084 ($authorised_valuecode) = $stackstatus->fetchrow;
1085 if ($authorised_valuecode) {
1086 $stackstatus = $dbh->prepare(
1088 FROM authorised_values
1090 AND authorised_value=?
1093 $stackstatus->execute( $authorised_valuecode, $data->{stack} );
1094 my ($lib) = $stackstatus->fetchrow;
1095 $data->{stack} = $lib;
1097 # Find the last 3 people who borrowed this item.
1098 my $sth2 = $dbh->prepare("SELECT * FROM issues,borrowers
1099 WHERE itemnumber = ?
1100 AND issues.borrowernumber = borrowers.borrowernumber
1101 AND returndate IS NOT NULL LIMIT 3");
1102 $sth2->execute($data->{'itemnumber'});
1104 while (my $data2 = $sth2->fetchrow_hashref()) {
1105 $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
1106 $data->{"card$ii"} = $data2->{'cardnumber'} if $data2->{'cardnumber'};
1107 $data->{"borrower$ii"} = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
1111 $results[$i] = $data;
1119 =head2 getitemstatus
1123 $itemstatushash = &getitemstatus($fwkcode);
1124 returns information about status.
1125 Can be MARC dependant.
1126 fwkcode is optional.
1127 But basically could be can be loan or not
1128 Create a status selector with the following code
1130 =head3 in PERL SCRIPT
1132 my $itemstatushash = getitemstatus;
1134 foreach my $thisstatus (keys %$itemstatushash) {
1135 my %row =(value => $thisstatus,
1136 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
1138 push @itemstatusloop, \%row;
1140 $template->param(statusloop=>\@itemstatusloop);
1145 <select name="statusloop">
1146 <option value="">Default</option>
1147 <!-- TMPL_LOOP name="statusloop" -->
1148 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
1156 # returns a reference to a hash of references to status...
1159 my $dbh = C4::Context->dbh;
1161 $fwk = '' unless ($fwk);
1162 my ( $tag, $subfield ) =
1163 GetMarcFromKohaField( "items.notforloan", $fwk );
1164 if ( $tag and $subfield ) {
1167 "SELECT authorised_value
1168 FROM marc_subfield_structure
1174 $sth->execute( $tag, $subfield, $fwk );
1175 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
1178 "SELECT authorised_value,lib
1179 FROM authorised_values
1184 $authvalsth->execute($authorisedvaluecat);
1185 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
1186 $itemstatus{$authorisedvalue} = $lib;
1188 $authvalsth->finish;
1189 return \%itemstatus;
1202 $itemstatus{"1"} = "Not For Loan";
1203 return \%itemstatus;
1206 =head2 getitemlocation
1210 $itemlochash = &getitemlocation($fwk);
1211 returns informations about location.
1212 where fwk stands for an optional framework code.
1213 Create a location selector with the following code
1215 =head3 in PERL SCRIPT
1217 my $itemlochash = getitemlocation;
1219 foreach my $thisloc (keys %$itemlochash) {
1220 my $selected = 1 if $thisbranch eq $branch;
1221 my %row =(locval => $thisloc,
1222 selected => $selected,
1223 locname => $itemlochash->{$thisloc},
1225 push @itemlocloop, \%row;
1227 $template->param(itemlocationloop => \@itemlocloop);
1231 <select name="location">
1232 <option value="">Default</option>
1233 <!-- TMPL_LOOP name="itemlocationloop" -->
1234 <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
1242 sub GetItemLocation {
1244 # returns a reference to a hash of references to location...
1247 my $dbh = C4::Context->dbh;
1249 $fwk = '' unless ($fwk);
1250 my ( $tag, $subfield ) =
1251 GetMarcFromKohaField( "items.location", $fwk );
1252 if ( $tag and $subfield ) {
1255 "SELECT authorised_value
1256 FROM marc_subfield_structure
1259 AND frameworkcode=?"
1261 $sth->execute( $tag, $subfield, $fwk );
1262 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
1265 "SELECT authorised_value,lib
1266 FROM authorised_values
1270 $authvalsth->execute($authorisedvaluecat);
1271 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
1272 $itemlocation{$authorisedvalue} = $lib;
1274 $authvalsth->finish;
1275 return \%itemlocation;
1288 $itemlocation{"1"} = "Not For Loan";
1289 return \%itemlocation;
1294 $items = GetLostItems($where,$orderby);
1296 This function get the items lost into C<$items>.
1301 C<$where> is a hashref. it containts a field of the items table as key
1302 and the value to match as value.
1303 C<$orderby> is a field of the items table.
1306 C<$items> is a reference to an array full of hasref which keys are items' table column.
1308 =item usage in the perl script:
1311 $where{barcode} = 0001548;
1312 my $items = GetLostItems( \%where, "homebranch" );
1313 $template->param(itemsloop => $items);
1320 # Getting input args.
1322 my $orderby = shift;
1323 my $dbh = C4::Context->dbh;
1328 WHERE itemlost IS NOT NULL
1331 foreach my $key (keys %$where) {
1332 $query .= " AND " . $key . " LIKE '%" . $where->{$key} . "%'";
1334 $query .= " ORDER BY ".$orderby if defined $orderby;
1336 my $sth = $dbh->prepare($query);
1339 while ( my $row = $sth->fetchrow_hashref ){
1345 =head2 GetItemsForInventory
1347 $itemlist = GetItemsForInventory($minlocation,$maxlocation,$datelastseen,$offset,$size)
1349 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
1351 The sub returns a list of hashes, containing itemnumber, author, title, barcode & item callnumber.
1352 It is ordered by callnumber,title.
1354 The minlocation & maxlocation parameters are used to specify a range of item callnumbers
1355 the datelastseen can be used to specify that you want to see items not seen since a past date only.
1356 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
1360 sub GetItemsForInventory {
1361 my ( $minlocation, $maxlocation,$location, $datelastseen, $branch, $offset, $size ) = @_;
1362 my $dbh = C4::Context->dbh;
1364 if ($datelastseen) {
1365 $datelastseen=format_date_in_iso($datelastseen);
1367 "SELECT itemnumber,barcode,itemcallnumber,title,author,biblio.biblionumber,datelastseen
1369 LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber
1370 WHERE itemcallnumber>= ?
1371 AND itemcallnumber <=?
1372 AND (datelastseen< ? OR datelastseen IS NULL)";
1373 $query.= " AND items.location=".$dbh->quote($location) if $location;
1374 $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
1375 $query .= " ORDER BY itemcallnumber,title";
1376 $sth = $dbh->prepare($query);
1377 $sth->execute( $minlocation, $maxlocation, $datelastseen );
1381 SELECT itemnumber,barcode,itemcallnumber,biblio.biblionumber,title,author,datelastseen
1383 LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber
1384 WHERE itemcallnumber>= ?
1385 AND itemcallnumber <=?";
1386 $query.= " AND items.location=".$dbh->quote($location) if $location;
1387 $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
1388 $query .= " ORDER BY itemcallnumber,title";
1389 $sth = $dbh->prepare($query);
1390 $sth->execute( $minlocation, $maxlocation );
1393 while ( my $row = $sth->fetchrow_hashref ) {
1394 $offset-- if ($offset);
1395 $row->{datelastseen}=format_date($row->{datelastseen});
1396 if ( ( !$offset ) && $size ) {
1397 push @results, $row;
1404 =head2 &GetBiblioItemData
1408 $itemdata = &GetBiblioItemData($biblioitemnumber);
1410 Looks up the biblioitem with the given biblioitemnumber. Returns a
1411 reference-to-hash. The keys are the fields from the C<biblio>,
1412 C<biblioitems>, and C<itemtypes> tables in the Koha database, except
1413 that C<biblioitems.notes> is given as C<$itemdata-E<gt>{bnotes}>.
1420 sub GetBiblioItemData {
1421 my ($biblioitemnumber) = @_;
1422 my $dbh = C4::Context->dbh;
1423 my $query = "SELECT *,biblioitems.notes AS bnotes
1424 FROM biblio, biblioitems ";
1425 unless(C4::Context->preference('item-level_itypes')) {
1426 $query .= "LEFT JOIN itemtypes on biblioitems.itemtype=itemtypes.itemtype ";
1428 $query .= " WHERE biblio.biblionumber = biblioitems.biblionumber
1429 AND biblioitemnumber = ? ";
1430 my $sth = $dbh->prepare($query);
1432 $sth->execute($biblioitemnumber);
1433 $data = $sth->fetchrow_hashref;
1436 } # sub &GetBiblioItemData
1438 =head2 GetItemnumberFromBarcode
1442 $result = GetItemnumberFromBarcode($barcode);
1448 sub GetItemnumberFromBarcode {
1450 my $dbh = C4::Context->dbh;
1453 $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
1454 $rq->execute($barcode);
1455 my ($result) = $rq->fetchrow;
1459 =head2 GetBiblioItemByBiblioNumber
1463 NOTE : This function has been copy/paste from C4/Biblio.pm from head before zebra integration.
1469 sub GetBiblioItemByBiblioNumber {
1470 my ($biblionumber) = @_;
1471 my $dbh = C4::Context->dbh;
1472 my $sth = $dbh->prepare("Select * FROM biblioitems WHERE biblionumber = ?");
1476 $sth->execute($biblionumber);
1478 while ( my $data = $sth->fetchrow_hashref ) {
1479 push @results, $data;
1486 =head2 GetBiblioFromItemNumber
1490 $item = &GetBiblioFromItemNumber($itemnumber,$barcode);
1492 Looks up the item with the given itemnumber. if undef, try the barcode.
1494 C<&itemnodata> returns a reference-to-hash whose keys are the fields
1495 from the C<biblio>, C<biblioitems>, and C<items> tables in the Koha
1503 sub GetBiblioFromItemNumber {
1504 my ( $itemnumber, $barcode ) = @_;
1505 my $dbh = C4::Context->dbh;
1508 $sth=$dbh->prepare( "SELECT * FROM items
1509 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1510 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1511 WHERE items.itemnumber = ?") ;
1512 $sth->execute($itemnumber);
1514 $sth=$dbh->prepare( "SELECT * FROM items
1515 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1516 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1517 WHERE items.barcode = ?") ;
1518 $sth->execute($barcode);
1520 my $data = $sth->fetchrow_hashref;
1529 ( $count, @results ) = &GetBiblio($biblionumber);
1536 my ($biblionumber) = @_;
1537 my $dbh = C4::Context->dbh;
1538 my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber = ?");
1541 $sth->execute($biblionumber);
1542 while ( my $data = $sth->fetchrow_hashref ) {
1543 $results[$count] = $data;
1547 return ( $count, @results );
1554 $data = &GetItem($itemnumber,$barcode);
1556 return Item information, for a given itemnumber or barcode
1563 my ($itemnumber,$barcode) = @_;
1564 my $dbh = C4::Context->dbh;
1566 my $sth = $dbh->prepare("
1568 WHERE itemnumber = ?");
1569 $sth->execute($itemnumber);
1570 my $data = $sth->fetchrow_hashref;
1573 my $sth = $dbh->prepare("
1577 $sth->execute($barcode);
1578 my $data = $sth->fetchrow_hashref;
1583 =head2 get_itemnumbers_of
1587 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1589 Given a list of biblionumbers, return the list of corresponding itemnumbers
1590 for each biblionumber.
1592 Return a reference on a hash where keys are biblionumbers and values are
1593 references on array of itemnumbers.
1599 sub get_itemnumbers_of {
1600 my @biblionumbers = @_;
1602 my $dbh = C4::Context->dbh;
1608 WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1610 my $sth = $dbh->prepare($query);
1611 $sth->execute(@biblionumbers);
1615 while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1616 push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1619 return \%itemnumbers_of;
1622 =head2 GetItemInfosOf
1626 GetItemInfosOf(@itemnumbers);
1632 sub GetItemInfosOf {
1633 my @itemnumbers = @_;
1638 WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1640 return get_infos_of( $query, 'itemnumber' );
1643 =head2 GetItemsByBiblioitemnumber
1647 GetItemsByBiblioitemnumber($biblioitemnumber);
1649 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
1650 Called by moredetail.pl
1656 sub GetItemsByBiblioitemnumber {
1657 my ( $bibitem ) = @_;
1658 my $dbh = C4::Context->dbh;
1659 my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
1660 # Get all items attached to a biblioitem
1663 $sth->execute($bibitem) || die $sth->errstr;
1664 while ( my $data = $sth->fetchrow_hashref ) {
1665 # Foreach item, get circulation information
1666 my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
1667 WHERE itemnumber = ?
1668 AND returndate is NULL
1669 AND issues.borrowernumber = borrowers.borrowernumber"
1671 $sth2->execute( $data->{'itemnumber'} );
1672 if ( my $data2 = $sth2->fetchrow_hashref ) {
1673 # if item is out, set the due date and who it is out too
1674 $data->{'date_due'} = $data2->{'date_due'};
1675 $data->{'cardnumber'} = $data2->{'cardnumber'};
1676 $data->{'borrowernumber'} = $data2->{'borrowernumber'};
1679 # set date_due to blank, so in the template we check itemlost, and wthdrawn
1680 $data->{'date_due'} = '';
1683 # Find the last 3 people who borrowed this item.
1684 my $query2 = "SELECT * FROM issues, borrowers WHERE itemnumber = ?
1685 AND issues.borrowernumber = borrowers.borrowernumber
1686 AND returndate is not NULL
1687 ORDER BY returndate desc,timestamp desc LIMIT 3";
1688 $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1689 $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
1691 while ( my $data2 = $sth2->fetchrow_hashref ) {
1692 $data->{"timestamp$i2"} = $data2->{'timestamp'};
1693 $data->{"card$i2"} = $data2->{'cardnumber'};
1694 $data->{"borrower$i2"} = $data2->{'borrowernumber'};
1698 push(@results,$data);
1705 =head2 GetBiblioItemInfosOf
1709 GetBiblioItemInfosOf(@biblioitemnumbers);
1715 sub GetBiblioItemInfosOf {
1716 my @biblioitemnumbers = @_;
1719 SELECT biblioitemnumber,
1723 WHERE biblioitemnumber IN (' . join( ',', @biblioitemnumbers ) . ')
1725 return get_infos_of( $query, 'biblioitemnumber' );
1728 =head1 FUNCTIONS FOR HANDLING MARC MANAGEMENT
1730 =head2 GetMarcStructure
1734 $res = GetMarcStructure($forlibrarian,$frameworkcode);
1736 Returns a reference to a big hash of hash, with the Marc structure for the given frameworkcode
1737 $forlibrarian :if set to 1, the MARC descriptions are the librarians ones, otherwise it's the public (OPAC) ones
1738 $frameworkcode : the framework code to read
1744 sub GetMarcStructure {
1745 my ( $forlibrarian, $frameworkcode ) = @_;
1746 my $dbh=C4::Context->dbh;
1747 $frameworkcode = "" unless $frameworkcode;
1749 my $libfield = ( $forlibrarian eq 1 ) ? 'liblibrarian' : 'libopac';
1751 # check that framework exists
1754 "SELECT COUNT(*) FROM marc_tag_structure WHERE frameworkcode=?");
1755 $sth->execute($frameworkcode);
1756 my ($total) = $sth->fetchrow;
1757 $frameworkcode = "" unless ( $total > 0 );
1760 "SELECT tagfield,liblibrarian,libopac,mandatory,repeatable
1761 FROM marc_tag_structure
1762 WHERE frameworkcode=?
1765 $sth->execute($frameworkcode);
1766 my ( $liblibrarian, $libopac, $tag, $res, $tab, $mandatory, $repeatable );
1768 while ( ( $tag, $liblibrarian, $libopac, $mandatory, $repeatable ) =
1771 $res->{$tag}->{lib} =
1772 ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
1773 $res->{$tab}->{tab} = "";
1774 $res->{$tag}->{mandatory} = $mandatory;
1775 $res->{$tag}->{repeatable} = $repeatable;
1780 "SELECT tagfield,tagsubfield,liblibrarian,libopac,tab,mandatory,repeatable,authorised_value,authtypecode,value_builder,kohafield,seealso,hidden,isurl,link,defaultvalue
1781 FROM marc_subfield_structure
1782 WHERE frameworkcode=?
1783 ORDER BY tagfield,tagsubfield
1787 $sth->execute($frameworkcode);
1790 my $authorised_value;
1802 $tag, $subfield, $liblibrarian,
1804 $mandatory, $repeatable, $authorised_value,
1805 $authtypecode, $value_builder, $kohafield,
1806 $seealso, $hidden, $isurl,
1812 $res->{$tag}->{$subfield}->{lib} =
1813 ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
1814 $res->{$tag}->{$subfield}->{tab} = $tab;
1815 $res->{$tag}->{$subfield}->{mandatory} = $mandatory;
1816 $res->{$tag}->{$subfield}->{repeatable} = $repeatable;
1817 $res->{$tag}->{$subfield}->{authorised_value} = $authorised_value;
1818 $res->{$tag}->{$subfield}->{authtypecode} = $authtypecode;
1819 $res->{$tag}->{$subfield}->{value_builder} = $value_builder;
1820 $res->{$tag}->{$subfield}->{kohafield} = $kohafield;
1821 $res->{$tag}->{$subfield}->{seealso} = $seealso;
1822 $res->{$tag}->{$subfield}->{hidden} = $hidden;
1823 $res->{$tag}->{$subfield}->{isurl} = $isurl;
1824 $res->{$tag}->{$subfield}->{'link'} = $link;
1825 $res->{$tag}->{$subfield}->{defaultvalue} = $defaultvalue;
1830 =head2 GetUsedMarcStructure
1832 the same function as GetMarcStructure expcet it just take field
1833 in tab 0-9. (used field)
1835 my $results = GetUsedMarcStructure($frameworkcode);
1837 L<$results> is a ref to an array which each case containts a ref
1838 to a hash which each keys is the columns from marc_subfield_structure
1840 L<$frameworkcode> is the framework code.
1844 sub GetUsedMarcStructure($){
1845 my $frameworkcode = shift || '';
1846 my $dbh = C4::Context->dbh;
1849 FROM marc_subfield_structure
1851 AND frameworkcode = ?
1854 my $sth = $dbh->prepare($query);
1855 $sth->execute($frameworkcode);
1856 while (my $row = $sth->fetchrow_hashref){
1862 =head2 GetMarcFromKohaField
1866 ($MARCfield,$MARCsubfield)=GetMarcFromKohaField($kohafield,$frameworkcode);
1867 Returns the MARC fields & subfields mapped to the koha field
1868 for the given frameworkcode
1874 sub GetMarcFromKohaField {
1875 my ( $kohafield, $frameworkcode ) = @_;
1876 return 0, 0 unless $kohafield;
1877 my $relations = C4::Context->marcfromkohafield;
1879 $relations->{$frameworkcode}->{$kohafield}->[0],
1880 $relations->{$frameworkcode}->{$kohafield}->[1]
1884 =head2 GetMarcBiblio
1888 Returns MARC::Record of the biblionumber passed in parameter.
1889 the marc record contains both biblio & item datas
1896 my $biblionumber = shift;
1897 my $dbh = C4::Context->dbh;
1899 $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
1900 $sth->execute($biblionumber);
1901 my ($marcxml) = $sth->fetchrow;
1902 MARC::File::XML->default_record_format(C4::Context->preference('marcflavour'));
1903 $marcxml =~ s/\x1e//g;
1904 $marcxml =~ s/\x1f//g;
1905 $marcxml =~ s/\x1d//g;
1906 $marcxml =~ s/\x0f//g;
1907 $marcxml =~ s/\x0c//g;
1909 my $record = MARC::Record->new();
1911 $record = eval {MARC::Record::new_from_xml( $marcxml, "utf8", C4::Context->preference('marcflavour'))};
1913 # $record = MARC::Record::new_from_usmarc( $marc) if $marc;
1924 my $marcxml = GetXmlBiblio($biblionumber);
1926 Returns biblioitems.marcxml of the biblionumber passed in parameter.
1927 The XML contains both biblio & item datas
1934 my ( $biblionumber ) = @_;
1935 my $dbh = C4::Context->dbh;
1937 $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
1938 $sth->execute($biblionumber);
1939 my ($marcxml) = $sth->fetchrow;
1943 =head2 GetAuthorisedValueDesc
1947 my $subfieldvalue =get_authorised_value_desc(
1948 $tag, $subf[$i][0],$subf[$i][1], '', $taglib, $category);
1949 Retrieve the complete description for a given authorised value.
1951 Now takes $category and $value pair too.
1952 my $auth_value_desc =GetAuthorisedValueDesc(
1953 '','', 'DVD' ,'','','CCODE');
1959 sub GetAuthorisedValueDesc {
1960 my ( $tag, $subfield, $value, $framework, $tagslib, $category ) = @_;
1961 my $dbh = C4::Context->dbh;
1965 if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
1966 return C4::Branch::GetBranchName($value);
1970 if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) {
1971 return getitemtypeinfo($value)->{description};
1974 #---- "true" authorized value
1975 $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'}
1978 if ( $category ne "" ) {
1981 "SELECT lib FROM authorised_values WHERE category = ? AND authorised_value = ?"
1983 $sth->execute( $category, $value );
1984 my $data = $sth->fetchrow_hashref;
1985 return $data->{'lib'};
1988 return $value; # if nothing is found return the original value
1996 Returns MARC::Record of the item passed in parameter.
2003 my ( $biblionumber, $itemnumber ) = @_;
2005 # GetMarcItem has been revised so that it does the following:
2006 # 1. Gets the item information from the items table.
2007 # 2. Converts it to a MARC field for storage in the bib record.
2009 # The previous behavior was:
2010 # 1. Get the bib record.
2011 # 2. Return the MARC tag corresponding to the item record.
2013 # The difference is that one treats the items row as authoritative,
2014 # while the other treats the MARC representation as authoritative
2015 # under certain circumstances.
2019 # As of 2007-11-27, this change hopefully does not introduce
2020 # any bugs. However, it does mean that for code that uses
2021 # ModItemInMarconefield to update one subfield (corresponding to
2022 # an items column) is now less efficient.
2024 # The API needs to be shifted to the following:
2025 # 1. User updates items record.
2026 # 2. Linked bib is sent for indexing.
2028 # The missing step 1.5 is updating the item tag in the bib MARC record
2029 # so that the indexes are updated. Depending on performance considerations,
2030 # this may ultimately mean of of the following:
2031 # a. MARC field for item is updated right away.
2032 # b. MARC field for item is updated only as part of indexing.
2033 # c. MARC field for item is never actually stored in bib record; instead
2034 # it is generated only when needed for indexing, item export, and
2035 # (maybe) OPAC display.
2038 my $itemrecord = GetItem($itemnumber);
2040 # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
2041 # Also, don't emit a subfield if the underlying field is blank.
2042 my $mungeditem = { map { $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : () } keys %{ $itemrecord } };
2044 my $itemmarc = TransformKohaToMarc($mungeditem);
2055 $marcnotesarray = GetMarcNotes( $record, $marcflavour );
2056 Get all notes from the MARC record and returns them in an array.
2057 The note are stored in differents places depending on MARC flavour
2064 my ( $record, $marcflavour ) = @_;
2066 if ( $marcflavour eq "MARC21" ) {
2069 else { # assume unimarc if not marc21
2076 foreach my $field ( $record->field($scope) ) {
2077 my $value = $field->as_string();
2078 if ( $note ne "" ) {
2079 $marcnote = { marcnote => $note, };
2080 push @marcnotes, $marcnote;
2083 if ( $note ne $value ) {
2084 $note = $note . " " . $value;
2089 $marcnote = { marcnote => $note };
2090 push @marcnotes, $marcnote; #load last tag into array
2093 } # end GetMarcNotes
2095 =head2 GetMarcSubjects
2099 $marcsubjcts = GetMarcSubjects($record,$marcflavour);
2100 Get all subjects from the MARC record and returns them in an array.
2101 The subjects are stored in differents places depending on MARC flavour
2107 sub GetMarcSubjects {
2108 my ( $record, $marcflavour ) = @_;
2109 my ( $mintag, $maxtag );
2110 if ( $marcflavour eq "MARC21" ) {
2114 else { # assume unimarc if not marc21
2124 foreach my $field ( $record->field('6..' )) {
2125 next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
2127 my @subfields = $field->subfields();
2130 # if there is an authority link, build the link with an= subfield9
2131 my $subfield9 = $field->subfield('9');
2132 for my $subject_subfield (@subfields ) {
2133 # don't load unimarc subfields 3,4,5
2134 next if (($marcflavour eq "UNIMARC") and ($subject_subfield->[0] =~ (3|4|5) ) );
2135 my $code = $subject_subfield->[0];
2136 my $value = $subject_subfield->[1];
2137 my $linkvalue = $value;
2138 $linkvalue =~ s/(\(|\))//g;
2139 my $operator = " and " unless $counter==0;
2141 @link_loop = ({'limit' => 'an' ,link => "$subfield9" });
2143 push @link_loop, {'limit' => 'su', link => $linkvalue, operator => $operator };
2145 my $separator = C4::Context->preference("authoritysep") unless $counter==0;
2147 my @this_link_loop = @link_loop;
2148 push @subfields_loop, {code => $code, value => $value, link_loop => \@this_link_loop, separator => $separator} unless ($subject_subfield->[0] == 9 );
2152 push @marcsubjects, { MARCSUBJECT_SUBFIELDS_LOOP => \@subfields_loop };
2155 return \@marcsubjects;
2156 } #end getMARCsubjects
2158 =head2 GetMarcAuthors
2162 authors = GetMarcAuthors($record,$marcflavour);
2163 Get all authors from the MARC record and returns them in an array.
2164 The authors are stored in differents places depending on MARC flavour
2170 sub GetMarcAuthors {
2171 my ( $record, $marcflavour ) = @_;
2172 my ( $mintag, $maxtag );
2173 # tagslib useful for UNIMARC author reponsabilities
2174 my $tagslib = &GetMarcStructure( 1, '' ); # FIXME : we don't have the framework available, we take the default framework. May be bugguy on some setups, will be usually correct.
2175 if ( $marcflavour eq "MARC21" ) {
2179 elsif ( $marcflavour eq "UNIMARC" ) { # assume unimarc if not marc21
2188 foreach my $field ( $record->fields ) {
2189 next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
2192 my @subfields = $field->subfields();
2194 # if there is an authority link, build the link with Koha-Auth-Number: subfield9
2195 my $subfield9 = $field->subfield('9');
2196 for my $authors_subfield (@subfields) {
2197 # don't load unimarc subfields 3, 5
2198 next if ($marcflavour eq 'UNIMARC' and ($authors_subfield->[0] =~ (3|5) ) );
2199 my $subfieldcode = $authors_subfield->[0];
2200 my $value = $authors_subfield->[1];
2201 my $linkvalue = $value;
2202 $linkvalue =~ s/(\(|\))//g;
2203 my $operator = " and " unless $count_auth==0;
2204 # if we have an authority link, use that as the link, otherwise use standard searching
2206 @link_loop = ({'limit' => 'Koha-Auth-Number' ,link => "$subfield9" });
2209 # reset $linkvalue if UNIMARC author responsibility
2210 if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] eq '4')) {
2211 $linkvalue = "(".GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ).")";
2213 push @link_loop, {'limit' => 'au', link => $linkvalue, operator => $operator };
2215 my @this_link_loop = @link_loop;
2216 my $separator = C4::Context->preference("authoritysep") unless $count_auth==0;
2217 push @subfields_loop, {code => $subfieldcode, value => $value, link_loop => \@this_link_loop, separator => $separator} unless ($authors_subfield->[0] == 9 );
2220 push @marcauthors, { MARCAUTHOR_SUBFIELDS_LOOP => \@subfields_loop };
2222 return \@marcauthors;
2229 $marcurls = GetMarcUrls($record,$marcflavour);
2230 Returns arrayref of URLs from MARC data, suitable to pass to tmpl loop.
2231 Assumes web resources (not uncommon in MARC21 to omit resource type ind)
2238 my ($record, $marcflavour) = @_;
2241 for my $field ($record->field('856')) {
2242 my $url = $field->subfield('u');
2244 for my $note ( $field->subfield('z')) {
2245 push @notes , {note => $note};
2247 $marcurl = { MARCURL => $url,
2250 if($marcflavour eq 'MARC21') {
2251 my $s3 = $field->subfield('3');
2252 my $link = $field->subfield('y');
2253 $marcurl->{'linktext'} = $link || $s3 || $url ;;
2254 $marcurl->{'part'} = $s3 if($link);
2255 $marcurl->{'toc'} = 1 if($s3 =~ /^[Tt]able/) ;
2257 $marcurl->{'linktext'} = $url;
2259 push @marcurls, $marcurl;
2264 =head2 GetMarcSeries
2268 $marcseriesarray = GetMarcSeries($record,$marcflavour);
2269 Get all series from the MARC record and returns them in an array.
2270 The series are stored in differents places depending on MARC flavour
2277 my ($record, $marcflavour) = @_;
2278 my ($mintag, $maxtag);
2279 if ($marcflavour eq "MARC21") {
2282 } else { # assume unimarc if not marc21
2292 foreach my $field ($record->field('440'), $record->field('490')) {
2294 #my $value = $field->subfield('a');
2295 #$marcsubjct = {MARCSUBJCT => $value,};
2296 my @subfields = $field->subfields();
2297 #warn "subfields:".join " ", @$subfields;
2300 for my $series_subfield (@subfields) {
2302 undef $volume_number;
2303 # see if this is an instance of a volume
2304 if ($series_subfield->[0] eq 'v') {
2308 my $code = $series_subfield->[0];
2309 my $value = $series_subfield->[1];
2310 my $linkvalue = $value;
2311 $linkvalue =~ s/(\(|\))//g;
2312 my $operator = " and " unless $counter==0;
2313 push @link_loop, {link => $linkvalue, operator => $operator };
2314 my $separator = C4::Context->preference("authoritysep") unless $counter==0;
2315 if ($volume_number) {
2316 push @subfields_loop, {volumenum => $value};
2319 push @subfields_loop, {code => $code, value => $value, link_loop => \@link_loop, separator => $separator, volumenum => $volume_number};
2323 push @marcseries, { MARCSERIES_SUBFIELDS_LOOP => \@subfields_loop };
2324 #$marcsubjct = {MARCSUBJCT => $field->as_string(),};
2325 #push @marcsubjcts, $marcsubjct;
2329 my $marcseriessarray=\@marcseries;
2330 return $marcseriessarray;
2331 } #end getMARCseriess
2333 =head2 GetFrameworkCode
2337 $frameworkcode = GetFrameworkCode( $biblionumber )
2343 sub GetFrameworkCode {
2344 my ( $biblionumber ) = @_;
2345 my $dbh = C4::Context->dbh;
2346 my $sth = $dbh->prepare("SELECT frameworkcode FROM biblio WHERE biblionumber=?");
2347 $sth->execute($biblionumber);
2348 my ($frameworkcode) = $sth->fetchrow;
2349 return $frameworkcode;
2352 =head2 GetPublisherNameFromIsbn
2354 $name = GetPublishercodeFromIsbn($isbn);
2361 sub GetPublisherNameFromIsbn($){
2363 $isbn =~ s/[- _]//g;
2365 my @codes = (split '-', DisplayISBN($isbn));
2366 my $code = $codes[0].$codes[1].$codes[2];
2367 my $dbh = C4::Context->dbh;
2369 SELECT distinct publishercode
2372 AND publishercode IS NOT NULL
2375 my $sth = $dbh->prepare($query);
2376 $sth->execute("$code%");
2377 my $name = $sth->fetchrow;
2378 return $name if length $name;
2382 =head2 TransformKohaToMarc
2386 $record = TransformKohaToMarc( $hash )
2387 This function builds partial MARC::Record from a hash
2388 Hash entries can be from biblio or biblioitems.
2389 This function is called in acquisition module, to create a basic catalogue entry from user entry
2395 sub TransformKohaToMarc {
2398 my $dbh = C4::Context->dbh;
2401 "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?"
2403 my $record = MARC::Record->new();
2404 foreach (keys %{$hash}) {
2405 &TransformKohaToMarcOneField( $sth, $record, $_,
2411 =head2 TransformKohaToMarcOneField
2415 $record = TransformKohaToMarcOneField( $sth, $record, $kohafieldname, $value, $frameworkcode );
2421 sub TransformKohaToMarcOneField {
2422 my ( $sth, $record, $kohafieldname, $value, $frameworkcode ) = @_;
2423 $frameworkcode='' unless $frameworkcode;
2427 if ( !defined $sth ) {
2428 my $dbh = C4::Context->dbh;
2429 $sth = $dbh->prepare(
2430 "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?"
2433 $sth->execute( $frameworkcode, $kohafieldname );
2434 if ( ( $tagfield, $tagsubfield ) = $sth->fetchrow ) {
2435 my $tag = $record->field($tagfield);
2437 $tag->update( $tagsubfield => $value );
2438 $record->delete_field($tag);
2439 $record->insert_fields_ordered($tag);
2442 $record->add_fields( $tagfield, " ", " ", $tagsubfield => $value );
2448 =head2 TransformHtmlToXml
2452 $xml = TransformHtmlToXml( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type )
2454 $auth_type contains :
2455 - nothing : rebuild a biblio, un UNIMARC the encoding is in 100$a pos 26/27
2456 - UNIMARCAUTH : rebuild an authority. In UNIMARC, the encoding is in 100$a pos 13/14
2457 - ITEM : rebuild an item : in UNIMARC, 100$a, it's in the biblio ! (otherwise, we would get 2 100 fields !)
2463 sub TransformHtmlToXml {
2464 my ( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type ) = @_;
2465 my $xml = MARC::File::XML::header('UTF-8');
2466 $auth_type = C4::Context->preference('marcflavour') unless $auth_type;
2467 MARC::File::XML->default_record_format($auth_type);
2468 # in UNIMARC, field 100 contains the encoding
2469 # check that there is one, otherwise the
2470 # MARC::Record->new_from_xml will fail (and Koha will die)
2471 my $unimarc_and_100_exist=0;
2472 $unimarc_and_100_exist=1 if $auth_type eq 'ITEM'; # if we rebuild an item, no need of a 100 field
2477 for ( my $i = 0 ; $i <= @$tags ; $i++ ) {
2478 if (C4::Context->preference('marcflavour') eq 'UNIMARC' and @$tags[$i] eq "100" and @$subfields[$i] eq "a") {
2479 # if we have a 100 field and it's values are not correct, skip them.
2480 # if we don't have any valid 100 field, we will create a default one at the end
2481 my $enc = substr( @$values[$i], 26, 2 );
2482 if ($enc eq '01' or $enc eq '50' or $enc eq '03') {
2483 $unimarc_and_100_exist=1;
2488 @$values[$i] =~ s/&/&/g;
2489 @$values[$i] =~ s/</</g;
2490 @$values[$i] =~ s/>/>/g;
2491 @$values[$i] =~ s/"/"/g;
2492 @$values[$i] =~ s/'/'/g;
2493 # if ( !utf8::is_utf8( @$values[$i] ) ) {
2494 # utf8::decode( @$values[$i] );
2496 if ( ( @$tags[$i] ne $prevtag ) ) {
2497 $j++ unless ( @$tags[$i] eq "" );
2499 $xml .= "</datafield>\n";
2500 if ( ( @$tags[$i] && @$tags[$i] > 10 )
2501 && ( @$values[$i] ne "" ) )
2503 my $ind1 = substr( @$indicator[$j], 0, 1 );
2505 if ( @$indicator[$j] ) {
2506 $ind2 = substr( @$indicator[$j], 1, 1 );
2509 warn "Indicator in @$tags[$i] is empty";
2513 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2515 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2523 if ( @$values[$i] ne "" ) {
2526 if ( @$tags[$i] eq "000" ) {
2527 $xml .= "<leader>@$values[$i]</leader>\n";
2530 # rest of the fixed fields
2532 elsif ( @$tags[$i] < 10 ) {
2534 "<controlfield tag=\"@$tags[$i]\">@$values[$i]</controlfield>\n";
2538 my $ind1 = substr( @$indicator[$j], 0, 1 );
2539 my $ind2 = substr( @$indicator[$j], 1, 1 );
2541 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2543 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2549 else { # @$tags[$i] eq $prevtag
2550 if ( @$values[$i] eq "" ) {
2554 my $ind1 = substr( @$indicator[$j], 0, 1 );
2555 my $ind2 = substr( @$indicator[$j], 1, 1 );
2557 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2561 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2564 $prevtag = @$tags[$i];
2566 if (C4::Context->preference('marcflavour') eq 'UNIMARC' and !$unimarc_and_100_exist) {
2567 # warn "SETTING 100 for $auth_type";
2568 use POSIX qw(strftime);
2569 my $string = strftime( "%Y%m%d", localtime(time) );
2570 # set 50 to position 26 is biblios, 13 if authorities
2572 $pos=13 if $auth_type eq 'UNIMARCAUTH';
2573 $string = sprintf( "%-*s", 35, $string );
2574 substr( $string, $pos , 6, "50" );
2575 $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
2576 $xml .= "<subfield code=\"a\">$string</subfield>\n";
2577 $xml .= "</datafield>\n";
2579 $xml .= MARC::File::XML::footer();
2583 =head2 TransformHtmlToMarc
2585 L<$record> = TransformHtmlToMarc(L<$params>,L<$cgi>)
2586 L<$params> is a ref to an array as below:
2588 'tag_010_indicator_531951' ,
2589 'tag_010_code_a_531951_145735' ,
2590 'tag_010_subfield_a_531951_145735' ,
2591 'tag_200_indicator_873510' ,
2592 'tag_200_code_a_873510_673465' ,
2593 'tag_200_subfield_a_873510_673465' ,
2594 'tag_200_code_b_873510_704318' ,
2595 'tag_200_subfield_b_873510_704318' ,
2596 'tag_200_code_e_873510_280822' ,
2597 'tag_200_subfield_e_873510_280822' ,
2598 'tag_200_code_f_873510_110730' ,
2599 'tag_200_subfield_f_873510_110730' ,
2601 L<$cgi> is the CGI object which containts the value.
2602 L<$record> is the MARC::Record object.
2606 sub TransformHtmlToMarc {
2610 # creating a new record
2611 my $record = MARC::Record->new();
2614 while ($params->[$i]){ # browse all CGI params
2615 my $param = $params->[$i];
2617 # if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields)
2618 if ($param eq 'biblionumber') {
2619 my ( $biblionumbertagfield, $biblionumbertagsubfield ) =
2620 &GetMarcFromKohaField( "biblio.biblionumber", '' );
2621 if ($biblionumbertagfield < 10) {
2622 $newfield = MARC::Field->new(
2623 $biblionumbertagfield,
2624 $cgi->param($param),
2627 $newfield = MARC::Field->new(
2628 $biblionumbertagfield,
2631 "$biblionumbertagsubfield" => $cgi->param($param),
2634 push @fields,$newfield if($newfield);
2636 elsif ($param =~ /^tag_(\d*)_indicator_/){ # new field start when having 'input name="..._indicator_..."
2639 my $ind1 = substr($cgi->param($param),0,1);
2640 my $ind2 = substr($cgi->param($param),1,1);
2644 if($tag < 10){ # no code for theses fields
2645 # in MARC editor, 000 contains the leader.
2646 if ($tag eq '000' ) {
2647 $record->leader($cgi->param($params->[$j+1])) if length($cgi->param($params->[$j+1]))==24;
2648 # between 001 and 009 (included)
2650 $newfield = MARC::Field->new(
2652 $cgi->param($params->[$j+1]),
2655 # > 009, deal with subfields
2657 while($params->[$j] =~ /_code_/){ # browse all it's subfield
2658 my $inner_param = $params->[$j];
2660 if($cgi->param($params->[$j+1])){ # only if there is a value (code => value)
2661 $newfield->add_subfields(
2662 $cgi->param($inner_param) => $cgi->param($params->[$j+1])
2666 if ( $cgi->param($params->[$j+1]) ) { # creating only if there is a value (code => value)
2667 $newfield = MARC::Field->new(
2671 $cgi->param($inner_param) => $cgi->param($params->[$j+1]),
2678 push @fields,$newfield if($newfield);
2683 $record->append_fields(@fields);
2687 # cache inverted MARC field map
2688 our $inverted_field_map;
2690 =head2 TransformMarcToKoha
2694 $result = TransformMarcToKoha( $dbh, $record, $frameworkcode )
2698 Extract data from a MARC bib record into a hashref representing
2699 Koha biblio, biblioitems, and items fields.
2702 sub TransformMarcToKoha {
2703 my ( $dbh, $record, $frameworkcode, $limit_table ) = @_;
2707 unless (defined $inverted_field_map) {
2708 $inverted_field_map = _get_inverted_marc_field_map();
2712 if ($limit_table eq 'items') {
2713 $tables{'items'} = 1;
2715 $tables{'items'} = 1;
2716 $tables{'biblio'} = 1;
2717 $tables{'biblioitems'} = 1;
2720 # traverse through record
2721 MARCFIELD: foreach my $field ($record->fields()) {
2722 my $tag = $field->tag();
2723 next MARCFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag};
2724 if ($field->is_control_field()) {
2725 my $kohafields = $inverted_field_map->{$frameworkcode}->{$tag}->{list};
2726 ENTRY: foreach my $entry (@{ $kohafields }) {
2727 my ($subfield, $table, $column) = @{ $entry };
2728 next ENTRY unless exists $tables{$table};
2729 my $key = _disambiguate($table, $column);
2730 if ($result->{$key}) {
2731 unless (($key eq "biblionumber" or $key eq "biblioitemnumber") and ($field->data() eq "")) {
2732 $result->{$key} .= " | " . $field->data();
2735 $result->{$key} = $field->data();
2739 # deal with subfields
2740 MARCSUBFIELD: foreach my $sf ($field->subfields()) {
2741 my $code = $sf->[0];
2742 next MARCSUBFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code};
2743 my $value = $sf->[1];
2744 SFENTRY: foreach my $entry (@{ $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code} }) {
2745 my ($table, $column) = @{ $entry };
2746 next SFENTRY unless exists $tables{$table};
2747 my $key = _disambiguate($table, $column);
2748 if ($result->{$key}) {
2749 unless (($key eq "biblionumber" or $key eq "biblioitemnumber") and ($value eq "")) {
2750 $result->{$key} .= " | " . $value;
2753 $result->{$key} = $value;
2760 # modify copyrightdate to keep only the 1st year found
2761 if (exists $result->{'copyrightdate'}) {
2762 my $temp = $result->{'copyrightdate'};
2763 $temp =~ m/c(\d\d\d\d)/; # search cYYYY first
2765 $result->{'copyrightdate'} = $1;
2767 else { # if no cYYYY, get the 1st date.
2768 $temp =~ m/(\d\d\d\d)/;
2769 $result->{'copyrightdate'} = $1;
2773 # modify publicationyear to keep only the 1st year found
2774 if (exists $result->{'publicationyear'}) {
2775 my $temp = $result->{'publicationyear'};
2776 $temp =~ m/c(\d\d\d\d)/; # search cYYYY first
2778 $result->{'publicationyear'} = $1;
2780 else { # if no cYYYY, get the 1st date.
2781 $temp =~ m/(\d\d\d\d)/;
2782 $result->{'publicationyear'} = $1;
2789 sub _get_inverted_marc_field_map {
2790 my $relations = C4::Context->marcfromkohafield;
2793 my $relations = C4::Context->marcfromkohafield;
2795 foreach my $frameworkcode (keys %{ $relations }) {
2796 foreach my $kohafield (keys %{ $relations->{$frameworkcode} }) {
2797 my $tag = $relations->{$frameworkcode}->{$kohafield}->[0];
2798 my $subfield = $relations->{$frameworkcode}->{$kohafield}->[1];
2799 my ($table, $column) = split /[.]/, $kohafield, 2;
2800 push @{ $field_map->{$frameworkcode}->{$tag}->{list} }, [ $subfield, $table, $column ];
2801 push @{ $field_map->{$frameworkcode}->{$tag}->{sfs}->{$subfield} }, [ $table, $column ];
2807 =head2 _disambiguate
2811 $newkey = _disambiguate($table, $field);
2813 This is a temporary hack to distinguish between the
2814 following sets of columns when using TransformMarcToKoha.
2816 items.cn_source & biblioitems.cn_source
2817 items.cn_sort & biblioitems.cn_sort
2819 Columns that are currently NOT distinguished (FIXME
2820 due to lack of time to fully test) are:
2822 biblio.notes and biblioitems.notes
2827 FIXME - this is necessary because prefixing each column
2828 name with the table name would require changing lots
2829 of code and templates, and exposing more of the DB
2830 structure than is good to the UI templates, particularly
2831 since biblio and bibloitems may well merge in a future
2832 version. In the future, it would also be good to
2833 separate DB access and UI presentation field names
2841 my ($table, $column) = @_;
2842 if ($column eq "cn_sort" or $column eq "cn_source") {
2843 return $table . '.' . $column;
2850 =head2 get_koha_field_from_marc
2854 $result->{_disambiguate($table, $field)} = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
2856 Internal function to map data from the MARC record to a specific non-MARC field.
2857 FIXME: this is meant to replace TransformMarcToKohaOneField after more testing.
2863 sub get_koha_field_from_marc {
2864 my ($koha_table,$koha_column,$record,$frameworkcode) = @_;
2865 my ( $tagfield, $subfield ) = GetMarcFromKohaField( $koha_table.'.'.$koha_column, $frameworkcode );
2867 foreach my $field ( $record->field($tagfield) ) {
2868 if ( $field->tag() < 10 ) {
2870 $kohafield .= " | " . $field->data();
2873 $kohafield = $field->data();
2877 if ( $field->subfields ) {
2878 my @subfields = $field->subfields();
2879 foreach my $subfieldcount ( 0 .. $#subfields ) {
2880 if ( $subfields[$subfieldcount][0] eq $subfield ) {
2883 " | " . $subfields[$subfieldcount][1];
2887 $subfields[$subfieldcount][1];
2898 =head2 TransformMarcToKohaOneField
2902 $result = TransformMarcToKohaOneField( $kohatable, $kohafield, $record, $result, $frameworkcode )
2908 sub TransformMarcToKohaOneField {
2910 # FIXME ? if a field has a repeatable subfield that is used in old-db,
2911 # only the 1st will be retrieved...
2912 my ( $kohatable, $kohafield, $record, $result, $frameworkcode ) = @_;
2914 my ( $tagfield, $subfield ) =
2915 GetMarcFromKohaField( $kohatable . "." . $kohafield,
2917 foreach my $field ( $record->field($tagfield) ) {
2918 if ( $field->tag() < 10 ) {
2919 if ( $result->{$kohafield} ) {
2920 $result->{$kohafield} .= " | " . $field->data();
2923 $result->{$kohafield} = $field->data();
2927 if ( $field->subfields ) {
2928 my @subfields = $field->subfields();
2929 foreach my $subfieldcount ( 0 .. $#subfields ) {
2930 if ( $subfields[$subfieldcount][0] eq $subfield ) {
2931 if ( $result->{$kohafield} ) {
2932 $result->{$kohafield} .=
2933 " | " . $subfields[$subfieldcount][1];
2936 $result->{$kohafield} =
2937 $subfields[$subfieldcount][1];
2947 =head1 OTHER FUNCTIONS
2953 my $string = char_decode( $string, $encoding );
2955 converts ISO 5426 coded string to UTF-8
2956 sloppy code : should be improved in next issue
2963 my ( $string, $encoding ) = @_;
2966 $encoding = C4::Context->preference("marcflavour") unless $encoding;
2967 if ( $encoding eq "UNIMARC" ) {
3037 # this handles non-sorting blocks (if implementation requires this)
3038 $string = nsb_clean($_);
3040 elsif ( $encoding eq "USMARC" || $encoding eq "MARC21" ) {
3099 #Additional Turkish characters
3102 s/(\xf0)s/\xc5\x9f/gm;
3103 s/(\xf0)S/\xc5\x9e/gm;
3106 s/\xe7\x49/\\xc4\xb0/gm;
3107 s/(\xe6)G/\xc4\x9e/gm;
3108 s/(\xe6)g/ğ\xc4\x9f/gm;
3111 s/(\xe8|\xc8)o/ö/gm;
3112 s/(\xe8|\xc8)O/Ö/gm;
3113 s/(\xe8|\xc8)u/ü/gm;
3114 s/(\xe8|\xc8)U/Ü/gm;
3115 s/\xc2\xb8/\xc4\xb1/gm;
3118 # this handles non-sorting blocks (if implementation requires this)
3119 $string = nsb_clean($_);
3128 my $string = nsb_clean( $string, $encoding );
3135 my $NSB = '\x88'; # NSB : begin Non Sorting Block
3136 my $NSE = '\x89'; # NSE : Non Sorting Block end
3137 # handles non sorting blocks
3141 s/[ ]{0,1}$NSE/) /gm;
3146 =head2 PrepareItemrecordDisplay
3150 PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber);
3152 Returns a hash with all the fields for Display a given item data in a template
3158 sub PrepareItemrecordDisplay {
3160 my ( $bibnum, $itemnum ) = @_;
3162 my $dbh = C4::Context->dbh;
3163 my $frameworkcode = &GetFrameworkCode( $bibnum );
3164 my ( $itemtagfield, $itemtagsubfield ) =
3165 &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
3166 my $tagslib = &GetMarcStructure( 1, $frameworkcode );
3167 my $itemrecord = GetMarcItem( $bibnum, $itemnum) if ($itemnum);
3169 my $authorised_values_sth =
3171 "SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib"
3173 foreach my $tag ( sort keys %{$tagslib} ) {
3174 my $previous_tag = '';
3176 # loop through each subfield
3178 foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
3179 next if ( subfield_is_koha_internal_p($subfield) );
3180 next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
3182 $subfield_data{tag} = $tag;
3183 $subfield_data{subfield} = $subfield;
3184 $subfield_data{countsubfield} = $cntsubf++;
3185 $subfield_data{kohafield} =
3186 $tagslib->{$tag}->{$subfield}->{'kohafield'};
3188 # $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
3189 $subfield_data{marc_lib} =
3190 "<span id=\"error\" title=\""
3191 . $tagslib->{$tag}->{$subfield}->{lib} . "\">"
3192 . substr( $tagslib->{$tag}->{$subfield}->{lib}, 0, 12 )
3194 $subfield_data{mandatory} =
3195 $tagslib->{$tag}->{$subfield}->{mandatory};
3196 $subfield_data{repeatable} =
3197 $tagslib->{$tag}->{$subfield}->{repeatable};
3198 $subfield_data{hidden} = "display:none"
3199 if $tagslib->{$tag}->{$subfield}->{hidden};
3201 ( $x, $value ) = _find_value( $tag, $subfield, $itemrecord )
3203 $value =~ s/"/"/g;
3205 # search for itemcallnumber if applicable
3206 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq
3207 'items.itemcallnumber'
3208 && C4::Context->preference('itemcallnumber') )
3211 substr( C4::Context->preference('itemcallnumber'), 0, 3 );
3213 substr( C4::Context->preference('itemcallnumber'), 3, 1 );
3214 my $temp = $itemrecord->field($CNtag) if ($itemrecord);
3216 $value = $temp->subfield($CNsubfield);
3219 if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
3220 my @authorised_values;
3223 # builds list, depending on authorised value...
3225 if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq
3228 if ( ( C4::Context->preference("IndependantBranches") )
3229 && ( C4::Context->userenv->{flags} != 1 ) )
3233 "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname"
3235 $sth->execute( C4::Context->userenv->{branch} );
3236 push @authorised_values, ""
3238 $tagslib->{$tag}->{$subfield}->{mandatory} );
3239 while ( my ( $branchcode, $branchname ) =
3240 $sth->fetchrow_array )
3242 push @authorised_values, $branchcode;
3243 $authorised_lib{$branchcode} = $branchname;
3249 "SELECT branchcode,branchname FROM branches ORDER BY branchname"
3252 push @authorised_values, ""
3254 $tagslib->{$tag}->{$subfield}->{mandatory} );
3255 while ( my ( $branchcode, $branchname ) =
3256 $sth->fetchrow_array )
3258 push @authorised_values, $branchcode;
3259 $authorised_lib{$branchcode} = $branchname;
3265 elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq
3270 "SELECT itemtype,description FROM itemtypes ORDER BY description"
3273 push @authorised_values, ""
3274 unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
3275 while ( my ( $itemtype, $description ) =
3276 $sth->fetchrow_array )
3278 push @authorised_values, $itemtype;
3279 $authorised_lib{$itemtype} = $description;
3282 #---- "true" authorised value
3285 $authorised_values_sth->execute(
3286 $tagslib->{$tag}->{$subfield}->{authorised_value} );
3287 push @authorised_values, ""
3288 unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
3289 while ( my ( $value, $lib ) =
3290 $authorised_values_sth->fetchrow_array )
3292 push @authorised_values, $value;
3293 $authorised_lib{$value} = $lib;
3296 $subfield_data{marc_value} = CGI::scrolling_list(
3297 -name => 'field_value',
3298 -values => \@authorised_values,
3299 -default => "$value",
3300 -labels => \%authorised_lib,
3306 elsif ( $tagslib->{$tag}->{$subfield}->{thesaurus_category} ) {
3307 $subfield_data{marc_value} =
3308 "<input type=\"text\" name=\"field_value\" size=47 maxlength=255> <a href=\"javascript:Dopop('cataloguing/thesaurus_popup.pl?category=$tagslib->{$tag}->{$subfield}->{thesaurus_category}&index=',)\">...</a>";
3311 # COMMENTED OUT because No $i is provided with this API.
3312 # And thus, no value_builder can be activated.
3313 # BUT could be thought over.
3314 # } elsif ($tagslib->{$tag}->{$subfield}->{'value_builder'}) {
3315 # my $plugin="value_builder/".$tagslib->{$tag}->{$subfield}->{'value_builder'};
3317 # my $extended_param = plugin_parameters($dbh,$itemrecord,$tagslib,$i,0);
3318 # my ($function_name,$javascript) = plugin_javascript($dbh,$record,$tagslib,$i,0);
3319 # $subfield_data{marc_value}="<input type=\"text\" value=\"$value\" name=\"field_value\" size=47 maxlength=255 DISABLE READONLY OnFocus=\"javascript:Focus$function_name()\" OnBlur=\"javascript:Blur$function_name()\"> <a href=\"javascript:Clic$function_name()\">...</a> $javascript";
3322 $subfield_data{marc_value} =
3323 "<input type=\"text\" name=\"field_value\" value=\"$value\" size=50 maxlength=255>";
3325 push( @loop_data, \%subfield_data );
3329 my $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield )
3330 if ( $itemrecord && $itemrecord->field($itemtagfield) );
3332 'itemtagfield' => $itemtagfield,
3333 'itemtagsubfield' => $itemtagsubfield,
3334 'itemnumber' => $itemnumber,
3335 'iteminformation' => \@loop_data
3341 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
3343 # replaced by a zebraqueue table, that is filled with ModZebra to run.
3344 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
3345 # =head2 ModZebrafiles
3347 # &ModZebrafiles( $dbh, $biblionumber, $record, $folder, $server );
3351 # sub ModZebrafiles {
3353 # my ( $dbh, $biblionumber, $record, $folder, $server ) = @_;
3357 # C4::Context->zebraconfig($server)->{directory} . "/" . $folder . "/";
3358 # unless ( opendir( DIR, "$zebradir" ) ) {
3359 # warn "$zebradir not found";
3363 # my $filename = $zebradir . $biblionumber;
3366 # open( OUTPUT, ">", $filename . ".xml" );
3367 # print OUTPUT $record;
3376 ModZebra( $biblionumber, $op, $server, $newRecord );
3378 $biblionumber is the biblionumber we want to index
3379 $op is specialUpdate or delete, and is used to know what we want to do
3380 $server is the server that we want to update
3381 $newRecord is the MARC::Record containing the new record. It is usefull only when NoZebra=1, and is used to know what to add to the nozebra database. (the record in mySQL being, if it exist, the previous record, the one just before the modif. We need both : the previous and the new one.
3388 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
3389 my ( $biblionumber, $op, $server, $newRecord ) = @_;
3390 my $dbh=C4::Context->dbh;
3392 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
3394 # replaced by a zebraqueue table, that is filled with ModZebra to run.
3395 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
3397 if (C4::Context->preference("NoZebra")) {
3398 # lock the nozebra table : we will read index lines, update them in Perl process
3399 # and write everything in 1 transaction.
3400 # lock the table to avoid someone else overwriting what we are doing
3401 $dbh->do('LOCK TABLES nozebra WRITE,biblio WRITE,biblioitems WRITE, systempreferences WRITE, auth_types WRITE, auth_header WRITE');
3402 my %result; # the result hash that will be builded by deletion / add, and written on mySQL at the end, to improve speed
3404 if ($server eq 'biblioserver') {
3405 $record= GetMarcBiblio($biblionumber);
3407 $record= C4::AuthoritiesMarc::GetAuthority($biblionumber);
3409 if ($op eq 'specialUpdate') {
3410 # OK, we have to add or update the record
3411 # 1st delete (virtually, in indexes), if record actually exists
3413 %result = _DelBiblioNoZebra($biblionumber,$record,$server);
3415 # ... add the record
3416 %result=_AddBiblioNoZebra($biblionumber,$newRecord, $server, %result);
3418 # it's a deletion, delete the record...
3419 # warn "DELETE the record $biblionumber on $server".$record->as_formatted;
3420 %result=_DelBiblioNoZebra($biblionumber,$record,$server);
3422 # ok, now update the database...
3423 my $sth = $dbh->prepare("UPDATE nozebra SET biblionumbers=? WHERE server=? AND indexname=? AND value=?");
3424 foreach my $key (keys %result) {
3425 foreach my $index (keys %{$result{$key}}) {
3426 $sth->execute($result{$key}->{$index}, $server, $key, $index);
3429 $dbh->do('UNLOCK TABLES');
3433 # we use zebra, just fill zebraqueue table
3435 my $sth=$dbh->prepare("INSERT INTO zebraqueue (biblio_auth_number,server,operation) VALUES(?,?,?)");
3436 $sth->execute($biblionumber,$server,$op);
3441 =head2 GetNoZebraIndexes
3443 %indexes = GetNoZebraIndexes;
3445 return the data from NoZebraIndexes syspref.
3449 sub GetNoZebraIndexes {
3450 my $index = C4::Context->preference('NoZebraIndexes');
3452 foreach my $line (split /('|"),/,$index) {
3453 $line =~ /(.*)=>(.*)/;
3454 my $index = substr($1,1); # get the index, don't forget to remove initial ' or "
3456 $index =~ s/'|"|\s//g;
3459 $fields =~ s/'|"|\s//g;
3460 $indexes{$index}=$fields;
3465 =head1 INTERNAL FUNCTIONS
3467 =head2 _DelBiblioNoZebra($biblionumber,$record,$server);
3469 function to delete a biblio in NoZebra indexes
3470 This function does NOT delete anything in database : it reads all the indexes entries
3471 that have to be deleted & delete them in the hash
3472 The SQL part is done either :
3473 - after the Add if we are modifying a biblio (delete + add again)
3474 - immediatly after this sub if we are doing a true deletion.
3475 $server can be 'biblioserver' or 'authorityserver' : it indexes biblios or authorities (in the same table, $server being part of the table itself
3480 sub _DelBiblioNoZebra {
3481 my ($biblionumber, $record, $server)=@_;
3484 my $dbh = C4::Context->dbh;
3488 if ($server eq 'biblioserver') {
3489 %index=GetNoZebraIndexes;
3490 # get title of the record (to store the 10 first letters with the index)
3491 my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title');
3492 $title = lc($record->subfield($titletag,$titlesubfield));
3494 # for authorities, the "title" is the $a mainentry
3495 my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield(152,'b'));
3496 warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
3497 $title = $record->subfield($authref->{auth_tag_to_report},'a');
3498 $index{'mainmainentry'}= $authref->{'auth_tag_to_report'}.'a';
3499 $index{'mainentry'} = $authref->{'auth_tag_to_report'}.'*';
3500 $index{'auth_type'} = '152b';
3504 # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
3505 $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
3506 # limit to 10 char, should be enough, and limit the DB size
3507 $title = substr($title,0,10);
3509 my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
3510 foreach my $field ($record->fields()) {
3511 #parse each subfield
3512 next if $field->tag <10;
3513 foreach my $subfield ($field->subfields()) {
3514 my $tag = $field->tag();
3515 my $subfieldcode = $subfield->[0];
3517 # check each index to see if the subfield is stored somewhere
3518 # otherwise, store it in __RAW__ index
3519 foreach my $key (keys %index) {
3520 # warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
3521 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
3523 my $line= lc $subfield->[1];
3524 # remove meaningless value in the field...
3525 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
3526 # ... and split in words
3527 foreach (split / /,$line) {
3528 next unless $_; # skip empty values (multiple spaces)
3529 # if the entry is already here, do nothing, the biblionumber has already be removed
3530 unless ($result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/) {
3531 # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
3532 $sth2->execute($server,$key,$_);
3533 my $existing_biblionumbers = $sth2->fetchrow;
3535 if ($existing_biblionumbers) {
3536 # warn " existing for $key $_: $existing_biblionumbers";
3537 $result{$key}->{$_} =$existing_biblionumbers;
3538 $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
3544 # the subfield is not indexed, store it in __RAW__ index anyway
3546 my $line= lc $subfield->[1];
3547 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
3548 # ... and split in words
3549 foreach (split / /,$line) {
3550 next unless $_; # skip empty values (multiple spaces)
3551 # if the entry is already here, do nothing, the biblionumber has already be removed
3552 unless ($result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/) {
3553 # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
3554 $sth2->execute($server,'__RAW__',$_);
3555 my $existing_biblionumbers = $sth2->fetchrow;
3557 if ($existing_biblionumbers) {
3558 $result{'__RAW__'}->{$_} =$existing_biblionumbers;
3559 $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
3569 =head2 _AddBiblioNoZebra($biblionumber, $record, $server, %result);
3571 function to add a biblio in NoZebra indexes
3575 sub _AddBiblioNoZebra {
3576 my ($biblionumber, $record, $server, %result)=@_;
3577 my $dbh = C4::Context->dbh;
3581 if ($server eq 'biblioserver') {
3582 %index=GetNoZebraIndexes;
3583 # get title of the record (to store the 10 first letters with the index)
3584 my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title');
3585 $title = lc($record->subfield($titletag,$titlesubfield));
3587 # warn "server : $server";
3588 # for authorities, the "title" is the $a mainentry
3589 my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield(152,'b'));
3590 warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
3591 $title = $record->subfield($authref->{auth_tag_to_report},'a');
3592 $index{'mainmainentry'} = $authref->{auth_tag_to_report}.'a';
3593 $index{'mainentry'} = $authref->{auth_tag_to_report}.'*';
3594 $index{'auth_type'} = '152b';
3597 # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
3598 $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
3599 # limit to 10 char, should be enough, and limit the DB size
3600 $title = substr($title,0,10);
3602 my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
3603 foreach my $field ($record->fields()) {
3604 #parse each subfield
3605 next if $field->tag <10;
3606 foreach my $subfield ($field->subfields()) {
3607 my $tag = $field->tag();
3608 my $subfieldcode = $subfield->[0];
3610 # check each index to see if the subfield is stored somewhere
3611 # otherwise, store it in __RAW__ index
3612 foreach my $key (keys %index) {
3613 # warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
3614 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
3616 my $line= lc $subfield->[1];
3617 # remove meaningless value in the field...
3618 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
3619 # ... and split in words
3620 foreach (split / /,$line) {
3621 next unless $_; # skip empty values (multiple spaces)
3622 # if the entry is already here, improve weight
3623 # warn "managing $_";
3624 if ($result{$key}->{"$_"} =~ /$biblionumber,$title\-(\d);/) {
3626 $result{$key}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
3627 $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
3629 # get the value if it exist in the nozebra table, otherwise, create it
3630 $sth2->execute($server,$key,$_);
3631 my $existing_biblionumbers = $sth2->fetchrow;
3633 if ($existing_biblionumbers) {
3634 $result{$key}->{"$_"} =$existing_biblionumbers;
3636 $result{$key}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
3637 $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
3638 # create a new ligne for this entry
3640 # warn "INSERT : $server / $key / $_";
3641 $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).', indexname='.$dbh->quote($key).',value='.$dbh->quote($_));
3642 $result{$key}->{"$_"}.="$biblionumber,$title-1;";
3648 # the subfield is not indexed, store it in __RAW__ index anyway
3650 my $line= lc $subfield->[1];
3651 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
3652 # ... and split in words
3653 foreach (split / /,$line) {
3654 next unless $_; # skip empty values (multiple spaces)
3655 # if the entry is already here, improve weight
3656 if ($result{'__RAW__'}->{"$_"} =~ /$biblionumber,$title\-(\d);/) {
3658 $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
3659 $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
3661 # get the value if it exist in the nozebra table, otherwise, create it
3662 $sth2->execute($server,'__RAW__',$_);
3663 my $existing_biblionumbers = $sth2->fetchrow;
3665 if ($existing_biblionumbers) {
3666 $result{'__RAW__'}->{"$_"} =$existing_biblionumbers;
3668 $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
3669 $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
3670 # create a new ligne for this entry
3672 $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).', indexname="__RAW__",value='.$dbh->quote($_));
3673 $result{'__RAW__'}->{"$_"}.="$biblionumber,$title-1;";
3684 =head2 MARCitemchange
3688 &MARCitemchange( $record, $itemfield, $newvalue )
3690 Function to update a single value in an item field.
3691 Used twice, could probably be replaced by something else, but works well...
3699 sub MARCitemchange {
3700 my ( $record, $itemfield, $newvalue ) = @_;
3701 my $dbh = C4::Context->dbh;
3703 my ( $tagfield, $tagsubfield ) =
3704 GetMarcFromKohaField( $itemfield, "" );
3705 if ( ($tagfield) && ($tagsubfield) ) {
3706 my $tag = $record->field($tagfield);
3708 $tag->update( $tagsubfield => $newvalue );
3709 $record->delete_field($tag);
3710 $record->insert_fields_ordered($tag);
3718 ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
3720 Find the given $subfield in the given $tag in the given
3721 MARC::Record $record. If the subfield is found, returns
3722 the (indicators, value) pair; otherwise, (undef, undef) is
3726 Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
3727 I suggest we export it from this module.
3734 my ( $tagfield, $insubfield, $record, $encoding ) = @_;
3737 if ( $tagfield < 10 ) {
3738 if ( $record->field($tagfield) ) {
3739 push @result, $record->field($tagfield)->data();
3746 foreach my $field ( $record->field($tagfield) ) {
3747 my @subfields = $field->subfields();
3748 foreach my $subfield (@subfields) {
3749 if ( @$subfield[0] eq $insubfield ) {
3750 push @result, @$subfield[1];
3751 $indicator = $field->indicator(1) . $field->indicator(2);
3756 return ( $indicator, @result );
3759 =head2 _koha_marc_update_bib_ids
3763 _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
3765 Internal function to add or update biblionumber and biblioitemnumber to
3772 sub _koha_marc_update_bib_ids {
3773 my ($record, $frameworkcode, $biblionumber, $biblioitemnumber) = @_;
3775 # we must add bibnum and bibitemnum in MARC::Record...
3776 # we build the new field with biblionumber and biblioitemnumber
3777 # we drop the original field
3778 # we add the new builded field.
3779 my ($biblio_tag, $biblio_subfield ) = GetMarcFromKohaField("biblio.biblionumber",$frameworkcode);
3780 my ($biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.biblioitemnumber",$frameworkcode);
3782 if ($biblio_tag != $biblioitem_tag) {
3783 # biblionumber & biblioitemnumber are in different fields
3785 # deal with biblionumber
3786 my ($new_field, $old_field);
3787 if ($biblio_tag < 10) {
3788 $new_field = MARC::Field->new( $biblio_tag, $biblionumber );
3791 MARC::Field->new( $biblio_tag, '', '',
3792 "$biblio_subfield" => $biblionumber );
3795 # drop old field and create new one...
3796 $old_field = $record->field($biblio_tag);
3797 $record->delete_field($old_field);
3798 $record->append_fields($new_field);
3800 # deal with biblioitemnumber
3801 if ($biblioitem_tag < 10) {
3802 $new_field = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
3805 MARC::Field->new( $biblioitem_tag, '', '',
3806 "$biblioitem_subfield" => $biblioitemnumber, );
3808 # drop old field and create new one...
3809 $old_field = $record->field($biblioitem_tag);
3810 $record->delete_field($old_field);
3811 $record->insert_fields_ordered($new_field);
3814 # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
3815 my $new_field = MARC::Field->new(
3816 $biblio_tag, '', '',
3817 "$biblio_subfield" => $biblionumber,
3818 "$biblioitem_subfield" => $biblioitemnumber
3821 # drop old field and create new one...
3822 my $old_field = $record->field($biblio_tag);
3823 $record->delete_field($old_field);
3824 $record->insert_fields_ordered($new_field);
3828 =head2 _koha_add_biblio
3832 my ($biblionumber,$error) = _koha_add_biblio($dbh,$biblioitem);
3834 Internal function to add a biblio ($biblio is a hash with the values)
3840 sub _koha_add_biblio {
3841 my ( $dbh, $biblio, $frameworkcode ) = @_;
3845 # set the series flag
3847 if ( $biblio->{'seriestitle'} ) { $serial = 1 };
3851 SET frameworkcode = ?,
3862 my $sth = $dbh->prepare($query);
3865 $biblio->{'author'},
3867 $biblio->{'unititle'},
3870 $biblio->{'seriestitle'},
3871 $biblio->{'copyrightdate'},
3872 $biblio->{'abstract'}
3875 my $biblionumber = $dbh->{'mysql_insertid'};
3876 if ( $dbh->errstr ) {
3877 $error.="ERROR in _koha_add_biblio $query".$dbh->errstr;
3882 #warn "LEAVING _koha_add_biblio: ".$biblionumber."\n";
3883 return ($biblionumber,$error);
3886 =head2 _koha_modify_biblio
3890 my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
3892 Internal function for updating the biblio table
3898 sub _koha_modify_biblio {
3899 my ( $dbh, $biblio, $frameworkcode ) = @_;
3904 SET frameworkcode = ?,
3913 WHERE biblionumber = ?
3916 my $sth = $dbh->prepare($query);
3920 $biblio->{'author'},
3922 $biblio->{'unititle'},
3924 $biblio->{'serial'},
3925 $biblio->{'seriestitle'},
3926 $biblio->{'copyrightdate'},
3927 $biblio->{'abstract'},
3928 $biblio->{'biblionumber'}
3929 ) if $biblio->{'biblionumber'};
3931 if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
3932 $error.="ERROR in _koha_modify_biblio $query".$dbh->errstr;
3935 return ( $biblio->{'biblionumber'},$error );
3938 =head2 _koha_modify_biblioitem_nonmarc
3942 my ($biblioitemnumber,$error) = _koha_modify_biblioitem_nonmarc( $dbh, $biblioitem );
3944 Updates biblioitems row except for marc and marcxml, which should be changed
3951 sub _koha_modify_biblioitem_nonmarc {
3952 my ( $dbh, $biblioitem ) = @_;
3955 # re-calculate the cn_sort, it may have changed
3956 my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3960 SET biblionumber = ?,
3966 publicationyear = ?,
3970 collectiontitle = ?,
3972 collectionvolume= ?,
3973 editionstatement= ?,
3974 editionresponsibility = ?,
3988 where biblioitemnumber = ?
3990 my $sth = $dbh->prepare($query);
3992 $biblioitem->{'biblionumber'},
3993 $biblioitem->{'volume'},
3994 $biblioitem->{'number'},
3995 $biblioitem->{'itemtype'},
3996 $biblioitem->{'isbn'},
3997 $biblioitem->{'issn'},
3998 $biblioitem->{'publicationyear'},
3999 $biblioitem->{'publishercode'},
4000 $biblioitem->{'volumedate'},
4001 $biblioitem->{'volumedesc'},
4002 $biblioitem->{'collectiontitle'},
4003 $biblioitem->{'collectionissn'},
4004 $biblioitem->{'collectionvolume'},
4005 $biblioitem->{'editionstatement'},
4006 $biblioitem->{'editionresponsibility'},
4007 $biblioitem->{'illus'},
4008 $biblioitem->{'pages'},
4009 $biblioitem->{'bnotes'},
4010 $biblioitem->{'size'},
4011 $biblioitem->{'place'},
4012 $biblioitem->{'lccn'},
4013 $biblioitem->{'url'},
4014 $biblioitem->{'biblioitems.cn_source'},
4015 $biblioitem->{'cn_class'},
4016 $biblioitem->{'cn_item'},
4017 $biblioitem->{'cn_suffix'},
4019 $biblioitem->{'totalissues'},
4020 $biblioitem->{'biblioitemnumber'}
4022 if ( $dbh->errstr ) {
4023 $error.="ERROR in _koha_modify_biblioitem_nonmarc $query".$dbh->errstr;
4026 return ($biblioitem->{'biblioitemnumber'},$error);
4029 =head2 _koha_add_biblioitem
4033 my ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $biblioitem );
4035 Internal function to add a biblioitem
4041 sub _koha_add_biblioitem {
4042 my ( $dbh, $biblioitem ) = @_;
4045 my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
4047 "INSERT INTO biblioitems SET
4054 publicationyear = ?,
4058 collectiontitle = ?,
4060 collectionvolume= ?,
4061 editionstatement= ?,
4062 editionresponsibility = ?,
4078 my $sth = $dbh->prepare($query);
4080 $biblioitem->{'biblionumber'},
4081 $biblioitem->{'volume'},
4082 $biblioitem->{'number'},
4083 $biblioitem->{'itemtype'},
4084 $biblioitem->{'isbn'},
4085 $biblioitem->{'issn'},
4086 $biblioitem->{'publicationyear'},
4087 $biblioitem->{'publishercode'},
4088 $biblioitem->{'volumedate'},
4089 $biblioitem->{'volumedesc'},
4090 $biblioitem->{'collectiontitle'},
4091 $biblioitem->{'collectionissn'},
4092 $biblioitem->{'collectionvolume'},
4093 $biblioitem->{'editionstatement'},
4094 $biblioitem->{'editionresponsibility'},
4095 $biblioitem->{'illus'},
4096 $biblioitem->{'pages'},
4097 $biblioitem->{'bnotes'},
4098 $biblioitem->{'size'},
4099 $biblioitem->{'place'},
4100 $biblioitem->{'lccn'},
4101 $biblioitem->{'marc'},
4102 $biblioitem->{'url'},
4103 $biblioitem->{'biblioitems.cn_source'},
4104 $biblioitem->{'cn_class'},
4105 $biblioitem->{'cn_item'},
4106 $biblioitem->{'cn_suffix'},
4108 $biblioitem->{'totalissues'}
4110 my $bibitemnum = $dbh->{'mysql_insertid'};
4111 if ( $dbh->errstr ) {
4112 $error.="ERROR in _koha_add_biblioitem $query".$dbh->errstr;
4116 return ($bibitemnum,$error);
4119 =head2 _koha_new_items
4123 my ($itemnumber,$error) = _koha_new_items( $dbh, $item, $barcode );
4129 sub _koha_new_items {
4130 my ( $dbh, $item, $barcode ) = @_;
4132 my ($items_cn_sort) = GetClassSort($item->{'items.cn_source'}, $item->{'itemcallnumber'}, "");
4134 # if dateaccessioned is provided, use it. Otherwise, set to NOW()
4135 if ( $item->{'dateaccessioned'} eq '' || !$item->{'dateaccessioned'} ) {
4136 my $today = C4::Dates->new();
4137 $item->{'dateaccessioned'} = $today->output("iso"); #TODO: check time issues
4140 "INSERT INTO items SET
4142 biblioitemnumber = ?,
4144 dateaccessioned = ?,
4148 replacementprice = ?,
4149 replacementpricedate = NOW(),
4150 datelastborrowed = ?,
4151 datelastseen = NOW(),
4174 my $sth = $dbh->prepare($query);
4176 $item->{'biblionumber'},
4177 $item->{'biblioitemnumber'},
4179 $item->{'dateaccessioned'},
4180 $item->{'booksellerid'},
4181 $item->{'homebranch'},
4183 $item->{'replacementprice'},
4184 $item->{datelastborrowed},
4186 $item->{'notforloan'},
4188 $item->{'itemlost'},
4189 $item->{'wthdrawn'},
4190 $item->{'itemcallnumber'},
4191 $item->{'restricted'},
4192 $item->{'itemnotes'},
4193 $item->{'holdingbranch'},
4195 $item->{'location'},
4198 $item->{'renewals'},
4199 $item->{'reserves'},
4200 $item->{'items.cn_source'},
4204 $item->{'materials'},
4207 my $itemnumber = $dbh->{'mysql_insertid'};
4208 if ( defined $sth->errstr ) {
4209 $error.="ERROR in _koha_new_items $query".$sth->errstr;
4212 return ( $itemnumber, $error );
4215 =head2 _koha_modify_item
4219 my ($itemnumber,$error) =_koha_modify_item( $dbh, $item, $op );
4225 sub _koha_modify_item {
4226 my ( $dbh, $item ) = @_;
4229 # calculate items.cn_sort
4230 if($item->{'itemcallnumber'}) {
4231 # This works, even when user is setting the call number blank (in which case
4232 # how would we get here to calculate new (blank) of items.cn_sort?).
4234 # Why? Because at present the only way to update itemcallnumber is via
4235 # additem.pl; since it uses a MARC data-entry form, TransformMarcToKoha
4236 # already has created $item->{'items.cn_sort'} and set it to undef because the
4237 # subfield for items.cn_sort in the framework is specified as ignored, meaning
4238 # that it is not supplied or passed to the form. Thus, if the user has
4239 # blanked itemcallnumber, there is already a undef value for $item->{'items.cn_sort'}.
4241 # This is subtle; it is also fragile.
4242 $item->{'items.cn_sort'} = GetClassSort($item->{'items.cn_source'}, $item->{'itemcallnumber'}, "");
4244 my $query = "UPDATE items SET ";
4246 for my $key ( keys %$item ) {
4248 push @bind, $item->{$key};
4251 $query .= " WHERE itemnumber=?";
4252 push @bind, $item->{'itemnumber'};
4253 my $sth = $dbh->prepare($query);
4254 $sth->execute(@bind);
4255 if ( $dbh->errstr ) {
4256 $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
4260 return ($item->{'itemnumber'},$error);
4263 =head2 _koha_delete_biblio
4267 $error = _koha_delete_biblio($dbh,$biblionumber);
4269 Internal sub for deleting from biblio table -- also saves to deletedbiblio
4271 C<$dbh> - the database handle
4272 C<$biblionumber> - the biblionumber of the biblio to be deleted
4278 # FIXME: add error handling
4280 sub _koha_delete_biblio {
4281 my ( $dbh, $biblionumber ) = @_;
4283 # get all the data for this biblio
4284 my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
4285 $sth->execute($biblionumber);
4287 if ( my $data = $sth->fetchrow_hashref ) {
4289 # save the record in deletedbiblio
4290 # find the fields to save
4291 my $query = "INSERT INTO deletedbiblio SET ";
4293 foreach my $temp ( keys %$data ) {
4294 $query .= "$temp = ?,";
4295 push( @bind, $data->{$temp} );
4298 # replace the last , by ",?)"
4300 my $bkup_sth = $dbh->prepare($query);
4301 $bkup_sth->execute(@bind);
4305 my $del_sth = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
4306 $del_sth->execute($biblionumber);
4313 =head2 _koha_delete_biblioitems
4317 $error = _koha_delete_biblioitems($dbh,$biblioitemnumber);
4319 Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems
4321 C<$dbh> - the database handle
4322 C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted
4328 # FIXME: add error handling
4330 sub _koha_delete_biblioitems {
4331 my ( $dbh, $biblioitemnumber ) = @_;
4333 # get all the data for this biblioitem
4335 $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
4336 $sth->execute($biblioitemnumber);
4338 if ( my $data = $sth->fetchrow_hashref ) {
4340 # save the record in deletedbiblioitems
4341 # find the fields to save
4342 my $query = "INSERT INTO deletedbiblioitems SET ";
4344 foreach my $temp ( keys %$data ) {
4345 $query .= "$temp = ?,";
4346 push( @bind, $data->{$temp} );
4349 # replace the last , by ",?)"
4351 my $bkup_sth = $dbh->prepare($query);
4352 $bkup_sth->execute(@bind);
4355 # delete the biblioitem
4357 $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
4358 $del_sth->execute($biblioitemnumber);
4365 =head2 _koha_delete_item
4369 _koha_delete_item( $dbh, $itemnum );
4371 Internal function to delete an item record from the koha tables
4377 sub _koha_delete_item {
4378 my ( $dbh, $itemnum ) = @_;
4380 # save the deleted item to deleteditems table
4381 my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
4382 $sth->execute($itemnum);
4383 my $data = $sth->fetchrow_hashref();
4385 my $query = "INSERT INTO deleteditems SET ";
4387 foreach my $key ( keys %$data ) {
4388 $query .= "$key = ?,";
4389 push( @bind, $data->{$key} );
4392 $sth = $dbh->prepare($query);
4393 $sth->execute(@bind);
4396 # delete from items table
4397 $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
4398 $sth->execute($itemnum);
4403 =head1 UNEXPORTED FUNCTIONS
4405 =head2 ModBiblioMarc
4407 &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
4409 Add MARC data for a biblio to koha
4411 Function exported, but should NOT be used, unless you really know what you're doing
4417 # pass the MARC::Record to this function, and it will create the records in the marc field
4418 my ( $record, $biblionumber, $frameworkcode ) = @_;
4419 my $dbh = C4::Context->dbh;
4420 my @fields = $record->fields();
4421 if ( !$frameworkcode ) {
4422 $frameworkcode = "";
4425 $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
4426 $sth->execute( $frameworkcode, $biblionumber );
4428 my $encoding = C4::Context->preference("marcflavour");
4430 # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
4431 if ( $encoding eq "UNIMARC" ) {
4433 if ( length($record->subfield( 100, "a" )) == 35 ) {
4434 $string = $record->subfield( 100, "a" );
4435 my $f100 = $record->field(100);
4436 $record->delete_field($f100);
4439 $string = POSIX::strftime( "%Y%m%d", localtime );
4441 $string = sprintf( "%-*s", 35, $string );
4443 substr( $string, 22, 6, "frey50" );
4444 unless ( $record->subfield( 100, "a" ) ) {
4445 $record->insert_grouped_field(
4446 MARC::Field->new( 100, "", "", "a" => $string ) );
4449 ModZebra($biblionumber,"specialUpdate","biblioserver",$record);
4452 "UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
4453 $sth->execute( $record->as_usmarc(), $record->as_xml_record($encoding),
4456 return $biblionumber;
4459 =head2 AddItemInMarc
4463 $newbiblionumber = AddItemInMarc( $record, $biblionumber, $frameworkcode );
4465 Add an item in a MARC record and save the MARC record
4467 Function exported, but should NOT be used, unless you really know what you're doing
4475 # pass the MARC::Record to this function, and it will create the records in the marc tables
4476 my ( $record, $biblionumber, $frameworkcode ) = @_;
4477 my $newrec = &GetMarcBiblio($biblionumber);
4480 my @fields = $record->fields();
4481 foreach my $field (@fields) {
4482 $newrec->append_fields($field);
4485 # FIXME: should we be making sure the biblionumbers are the same?
4486 my $newbiblionumber =
4487 &ModBiblioMarc( $newrec, $biblionumber, $frameworkcode );
4488 return $newbiblionumber;
4491 =head2 z3950_extended_services
4493 z3950_extended_services($serviceType,$serviceOptions,$record);
4495 z3950_extended_services is used to handle all interactions with Zebra's extended serices package, which is employed to perform all management of the MARC data stored in Zebra.
4497 C<$serviceType> one of: itemorder,create,drop,commit,update,xmlupdate
4499 C<$serviceOptions> a has of key/value pairs. For instance, if service_type is 'update', $service_options should contain:
4501 action => update action, one of specialUpdate, recordInsert, recordReplace, recordDelete, elementUpdate.
4505 recordidOpaque => Opaque Record ID (user supplied) or recordidNumber => Record ID number (system number).
4506 syntax => the record syntax (transfer syntax)
4507 databaseName = Database from connection object
4509 To set serviceOptions, call set_service_options($serviceType)
4511 C<$record> the record, if one is needed for the service type
4513 A record should be in XML. You can convert it to XML from MARC by running it through marc2xml().
4517 sub z3950_extended_services {
4518 my ( $server, $serviceType, $action, $serviceOptions ) = @_;
4520 # get our connection object
4521 my $Zconn = C4::Context->Zconn( $server, 0, 1 );
4523 # create a new package object
4524 my $Zpackage = $Zconn->package();
4527 $Zpackage->option( action => $action );
4529 if ( $serviceOptions->{'databaseName'} ) {
4530 $Zpackage->option( databaseName => $serviceOptions->{'databaseName'} );
4532 if ( $serviceOptions->{'recordIdNumber'} ) {
4534 recordIdNumber => $serviceOptions->{'recordIdNumber'} );
4536 if ( $serviceOptions->{'recordIdOpaque'} ) {
4538 recordIdOpaque => $serviceOptions->{'recordIdOpaque'} );
4541 # this is an ILL request (Zebra doesn't support it, but Koha could eventually)
4542 #if ($serviceType eq 'itemorder') {
4543 # $Zpackage->option('contact-name' => $serviceOptions->{'contact-name'});
4544 # $Zpackage->option('contact-phone' => $serviceOptions->{'contact-phone'});
4545 # $Zpackage->option('contact-email' => $serviceOptions->{'contact-email'});
4546 # $Zpackage->option('itemorder-item' => $serviceOptions->{'itemorder-item'});
4549 if ( $serviceOptions->{record} ) {
4550 $Zpackage->option( record => $serviceOptions->{record} );
4552 # can be xml or marc
4553 if ( $serviceOptions->{'syntax'} ) {
4554 $Zpackage->option( syntax => $serviceOptions->{'syntax'} );
4558 # send the request, handle any exception encountered
4559 eval { $Zpackage->send($serviceType) };
4560 if ( $@ && $@->isa("ZOOM::Exception") ) {
4561 return "error: " . $@->code() . " " . $@->message() . "\n";
4564 # free up package resources
4565 $Zpackage->destroy();
4568 =head2 set_service_options
4570 my $serviceOptions = set_service_options($serviceType);
4572 C<$serviceType> itemorder,create,drop,commit,update,xmlupdate
4574 Currently, we only support 'create', 'commit', and 'update'. 'drop' support will be added as soon as Zebra supports it.
4578 sub set_service_options {
4579 my ($serviceType) = @_;
4582 # FIXME: This needs to be an OID ... if we ever need 'syntax' this sub will need to change
4583 # $serviceOptions->{ 'syntax' } = ''; #zebra doesn't support syntaxes other than xml
4585 if ( $serviceType eq 'commit' ) {
4589 if ( $serviceType eq 'create' ) {
4593 if ( $serviceType eq 'drop' ) {
4594 die "ERROR: 'drop' not currently supported (by Zebra)";
4596 return $serviceOptions;
4599 =head2 GetItemsCount
4601 $count = &GetItemsCount( $biblionumber);
4602 this function return count of item with $biblionumber
4606 my ( $biblionumber ) = @_;
4607 my $dbh = C4::Context->dbh;
4608 my $query = "SELECT count(*)
4610 WHERE biblionumber=?";
4611 my $sth = $dbh->prepare($query);
4612 $sth->execute($biblionumber);
4613 my $count = $sth->fetchrow;
4618 END { } # module clean-up code here (global destructor)
4626 Koha Developement team <info@koha.org>
4628 Paul POULAIN paul.poulain@free.fr
4630 Joshua Ferraro jmf@liblime.com