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::Log; # logaction
33 use vars qw($VERSION @ISA @EXPORT);
35 # set the version for version checking
36 $VERSION = do { my @v = '$Revision$' =~ /\d+/g; shift(@v).".".join( "_", map { sprintf "%03d", $_ } @v ); };
38 @ISA = qw( Exporter );
42 # to add biblios or items
43 push @EXPORT, qw( &AddBiblio &AddItem );
51 &GetBiblioItemByBiblioNumber
52 &GetBiblioFromItemNumber
71 &GetItemnumberFromBarcode
75 &GetAuthorisedValueDesc
79 &GetPublisherNameFromIsbn
91 &ModItemInMarconefield
102 # those functions are exported but should not be used
103 # they are usefull is few circumstances, so are exported.
104 # but don't use them unless you're a core developer ;-)
115 &TransformHtmlToMarc2
118 &PrepareItemrecordDisplay
125 C4::Biblio - cataloging management functions
129 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:
133 =item 1. in the biblio,biblioitems,items, etc tables, which are limited to a one-to-one mapping to underlying MARC data
135 =item 2. as raw MARC in the Zebra index and storage engine
137 =item 3. as raw MARC the biblioitems.marc and biblioitems.marcxml
141 In the 3.0 version of Koha, the authoritative record-level information is in biblioitems.marcxml
143 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.
147 =item 1. Compared with MySQL, Zebra is slow to update an index for small data changes -- especially for proc-intensive operations like circulation
149 =item 2. Zebra's index has been known to crash and a backup of the data is necessary to rebuild it in such cases
153 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:
157 =item 1. Add*/Mod*/Del*/ - high-level external functions suitable for being called from external scripts to manage the collection
159 =item 2. _koha_* - low-level internal functions for managing the koha tables
161 =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.
163 =item 4. Zebra functions used to update the Zebra index
165 =item 5. internal helper functions such as char_decode, checkitems, etc. Some of these probably belong in Koha.pm
169 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 :
173 =item 1. save datas in biblio and biblioitems table, that gives us a biblionumber and a biblioitemnumber
175 =item 2. add the biblionumber and biblioitemnumber into the MARC records
177 =item 3. save the marc record
181 When dealing with items, we must :
185 =item 1. save the item in items table, that gives us an itemnumber
187 =item 2. add the itemnumber to the item MARC field
189 =item 3. overwrite the MARC record (with the added item) into biblioitems.marc(xml)
191 When modifying a biblio or an item, the behaviour is quite similar.
195 =head1 EXPORTED FUNCTIONS
201 ($biblionumber,$biblioitemnumber) = AddBiblio($record,$frameworkcode);
202 Exported function (core API) for adding a new biblio to koha.
209 my ( $record, $frameworkcode ) = @_;
211 my $biblioitemnumber;
212 my $dbh = C4::Context->dbh;
213 # transform the data into koha-table style data
214 my $olddata = TransformMarcToKoha( $dbh, $record, $frameworkcode );
215 $biblionumber = _koha_add_biblio( $dbh, $olddata, $frameworkcode );
216 $olddata->{'biblionumber'} = $biblionumber;
217 $biblioitemnumber = _koha_add_biblioitem( $dbh, $olddata );
219 # we must add bibnum and bibitemnum in MARC::Record...
220 # we build the new field with biblionumber and biblioitemnumber
221 # we drop the original field
222 # we add the new builded field.
223 ( my $biblio_tag, my $biblio_subfield ) = GetMarcFromKohaField("biblio.biblionumber",$frameworkcode);
224 ( my $biblioitem_tag, my $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.biblioitemnumber",$frameworkcode);
228 # biblionumber & biblioitemnumber are in different fields
229 if ( $biblio_tag != $biblioitem_tag ) {
231 # deal with biblionumber
232 if ( $biblio_tag < 10 ) {
233 $newfield = MARC::Field->new( $biblio_tag, $biblionumber );
237 MARC::Field->new( $biblio_tag, '', '',
238 "$biblio_subfield" => $biblionumber );
241 # drop old field and create new one...
242 my $old_field = $record->field($biblio_tag);
243 $record->delete_field($old_field);
244 $record->append_fields($newfield);
246 # deal with biblioitemnumber
247 if ( $biblioitem_tag < 10 ) {
248 $newfield = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
252 MARC::Field->new( $biblioitem_tag, '', '',
253 "$biblioitem_subfield" => $biblioitemnumber, );
255 # drop old field and create new one...
256 $old_field = $record->field($biblioitem_tag);
257 $record->delete_field($old_field);
258 $record->insert_fields_ordered($newfield);
260 # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
263 my $newfield = MARC::Field->new(
265 "$biblio_subfield" => $biblionumber,
266 "$biblioitem_subfield" => $biblioitemnumber
269 # drop old field and create new one...
270 my $old_field = $record->field($biblio_tag);
271 $record->delete_field($old_field);
272 $record->insert_fields_ordered($newfield);
276 $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
278 &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$biblionumber,"biblio")
279 if C4::Context->preference("CataloguingLog");
281 return ( $biblionumber, $biblioitemnumber );
288 $biblionumber = AddItem( $record, $biblionumber)
289 Exported function (core API) for adding a new item to Koha
296 my ( $record, $biblionumber ) = @_;
297 my $dbh = C4::Context->dbh;
300 my $frameworkcode = GetFrameworkCode( $biblionumber );
301 my $item = &TransformMarcToKoha( $dbh, $record, $frameworkcode );
303 # needs old biblionumber and biblioitemnumber
304 $item->{'biblionumber'} = $biblionumber;
307 "select biblioitemnumber,itemtype from biblioitems where biblionumber=?"
309 $sth->execute( $item->{'biblionumber'} );
311 ( $item->{'biblioitemnumber'}, $itemtype ) = $sth->fetchrow;
314 "select notforloan from itemtypes where itemtype='$itemtype'");
316 my $notforloan = $sth->fetchrow;
317 ##Change the notforloan field if $notforloan found
318 if ( $notforloan > 0 ) {
319 $item->{'notforloan'} = $notforloan;
320 &MARCitemchange( $record, "items.notforloan", $notforloan );
322 if ( !$item->{'dateaccessioned'} || $item->{'dateaccessioned'} eq '' ) {
325 my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
330 "$year-" . sprintf( "%0.2d", $mon ) . "-" . sprintf( "%0.2d", $mday );
331 $item->{'dateaccessioned'} = $date;
332 &MARCitemchange( $record, "items.dateaccessioned", $date );
334 my ( $itemnumber, $error ) =
335 &_koha_new_items( $dbh, $item, $item->{barcode} );
337 # add itemnumber to MARC::Record before adding the item.
338 $sth = $dbh->prepare(
339 "select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
341 &TransformKohaToMarcOneField( $sth, $record, "items.itemnumber", $itemnumber,
345 &AddItemInMarc( $record, $item->{'biblionumber'},$frameworkcode );
347 &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item")
348 if C4::Context->preference("CataloguingLog");
350 return ($item->{biblionumber}, $item->{biblioitemnumber},$itemnumber);
355 ModBiblio( $record,$biblionumber,$frameworkcode);
356 Exported function (core API) to modify a biblio
361 my ( $record, $biblionumber, $frameworkcode ) = @_;
362 if (C4::Context->preference("CataloguingLog")) {
363 my $newrecord = GetMarcBiblio($biblionumber);
364 &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$biblionumber,"BEFORE=>".$newrecord->as_formatted);
367 my $dbh = C4::Context->dbh;
369 $frameworkcode = "" unless $frameworkcode;
371 # get the items before and append them to the biblio before updating the record, atm we just have the biblio
372 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
373 my $oldRecord = GetMarcBiblio( $biblionumber );
374 #$oldRecord->encoding('UTF-8');
376 my @fields = $oldRecord->field( $itemtag );
378 if ( !utf8::is_utf8( $_ ) ) {
382 $record->append_fields( @fields ); # FIXME : encoding error...
384 # adding biblionumber
385 my ($tag_biblionumber, $subfield_biblionumber) = GetMarcFromKohaField('biblio.biblionumber',$frameworkcode);
386 $record->append_fields(
388 $tag_biblionumber,'','',$subfield_biblionumber => $biblionumber
392 # update the MARC record (that now contains biblio and items) with the new record data
393 &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
395 # load the koha-table data object
396 my $oldbiblio = TransformMarcToKoha( $dbh, $record, $frameworkcode );
398 # modify the other koha tables
399 _koha_modify_biblio( $dbh, $oldbiblio );
400 _koha_modify_biblioitem( $dbh, $oldbiblio );
408 Exported function (core API) for modifying an item in Koha.
415 my ( $record, $biblionumber, $itemnumber, $delete, $new_item_hashref )
419 &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$itemnumber,$record->as_formatted)
420 if C4::Context->preference("CataloguingLog");
422 my $dbh = C4::Context->dbh;
424 # if we have a MARC record, we're coming from cataloging and so
425 # we do the whole routine: update the MARC and zebra, then update the koha
428 my $frameworkcode = GetFrameworkCode( $biblionumber );
429 ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode );
430 my $olditem = TransformMarcToKoha( $dbh, $record, $frameworkcode );
431 _koha_modify_item( $dbh, $olditem );
432 return $biblionumber;
435 # otherwise, we're just looking to modify something quickly
436 # (like a status) so we just update the koha tables
437 elsif ($new_item_hashref) {
438 _koha_modify_item( $dbh, $new_item_hashref );
442 sub ModItemTransfer {
443 my ( $itemnumber, $frombranch, $tobranch ) = @_;
445 my $dbh = C4::Context->dbh;
447 #new entry in branchtransfers....
448 my $sth = $dbh->prepare(
449 "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
450 VALUES (?, ?, now(), ?)");
451 $sth->execute($itemnumber, $frombranch, $tobranch);
452 #update holdingbranch in items .....
454 "UPDATE items set holdingbranch = ? WHERE items.itemnumber = ?");
455 $sth->execute($tobranch,$itemnumber);
456 &ModDateLastSeen($itemnumber);
457 $sth = $dbh->prepare(
458 "SELECT biblionumber FROM items WHERE itemnumber=?"
460 $sth->execute($itemnumber);
461 while ( my ( $biblionumber ) = $sth->fetchrow ) {
462 &ModItemInMarconefield( $biblionumber, $itemnumber,
463 'items.holdingbranch', $tobranch );
468 ##New sub to dotransfer in marc tables as well. Not exported -TG 10/04/2006
469 # sub domarctransfer {
470 # my ( $dbh, $itemnumber ) = @_;
471 # $itemnumber =~ s /\'//g; ##itemnumber seems to come with quotes-TG
474 # "select biblionumber,holdingbranch from items where itemnumber=$itemnumber"
477 # while ( my ( $biblionumber, $holdingbranch ) = $sth->fetchrow ) {
478 # &ModItemInMarconefield( $biblionumber, $itemnumber,
479 # 'items.holdingbranch', $holdingbranch );
484 =head2 ModBiblioframework
486 ModBiblioframework($biblionumber,$frameworkcode);
487 Exported function to modify a biblio framework
491 sub ModBiblioframework {
492 my ( $biblionumber, $frameworkcode ) = @_;
493 my $dbh = C4::Context->dbh;
494 my $sth = $dbh->prepare(
495 "UPDATE biblio SET frameworkcode=? WHERE biblionumber=$biblionumber"
497 $sth->execute($frameworkcode);
501 =head2 ModItemInMarconefield
505 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)
506 &ModItemInMarconefield( $biblionumber, $itemnumber, $itemfield, $newvalue )
512 sub ModItemInMarconefield {
513 my ( $biblionumber, $itemnumber, $itemfield, $newvalue ) = @_;
514 my $dbh = C4::Context->dbh;
515 if ( !defined $newvalue ) {
519 my $record = GetMarcItem( $biblionumber, $itemnumber );
520 my ($tagfield, $tagsubfield) = GetMarcFromKohaField( $itemfield,'');
521 if ($tagfield && $tagsubfield) {
522 my $tag = $record->field($tagfield);
524 # my $tagsubs = $record->field($tagfield)->subfield($tagsubfield);
525 $tag->update( $tagsubfield => $newvalue );
526 $record->delete_field($tag);
527 $record->insert_fields_ordered($tag);
528 &ModItemInMarc( $record, $biblionumber, $itemnumber, 0 );
537 &ModItemInMarc( $record, $biblionumber, $itemnumber )
544 my ( $ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
545 my $dbh = C4::Context->dbh;
547 # get complete MARC record & replace the item field by the new one
548 my $completeRecord = GetMarcBiblio($biblionumber);
549 my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
550 my $itemField = $ItemRecord->field($itemtag);
551 my @items = $completeRecord->field($itemtag);
553 if ($_->subfield($itemsubfield) eq $itemnumber) {
554 # $completeRecord->delete_field($_);
555 $_->replace_with($itemField);
559 my $sth = $dbh->prepare("update biblioitems set marc=?,marcxml=? where biblionumber=?");
560 $sth->execute( $completeRecord->as_usmarc(), $completeRecord->as_xml_record(),$biblionumber );
562 ModZebra($biblionumber,"specialUpdate","biblioserver",$completeRecord);
565 =head2 ModDateLastSeen
567 &ModDateLastSeen($itemnum)
568 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking
569 C<$itemnum> is the item number
573 sub ModDateLastSeen {
575 my $dbh = C4::Context->dbh;
578 "update items set itemlost=0, datelastseen = now() where items.itemnumber = ?"
580 $sth->execute($itemnum);
587 my $error = &DelBiblio($dbh,$biblionumber);
588 Exported function (core API) for deleting a biblio in koha.
589 Deletes biblio record from Zebra and Koha tables (biblio,biblioitems,items)
590 Also backs it up to deleted* tables
591 Checks to make sure there are not issues on any of the items
593 C<$error> : undef unless an error occurs
600 my ( $biblionumber ) = @_;
601 my $dbh = C4::Context->dbh;
602 my $error; # for error handling
604 # First make sure there are no items with issues are still attached
607 "SELECT itemnumber FROM items WHERE biblionumber=?");
608 $sth->execute($biblionumber);
609 while ( my $itemnumber = $sth->fetchrow ) {
610 my $issues = GetItemIssues($itemnumber);
611 foreach my $issue (@$issues) {
612 if ( ( $issue->{date_due} )
613 && ( $issue->{date_due} ne "Available" ) )
616 #FIXME: we need a status system in Biblio like in Circ to return standard codes and messages
617 # instead of hard-coded strings
619 "Item is checked out to a patron -- you must return it before deleting the Biblio";
623 return $error if $error;
625 # Delete in Zebra. Be careful NOT to move this line after _koha_delete_biblio
626 # for at least 2 reasons :
627 # - we need to read the biblio if NoZebra is set (to remove it from the indexes
628 # - if something goes wrong, the biblio may be deleted from Koha but not from zebra
629 # 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)
630 ModZebra($biblionumber, "delete_record", "biblioserver", undef);
632 # delete biblio from Koha tables and save in deletedbiblio
633 $error = &_koha_delete_biblio( $dbh, $biblionumber );
635 # delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
638 "SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
639 $sth->execute($biblionumber);
640 while ( my $biblioitemnumber = $sth->fetchrow ) {
642 # delete this biblioitem
643 $error = &_koha_delete_biblioitems( $dbh, $biblioitemnumber );
644 return $error if $error;
649 "SELECT itemnumber FROM items WHERE biblioitemnumber=?");
650 $items_sth->execute($biblioitemnumber);
651 while ( my $itemnumber = $items_sth->fetchrow ) {
652 $error = &_koha_delete_item( $dbh, $itemnumber );
653 return $error if $error;
656 &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$biblionumber,"")
657 if C4::Context->preference("CataloguingLog");
665 DelItem( $biblionumber, $itemnumber );
666 Exported function (core API) for deleting an item record in Koha.
673 my ( $biblionumber, $itemnumber ) = @_;
674 my $dbh = C4::Context->dbh;
675 &_koha_delete_item( $dbh, $itemnumber );
676 # get the MARC record
677 my $record = GetMarcBiblio($biblionumber);
678 my $frameworkcode = GetFrameworkCode($biblionumber);
682 $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
683 $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
685 #search item field code
686 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
687 my @fields = $record->field($itemtag);
688 # delete the item specified
689 foreach my $field (@fields) {
690 if ( $field->subfield($itemsubfield) eq $itemnumber ) {
691 $record->delete_field($field);
694 &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
695 &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$itemnumber,"item")
696 if C4::Context->preference("CataloguingLog");
703 $data = &GetBiblioData($biblionumber);
704 Returns information about the book with the given biblionumber.
705 C<&GetBiblioData> returns a reference-to-hash. The keys are the fields in
706 the C<biblio> and C<biblioitems> tables in the
708 In addition, C<$data-E<gt>{subject}> is the list of the book's
709 subjects, separated by C<" , "> (space, comma, space).
710 If there are multiple biblioitems with the given biblionumber, only
711 the first one is considered.
719 my $dbh = C4::Context->dbh;
722 SELECT * , biblioitems.notes AS bnotes, biblio.notes
724 LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
725 LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
726 WHERE biblio.biblionumber = ?
727 AND biblioitems.biblionumber = biblio.biblionumber
729 my $sth = $dbh->prepare($query);
730 $sth->execute($bibnum);
732 $data = $sth->fetchrow_hashref;
736 } # sub GetBiblioData
743 @results = &GetItemsInfo($biblionumber, $type);
745 Returns information about books with the given biblionumber.
747 C<$type> may be either C<intra> or anything else. If it is not set to
748 C<intra>, then the search will exclude lost, very overdue, and
751 C<&GetItemsInfo> returns a list of references-to-hash. Each element
752 contains a number of keys. Most of them are table items from the
753 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
754 Koha database. Other keys include:
758 =item C<$data-E<gt>{branchname}>
760 The name (not the code) of the branch to which the book belongs.
762 =item C<$data-E<gt>{datelastseen}>
764 This is simply C<items.datelastseen>, except that while the date is
765 stored in YYYY-MM-DD format in the database, here it is converted to
766 DD/MM/YYYY format. A NULL date is returned as C<//>.
768 =item C<$data-E<gt>{datedue}>
770 =item C<$data-E<gt>{class}>
772 This is the concatenation of C<biblioitems.classification>, the book's
773 Dewey code, and C<biblioitems.subclass>.
775 =item C<$data-E<gt>{ocount}>
777 I think this is the number of copies of the book available.
779 =item C<$data-E<gt>{order}>
781 If this is set, it is set to C<One Order>.
790 my ( $biblionumber, $type ) = @_;
791 my $dbh = C4::Context->dbh;
792 my $query = "SELECT *,items.notforloan as itemnotforloan
793 FROM items, biblio, biblioitems
794 LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype
795 WHERE items.biblionumber = ?
796 AND biblioitems.biblioitemnumber = items.biblioitemnumber
797 AND biblio.biblionumber = items.biblionumber
798 ORDER BY items.dateaccessioned desc
800 my $sth = $dbh->prepare($query);
801 $sth->execute($biblionumber);
804 my ( $date_due, $count_reserves );
806 while ( my $data = $sth->fetchrow_hashref ) {
808 my $isth = $dbh->prepare(
809 "SELECT issues.*,borrowers.cardnumber
810 FROM issues, borrowers
812 AND returndate IS NULL
813 AND issues.borrowernumber=borrowers.borrowernumber"
815 $isth->execute( $data->{'itemnumber'} );
816 if ( my $idata = $isth->fetchrow_hashref ) {
817 $data->{borrowernumber} = $idata->{borrowernumber};
818 $data->{cardnumber} = $idata->{cardnumber};
819 $datedue = format_date( $idata->{'date_due'} );
821 if ( $datedue eq '' ) {
822 #$datedue="Available";
823 my ( $restype, $reserves ) =
824 C4::Reserves::CheckReserves( $data->{'itemnumber'} );
828 $count_reserves = $restype;
833 #get branch information.....
834 my $bsth = $dbh->prepare(
835 "SELECT * FROM branches WHERE branchcode = ?
838 $bsth->execute( $data->{'holdingbranch'} );
839 if ( my $bdata = $bsth->fetchrow_hashref ) {
840 $data->{'branchname'} = $bdata->{'branchname'};
842 my $date = format_date( $data->{'datelastseen'} );
843 $data->{'datelastseen'} = $date;
844 $data->{'datedue'} = $datedue;
845 $data->{'count_reserves'} = $count_reserves;
847 # get notforloan complete status if applicable
848 my $sthnflstatus = $dbh->prepare(
849 'SELECT authorised_value
850 FROM marc_subfield_structure
851 WHERE kohafield="items.notforloan"
855 $sthnflstatus->execute;
856 my ($authorised_valuecode) = $sthnflstatus->fetchrow;
857 if ($authorised_valuecode) {
858 $sthnflstatus = $dbh->prepare(
859 "SELECT lib FROM authorised_values
861 AND authorised_value=?"
863 $sthnflstatus->execute( $authorised_valuecode,
864 $data->{itemnotforloan} );
865 my ($lib) = $sthnflstatus->fetchrow;
866 $data->{notforloan} = $lib;
869 # my stack procedures
870 my $stackstatus = $dbh->prepare(
871 'SELECT authorised_value
872 FROM marc_subfield_structure
873 WHERE kohafield="items.stack"
876 $stackstatus->execute;
878 ($authorised_valuecode) = $stackstatus->fetchrow;
879 if ($authorised_valuecode) {
880 $stackstatus = $dbh->prepare(
882 FROM authorised_values
884 AND authorised_value=?
887 $stackstatus->execute( $authorised_valuecode, $data->{stack} );
888 my ($lib) = $stackstatus->fetchrow;
889 $data->{stack} = $lib;
891 $results[$i] = $data;
903 $itemstatushash = &getitemstatus($fwkcode);
904 returns information about status.
905 Can be MARC dependant.
907 But basically could be can be loan or not
908 Create a status selector with the following code
910 =head3 in PERL SCRIPT
912 my $itemstatushash = getitemstatus;
914 foreach my $thisstatus (keys %$itemstatushash) {
915 my %row =(value => $thisstatus,
916 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
918 push @itemstatusloop, \%row;
920 $template->param(statusloop=>\@itemstatusloop);
925 <select name="statusloop">
926 <option value="">Default</option>
927 <!-- TMPL_LOOP name="statusloop" -->
928 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
936 # returns a reference to a hash of references to status...
939 my $dbh = C4::Context->dbh;
941 $fwk = '' unless ($fwk);
942 my ( $tag, $subfield ) =
943 GetMarcFromKohaField( "items.notforloan", $fwk );
944 if ( $tag and $subfield ) {
947 "select authorised_value from marc_subfield_structure where tagfield=? and tagsubfield=? and frameworkcode=?"
949 $sth->execute( $tag, $subfield, $fwk );
950 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
953 "select authorised_value, lib from authorised_values where category=? order by lib"
955 $authvalsth->execute($authorisedvaluecat);
956 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
957 $itemstatus{$authorisedvalue} = $lib;
973 $itemstatus{"1"} = "Not For Loan";
977 =head2 getitemlocation
981 $itemlochash = &getitemlocation($fwk);
982 returns informations about location.
983 where fwk stands for an optional framework code.
984 Create a location selector with the following code
986 =head3 in PERL SCRIPT
988 my $itemlochash = getitemlocation;
990 foreach my $thisloc (keys %$itemlochash) {
991 my $selected = 1 if $thisbranch eq $branch;
992 my %row =(locval => $thisloc,
993 selected => $selected,
994 locname => $itemlochash->{$thisloc},
996 push @itemlocloop, \%row;
998 $template->param(itemlocationloop => \@itemlocloop);
1002 <select name="location">
1003 <option value="">Default</option>
1004 <!-- TMPL_LOOP name="itemlocationloop" -->
1005 <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
1013 sub GetItemLocation {
1015 # returns a reference to a hash of references to location...
1018 my $dbh = C4::Context->dbh;
1020 $fwk = '' unless ($fwk);
1021 my ( $tag, $subfield ) =
1022 GetMarcFromKohaField( "items.location", $fwk );
1023 if ( $tag and $subfield ) {
1026 "select authorised_value from marc_subfield_structure where tagfield=? and tagsubfield=? and frameworkcode=?"
1028 $sth->execute( $tag, $subfield, $fwk );
1029 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
1032 "select authorised_value, lib from authorised_values where category=? order by lib"
1034 $authvalsth->execute($authorisedvaluecat);
1035 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
1036 $itemlocation{$authorisedvalue} = $lib;
1038 $authvalsth->finish;
1039 return \%itemlocation;
1052 $itemlocation{"1"} = "Not For Loan";
1053 return \%itemlocation;
1058 $items = GetLostItems($where,$orderby);
1060 This function get the items lost into C<$items>.
1065 C<$where> is a hashref. it containts a field of the items table as key
1066 and the value to match as value.
1067 C<$orderby> is a field of the items table.
1070 C<$items> is a reference to an array full of hasref which keys are items' table column.
1072 =item usage in the perl script:
1075 $where{barcode} = 0001548;
1076 my $items = GetLostItems( \%where, "homebranch" );
1077 $template->param(itemsloop => $items);
1084 # Getting input args.
1086 my $orderby = shift;
1087 my $dbh = C4::Context->dbh;
1092 WHERE itemlost IS NOT NULL
1095 foreach my $key (keys %$where) {
1096 $query .= " AND " . $key . " LIKE '%" . $where->{$key} . "%'";
1098 $query .= " ORDER BY ".$orderby if defined $orderby;
1100 my $sth = $dbh->prepare($query);
1103 while ( my $row = $sth->fetchrow_hashref ){
1109 =head2 GetItemsForInventory
1111 $itemlist = GetItemsForInventory($minlocation,$maxlocation,$datelastseen,$offset,$size)
1113 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
1115 The sub returns a list of hashes, containing itemnumber, author, title, barcode & item callnumber.
1116 It is ordered by callnumber,title.
1118 The minlocation & maxlocation parameters are used to specify a range of item callnumbers
1119 the datelastseen can be used to specify that you want to see items not seen since a past date only.
1120 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
1124 sub GetItemsForInventory {
1125 my ( $minlocation, $maxlocation,$location, $datelastseen, $branch, $offset, $size ) = @_;
1126 my $dbh = C4::Context->dbh;
1128 if ($datelastseen) {
1129 $datelastseen=format_date_in_iso($datelastseen);
1131 "SELECT itemnumber,barcode,itemcallnumber,title,author,biblio.biblionumber,datelastseen
1133 LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber
1134 WHERE itemcallnumber>= ?
1135 AND itemcallnumber <=?
1136 AND (datelastseen< ? OR datelastseen IS NULL)";
1137 $query.= " AND items.location=".$dbh->quote($location) if $location;
1138 $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
1139 $query .= " ORDER BY itemcallnumber,title";
1140 $sth = $dbh->prepare($query);
1141 $sth->execute( $minlocation, $maxlocation, $datelastseen );
1145 SELECT itemnumber,barcode,itemcallnumber,biblio.biblionumber,title,author,datelastseen
1147 LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber
1148 WHERE itemcallnumber>= ?
1149 AND itemcallnumber <=?";
1150 $query.= " AND items.location=".$dbh->quote($location) if $location;
1151 $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
1152 $query .= " ORDER BY itemcallnumber,title";
1153 $sth = $dbh->prepare($query);
1154 $sth->execute( $minlocation, $maxlocation );
1157 while ( my $row = $sth->fetchrow_hashref ) {
1158 $offset-- if ($offset);
1159 $row->{datelastseen}=format_date($row->{datelastseen});
1160 if ( ( !$offset ) && $size ) {
1161 push @results, $row;
1168 =head2 &GetBiblioItemData
1172 $itemdata = &GetBiblioItemData($biblioitemnumber);
1174 Looks up the biblioitem with the given biblioitemnumber. Returns a
1175 reference-to-hash. The keys are the fields from the C<biblio>,
1176 C<biblioitems>, and C<itemtypes> tables in the Koha database, except
1177 that C<biblioitems.notes> is given as C<$itemdata-E<gt>{bnotes}>.
1184 sub GetBiblioItemData {
1186 my $dbh = C4::Context->dbh;
1189 "Select *,biblioitems.notes as bnotes from biblioitems, biblio,itemtypes where biblio.biblionumber = biblioitems.biblionumber and biblioitemnumber = ? and biblioitems.itemtype = itemtypes.itemtype"
1193 $sth->execute($bibitem);
1195 $data = $sth->fetchrow_hashref;
1199 } # sub &GetBiblioItemData
1201 =head2 GetItemnumberFromBarcode
1205 $result = GetItemnumberFromBarcode($barcode);
1211 sub GetItemnumberFromBarcode {
1213 my $dbh = C4::Context->dbh;
1216 $dbh->prepare("SELECT itemnumber from items where items.barcode=?");
1217 $rq->execute($barcode);
1218 my ($result) = $rq->fetchrow;
1222 =head2 GetBiblioItemByBiblioNumber
1226 NOTE : This function has been copy/paste from C4/Biblio.pm from head before zebra integration.
1232 sub GetBiblioItemByBiblioNumber {
1233 my ($biblionumber) = @_;
1234 my $dbh = C4::Context->dbh;
1235 my $sth = $dbh->prepare("Select * from biblioitems where biblionumber = ?");
1239 $sth->execute($biblionumber);
1241 while ( my $data = $sth->fetchrow_hashref ) {
1242 push @results, $data;
1249 =head2 GetBiblioFromItemNumber
1253 $item = &GetBiblioFromItemNumber($itemnumber);
1255 Looks up the item with the given itemnumber.
1257 C<&itemnodata> returns a reference-to-hash whose keys are the fields
1258 from the C<biblio>, C<biblioitems>, and C<items> tables in the Koha
1266 sub GetBiblioFromItemNumber {
1267 my ( $itemnumber ) = @_;
1268 my $dbh = C4::Context->dbh;
1269 my $sth = $dbh->prepare(
1270 "SELECT * FROM biblio,items,biblioitems
1271 WHERE items.itemnumber = ?
1272 AND biblio.biblionumber = items.biblionumber
1273 AND biblioitems.biblioitemnumber = items.biblioitemnumber"
1276 $sth->execute($itemnumber);
1277 my $data = $sth->fetchrow_hashref;
1286 ( $count, @results ) = &GetBiblio($biblionumber);
1293 my ($biblionumber) = @_;
1294 my $dbh = C4::Context->dbh;
1295 my $sth = $dbh->prepare("Select * from biblio where biblionumber = ?");
1298 $sth->execute($biblionumber);
1299 while ( my $data = $sth->fetchrow_hashref ) {
1300 $results[$count] = $data;
1304 return ( $count, @results );
1311 $data = &GetItem($itemnumber,$barcode);
1313 return Item information, for a given itemnumber or barcode
1320 my ($itemnumber,$barcode) = @_;
1321 my $dbh = C4::Context->dbh;
1323 my $sth = $dbh->prepare("
1325 WHERE itemnumber = ?");
1326 $sth->execute($itemnumber);
1327 my $data = $sth->fetchrow_hashref;
1330 my $sth = $dbh->prepare("
1334 $sth->execute($barcode);
1335 my $data = $sth->fetchrow_hashref;
1340 =head2 get_itemnumbers_of
1344 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1346 Given a list of biblionumbers, return the list of corresponding itemnumbers
1347 for each biblionumber.
1349 Return a reference on a hash where keys are biblionumbers and values are
1350 references on array of itemnumbers.
1356 sub get_itemnumbers_of {
1357 my @biblionumbers = @_;
1359 my $dbh = C4::Context->dbh;
1365 WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1367 my $sth = $dbh->prepare($query);
1368 $sth->execute(@biblionumbers);
1372 while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1373 push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1376 return \%itemnumbers_of;
1379 =head2 GetItemInfosOf
1383 GetItemInfosOf(@itemnumbers);
1389 sub GetItemInfosOf {
1390 my @itemnumbers = @_;
1395 WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1397 return get_infos_of( $query, 'itemnumber' );
1400 =head2 GetBiblioItemInfosOf
1404 GetBiblioItemInfosOf(@biblioitemnumbers);
1410 sub GetBiblioItemInfosOf {
1411 my @biblioitemnumbers = @_;
1414 SELECT biblioitemnumber,
1418 WHERE biblioitemnumber IN (' . join( ',', @biblioitemnumbers ) . ')
1420 return get_infos_of( $query, 'biblioitemnumber' );
1423 =head1 FUNCTIONS FOR HANDLING MARC MANAGEMENT
1425 =head2 GetMarcStructure
1429 $res = GetMarcStructure($forlibrarian,$frameworkcode);
1431 Returns a reference to a big hash of hash, with the Marc structure for the given frameworkcode
1432 $forlibrarian :if set to 1, the MARC descriptions are the librarians ones, otherwise it's the public (OPAC) ones
1433 $frameworkcode : the framework code to read
1439 sub GetMarcStructure {
1440 my ( $forlibrarian, $frameworkcode ) = @_;
1441 my $dbh=C4::Context->dbh;
1442 $frameworkcode = "" unless $frameworkcode;
1444 my $libfield = ( $forlibrarian eq 1 ) ? 'liblibrarian' : 'libopac';
1446 # check that framework exists
1449 "select count(*) from marc_tag_structure where frameworkcode=?");
1450 $sth->execute($frameworkcode);
1451 my ($total) = $sth->fetchrow;
1452 $frameworkcode = "" unless ( $total > 0 );
1455 "select tagfield,liblibrarian,libopac,mandatory,repeatable from marc_tag_structure where frameworkcode=? order by tagfield"
1457 $sth->execute($frameworkcode);
1458 my ( $liblibrarian, $libopac, $tag, $res, $tab, $mandatory, $repeatable );
1460 while ( ( $tag, $liblibrarian, $libopac, $mandatory, $repeatable ) =
1463 $res->{$tag}->{lib} =
1464 ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
1465 # why the hell do we need to explicitly decode utf8 ?
1466 # that's a good question, but we must do it...
1467 $res->{$tab}->{tab} = ""; # XXX
1468 $res->{$tag}->{mandatory} = $mandatory;
1469 $res->{$tag}->{repeatable} = $repeatable;
1474 "select tagfield,tagsubfield,liblibrarian,libopac,tab, mandatory, repeatable,authorised_value,authtypecode,value_builder,kohafield,seealso,hidden,isurl,link,defaultvalue from marc_subfield_structure where frameworkcode=? order by tagfield,tagsubfield"
1477 $sth->execute($frameworkcode);
1480 my $authorised_value;
1492 $tag, $subfield, $liblibrarian,
1494 $mandatory, $repeatable, $authorised_value,
1495 $authtypecode, $value_builder, $kohafield,
1496 $seealso, $hidden, $isurl,
1502 $res->{$tag}->{$subfield}->{lib} =
1503 ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
1504 $res->{$tag}->{$subfield}->{tab} = $tab;
1505 $res->{$tag}->{$subfield}->{mandatory} = $mandatory;
1506 $res->{$tag}->{$subfield}->{repeatable} = $repeatable;
1507 $res->{$tag}->{$subfield}->{authorised_value} = $authorised_value;
1508 $res->{$tag}->{$subfield}->{authtypecode} = $authtypecode;
1509 $res->{$tag}->{$subfield}->{value_builder} = $value_builder;
1510 $res->{$tag}->{$subfield}->{kohafield} = $kohafield;
1511 $res->{$tag}->{$subfield}->{seealso} = $seealso;
1512 $res->{$tag}->{$subfield}->{hidden} = $hidden;
1513 $res->{$tag}->{$subfield}->{isurl} = $isurl;
1514 $res->{$tag}->{$subfield}->{'link'} = $link;
1515 $res->{$tag}->{$subfield}->{defaultvalue} = $defaultvalue;
1520 =head2 GetUsedMarcStructure
1522 the same function as GetMarcStructure expcet it just take field
1523 in tab 0-9. (used field)
1525 my $results = GetUsedMarcStructure($frameworkcode);
1527 L<$results> is a ref to an array which each case containts a ref
1528 to a hash which each keys is the columns from marc_subfield_structure
1530 L<$frameworkcode> is the framework code.
1534 sub GetUsedMarcStructure($){
1535 my $frameworkcode = shift || '';
1536 my $dbh = C4::Context->dbh;
1539 FROM marc_subfield_structure
1541 AND frameworkcode = ?
1544 my $sth = $dbh->prepare($query);
1545 $sth->execute($frameworkcode);
1546 while (my $row = $sth->fetchrow_hashref){
1552 =head2 GetMarcFromKohaField
1556 ($MARCfield,$MARCsubfield)=GetMarcFromKohaField($kohafield,$frameworkcode);
1557 Returns the MARC fields & subfields mapped to the koha field
1558 for the given frameworkcode
1564 sub GetMarcFromKohaField {
1565 my ( $kohafield, $frameworkcode ) = @_;
1566 return 0, 0 unless $kohafield;
1567 my $relations = C4::Context->marcfromkohafield;
1569 $relations->{$frameworkcode}->{$kohafield}->[0],
1570 $relations->{$frameworkcode}->{$kohafield}->[1]
1574 =head2 GetMarcBiblio
1578 Returns MARC::Record of the biblionumber passed in parameter.
1579 the marc record contains both biblio & item datas
1586 my $biblionumber = shift;
1587 my $dbh = C4::Context->dbh;
1589 $dbh->prepare("select marcxml from biblioitems where biblionumber=? ");
1590 $sth->execute($biblionumber);
1591 my ($marcxml) = $sth->fetchrow;
1592 MARC::File::XML->default_record_format(C4::Context->preference('marcflavour'));
1593 $marcxml =~ s/\x1e//g;
1594 $marcxml =~ s/\x1f//g;
1595 $marcxml =~ s/\x1d//g;
1596 $marcxml =~ s/\x0f//g;
1597 $marcxml =~ s/\x0c//g;
1599 my $record = MARC::Record->new();
1600 $record = MARC::Record::new_from_xml( $marcxml, "utf8",C4::Context->preference('marcflavour')) if $marcxml;
1601 # $record = MARC::Record::new_from_usmarc( $marc) if $marc;
1609 my $marcxml = GetXmlBiblio($biblionumber);
1611 Returns biblioitems.marcxml of the biblionumber passed in parameter.
1612 The XML contains both biblio & item datas
1619 my ( $biblionumber ) = @_;
1620 my $dbh = C4::Context->dbh;
1622 $dbh->prepare("select marcxml from biblioitems where biblionumber=? ");
1623 $sth->execute($biblionumber);
1624 my ($marcxml) = $sth->fetchrow;
1628 =head2 GetAuthorisedValueDesc
1632 my $subfieldvalue =get_authorised_value_desc(
1633 $tag, $subf[$i][0],$subf[$i][1], '', $taglib);
1634 Retrieve the complete description for a given authorised value.
1640 sub GetAuthorisedValueDesc {
1641 my ( $tag, $subfield, $value, $framework, $tagslib ) = @_;
1642 my $dbh = C4::Context->dbh;
1645 if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
1646 return C4::Branch::GetBranchName($value);
1650 if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) {
1651 return getitemtypeinfo($value)->{description};
1654 #---- "true" authorized value
1655 my $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'};
1656 if ( $category ne "" ) {
1659 "select lib from authorised_values where category = ? and authorised_value = ?"
1661 $sth->execute( $category, $value );
1662 my $data = $sth->fetchrow_hashref;
1663 return $data->{'lib'};
1666 return $value; # if nothing is found return the original value
1674 Returns MARC::Record of the item passed in parameter.
1681 my ( $biblionumber, $itemnumber ) = @_;
1682 my $dbh = C4::Context->dbh;
1683 my $newrecord = MARC::Record->new();
1684 my $marcflavour = C4::Context->preference('marcflavour');
1686 my $marcxml = GetXmlBiblio($biblionumber);
1687 my $record = MARC::Record->new();
1688 $record = MARC::Record::new_from_xml( $marcxml, "utf8", $marcflavour );
1689 # now, find where the itemnumber is stored & extract only the item
1690 my ( $itemnumberfield, $itemnumbersubfield ) =
1691 GetMarcFromKohaField( 'items.itemnumber', '' );
1692 my @fields = $record->field($itemnumberfield);
1693 foreach my $field (@fields) {
1694 if ( $field->subfield($itemnumbersubfield) eq $itemnumber ) {
1695 $newrecord->insert_fields_ordered($field);
1707 $marcnotesarray = GetMarcNotes( $record, $marcflavour );
1708 Get all notes from the MARC record and returns them in an array.
1709 The note are stored in differents places depending on MARC flavour
1716 my ( $record, $marcflavour ) = @_;
1718 if ( $marcflavour eq "MARC21" ) {
1721 else { # assume unimarc if not marc21
1728 foreach my $field ( $record->field($scope) ) {
1729 my $value = $field->as_string();
1730 if ( $note ne "" ) {
1731 $marcnote = { marcnote => $note, };
1732 push @marcnotes, $marcnote;
1735 if ( $note ne $value ) {
1736 $note = $note . " " . $value;
1741 $marcnote = { marcnote => $note };
1742 push @marcnotes, $marcnote; #load last tag into array
1745 } # end GetMarcNotes
1747 =head2 GetMarcSubjects
1751 $marcsubjcts = GetMarcSubjects($record,$marcflavour);
1752 Get all subjects from the MARC record and returns them in an array.
1753 The subjects are stored in differents places depending on MARC flavour
1759 sub GetMarcSubjects {
1760 my ( $record, $marcflavour ) = @_;
1761 my ( $mintag, $maxtag );
1762 if ( $marcflavour eq "MARC21" ) {
1766 else { # assume unimarc if not marc21
1773 foreach my $field ( $record->fields ) {
1774 next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1775 my @subfields = $field->subfields();
1779 my $authoritysep=C4::Context->preference("authoritysep");
1780 for my $subject_subfield ( @subfields ) {
1782 $marcflavour ne 'MARC21'
1784 ($subject_subfield->[0] eq '3') or
1785 ($subject_subfield->[0] eq '4') or
1786 ($subject_subfield->[0] eq '5')
1792 my $code = $subject_subfield->[0];
1793 $label .= $subject_subfield->[1].$authoritysep unless ( $code == 9 );
1794 $link .= " and su-to:".$subject_subfield->[1] unless ( $code == 9 );
1796 $link = "an:".$subject_subfield->[1];
1800 $link =~ s/ and\ssu-to:$//;
1803 $label =~ s/$authoritysep$//;
1810 return \@marcsubjcts;
1811 } #end GetMarcSubjects
1813 =head2 GetMarcAuthors
1817 authors = GetMarcAuthors($record,$marcflavour);
1818 Get all authors from the MARC record and returns them in an array.
1819 The authors are stored in differents places depending on MARC flavour
1825 sub GetMarcAuthors {
1826 my ( $record, $marcflavour ) = @_;
1827 my ( $mintag, $maxtag );
1828 if ( $marcflavour eq "MARC21" ) {
1832 else { # assume unimarc if not marc21
1839 foreach my $field ( $record->fields ) {
1840 next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1842 my @subfields = $field->subfields();
1845 for my $authors_subfield (@subfields) {
1847 $marcflavour ne 'MARC21'
1849 ($authors_subfield->[0] eq '3') or
1850 ($authors_subfield->[0] eq '4') or
1851 ($authors_subfield->[0] eq '5')
1857 if ($count_auth ne '0'){
1861 my $subfieldcode = $authors_subfield->[0];
1862 my $value = $authors_subfield->[1];
1863 $hash{tag} = $field->tag;
1864 $hash{value} .= $value . " " if ($subfieldcode != 9) ;
1865 $hash{link} .= $value if ($subfieldcode eq 9);
1867 push @marcauthors, \%hash;
1869 return \@marcauthors;
1872 =head2 GetMarcSeries
1876 $marcseriessarray = GetMarcSeries($record,$marcflavour);
1877 Get all series from the MARC record and returns them in an array.
1878 The series are stored in differents places depending on MARC flavour
1885 my ($record, $marcflavour) = @_;
1886 my ($mintag, $maxtag);
1887 if ($marcflavour eq "MARC21") {
1890 } else { # assume unimarc if not marc21
1900 foreach my $field ($record->field('440'), $record->field('490')) {
1902 #my $value = $field->subfield('a');
1903 #$marcsubjct = {MARCSUBJCT => $value,};
1904 my @subfields = $field->subfields();
1905 #warn "subfields:".join " ", @$subfields;
1908 for my $series_subfield (@subfields) {
1910 undef $volume_number;
1911 # see if this is an instance of a volume
1912 if ($series_subfield->[0] eq 'v') {
1916 my $code = $series_subfield->[0];
1917 my $value = $series_subfield->[1];
1918 my $linkvalue = $value;
1919 $linkvalue =~ s/(\(|\))//g;
1920 my $operator = " and " unless $counter==0;
1921 push @link_loop, {link => $linkvalue, operator => $operator };
1922 my $separator = C4::Context->preference("authoritysep") unless $counter==0;
1923 if ($volume_number) {
1924 push @subfields_loop, {volumenum => $value};
1927 push @subfields_loop, {code => $code, value => $value, link_loop => \@link_loop, separator => $separator, volumenum => $volume_number};
1931 push @marcseries, { MARCSERIES_SUBFIELDS_LOOP => \@subfields_loop };
1932 #$marcsubjct = {MARCSUBJCT => $field->as_string(),};
1933 #push @marcsubjcts, $marcsubjct;
1937 my $marcseriessarray=\@marcseries;
1938 return $marcseriessarray;
1939 } #end getMARCseriess
1941 =head2 GetFrameworkCode
1945 $frameworkcode = GetFrameworkCode( $biblionumber )
1951 sub GetFrameworkCode {
1952 my ( $biblionumber ) = @_;
1953 my $dbh = C4::Context->dbh;
1954 my $sth = $dbh->prepare("select frameworkcode from biblio where biblionumber=?");
1955 $sth->execute($biblionumber);
1956 my ($frameworkcode) = $sth->fetchrow;
1957 return $frameworkcode;
1960 =head2 GetPublisherNameFromIsbn
1962 $name = GetPublishercodeFromIsbn($isbn);
1969 sub GetPublisherNameFromIsbn($){
1971 $isbn =~ s/[- _]//g;
1973 my @codes = (split '-', DisplayISBN($isbn));
1974 my $code = $codes[0].$codes[1].$codes[2];
1975 my $dbh = C4::Context->dbh;
1977 SELECT distinct publishercode
1980 AND publishercode IS NOT NULL
1983 my $sth = $dbh->prepare($query);
1984 $sth->execute("$code%");
1985 my $name = $sth->fetchrow;
1986 return $name if length $name;
1990 =head2 TransformKohaToMarc
1994 $record = TransformKohaToMarc( $hash )
1995 This function builds partial MARC::Record from a hash
1996 Hash entries can be from biblio or biblioitems.
1997 This function is called in acquisition module, to create a basic catalogue entry from user entry
2003 sub TransformKohaToMarc {
2006 my $dbh = C4::Context->dbh;
2009 "select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
2011 my $record = MARC::Record->new();
2012 foreach (keys %{$hash}) {
2013 &TransformKohaToMarcOneField( $sth, $record, $_,
2019 =head2 TransformKohaToMarcOneField
2023 $record = TransformKohaToMarcOneField( $sth, $record, $kohafieldname, $value, $frameworkcode );
2029 sub TransformKohaToMarcOneField {
2030 my ( $sth, $record, $kohafieldname, $value, $frameworkcode ) = @_;
2031 $frameworkcode='' unless $frameworkcode;
2035 if ( !defined $sth ) {
2036 my $dbh = C4::Context->dbh;
2037 $sth = $dbh->prepare(
2038 "select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
2041 $sth->execute( $frameworkcode, $kohafieldname );
2042 if ( ( $tagfield, $tagsubfield ) = $sth->fetchrow ) {
2043 my $tag = $record->field($tagfield);
2045 $tag->update( $tagsubfield => $value );
2046 $record->delete_field($tag);
2047 $record->insert_fields_ordered($tag);
2050 $record->add_fields( $tagfield, " ", " ", $tagsubfield => $value );
2056 =head2 TransformHtmlToXml
2060 $xml = TransformHtmlToXml( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type )
2062 $auth_type contains :
2063 - nothing : rebuild a biblio, un UNIMARC the encoding is in 100$a pos 26/27
2064 - UNIMARCAUTH : rebuild an authority. In UNIMARC, the encoding is in 100$a pos 13/14
2065 - ITEM : rebuild an item : in UNIMARC, 100$a, it's in the biblio ! (otherwise, we would get 2 100 fields !)
2071 sub TransformHtmlToXml {
2072 my ( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type ) = @_;
2073 my $xml = MARC::File::XML::header('UTF-8');
2074 $auth_type = C4::Context->preference('marcflavour') unless $auth_type;
2075 MARC::File::XML->default_record_format($auth_type);
2076 # in UNIMARC, field 100 contains the encoding
2077 # check that there is one, otherwise the
2078 # MARC::Record->new_from_xml will fail (and Koha will die)
2079 my $unimarc_and_100_exist=0;
2080 $unimarc_and_100_exist=1 if $auth_type eq 'ITEM'; # if we rebuild an item, no need of a 100 field
2085 for ( my $i = 0 ; $i <= @$tags ; $i++ ) {
2086 if (C4::Context->preference('marcflavour') eq 'UNIMARC' and @$tags[$i] eq "100" and @$subfields[$i] eq "a") {
2087 # if we have a 100 field and it's values are not correct, skip them.
2088 # if we don't have any valid 100 field, we will create a default one at the end
2089 my $enc = substr( @$values[$i], 26, 2 );
2090 if ($enc eq '01' or $enc eq '50' or $enc eq '03') {
2091 $unimarc_and_100_exist=1;
2096 @$values[$i] =~ s/&/&/g;
2097 @$values[$i] =~ s/</</g;
2098 @$values[$i] =~ s/>/>/g;
2099 @$values[$i] =~ s/"/"/g;
2100 @$values[$i] =~ s/'/'/g;
2101 # if ( !utf8::is_utf8( @$values[$i] ) ) {
2102 # utf8::decode( @$values[$i] );
2104 if ( ( @$tags[$i] ne $prevtag ) ) {
2105 $j++ unless ( @$tags[$i] eq "" );
2107 $xml .= "</datafield>\n";
2108 if ( ( @$tags[$i] && @$tags[$i] > 10 )
2109 && ( @$values[$i] ne "" ) )
2111 my $ind1 = substr( @$indicator[$j], 0, 1 );
2113 if ( @$indicator[$j] ) {
2114 $ind2 = substr( @$indicator[$j], 1, 1 );
2117 warn "Indicator in @$tags[$i] is empty";
2121 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2123 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2131 if ( @$values[$i] ne "" ) {
2134 if ( @$tags[$i] eq "000" ) {
2135 $xml .= "<leader>@$values[$i]</leader>\n";
2138 # rest of the fixed fields
2140 elsif ( @$tags[$i] < 10 ) {
2142 "<controlfield tag=\"@$tags[$i]\">@$values[$i]</controlfield>\n";
2146 my $ind1 = substr( @$indicator[$j], 0, 1 );
2147 my $ind2 = substr( @$indicator[$j], 1, 1 );
2149 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2151 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2157 else { # @$tags[$i] eq $prevtag
2158 if ( @$values[$i] eq "" ) {
2162 my $ind1 = substr( @$indicator[$j], 0, 1 );
2163 my $ind2 = substr( @$indicator[$j], 1, 1 );
2165 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2169 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2172 $prevtag = @$tags[$i];
2174 if (C4::Context->preference('marcflavour') and !$unimarc_and_100_exist) {
2175 # warn "SETTING 100 for $auth_type";
2176 use POSIX qw(strftime);
2177 my $string = strftime( "%Y%m%d", localtime(time) );
2178 # set 50 to position 26 is biblios, 13 if authorities
2180 $pos=13 if $auth_type eq 'UNIMARCAUTH';
2181 $string = sprintf( "%-*s", 35, $string );
2182 substr( $string, $pos , 6, "50" );
2183 $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
2184 $xml .= "<subfield code=\"a\">$string</subfield>\n";
2185 $xml .= "</datafield>\n";
2187 $xml .= MARC::File::XML::footer();
2191 =head2 TransformHtmlToMarc
2193 L<$record> = TransformHtmlToMarc(L<$params>,L<$cgi>)
2194 L<$params> is a ref to an array as below:
2196 'tag_010_indicator_531951' ,
2197 'tag_010_code_a_531951_145735' ,
2198 'tag_010_subfield_a_531951_145735' ,
2199 'tag_200_indicator_873510' ,
2200 'tag_200_code_a_873510_673465' ,
2201 'tag_200_subfield_a_873510_673465' ,
2202 'tag_200_code_b_873510_704318' ,
2203 'tag_200_subfield_b_873510_704318' ,
2204 'tag_200_code_e_873510_280822' ,
2205 'tag_200_subfield_e_873510_280822' ,
2206 'tag_200_code_f_873510_110730' ,
2207 'tag_200_subfield_f_873510_110730' ,
2209 L<$cgi> is the CGI object which containts the value.
2210 L<$record> is the MARC::Record object.
2214 sub TransformHtmlToMarc {
2218 # creating a new record
2219 my $record = MARC::Record->new();
2223 while ($params->[$i]){ # browse all CGI params
2224 my $param = $params->[$i];
2226 if($param =~ /^tag_(\d*)_indicator_/){ # new field start when having 'input name="..._indicator_..."
2229 my $ind1 = substr($cgi->param($param),0,1);
2230 my $ind2 = substr($cgi->param($param),1,1);
2235 if($tag < 10){ # no code for theses fields
2236 my $inner_param = $params->[$j];
2237 $newfield = MARC::Field->new(
2239 $cgi->param($params->[$j+1]),
2242 while($params->[$j] =~ /_code_/){ # browse all it's subfield
2243 my $inner_param = $params->[$j];
2245 if($cgi->param($params->[$j+1])){ # only if there is a value (code => value)
2246 $newfield->add_subfields(
2247 $cgi->param($inner_param) => $cgi->param($params->[$j+1])
2251 if ( $cgi->param($params->[$j+1]) ) { # creating only if there is a value (code => value)
2252 $newfield = MARC::Field->new(
2256 $cgi->param($inner_param) => $cgi->param($params->[$j+1]),
2263 push @fields,$newfield if($newfield);
2268 $record->append_fields(@fields);
2272 =head2 TransformMarcToKoha
2276 $result = TransformMarcToKoha( $dbh, $record, $frameworkcode )
2282 sub TransformMarcToKoha {
2283 my ( $dbh, $record, $frameworkcode ) = @_;
2285 # FIXME :: This query is unused..
2288 #"select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
2292 my $sth2 = $dbh->prepare("SHOW COLUMNS from biblio");
2295 while ( ($field) = $sth2->fetchrow ) {
2297 &TransformMarcToKohaOneField( "biblio", $field, $record, $result,
2301 while ( ($field) = $sth2->fetchrow ) {
2302 if ( $field eq 'notes' ) { $field = 'bnotes'; }
2304 &TransformMarcToKohaOneField( "biblioitems", $field, $record, $result,
2307 $sth2 = $dbh->prepare("SHOW COLUMNS from items");
2309 while ( ($field) = $sth2->fetchrow ) {
2311 &TransformMarcToKohaOneField( "items", $field, $record, $result,
2316 # modify copyrightdate to keep only the 1st year found
2317 my $temp = $result->{'copyrightdate'};
2318 $temp =~ m/c(\d\d\d\d)/; # search cYYYY first
2320 $result->{'copyrightdate'} = $1;
2322 else { # if no cYYYY, get the 1st date.
2323 $temp =~ m/(\d\d\d\d)/;
2324 $result->{'copyrightdate'} = $1;
2327 # modify publicationyear to keep only the 1st year found
2328 $temp = $result->{'publicationyear'};
2329 $temp =~ m/c(\d\d\d\d)/; # search cYYYY first
2331 $result->{'publicationyear'} = $1;
2333 else { # if no cYYYY, get the 1st date.
2334 $temp =~ m/(\d\d\d\d)/;
2335 $result->{'publicationyear'} = $1;
2340 =head2 TransformMarcToKohaOneField
2344 $result = TransformMarcToKohaOneField( $kohatable, $kohafield, $record, $result, $frameworkcode )
2350 sub TransformMarcToKohaOneField {
2352 # FIXME ? if a field has a repeatable subfield that is used in old-db,
2353 # only the 1st will be retrieved...
2354 my ( $kohatable, $kohafield, $record, $result, $frameworkcode ) = @_;
2356 my ( $tagfield, $subfield ) =
2357 GetMarcFromKohaField( $kohatable . "." . $kohafield,
2359 foreach my $field ( $record->field($tagfield) ) {
2360 if ( $field->tag() < 10 ) {
2361 if ( $result->{$kohafield} ) {
2362 $result->{$kohafield} .= " | " . $field->data();
2365 $result->{$kohafield} = $field->data();
2369 if ( $field->subfields ) {
2370 my @subfields = $field->subfields();
2371 foreach my $subfieldcount ( 0 .. $#subfields ) {
2372 if ( $subfields[$subfieldcount][0] eq $subfield ) {
2373 if ( $result->{$kohafield} ) {
2374 $result->{$kohafield} .=
2375 " | " . $subfields[$subfieldcount][1];
2378 $result->{$kohafield} =
2379 $subfields[$subfieldcount][1];
2389 =head1 OTHER FUNCTIONS
2395 my $string = char_decode( $string, $encoding );
2397 converts ISO 5426 coded string to UTF-8
2398 sloppy code : should be improved in next issue
2405 my ( $string, $encoding ) = @_;
2408 $encoding = C4::Context->preference("marcflavour") unless $encoding;
2409 if ( $encoding eq "UNIMARC" ) {
2479 # this handles non-sorting blocks (if implementation requires this)
2480 $string = nsb_clean($_);
2482 elsif ( $encoding eq "USMARC" || $encoding eq "MARC21" ) {
2541 #Additional Turkish characters
2544 s/(\xf0)s/\xc5\x9f/gm;
2545 s/(\xf0)S/\xc5\x9e/gm;
2548 s/\xe7\x49/\\xc4\xb0/gm;
2549 s/(\xe6)G/\xc4\x9e/gm;
2550 s/(\xe6)g/ğ\xc4\x9f/gm;
2553 s/(\xe8|\xc8)o/ö/gm;
2554 s/(\xe8|\xc8)O/Ö/gm;
2555 s/(\xe8|\xc8)u/ü/gm;
2556 s/(\xe8|\xc8)U/Ü/gm;
2557 s/\xc2\xb8/\xc4\xb1/gm;
2560 # this handles non-sorting blocks (if implementation requires this)
2561 $string = nsb_clean($_);
2570 my $string = nsb_clean( $string, $encoding );
2577 my $NSB = '\x88'; # NSB : begin Non Sorting Block
2578 my $NSE = '\x89'; # NSE : Non Sorting Block end
2579 # handles non sorting blocks
2583 s/[ ]{0,1}$NSE/) /gm;
2588 =head2 PrepareItemrecordDisplay
2592 PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber);
2594 Returns a hash with all the fields for Display a given item data in a template
2600 sub PrepareItemrecordDisplay {
2602 my ( $bibnum, $itemnum ) = @_;
2604 my $dbh = C4::Context->dbh;
2605 my $frameworkcode = &GetFrameworkCode( $bibnum );
2606 my ( $itemtagfield, $itemtagsubfield ) =
2607 &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2608 my $tagslib = &GetMarcStructure( 1, $frameworkcode );
2609 my $itemrecord = GetMarcItem( $bibnum, $itemnum) if ($itemnum);
2611 my $authorised_values_sth =
2613 "select authorised_value,lib from authorised_values where category=? order by lib"
2615 foreach my $tag ( sort keys %{$tagslib} ) {
2616 my $previous_tag = '';
2618 # loop through each subfield
2620 foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
2621 next if ( subfield_is_koha_internal_p($subfield) );
2622 next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
2624 $subfield_data{tag} = $tag;
2625 $subfield_data{subfield} = $subfield;
2626 $subfield_data{countsubfield} = $cntsubf++;
2627 $subfield_data{kohafield} =
2628 $tagslib->{$tag}->{$subfield}->{'kohafield'};
2630 # $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
2631 $subfield_data{marc_lib} =
2632 "<span id=\"error\" title=\""
2633 . $tagslib->{$tag}->{$subfield}->{lib} . "\">"
2634 . substr( $tagslib->{$tag}->{$subfield}->{lib}, 0, 12 )
2636 $subfield_data{mandatory} =
2637 $tagslib->{$tag}->{$subfield}->{mandatory};
2638 $subfield_data{repeatable} =
2639 $tagslib->{$tag}->{$subfield}->{repeatable};
2640 $subfield_data{hidden} = "display:none"
2641 if $tagslib->{$tag}->{$subfield}->{hidden};
2643 ( $x, $value ) = _find_value( $tag, $subfield, $itemrecord )
2645 $value =~ s/"/"/g;
2647 # search for itemcallnumber if applicable
2648 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq
2649 'items.itemcallnumber'
2650 && C4::Context->preference('itemcallnumber') )
2653 substr( C4::Context->preference('itemcallnumber'), 0, 3 );
2655 substr( C4::Context->preference('itemcallnumber'), 3, 1 );
2656 my $temp = $itemrecord->field($CNtag) if ($itemrecord);
2658 $value = $temp->subfield($CNsubfield);
2661 if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
2662 my @authorised_values;
2665 # builds list, depending on authorised value...
2667 if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq
2670 if ( ( C4::Context->preference("IndependantBranches") )
2671 && ( C4::Context->userenv->{flags} != 1 ) )
2675 "select branchcode,branchname from branches where branchcode = ? order by branchname"
2677 $sth->execute( C4::Context->userenv->{branch} );
2678 push @authorised_values, ""
2680 $tagslib->{$tag}->{$subfield}->{mandatory} );
2681 while ( my ( $branchcode, $branchname ) =
2682 $sth->fetchrow_array )
2684 push @authorised_values, $branchcode;
2685 $authorised_lib{$branchcode} = $branchname;
2691 "select branchcode,branchname from branches order by branchname"
2694 push @authorised_values, ""
2696 $tagslib->{$tag}->{$subfield}->{mandatory} );
2697 while ( my ( $branchcode, $branchname ) =
2698 $sth->fetchrow_array )
2700 push @authorised_values, $branchcode;
2701 $authorised_lib{$branchcode} = $branchname;
2707 elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq
2712 "select itemtype,description from itemtypes order by description"
2715 push @authorised_values, ""
2716 unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2717 while ( my ( $itemtype, $description ) =
2718 $sth->fetchrow_array )
2720 push @authorised_values, $itemtype;
2721 $authorised_lib{$itemtype} = $description;
2724 #---- "true" authorised value
2727 $authorised_values_sth->execute(
2728 $tagslib->{$tag}->{$subfield}->{authorised_value} );
2729 push @authorised_values, ""
2730 unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2731 while ( my ( $value, $lib ) =
2732 $authorised_values_sth->fetchrow_array )
2734 push @authorised_values, $value;
2735 $authorised_lib{$value} = $lib;
2738 $subfield_data{marc_value} = CGI::scrolling_list(
2739 -name => 'field_value',
2740 -values => \@authorised_values,
2741 -default => "$value",
2742 -labels => \%authorised_lib,
2748 elsif ( $tagslib->{$tag}->{$subfield}->{thesaurus_category} ) {
2749 $subfield_data{marc_value} =
2750 "<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>";
2753 # COMMENTED OUT because No $i is provided with this API.
2754 # And thus, no value_builder can be activated.
2755 # BUT could be thought over.
2756 # } elsif ($tagslib->{$tag}->{$subfield}->{'value_builder'}) {
2757 # my $plugin="value_builder/".$tagslib->{$tag}->{$subfield}->{'value_builder'};
2759 # my $extended_param = plugin_parameters($dbh,$itemrecord,$tagslib,$i,0);
2760 # my ($function_name,$javascript) = plugin_javascript($dbh,$record,$tagslib,$i,0);
2761 # $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";
2764 $subfield_data{marc_value} =
2765 "<input type=\"text\" name=\"field_value\" value=\"$value\" size=50 maxlength=255>";
2767 push( @loop_data, \%subfield_data );
2771 my $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield )
2772 if ( $itemrecord && $itemrecord->field($itemtagfield) );
2774 'itemtagfield' => $itemtagfield,
2775 'itemtagsubfield' => $itemtagsubfield,
2776 'itemnumber' => $itemnumber,
2777 'iteminformation' => \@loop_data
2783 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2785 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2786 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2787 # =head2 ModZebrafiles
2789 # &ModZebrafiles( $dbh, $biblionumber, $record, $folder, $server );
2793 # sub ModZebrafiles {
2795 # my ( $dbh, $biblionumber, $record, $folder, $server ) = @_;
2799 # C4::Context->zebraconfig($server)->{directory} . "/" . $folder . "/";
2800 # unless ( opendir( DIR, "$zebradir" ) ) {
2801 # warn "$zebradir not found";
2805 # my $filename = $zebradir . $biblionumber;
2808 # open( OUTPUT, ">", $filename . ".xml" );
2809 # print OUTPUT $record;
2818 ModZebra( $biblionumber, $op, $server, $newRecord );
2820 $biblionumber is the biblionumber we want to index
2821 $op is specialUpdate or delete, and is used to know what we want to do
2822 $server is the server that we want to update
2823 $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.
2830 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
2831 my ( $biblionumber, $op, $server, $newRecord ) = @_;
2832 my $dbh=C4::Context->dbh;
2834 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2836 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2837 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2839 if (C4::Context->preference("NoZebra")) {
2840 # lock the nozebra table : we will read index lines, update them in Perl process
2841 # and write everything in 1 transaction.
2842 # lock the table to avoid someone else overwriting what we are doing
2843 $dbh->do('LOCK TABLES nozebra WRITE,biblio WRITE,biblioitems WRITE, systempreferences WRITE, auth_types WRITE, auth_header WRITE');
2844 my %result; # the result hash that will be builded by deletion / add, and written on mySQL at the end, to improve speed
2846 if ($server eq 'biblioserver') {
2847 $record= GetMarcBiblio($biblionumber);
2849 $record= C4::AuthoritiesMarc::GetAuthority($biblionumber);
2851 if ($op eq 'specialUpdate') {
2852 # OK, we have to add or update the record
2853 # 1st delete (virtually, in indexes) ...
2854 %result = _DelBiblioNoZebra($biblionumber,$record,$server);
2855 # ... add the record
2856 %result=_AddBiblioNoZebra($biblionumber,$newRecord, $server, %result);
2858 # it's a deletion, delete the record...
2859 # warn "DELETE the record $biblionumber on $server".$record->as_formatted;
2860 %result=_DelBiblioNoZebra($biblionumber,$record,$server);
2862 # ok, now update the database...
2863 my $sth = $dbh->prepare("UPDATE nozebra SET biblionumbers=? WHERE server=? AND indexname=? AND value=?");
2864 foreach my $key (keys %result) {
2865 foreach my $index (keys %{$result{$key}}) {
2866 $sth->execute($result{$key}->{$index}, $server, $key, $index);
2869 $dbh->do('UNLOCK TABLES');
2873 # we use zebra, just fill zebraqueue table
2875 my $sth=$dbh->prepare("insert into zebraqueue (biblio_auth_number ,server,operation) values(?,?,?)");
2876 $sth->execute($biblionumber,$server,$op);
2881 =head2 GetNoZebraIndexes
2883 %indexes = GetNoZebraIndexes;
2885 return the data from NoZebraIndexes syspref.
2889 sub GetNoZebraIndexes {
2890 my $index = C4::Context->preference('NoZebraIndexes');
2892 foreach my $line (split /('|"),/,$index) {
2893 $line =~ /(.*)=>(.*)/;
2894 my $index = substr($1,1); # get the index, don't forget to remove initial ' or "
2896 $index =~ s/'|"| //g;
2897 $fields =~ s/'|"| //g;
2898 $indexes{$index}=$fields;
2903 =head1 INTERNAL FUNCTIONS
2905 =head2 _DelBiblioNoZebra($biblionumber,$record,$server);
2907 function to delete a biblio in NoZebra indexes
2908 This function does NOT delete anything in database : it reads all the indexes entries
2909 that have to be deleted & delete them in the hash
2910 The SQL part is done either :
2911 - after the Add if we are modifying a biblio (delete + add again)
2912 - immediatly after this sub if we are doing a true deletion.
2913 $server can be 'biblioserver' or 'authorityserver' : it indexes biblios or authorities (in the same table, $server being part of the table itself
2918 sub _DelBiblioNoZebra {
2919 my ($biblionumber, $record, $server)=@_;
2922 my $dbh = C4::Context->dbh;
2926 if ($server eq 'biblioserver') {
2927 %index=GetNoZebraIndexes;
2928 # get title of the record (to store the 10 first letters with the index)
2929 my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title');
2930 $title = lc($record->subfield($titletag,$titlesubfield));
2932 # for authorities, the "title" is the $a mainentry
2933 my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield(152,'b'));
2934 warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
2935 $title = $record->subfield($authref->{auth_tag_to_report},'a');
2936 $index{'mainmainentry'}= $authref->{'auth_tag_to_report'}.'a';
2937 $index{'mainentry'} = $authref->{'auth_tag_to_report'}.'*';
2938 $index{'auth_type'} = '152b';
2942 # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2943 $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
2944 # limit to 10 char, should be enough, and limit the DB size
2945 $title = substr($title,0,10);
2947 my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2948 foreach my $field ($record->fields()) {
2949 #parse each subfield
2950 next if $field->tag <10;
2951 foreach my $subfield ($field->subfields()) {
2952 my $tag = $field->tag();
2953 my $subfieldcode = $subfield->[0];
2955 # check each index to see if the subfield is stored somewhere
2956 # otherwise, store it in __RAW__ index
2957 foreach my $key (keys %index) {
2958 # warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2959 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
2961 my $line= lc $subfield->[1];
2962 # remove meaningless value in the field...
2963 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2964 # ... and split in words
2965 foreach (split / /,$line) {
2966 next unless $_; # skip empty values (multiple spaces)
2967 # if the entry is already here, do nothing, the biblionumber has already be removed
2968 unless ($result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/) {
2969 # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2970 $sth2->execute($server,$key,$_);
2971 my $existing_biblionumbers = $sth2->fetchrow;
2973 if ($existing_biblionumbers) {
2974 # warn " existing for $key $_: $existing_biblionumbers";
2975 $result{$key}->{$_} =$existing_biblionumbers;
2976 $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2982 # the subfield is not indexed, store it in __RAW__ index anyway
2984 my $line= lc $subfield->[1];
2985 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2986 # ... and split in words
2987 foreach (split / /,$line) {
2988 next unless $_; # skip empty values (multiple spaces)
2989 # if the entry is already here, do nothing, the biblionumber has already be removed
2990 unless ($result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/) {
2991 # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2992 $sth2->execute($server,'__RAW__',$_);
2993 my $existing_biblionumbers = $sth2->fetchrow;
2995 if ($existing_biblionumbers) {
2996 $result{'__RAW__'}->{$_} =$existing_biblionumbers;
2997 $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
3007 =head2 _AddBiblioNoZebra($biblionumber, $record, $server, %result);
3009 function to add a biblio in NoZebra indexes
3013 sub _AddBiblioNoZebra {
3014 my ($biblionumber, $record, $server, %result)=@_;
3015 my $dbh = C4::Context->dbh;
3019 if ($server eq 'biblioserver') {
3020 %index=GetNoZebraIndexes;
3021 # get title of the record (to store the 10 first letters with the index)
3022 my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title');
3023 $title = lc($record->subfield($titletag,$titlesubfield));
3025 # warn "server : $server";
3026 # for authorities, the "title" is the $a mainentry
3027 my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield(152,'b'));
3028 warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
3029 $title = $record->subfield($authref->{auth_tag_to_report},'a');
3030 $index{'mainmainentry'} = $authref->{auth_tag_to_report}.'a';
3031 $index{'mainentry'} = $authref->{auth_tag_to_report}.'*';
3032 $index{'auth_type'} = '152b';
3035 # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
3036 $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
3037 # limit to 10 char, should be enough, and limit the DB size
3038 $title = substr($title,0,10);
3040 my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
3041 foreach my $field ($record->fields()) {
3042 #parse each subfield
3043 next if $field->tag <10;
3044 foreach my $subfield ($field->subfields()) {
3045 my $tag = $field->tag();
3046 my $subfieldcode = $subfield->[0];
3048 # check each index to see if the subfield is stored somewhere
3049 # otherwise, store it in __RAW__ index
3050 foreach my $key (keys %index) {
3051 # warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
3052 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
3054 my $line= lc $subfield->[1];
3055 # remove meaningless value in the field...
3056 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
3057 # ... and split in words
3058 foreach (split / /,$line) {
3059 next unless $_; # skip empty values (multiple spaces)
3060 # if the entry is already here, improve weight
3061 # warn "managing $_";
3062 if ($result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/) {
3064 $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
3065 $result{$key}->{$_} .= "$biblionumber,$title-$weight;";
3067 # get the value if it exist in the nozebra table, otherwise, create it
3068 $sth2->execute($server,$key,$_);
3069 my $existing_biblionumbers = $sth2->fetchrow;
3071 if ($existing_biblionumbers) {
3072 $result{$key}->{$_} =$existing_biblionumbers;
3074 $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
3075 $result{$key}->{$_} .= "$biblionumber,$title-$weight;";
3076 # create a new ligne for this entry
3078 # warn "INSERT : $server / $key / $_";
3079 $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).', indexname='.$dbh->quote($key).',value='.$dbh->quote($_));
3080 $result{$key}->{$_}.="$biblionumber,$title-1;";
3086 # the subfield is not indexed, store it in __RAW__ index anyway
3088 my $line= lc $subfield->[1];
3089 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
3090 # ... and split in words
3091 foreach (split / /,$line) {
3092 next unless $_; # skip empty values (multiple spaces)
3093 # if the entry is already here, improve weight
3094 if ($result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/) {
3096 $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
3097 $result{'__RAW__'}->{$_} .= "$biblionumber,$title-$weight;";
3099 # get the value if it exist in the nozebra table, otherwise, create it
3100 $sth2->execute($server,'__RAW__',$_);
3101 my $existing_biblionumbers = $sth2->fetchrow;
3103 if ($existing_biblionumbers) {
3104 $result{'__RAW__'}->{$_} =$existing_biblionumbers;
3106 $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
3107 $result{'__RAW__'}->{$_} .= "$biblionumber,$title-$weight;";
3108 # create a new ligne for this entry
3110 $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).', indexname="__RAW__",value='.$dbh->quote($_));
3111 $result{'__RAW__'}->{$_}.="$biblionumber,$title-1;";
3122 =head2 MARCitemchange
3126 &MARCitemchange( $record, $itemfield, $newvalue )
3128 Function to update a single value in an item field.
3129 Used twice, could probably be replaced by something else, but works well...
3137 sub MARCitemchange {
3138 my ( $record, $itemfield, $newvalue ) = @_;
3139 my $dbh = C4::Context->dbh;
3141 my ( $tagfield, $tagsubfield ) =
3142 GetMarcFromKohaField( $itemfield, "" );
3143 if ( ($tagfield) && ($tagsubfield) ) {
3144 my $tag = $record->field($tagfield);
3146 $tag->update( $tagsubfield => $newvalue );
3147 $record->delete_field($tag);
3148 $record->insert_fields_ordered($tag);
3153 =head2 _koha_add_biblio
3157 _koha_add_biblio($dbh,$biblioitem);
3159 Internal function to add a biblio ($biblio is a hash with the values)
3165 sub _koha_add_biblio {
3166 my ( $dbh, $biblio, $frameworkcode ) = @_;
3167 my $sth = $dbh->prepare("Select max(biblionumber) from biblio");
3169 my $data = $sth->fetchrow_arrayref;
3170 my $biblionumber = $$data[0] + 1;
3173 if ( $biblio->{'seriestitle'} ) { $series = 1 }
3175 $sth = $dbh->prepare(
3177 SET biblionumber = ?, title = ?, author = ?, copyrightdate = ?, serial = ?, seriestitle = ?, notes = ?, abstract = ?, unititle = ?, frameworkcode = ? "
3180 $biblionumber, $biblio->{'title'},
3181 $biblio->{'author'}, $biblio->{'copyrightdate'},
3182 $biblio->{'serial'}, $biblio->{'seriestitle'},
3183 $biblio->{'notes'}, $biblio->{'abstract'},
3184 $biblio->{'unititle'}, $frameworkcode
3188 return ($biblionumber);
3195 ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
3197 Find the given $subfield in the given $tag in the given
3198 MARC::Record $record. If the subfield is found, returns
3199 the (indicators, value) pair; otherwise, (undef, undef) is
3203 Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
3204 I suggest we export it from this module.
3211 my ( $tagfield, $insubfield, $record, $encoding ) = @_;
3214 if ( $tagfield < 10 ) {
3215 if ( $record->field($tagfield) ) {
3216 push @result, $record->field($tagfield)->data();
3223 foreach my $field ( $record->field($tagfield) ) {
3224 my @subfields = $field->subfields();
3225 foreach my $subfield (@subfields) {
3226 if ( @$subfield[0] eq $insubfield ) {
3227 push @result, @$subfield[1];
3228 $indicator = $field->indicator(1) . $field->indicator(2);
3233 return ( $indicator, @result );
3236 =head2 _koha_modify_biblio
3240 $biblionumber = _koha_modify_biblio($dbh,$biblio);
3241 Internal function for updating the biblio table
3247 sub _koha_modify_biblio {
3248 my ( $dbh, $biblio ) = @_;
3249 # FIXME: this code could be made more portable by not hard-coding
3250 # the values that are supposed to be in biblio table
3261 WHERE biblionumber = ?
3263 my $sth = $dbh->prepare($query);
3267 $biblio->{'author'},
3268 $biblio->{'abstract'},
3269 $biblio->{'copyrightdate'},
3270 $biblio->{'seriestitle'},
3271 $biblio->{'serial'},
3272 $biblio->{'unititle'},
3274 $biblio->{'biblionumber'}
3275 ) if $biblio->{'biblionumber'};
3277 warn $sth->err if $sth->err;
3278 warn "BIG ERROR :: No biblionumber for $biblio->{title}" if $biblio->{biblionumber} !~ /\d+/; # if it is not a number
3279 return ( $biblio->{'biblionumber'} );
3282 =head2 _koha_modify_biblioitem
3286 _koha_modify_biblioitem( $dbh, $biblioitem );
3292 sub _koha_modify_biblioitem {
3293 my ( $dbh, $biblioitem ) = @_;
3295 ##Recalculate LC in case it changed --TG
3297 $biblioitem->{'itemtype'} = $dbh->quote( $biblioitem->{'itemtype'} );
3298 $biblioitem->{'url'} = $dbh->quote( $biblioitem->{'url'} );
3299 $biblioitem->{'isbn'} = $dbh->quote( $biblioitem->{'isbn'} );
3300 $biblioitem->{'issn'} = $dbh->quote( $biblioitem->{'issn'} );
3301 $biblioitem->{'publishercode'} =
3302 $dbh->quote( $biblioitem->{'publishercode'} );
3303 $biblioitem->{'publicationyear'} =
3304 $dbh->quote( $biblioitem->{'publicationyear'} );
3305 $biblioitem->{'classification'} =
3306 $dbh->quote( $biblioitem->{'classification'} );
3307 $biblioitem->{'dewey'} = $dbh->quote( $biblioitem->{'dewey'} );
3308 $biblioitem->{'subclass'} = $dbh->quote( $biblioitem->{'subclass'} );
3309 $biblioitem->{'illus'} = $dbh->quote( $biblioitem->{'illus'} );
3310 $biblioitem->{'pages'} = $dbh->quote( $biblioitem->{'pages'} );
3311 $biblioitem->{'volumeddesc'} = $dbh->quote( $biblioitem->{'volumeddesc'} );
3312 $biblioitem->{'bnotes'} = $dbh->quote( $biblioitem->{'bnotes'} );
3313 $biblioitem->{'size'} = $dbh->quote( $biblioitem->{'size'} );
3314 $biblioitem->{'place'} = $dbh->quote( $biblioitem->{'place'} );
3315 $biblioitem->{'ccode'} = $dbh->quote( $biblioitem->{'ccode'} );
3316 $biblioitem->{'biblionumber'} =
3317 $dbh->quote( $biblioitem->{'biblionumber'} );
3319 $query = "Update biblioitems set
3320 itemtype = $biblioitem->{'itemtype'},
3321 url = $biblioitem->{'url'},
3322 isbn = $biblioitem->{'isbn'},
3323 issn = $biblioitem->{'issn'},
3324 publishercode = $biblioitem->{'publishercode'},
3325 publicationyear = $biblioitem->{'publicationyear'},
3326 classification = $biblioitem->{'classification'},
3327 dewey = $biblioitem->{'dewey'},
3328 subclass = $biblioitem->{'subclass'},
3329 illus = $biblioitem->{'illus'},
3330 pages = $biblioitem->{'pages'},
3331 volumeddesc = $biblioitem->{'volumeddesc'},
3332 notes = $biblioitem->{'bnotes'},
3333 size = $biblioitem->{'size'},
3334 place = $biblioitem->{'place'},
3335 ccode = $biblioitem->{'ccode'}
3336 where biblionumber = $biblioitem->{'biblionumber'}";
3339 if ( $dbh->errstr ) {
3340 warn "ERROR in _koha_modify_biblioitem $query";
3344 =head2 _koha_add_biblioitem
3348 _koha_add_biblioitem( $dbh, $biblioitem );
3350 Internal function to add a biblioitem
3356 sub _koha_add_biblioitem {
3357 my ( $dbh, $biblioitem ) = @_;
3359 # my $dbh = C4Connect;
3360 my $sth = $dbh->prepare("SELECT max(biblioitemnumber) FROM biblioitems");
3365 $data = $sth->fetchrow_arrayref;
3366 $bibitemnum = $$data[0] + 1;
3370 $sth = $dbh->prepare(
3371 "INSERT INTO biblioitems SET
3372 biblioitemnumber = ?, biblionumber = ?,
3373 volume = ?, number = ?,
3374 classification = ?, itemtype = ?,
3376 issn = ?, dewey = ?,
3377 subclass = ?, publicationyear = ?,
3378 publishercode = ?, volumedate = ?,
3379 volumeddesc = ?, illus = ?,
3380 pages = ?, notes = ?,
3382 marc = ?, lcsort =?,
3383 place = ?, ccode = ?
3387 calculatelc( $biblioitem->{'classification'} )
3388 . $biblioitem->{'subclass'};
3390 $bibitemnum, $biblioitem->{'biblionumber'},
3391 $biblioitem->{'volume'}, $biblioitem->{'number'},
3392 $biblioitem->{'classification'}, $biblioitem->{'itemtype'},
3393 $biblioitem->{'url'}, $biblioitem->{'isbn'},
3394 $biblioitem->{'issn'}, $biblioitem->{'dewey'},
3395 $biblioitem->{'subclass'}, $biblioitem->{'publicationyear'},
3396 $biblioitem->{'publishercode'}, $biblioitem->{'volumedate'},
3397 $biblioitem->{'volumeddesc'}, $biblioitem->{'illus'},
3398 $biblioitem->{'pages'}, $biblioitem->{'bnotes'},
3399 $biblioitem->{'size'}, $biblioitem->{'lccn'},
3400 $biblioitem->{'marc'}, $biblioitem->{'place'},
3401 $lcsort, $biblioitem->{'ccode'}
3404 return ($bibitemnum);
3407 =head2 _koha_new_items
3411 _koha_new_items( $dbh, $item, $barcode );
3417 sub _koha_new_items {
3418 my ( $dbh, $item, $barcode ) = @_;
3420 # my $dbh = C4Connect;
3421 my $sth = $dbh->prepare("Select max(itemnumber) from items");
3427 $data = $sth->fetchrow_hashref;
3428 $itemnumber = $data->{'max(itemnumber)'} + 1;
3430 ## Now calculate lccalnumber
3431 my ($cutterextra) = itemcalculator(
3433 $item->{'biblioitemnumber'},
3434 $item->{'itemcallnumber'}
3437 # FIXME the "notforloan" field seems to be named "loan" in some places. workaround bugfix.
3438 if ( $item->{'loan'} ) {
3439 $item->{'notforloan'} = $item->{'loan'};
3442 # if dateaccessioned is provided, use it. Otherwise, set to NOW()
3443 if ( $item->{'dateaccessioned'} eq '' || !$item->{'dateaccessioned'} ) {
3445 $sth = $dbh->prepare(
3446 "Insert into items set
3447 itemnumber = ?, biblionumber = ?,
3448 multivolumepart = ?,
3449 biblioitemnumber = ?, barcode = ?,
3450 booksellerid = ?, dateaccessioned = NOW(),
3451 homebranch = ?, holdingbranch = ?,
3452 price = ?, replacementprice = ?,
3453 replacementpricedate = NOW(), datelastseen = NOW(),
3454 multivolume = ?, stack = ?,
3455 itemlost = ?, wthdrawn = ?,
3456 paidfor = ?, itemnotes = ?,
3457 itemcallnumber =?, notforloan = ?,
3458 location = ?, Cutterextra = ?
3462 $itemnumber, $item->{'biblionumber'},
3463 $item->{'multivolumepart'}, $item->{'biblioitemnumber'},
3464 $barcode, $item->{'booksellerid'},
3465 $item->{'homebranch'}, $item->{'holdingbranch'},
3466 $item->{'price'}, $item->{'replacementprice'},
3467 $item->{multivolume}, $item->{stack},
3468 $item->{itemlost}, $item->{wthdrawn},
3469 $item->{paidfor}, $item->{'itemnotes'},
3470 $item->{'itemcallnumber'}, $item->{'notforloan'},
3471 $item->{'location'}, $cutterextra
3475 $sth = $dbh->prepare(
3476 "INSERT INTO items SET
3477 itemnumber = ?, biblionumber = ?,
3478 multivolumepart = ?,
3479 biblioitemnumber = ?, barcode = ?,
3480 booksellerid = ?, dateaccessioned = ?,
3481 homebranch = ?, holdingbranch = ?,
3482 price = ?, replacementprice = ?,
3483 replacementpricedate = NOW(), datelastseen = NOW(),
3484 multivolume = ?, stack = ?,
3485 itemlost = ?, wthdrawn = ?,
3486 paidfor = ?, itemnotes = ?,
3487 itemcallnumber = ?, notforloan = ?,
3493 $itemnumber, $item->{'biblionumber'},
3494 $item->{'multivolumepart'}, $item->{'biblioitemnumber'},
3495 $barcode, $item->{'booksellerid'},
3496 $item->{'dateaccessioned'}, $item->{'homebranch'},
3497 $item->{'holdingbranch'}, $item->{'price'},
3498 $item->{'replacementprice'}, $item->{multivolume},
3499 $item->{stack}, $item->{itemlost},
3500 $item->{wthdrawn}, $item->{paidfor},
3501 $item->{'itemnotes'}, $item->{'itemcallnumber'},
3502 $item->{'notforloan'}, $item->{'location'},
3506 if ( defined $sth->errstr ) {
3507 $error .= $sth->errstr;
3509 return ( $itemnumber, $error );
3512 =head2 _koha_modify_item
3516 _koha_modify_item( $dbh, $item, $op );
3522 sub _koha_modify_item {
3523 my ( $dbh, $item, $op ) = @_;
3524 $item->{'itemnum'} = $item->{'itemnumber'} unless $item->{'itemnum'};
3526 # if all we're doing is setting statuses, just update those and get out
3527 if ( $op eq "setstatus" ) {
3529 "UPDATE items SET itemlost=?,wthdrawn=?,binding=? WHERE itemnumber=?";
3531 $item->{'itemlost'}, $item->{'wthdrawn'},
3532 $item->{'binding'}, $item->{'itemnumber'}
3534 my $sth = $dbh->prepare($query);
3535 $sth->execute(@bind);
3539 ## Now calculate lccalnumber
3541 itemcalculator( $dbh, $item->{'bibitemnum'}, $item->{'itemcallnumber'} );
3543 my $query = "UPDATE items SET
3544 barcode=?,itemnotes=?,itemcallnumber=?,notforloan=?,location=?,multivolumepart=?,multivolume=?,stack=?,wthdrawn=?,holdingbranch=?,homebranch=?,cutterextra=?, onloan=?, binding=?";
3547 $item->{'barcode'}, $item->{'notes'},
3548 $item->{'itemcallnumber'}, $item->{'notforloan'},
3549 $item->{'location'}, $item->{multivolumepart},
3550 $item->{multivolume}, $item->{stack},
3551 $item->{wthdrawn}, $item->{holdingbranch},
3552 $item->{homebranch}, $cutterextra,
3553 $item->{onloan}, $item->{binding}
3555 if ( $item->{'lost'} ne '' ) {
3557 "update items set biblioitemnumber=?,barcode=?,itemnotes=?,homebranch=?,
3558 itemlost=?,wthdrawn=?,itemcallnumber=?,notforloan=?,
3559 location=?,multivolumepart=?,multivolume=?,stack=?,wthdrawn=?,holdingbranch=?,cutterextra=?,onloan=?, binding=?";
3561 $item->{'bibitemnum'}, $item->{'barcode'},
3562 $item->{'notes'}, $item->{'homebranch'},
3563 $item->{'lost'}, $item->{'wthdrawn'},
3564 $item->{'itemcallnumber'}, $item->{'notforloan'},
3565 $item->{'location'}, $item->{multivolumepart},
3566 $item->{multivolume}, $item->{stack},
3567 $item->{wthdrawn}, $item->{holdingbranch},
3568 $cutterextra, $item->{onloan},
3571 if ( $item->{homebranch} ) {
3572 $query .= ",homebranch=?";
3573 push @bind, $item->{homebranch};
3575 if ( $item->{holdingbranch} ) {
3576 $query .= ",holdingbranch=?";
3577 push @bind, $item->{holdingbranch};
3580 $query .= " where itemnumber=?";
3581 push @bind, $item->{'itemnum'};
3582 if ( $item->{'replacement'} ne '' ) {
3583 $query =~ s/ where/,replacementprice='$item->{'replacement'}' where/;
3585 my $sth = $dbh->prepare($query);
3586 $sth->execute(@bind);
3590 =head2 _koha_delete_biblio
3594 $error = _koha_delete_biblio($dbh,$biblionumber);
3596 Internal sub for deleting from biblio table -- also saves to deletedbiblio
3598 C<$dbh> - the database handle
3599 C<$biblionumber> - the biblionumber of the biblio to be deleted
3605 # FIXME: add error handling
3607 sub _koha_delete_biblio {
3608 my ( $dbh, $biblionumber ) = @_;
3610 # get all the data for this biblio
3611 my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
3612 $sth->execute($biblionumber);
3614 if ( my $data = $sth->fetchrow_hashref ) {
3616 # save the record in deletedbiblio
3617 # find the fields to save
3618 my $query = "INSERT INTO deletedbiblio SET ";
3620 foreach my $temp ( keys %$data ) {
3621 $query .= "$temp = ?,";
3622 push( @bind, $data->{$temp} );
3625 # replace the last , by ",?)"
3627 my $bkup_sth = $dbh->prepare($query);
3628 $bkup_sth->execute(@bind);
3632 my $del_sth = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
3633 $del_sth->execute($biblionumber);
3640 =head2 _koha_delete_biblioitems
3644 $error = _koha_delete_biblioitems($dbh,$biblioitemnumber);
3646 Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems
3648 C<$dbh> - the database handle
3649 C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted
3655 # FIXME: add error handling
3657 sub _koha_delete_biblioitems {
3658 my ( $dbh, $biblioitemnumber ) = @_;
3660 # get all the data for this biblioitem
3662 $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
3663 $sth->execute($biblioitemnumber);
3665 if ( my $data = $sth->fetchrow_hashref ) {
3667 # save the record in deletedbiblioitems
3668 # find the fields to save
3669 my $query = "INSERT INTO deletedbiblioitems SET ";
3671 foreach my $temp ( keys %$data ) {
3672 $query .= "$temp = ?,";
3673 push( @bind, $data->{$temp} );
3676 # replace the last , by ",?)"
3678 my $bkup_sth = $dbh->prepare($query);
3679 $bkup_sth->execute(@bind);
3682 # delete the biblioitem
3684 $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
3685 $del_sth->execute($biblioitemnumber);
3692 =head2 _koha_delete_item
3696 _koha_delete_item( $dbh, $itemnum );
3698 Internal function to delete an item record from the koha tables
3704 sub _koha_delete_item {
3705 my ( $dbh, $itemnum ) = @_;
3707 my $sth = $dbh->prepare("select * from items where itemnumber=?");
3708 $sth->execute($itemnum);
3709 my $data = $sth->fetchrow_hashref;
3711 my $query = "Insert into deleteditems set ";
3713 foreach my $temp ( keys %$data ) {
3714 $query .= "$temp = ?,";
3715 push( @bind, $data->{$temp} );
3720 $sth = $dbh->prepare($query);
3721 $sth->execute(@bind);
3723 $sth = $dbh->prepare("Delete from items where itemnumber=?");
3724 $sth->execute($itemnum);
3728 =head1 UNEXPORTED FUNCTIONS
3734 $lc = calculatelc($classification);
3741 my ($classification) = @_;
3742 $classification =~ s/^\s+|\s+$//g;
3747 for ( $i = 0 ; $i < length($classification) ; $i++ ) {
3748 my $c = ( substr( $classification, $i, 1 ) );
3749 if ( $c ge '0' && $c le '9' ) {
3751 $lc2 = substr( $classification, $i );
3755 $lc1 .= substr( $classification, $i, 1 );
3760 my $other = length($lc1);
3767 for ( 1 .. ( 4 - $other ) ) {
3776 ##Find the decimal part of $lc2
3777 my $pos = index( $lc2, "." );
3778 if ( $pos < 0 ) { $pos = length($lc2); }
3779 if ( $pos >= 0 && $pos < 5 ) {
3780 ##Pad lc2 with zeros to create a 5digit decimal needed in marc record to sort as numeric
3782 for ( 1 .. ( 5 - $pos ) ) {
3786 $lc2 = $extras . $lc2;
3787 return ( $lc1 . $lc2 );
3790 =head2 itemcalculator
3794 $cutterextra = itemcalculator( $dbh, $biblioitem, $callnumber );
3800 sub itemcalculator {
3801 my ( $dbh, $biblioitem, $callnumber ) = @_;
3804 "select classification, subclass from biblioitems where biblioitemnumber=?"
3807 $sth->execute($biblioitem);
3808 my ( $classification, $subclass ) = $sth->fetchrow;
3809 my $all = $classification . " " . $subclass;
3810 my $total = length($all);
3811 my $cutterextra = substr( $callnumber, $total - 1 );
3813 return $cutterextra;
3816 =head2 ModBiblioMarc
3818 &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
3820 Add MARC data for a biblio to koha
3822 Function exported, but should NOT be used, unless you really know what you're doing
3828 # pass the MARC::Record to this function, and it will create the records in the marc field
3829 my ( $record, $biblionumber, $frameworkcode ) = @_;
3830 my $dbh = C4::Context->dbh;
3831 my @fields = $record->fields();
3832 if ( !$frameworkcode ) {
3833 $frameworkcode = "";
3836 $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
3837 $sth->execute( $frameworkcode, $biblionumber );
3839 my $encoding = C4::Context->preference("marcflavour");
3841 # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
3842 if ( $encoding eq "UNIMARC" ) {
3844 if ( length($record->subfield( 100, "a" )) == 35 ) {
3845 $string = $record->subfield( 100, "a" );
3846 my $f100 = $record->field(100);
3847 $record->delete_field($f100);
3850 $string = POSIX::strftime( "%Y%m%d", localtime );
3852 $string = sprintf( "%-*s", 35, $string );
3854 substr( $string, 22, 6, "frey50" );
3855 unless ( $record->subfield( 100, "a" ) ) {
3856 $record->insert_grouped_field(
3857 MARC::Field->new( 100, "", "", "a" => $string ) );
3860 ModZebra($biblionumber,"specialUpdate","biblioserver",$record);
3863 "update biblioitems set marc=?,marcxml=? where biblionumber=?");
3864 $sth->execute( $record->as_usmarc(), $record->as_xml_record(),
3867 return $biblionumber;
3870 =head2 AddItemInMarc
3874 $newbiblionumber = AddItemInMarc( $record, $biblionumber, $frameworkcode );
3876 Add an item in a MARC record and save the MARC record
3878 Function exported, but should NOT be used, unless you really know what you're doing
3886 # pass the MARC::Record to this function, and it will create the records in the marc tables
3887 my ( $record, $biblionumber, $frameworkcode ) = @_;
3888 my $newrec = &GetMarcBiblio($biblionumber);
3891 my @fields = $record->fields();
3892 foreach my $field (@fields) {
3893 $newrec->append_fields($field);
3896 # FIXME: should we be making sure the biblionumbers are the same?
3897 my $newbiblionumber =
3898 &ModBiblioMarc( $newrec, $biblionumber, $frameworkcode );
3899 return $newbiblionumber;
3902 =head2 z3950_extended_services
3904 z3950_extended_services($serviceType,$serviceOptions,$record);
3906 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.
3908 C<$serviceType> one of: itemorder,create,drop,commit,update,xmlupdate
3910 C<$serviceOptions> a has of key/value pairs. For instance, if service_type is 'update', $service_options should contain:
3912 action => update action, one of specialUpdate, recordInsert, recordReplace, recordDelete, elementUpdate.
3916 recordidOpaque => Opaque Record ID (user supplied) or recordidNumber => Record ID number (system number).
3917 syntax => the record syntax (transfer syntax)
3918 databaseName = Database from connection object
3920 To set serviceOptions, call set_service_options($serviceType)
3922 C<$record> the record, if one is needed for the service type
3924 A record should be in XML. You can convert it to XML from MARC by running it through marc2xml().
3928 sub z3950_extended_services {
3929 my ( $server, $serviceType, $action, $serviceOptions ) = @_;
3931 # get our connection object
3932 my $Zconn = C4::Context->Zconn( $server, 0, 1 );
3934 # create a new package object
3935 my $Zpackage = $Zconn->package();
3938 $Zpackage->option( action => $action );
3940 if ( $serviceOptions->{'databaseName'} ) {
3941 $Zpackage->option( databaseName => $serviceOptions->{'databaseName'} );
3943 if ( $serviceOptions->{'recordIdNumber'} ) {
3945 recordIdNumber => $serviceOptions->{'recordIdNumber'} );
3947 if ( $serviceOptions->{'recordIdOpaque'} ) {
3949 recordIdOpaque => $serviceOptions->{'recordIdOpaque'} );
3952 # this is an ILL request (Zebra doesn't support it, but Koha could eventually)
3953 #if ($serviceType eq 'itemorder') {
3954 # $Zpackage->option('contact-name' => $serviceOptions->{'contact-name'});
3955 # $Zpackage->option('contact-phone' => $serviceOptions->{'contact-phone'});
3956 # $Zpackage->option('contact-email' => $serviceOptions->{'contact-email'});
3957 # $Zpackage->option('itemorder-item' => $serviceOptions->{'itemorder-item'});
3960 if ( $serviceOptions->{record} ) {
3961 $Zpackage->option( record => $serviceOptions->{record} );
3963 # can be xml or marc
3964 if ( $serviceOptions->{'syntax'} ) {
3965 $Zpackage->option( syntax => $serviceOptions->{'syntax'} );
3969 # send the request, handle any exception encountered
3970 eval { $Zpackage->send($serviceType) };
3971 if ( $@ && $@->isa("ZOOM::Exception") ) {
3972 return "error: " . $@->code() . " " . $@->message() . "\n";
3975 # free up package resources
3976 $Zpackage->destroy();
3979 =head2 set_service_options
3981 my $serviceOptions = set_service_options($serviceType);
3983 C<$serviceType> itemorder,create,drop,commit,update,xmlupdate
3985 Currently, we only support 'create', 'commit', and 'update'. 'drop' support will be added as soon as Zebra supports it.
3989 sub set_service_options {
3990 my ($serviceType) = @_;
3993 # FIXME: This needs to be an OID ... if we ever need 'syntax' this sub will need to change
3994 # $serviceOptions->{ 'syntax' } = ''; #zebra doesn't support syntaxes other than xml
3996 if ( $serviceType eq 'commit' ) {
4000 if ( $serviceType eq 'create' ) {
4004 if ( $serviceType eq 'drop' ) {
4005 die "ERROR: 'drop' not currently supported (by Zebra)";
4007 return $serviceOptions;
4010 =head2 GetItemsCount
4012 $count = &GetItemsCount( $biblionumber);
4013 this function return count of item with $biblionumber
4017 my ( $biblionumber ) = @_;
4018 my $dbh = C4::Context->dbh;
4019 my $query = qq|SELECT count(*)
4021 WHERE biblionumber=?|;
4022 my $sth = $dbh->prepare($query);
4023 $sth->execute($biblionumber);
4024 my $count = $sth->fetchrow;
4029 END { } # module clean-up code here (global destructor)
4037 Koha Developement team <info@koha.org>
4039 Paul POULAIN paul.poulain@free.fr
4041 Joshua Ferraro jmf@liblime.com
4047 # Revision 1.221 2007/07/31 16:01:11 toins
4048 # Some new functions.
4049 # TransformHTMLtoMarc rewrited.
4051 # Revision 1.220 2007/07/20 15:43:16 hdl
4052 # Bug Fixing GetMarcSubjects.
4053 # Links parameters were mixed.
4055 # Revision 1.218 2007/07/19 07:40:08 hdl
4056 # Adding selection by location for inventory
4058 # Revision 1.217 2007/07/03 13:47:44 tipaul
4059 # fixing some display bugs (itemtype not properly returned and a html table bug that makes items appear strangely
4061 # Revision 1.216 2007/07/03 09:40:58 tipaul
4062 # return itemtype description properly
4064 # Revision 1.215 2007/07/03 09:33:05 tipaul
4065 # if you just replace su by a space in subjects, you'll replace jesus by je s, which is strange for users. this fix solves the problem and introduces authoritysep systempref as separator of subfields, for a better identification of where the authority starts and end
4067 # Revision 1.214 2007/07/02 09:13:22 tipaul
4068 # unimarc bugfix : the encoding is in field 100 in UNIMARC. when TransformHTMLtoXML on an item, you must not automatically add a 100 field in items, otherwise there will be 2 100 fields in the biblio, which is wrong
4070 # Revision 1.213 2007/06/25 15:01:45 tipaul
4071 # bugfixes on unimarc 100 handling (the field used for encoding)
4073 # Revision 1.212 2007/06/15 13:44:44 tipaul
4074 # some fixes (and only fixes)
4076 # Revision 1.211 2007/06/15 09:40:06 toins
4077 # do not get $3 $4 and $5 on GetMarcSubjects GetMarcAuthors on unimarc flavour.
4079 # Revision 1.210 2007/06/13 13:03:34 toins
4080 # removing warn compilation.
4082 # Revision 1.209 2007/05/23 16:19:40 tipaul
4083 # various bugfixes (minor) and french translation updated
4085 # Revision 1.208 2007/05/22 09:13:54 tipaul
4086 # Bugfixes & improvements (various and minor) :
4087 # - updating templates to have tmpl_process3.pl running without any errors
4088 # - adding a drupal-like css for prog templates (with 3 small images)
4089 # - fixing some bugs in circulation & other scripts
4090 # - updating french translation
4091 # - fixing some typos in templates
4093 # Revision 1.207 2007/05/22 08:51:19 hdl
4094 # Changing GetMarcStructure signature.
4095 # Deleting first parameter $dbh
4097 # Revision 1.206 2007/05/21 08:44:17 btoumi
4098 # add security when u delete biblio :
4099 # u must delete linked items before delete biblio
4101 # Revision 1.205 2007/05/11 16:04:03 btoumi
4103 # problem in displayed label link with subject in detail.tmpl
4104 # ex: label random => rdom
4106 # Revision 1.204 2007/05/10 14:45:15 tipaul
4108 # - support for authorities
4109 # - some bugfixes in ordering and "CCL" parsing
4110 # - support for authorities <=> biblios walking
4112 # Seems I can do what I want now, so I consider its done, except for bugfixes that will be needed i m sure !
4114 # Revision 1.203 2007/05/03 15:16:02 tipaul
4115 # BUGFIX for : NoZebra
4116 # - NoZebra features : seems they work fine now (adding, modifying, deleting)
4117 # - Biblio edition major bugfix : before this commit editing a biblio resulted in an item removal in marcxml field
4119 # Revision 1.202 2007/05/02 16:44:31 tipaul
4120 # NoZebra SQL index management :
4121 # * adding 3 subs in Biblio.pm
4122 # - GetNoZebraIndexes, that get the index structure in a new systempreference (added with this commit)
4123 # - _DelBiblioNoZebra, that retrieve all index entries for a biblio and remove in a variable the biblio reference
4124 # - _AddBiblioNoZebra, that add index entries for a biblio.
4125 # Note that the 2 _Add and _Del subs work only in a hash variable, to speed up things in case of a modif (ie : delete+add). The effective SQL update is done in the ModZebra sub (that existed before, and dealed with zebra index).
4126 # I think the code has to be more deeply tested, but it works at least partially.
4128 # Revision 1.201 2007/04/27 14:00:49 hdl
4129 # Removing $dbh from GetMarcFromKohaField (dbh is not used in this function.)
4131 # Revision 1.200 2007/04/25 16:26:42 tipaul
4132 # Koha 3.0 nozebra 1st commit : the script misc/migration_tools/rebuild_nozebra.pl build the nozebra table, and, if you set NoZebra to Yes, queries will be done through zebra. TODO :
4133 # - add nozebra table management on biblio editing
4134 # - the index table content is hardcoded. I still have to add some specific systempref to let the library update it
4135 # - manage pagination (next/previous)
4138 # - NZgetRecords : has exactly the same API & returns as zebra getQuery, except that some parameters are unused
4139 # - search & sort works quite good
4140 # - CQL parser is better that what I thought I could do : title="harry and sally" and publicationyear>2000 not itemtype=LIVR should work fine
4142 # Revision 1.199 2007/04/24 09:07:53 tipaul
4143 # moving dotransfer to Biblio.pm::ModItemTransfer + some CheckReserves fixes
4145 # Revision 1.198 2007/04/23 15:21:17 tipaul
4146 # renaming currenttransfers to transferstoreceive
4148 # Revision 1.197 2007/04/18 17:00:14 tipaul
4149 # removing all useless %env / $env
4151 # Revision 1.196 2007/04/17 08:48:00 tipaul
4152 # circulation cleaning continued: bufixing
4154 # Revision 1.195 2007/04/04 16:46:22 tipaul
4155 # HUGE COMMIT : code cleaning circulation.
4157 # some stuff to do, i'll write a mail on koha-devel NOW !
4159 # Revision 1.194 2007/03/30 12:00:42 tipaul
4160 # why the hell do we need to explicitly utf8 decode this string ? I really don't know, but it seems it's mandatory, otherwise, tag descriptions are not properly encoded...
4162 # Revision 1.193 2007/03/29 16:45:53 tipaul
4163 # Code cleaning of Biblio.pm (continued)
4165 # All subs have be cleaned :
4168 # - reordering Biblio.pm completly
4169 # - using only naming conventions
4171 # Seems to have broken nothing, but it still has to be heavily tested.
4172 # Note that Biblio.pm is now much more efficient than previously & probably more reliable as well.
4174 # Revision 1.192 2007/03/29 13:30:31 tipaul
4176 # == Biblio.pm cleaning (useless) ==
4177 # * some sub declaration dropped
4178 # * removed modbiblio sub
4179 # * removed moditem sub
4180 # * removed newitems. It was used only in finishrecieve. Replaced by a TransformKohaToMarc+AddItem, that is better.
4181 # * removed MARCkoha2marcItem
4182 # * removed MARCdelsubfield declaration
4183 # * removed MARCkoha2marcBiblio
4185 # == Biblio.pm cleaning (naming conventions) ==
4186 # * MARCgettagslib renamed to GetMarcStructure
4187 # * MARCgetitems renamed to GetMarcItem
4188 # * MARCfind_frameworkcode renamed to GetFrameworkCode
4189 # * MARCmarc2koha renamed to TransformMarcToKoha
4190 # * MARChtml2marc renamed to TransformHtmlToMarc
4191 # * MARChtml2xml renamed to TranformeHtmlToXml
4192 # * zebraop renamed to ModZebra
4195 # * removing MARC=OFF related scripts (in cataloguing directory)
4196 # * removed checkitems (function related to MARC=off feature, that is completly broken in head. If someone want to reintroduce it, hard work coming...)
4197 # * removed getitemsbybiblioitem (used only by MARC=OFF scripts, that is removed as well)
4199 # Revision 1.191 2007/03/29 09:42:13 tipaul
4200 # adding default value new feature into cataloguing. The system (definition) part has already been added by toins
4202 # Revision 1.190 2007/03/29 08:45:19 hdl
4203 # Deleting ignore_errors(1) pour MARC::Charset
4205 # Revision 1.189 2007/03/28 10:39:16 hdl
4206 # removing $dbh as a parameter in AuthoritiesMarc functions
4207 # And reporting all differences into the scripts taht relies on those functions.