do not get $3 $4 and $5 on GetMarcSubjects GetMarcAuthors on unimarc flavour.
[koha.git] / C4 / Biblio.pm
1 package C4::Biblio;
2
3 # Copyright 2000-2002 Katipo Communications
4 #
5 # This file is part of Koha.
6 #
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
10 # version.
11 #
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.
15 #
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
19
20 use strict;
21
22 require Exporter;
23 use C4::Context;
24 use MARC::Record;
25 use MARC::File::USMARC;
26 use MARC::File::XML;
27 use ZOOM;
28 use C4::Koha;
29 use C4::Date;
30 use utf8;
31 use C4::Log; # logaction
32
33 use vars qw($VERSION @ISA @EXPORT);
34
35 # set the version for version checking
36 $VERSION = do { my @v = '$Revision$' =~ /\d+/g; shift(@v).".".join( "_", map { sprintf "%03d", $_ } @v ); };
37
38 @ISA = qw( Exporter );
39
40 # EXPORTED FUNCTIONS.
41
42 # to add biblios or items
43 push @EXPORT, qw( &AddBiblio &AddItem );
44
45 # to get something
46 push @EXPORT, qw(
47   &GetBiblio
48   &GetBiblioData
49   &GetBiblioItemData
50   &GetBiblioItemInfosOf
51   &GetBiblioItemByBiblioNumber
52   &GetBiblioFromItemNumber
53   
54   &GetMarcItem
55   &GetItem
56   &GetItemInfosOf
57   &GetItemStatus
58   &GetItemLocation
59   &GetLostItems
60   &GetItemsForInventory
61   &GetItemsCount
62
63   &GetMarcNotes
64   &GetMarcSubjects
65   &GetMarcBiblio
66   &GetMarcAuthors
67   &GetMarcSeries
68
69   &GetItemsInfo
70   &GetItemnumberFromBarcode
71   &get_itemnumbers_of
72   &GetXmlBiblio
73
74   &GetAuthorisedValueDesc
75   &GetMarcStructure
76   &GetMarcFromKohaField
77   &GetFrameworkCode
78   &TransformKohaToMarc
79 );
80
81 # To modify something
82 push @EXPORT, qw(
83   &ModBiblio
84   &ModItem
85   &ModItemTransfer
86   &ModBiblioframework
87   &ModZebra
88   &ModItemInMarc
89   &ModItemInMarconefield
90   &ModDateLastSeen
91 );
92
93 # To delete something
94 push @EXPORT, qw(
95   &DelBiblio
96   &DelItem
97 );
98
99 # Internal functions
100 # those functions are exported but should not be used
101 # they are usefull is few circumstances, so are exported.
102 # but don't use them unless you're a core developer ;-)
103 push @EXPORT, qw(
104   &ModBiblioMarc
105   &AddItemInMarc
106   &calculatelc
107   &itemcalculator
108 );
109
110 # Others functions
111 push @EXPORT, qw(
112   &TransformMarcToKoha
113   &TransformHtmlToMarc
114   &TransformHtmlToXml
115   &PrepareItemrecordDisplay
116   &char_decode
117   &GetNoZebraIndexes
118 );
119
120 =head1 NAME
121
122 C4::Biblio - cataloging management functions
123
124 =head1 DESCRIPTION
125
126 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:
127
128 =over 4
129
130 =item 1. in the biblio,biblioitems,items, etc tables, which are limited to a one-to-one mapping to underlying MARC data
131
132 =item 2. as raw MARC in the Zebra index and storage engine
133
134 =item 3. as raw MARC the biblioitems.marc and biblioitems.marcxml
135
136 =back
137
138 In the 3.0 version of Koha, the authoritative record-level information is in biblioitems.marcxml
139
140 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.
141
142 =over 4
143
144 =item 1. Compared with MySQL, Zebra is slow to update an index for small data changes -- especially for proc-intensive operations like circulation
145
146 =item 2. Zebra's index has been known to crash and a backup of the data is necessary to rebuild it in such cases
147
148 =back
149
150 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:
151
152 =over 4
153
154 =item 1. Add*/Mod*/Del*/ - high-level external functions suitable for being called from external scripts to manage the collection
155
156 =item 2. _koha_* - low-level internal functions for managing the koha tables
157
158 =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.
159
160 =item 4. Zebra functions used to update the Zebra index
161
162 =item 5. internal helper functions such as char_decode, checkitems, etc. Some of these probably belong in Koha.pm
163
164 =back
165
166 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 :
167
168 =over 4
169
170 =item 1. save datas in biblio and biblioitems table, that gives us a biblionumber and a biblioitemnumber
171
172 =item 2. add the biblionumber and biblioitemnumber into the MARC records
173
174 =item 3. save the marc record
175
176 =back
177
178 When dealing with items, we must :
179
180 =over 4
181
182 =item 1. save the item in items table, that gives us an itemnumber
183
184 =item 2. add the itemnumber to the item MARC field
185
186 =item 3. overwrite the MARC record (with the added item) into biblioitems.marc(xml)
187
188 When modifying a biblio or an item, the behaviour is quite similar.
189
190 =back
191
192 =head1 EXPORTED FUNCTIONS
193
194 =head2 AddBiblio
195
196 =over 4
197
198 ($biblionumber,$biblioitemnumber) = AddBiblio($record,$frameworkcode);
199 Exported function (core API) for adding a new biblio to koha.
200
201 =back
202
203 =cut
204
205 sub AddBiblio {
206     my ( $record, $frameworkcode ) = @_;
207     my $biblionumber;
208     my $biblioitemnumber;
209     my $dbh = C4::Context->dbh;
210     # transform the data into koha-table style data
211     my $olddata = TransformMarcToKoha( $dbh, $record, $frameworkcode );
212     $biblionumber = _koha_add_biblio( $dbh, $olddata, $frameworkcode );
213     $olddata->{'biblionumber'} = $biblionumber;
214     $biblioitemnumber = _koha_add_biblioitem( $dbh, $olddata );
215
216     # we must add bibnum and bibitemnum in MARC::Record...
217     # we build the new field with biblionumber and biblioitemnumber
218     # we drop the original field
219     # we add the new builded field.
220     ( my $biblio_tag, my $biblio_subfield ) = GetMarcFromKohaField("biblio.biblionumber",$frameworkcode);
221     ( my $biblioitem_tag, my $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.biblioitemnumber",$frameworkcode);
222
223     my $newfield;
224
225     # biblionumber & biblioitemnumber are in different fields
226     if ( $biblio_tag != $biblioitem_tag ) {
227
228         # deal with biblionumber
229         if ( $biblio_tag < 10 ) {
230             $newfield = MARC::Field->new( $biblio_tag, $biblionumber );
231         }
232         else {
233             $newfield =
234               MARC::Field->new( $biblio_tag, '', '',
235                 "$biblio_subfield" => $biblionumber );
236         }
237
238         # drop old field and create new one...
239         my $old_field = $record->field($biblio_tag);
240         $record->delete_field($old_field);
241         $record->append_fields($newfield);
242
243         # deal with biblioitemnumber
244         if ( $biblioitem_tag < 10 ) {
245             $newfield = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
246         }
247         else {
248             $newfield =
249               MARC::Field->new( $biblioitem_tag, '', '',
250                 "$biblioitem_subfield" => $biblioitemnumber, );
251         }
252         # drop old field and create new one...
253         $old_field = $record->field($biblioitem_tag);
254         $record->delete_field($old_field);
255         $record->insert_fields_ordered($newfield);
256
257 # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
258     }
259     else {
260         my $newfield = MARC::Field->new(
261             $biblio_tag, '', '',
262             "$biblio_subfield" => $biblionumber,
263             "$biblioitem_subfield" => $biblioitemnumber
264         );
265
266         # drop old field and create new one...
267         my $old_field = $record->field($biblio_tag);
268         $record->delete_field($old_field);
269         $record->insert_fields_ordered($newfield);
270     }
271
272     # now add the record
273     $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
274       
275     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$biblionumber,"biblio") 
276         if C4::Context->preference("CataloguingLog");
277       
278     return ( $biblionumber, $biblioitemnumber );
279 }
280
281 =head2 AddItem
282
283 =over
284
285 $biblionumber = AddItem( $record, $biblionumber)
286 Exported function (core API) for adding a new item to Koha
287
288 =back
289
290 =cut
291
292 sub AddItem {
293     my ( $record, $biblionumber ) = @_;
294     my $dbh = C4::Context->dbh;
295     
296     # add item in old-DB
297     my $frameworkcode = GetFrameworkCode( $biblionumber );
298     my $item = &TransformMarcToKoha( $dbh, $record, $frameworkcode );
299
300     # needs old biblionumber and biblioitemnumber
301     $item->{'biblionumber'} = $biblionumber;
302     my $sth =
303       $dbh->prepare(
304         "select biblioitemnumber,itemtype from biblioitems where biblionumber=?"
305       );
306     $sth->execute( $item->{'biblionumber'} );
307     my $itemtype;
308     ( $item->{'biblioitemnumber'}, $itemtype ) = $sth->fetchrow;
309     $sth =
310       $dbh->prepare(
311         "select notforloan from itemtypes where itemtype='$itemtype'");
312     $sth->execute();
313     my $notforloan = $sth->fetchrow;
314     ##Change the notforloan field if $notforloan found
315     if ( $notforloan > 0 ) {
316         $item->{'notforloan'} = $notforloan;
317         &MARCitemchange( $record, "items.notforloan", $notforloan );
318     }
319     if ( !$item->{'dateaccessioned'} || $item->{'dateaccessioned'} eq '' ) {
320
321         # find today's date
322         my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
323           localtime(time);
324         $year += 1900;
325         $mon  += 1;
326         my $date =
327           "$year-" . sprintf( "%0.2d", $mon ) . "-" . sprintf( "%0.2d", $mday );
328         $item->{'dateaccessioned'} = $date;
329         &MARCitemchange( $record, "items.dateaccessioned", $date );
330     }
331     my ( $itemnumber, $error ) =
332       &_koha_new_items( $dbh, $item, $item->{barcode} );
333
334     # add itemnumber to MARC::Record before adding the item.
335     $sth =
336       $dbh->prepare(
337 "select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
338       );
339     &TransformKohaToMarcOneField( $sth, $record, "items.itemnumber", $itemnumber,
340         $frameworkcode );
341
342     # add the item
343     &AddItemInMarc( $record, $item->{'biblionumber'},$frameworkcode );
344     
345     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item") 
346         if C4::Context->preference("CataloguingLog");
347     
348     return ($item->{biblionumber}, $item->{biblioitemnumber},$itemnumber);
349 }
350
351 =head2 ModBiblio
352
353 =over
354
355 ModBiblio( $record,$biblionumber,$frameworkcode);
356 Exported function (core API) to modify a biblio
357
358 =back
359
360 =cut
361
362 sub ModBiblio {
363     my ( $record, $biblionumber, $frameworkcode ) = @_;
364     
365     if (C4::Context->preference("CataloguingLog")) {    
366         my $newrecord = GetMarcBiblio($biblionumber);
367         &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$biblionumber,$newrecord->as_formatted) 
368     }
369     
370     my $dbh = C4::Context->dbh;
371     
372     $frameworkcode = "" unless $frameworkcode;
373
374     # get the items before and append them to the biblio before updating the record, atm we just have the biblio
375     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
376     my $oldRecord = GetMarcBiblio($biblionumber);
377     my @fields = $oldRecord->field($itemtag);
378     $record->append_fields(@fields);
379
380     # update the MARC record (that now contains biblio and items) with the new record data
381     &ModBiblioMarc($record, $biblionumber, $frameworkcode );
382
383     # load the koha-table data object
384     my $oldbiblio = TransformMarcToKoha( $dbh, $record, $frameworkcode );
385
386     # modify the other koha tables
387     $biblionumber = _koha_modify_biblio( $dbh, $oldbiblio );
388     _koha_modify_biblioitem( $dbh, $oldbiblio );
389
390     return 1;
391 }
392
393 =head2 ModItem
394
395 =over
396
397 Exported function (core API) for modifying an item in Koha.
398
399 =back
400
401 =cut
402
403 sub ModItem {
404     my ( $record, $biblionumber, $itemnumber, $delete, $new_item_hashref )
405       = @_;
406     
407     #logging
408     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$itemnumber,$record->as_formatted) 
409         if C4::Context->preference("CataloguingLog");
410       
411     my $dbh = C4::Context->dbh;
412     
413     # if we have a MARC record, we're coming from cataloging and so
414     # we do the whole routine: update the MARC and zebra, then update the koha
415     # tables
416     if ($record) {
417         my $frameworkcode = GetFrameworkCode( $biblionumber );
418         ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode );
419         my $olditem       = TransformMarcToKoha( $dbh, $record, $frameworkcode );
420         _koha_modify_item( $dbh, $olditem );
421         return $biblionumber;
422     }
423
424     # otherwise, we're just looking to modify something quickly
425     # (like a status) so we just update the koha tables
426     elsif ($new_item_hashref) {
427         _koha_modify_item( $dbh, $new_item_hashref );
428     }
429 }
430
431 sub ModItemTransfer {
432     my ( $itemnumber, $frombranch, $tobranch ) = @_;
433     
434     my $dbh = C4::Context->dbh;
435     
436     #new entry in branchtransfers....
437     my $sth = $dbh->prepare(
438         "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
439         VALUES (?, ?, now(), ?)");
440     $sth->execute($itemnumber, $frombranch, $tobranch);
441     #update holdingbranch in items .....
442      $sth= $dbh->prepare(
443           "UPDATE items set holdingbranch = ? WHERE items.itemnumber = ?");
444     $sth->execute($tobranch,$itemnumber);
445     &ModDateLastSeen($itemnumber);
446     $sth = $dbh->prepare(
447         "SELECT biblionumber FROM items WHERE itemnumber=?"
448       );
449     $sth->execute($itemnumber);
450     while ( my ( $biblionumber ) = $sth->fetchrow ) {
451         &ModItemInMarconefield( $biblionumber, $itemnumber,
452             'items.holdingbranch', $tobranch );
453     }
454     return;
455 }
456
457 ##New sub to dotransfer in marc tables as well. Not exported -TG 10/04/2006
458 # sub domarctransfer {
459 #     my ( $dbh, $itemnumber ) = @_;
460 #     $itemnumber =~ s /\'//g;    ##itemnumber seems to come with quotes-TG
461 #     my $sth =
462 #       $dbh->prepare(
463 #         "select biblionumber,holdingbranch from items where itemnumber=$itemnumber"
464 #       );
465 #     $sth->execute();
466 #     while ( my ( $biblionumber, $holdingbranch ) = $sth->fetchrow ) {
467 #         &ModItemInMarconefield( $biblionumber, $itemnumber,
468 #             'items.holdingbranch', $holdingbranch );
469 #     }
470 #     return;
471 # }
472 =head2 ModBiblioframework
473
474 =over
475
476 ModBiblioframework($biblionumber,$frameworkcode);
477 Exported function to modify a biblio framework
478
479 =back
480
481 =cut
482
483 sub ModBiblioframework {
484     my ( $biblionumber, $frameworkcode ) = @_;
485     my $dbh = C4::Context->dbh;
486     my $sth =
487       $dbh->prepare(
488         "UPDATE biblio SET frameworkcode=? WHERE biblionumber=$biblionumber");
489     $sth->execute($frameworkcode);
490     return 1;
491 }
492
493 =head2 ModItemInMarconefield
494
495 =over
496
497 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)
498 &ModItemInMarconefield( $biblionumber, $itemnumber, $itemfield, $newvalue )
499
500 =back
501
502 =cut
503
504 sub ModItemInMarconefield {
505     my ( $biblionumber, $itemnumber, $itemfield, $newvalue ) = @_;
506     my $dbh = C4::Context->dbh;
507     if ( !defined $newvalue ) {
508         $newvalue = "";
509     }
510
511     my $record = GetMarcItem( $biblionumber, $itemnumber );
512     my ($tagfield, $tagsubfield) = GetMarcFromKohaField( $itemfield,'');
513     if ($tagfield && $tagsubfield) {
514         my $tag = $record->field($tagfield);
515         if ($tag) {
516 #             my $tagsubs = $record->field($tagfield)->subfield($tagsubfield);
517             $tag->update( $tagsubfield => $newvalue );
518             $record->delete_field($tag);
519             $record->insert_fields_ordered($tag);
520             &ModItemInMarc( $record, $biblionumber, $itemnumber, 0 );
521         }
522     }
523 }
524
525 =head2 ModItemInMarc
526
527 =over
528
529 &ModItemInMarc( $record, $biblionumber, $itemnumber )
530
531 =back
532
533 =cut
534
535 sub ModItemInMarc {
536     my ( $ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
537     my $dbh = C4::Context->dbh;
538     
539     # get complete MARC record & replace the item field by the new one
540     my $completeRecord = GetMarcBiblio($biblionumber);
541     my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
542     my $itemField = $ItemRecord->field($itemtag);
543     my @items = $completeRecord->field($itemtag);
544     foreach (@items) {
545         if ($_->subfield($itemsubfield) eq $itemnumber) {
546 #             $completeRecord->delete_field($_);
547             $_->replace_with($itemField);
548         }
549     }
550     # save the record
551     my $sth = $dbh->prepare("update biblioitems set marc=?,marcxml=?  where biblionumber=?");
552     $sth->execute( $completeRecord->as_usmarc(), $completeRecord->as_xml_record(),$biblionumber );
553     $sth->finish;
554     ModZebra($biblionumber,"specialUpdate","biblioserver",$completeRecord);
555 }
556
557 =head2 ModDateLastSeen
558
559 &ModDateLastSeen($itemnum)
560 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking
561 C<$itemnum> is the item number
562
563 =cut
564
565 sub ModDateLastSeen {
566     my ($itemnum) = @_;
567     my $dbh       = C4::Context->dbh;
568     my $sth       =
569       $dbh->prepare(
570           "update items set itemlost=0, datelastseen  = now() where items.itemnumber = ?"
571       );
572     $sth->execute($itemnum);
573     return;
574 }
575 =head2 DelBiblio
576
577 =over
578
579 my $error = &DelBiblio($dbh,$biblionumber);
580 Exported function (core API) for deleting a biblio in koha.
581 Deletes biblio record from Zebra and Koha tables (biblio,biblioitems,items)
582 Also backs it up to deleted* tables
583 Checks to make sure there are not issues on any of the items
584 return:
585 C<$error> : undef unless an error occurs
586
587 =back
588
589 =cut
590
591 sub DelBiblio {
592     my ( $biblionumber ) = @_;
593     my $dbh = C4::Context->dbh;
594     my $error;    # for error handling
595
596     # First make sure there are no items with issues are still attached
597     my $sth =
598       $dbh->prepare(
599         "SELECT itemnumber FROM items WHERE biblionumber=?");
600     $sth->execute($biblionumber);
601     while ( my $itemnumber = $sth->fetchrow ) {
602         my $issues = GetItemIssues($itemnumber);
603         foreach my $issue (@$issues) {
604             if (   ( $issue->{date_due} )
605                 && ( $issue->{date_due} ne "Available" ) )
606             {
607
608 #FIXME: we need a status system in Biblio like in Circ to return standard codes and messages
609 # instead of hard-coded strings
610                 $error .=
611 "Item is checked out to a patron -- you must return it before deleting the Biblio";
612             }
613         }
614     }
615     return $error if $error;
616
617     # Delete in Zebra. Be careful NOT to move this line after _koha_delete_biblio
618     # for at least 2 reasons :
619     # - we need to read the biblio if NoZebra is set (to remove it from the indexes
620     # - if something goes wrong, the biblio may be deleted from Koha but not from zebra
621     #   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)
622     ModZebra($biblionumber, "delete_record", "biblioserver", undef);
623
624     # delete biblio from Koha tables and save in deletedbiblio
625     $error = &_koha_delete_biblio( $dbh, $biblionumber );
626
627     # delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
628     $sth =
629       $dbh->prepare(
630         "SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
631     $sth->execute($biblionumber);
632     while ( my $biblioitemnumber = $sth->fetchrow ) {
633
634         # delete this biblioitem
635         $error = &_koha_delete_biblioitems( $dbh, $biblioitemnumber );
636         return $error if $error;
637
638         # delete items
639         my $items_sth =
640           $dbh->prepare(
641             "SELECT itemnumber FROM items WHERE biblioitemnumber=?");
642         $items_sth->execute($biblioitemnumber);
643         while ( my $itemnumber = $items_sth->fetchrow ) {
644             $error = &_koha_delete_item( $dbh, $itemnumber );
645             return $error if $error;
646         }
647     }
648     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$biblionumber,"") 
649         if C4::Context->preference("CataloguingLog");
650     return;
651 }
652
653 =head2 DelItem
654
655 =over
656
657 DelItem( $biblionumber, $itemnumber );
658 Exported function (core API) for deleting an item record in Koha.
659
660 =back
661
662 =cut
663
664 sub DelItem {
665     my ( $biblionumber, $itemnumber ) = @_;
666     my $dbh = C4::Context->dbh;
667     &_koha_delete_item( $dbh, $itemnumber );
668     # get the MARC record
669     my $record = GetMarcBiblio($biblionumber);
670     my $frameworkcode = GetFrameworkCode($biblionumber);
671
672     # backup the record
673     my $copy2deleted =
674       $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
675     $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
676
677     #search item field code
678     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
679     my @fields = $record->field($itemtag);
680     # delete the item specified
681     foreach my $field (@fields) {
682         if ( $field->subfield($itemsubfield) eq $itemnumber ) {
683             $record->delete_field($field);
684         }
685     }
686     &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
687     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$itemnumber,"item") 
688         if C4::Context->preference("CataloguingLog");
689 }
690
691 =head2 GetBiblioData
692
693 =over 4
694
695 $data = &GetBiblioData($biblionumber);
696 Returns information about the book with the given biblionumber.
697 C<&GetBiblioData> returns a reference-to-hash. The keys are the fields in
698 the C<biblio> and C<biblioitems> tables in the
699 Koha database.
700 In addition, C<$data-E<gt>{subject}> is the list of the book's
701 subjects, separated by C<" , "> (space, comma, space).
702 If there are multiple biblioitems with the given biblionumber, only
703 the first one is considered.
704
705 =back
706
707 =cut
708
709 sub GetBiblioData {
710     my ( $bibnum ) = @_;
711     my $dbh = C4::Context->dbh;
712
713     my $query = "
714         SELECT * , biblioitems.notes AS bnotes, biblio.notes
715         FROM biblio
716             LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
717             LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
718         WHERE biblio.biblionumber = ?
719             AND biblioitems.biblionumber = biblio.biblionumber
720     ";
721     my $sth = $dbh->prepare($query);
722     $sth->execute($bibnum);
723     my $data;
724     $data = $sth->fetchrow_hashref;
725     $sth->finish;
726
727     return ($data);
728 }    # sub GetBiblioData
729
730
731 =head2 GetItemsInfo
732
733 =over 4
734
735   @results = &GetItemsInfo($biblionumber, $type);
736
737 Returns information about books with the given biblionumber.
738
739 C<$type> may be either C<intra> or anything else. If it is not set to
740 C<intra>, then the search will exclude lost, very overdue, and
741 withdrawn items.
742
743 C<&GetItemsInfo> returns a list of references-to-hash. Each element
744 contains a number of keys. Most of them are table items from the
745 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
746 Koha database. Other keys include:
747
748 =over 4
749
750 =item C<$data-E<gt>{branchname}>
751
752 The name (not the code) of the branch to which the book belongs.
753
754 =item C<$data-E<gt>{datelastseen}>
755
756 This is simply C<items.datelastseen>, except that while the date is
757 stored in YYYY-MM-DD format in the database, here it is converted to
758 DD/MM/YYYY format. A NULL date is returned as C<//>.
759
760 =item C<$data-E<gt>{datedue}>
761
762 =item C<$data-E<gt>{class}>
763
764 This is the concatenation of C<biblioitems.classification>, the book's
765 Dewey code, and C<biblioitems.subclass>.
766
767 =item C<$data-E<gt>{ocount}>
768
769 I think this is the number of copies of the book available.
770
771 =item C<$data-E<gt>{order}>
772
773 If this is set, it is set to C<One Order>.
774
775 =back
776
777 =back
778
779 =cut
780
781 sub GetItemsInfo {
782     my ( $biblionumber, $type ) = @_;
783     my $dbh   = C4::Context->dbh;
784     my $query = "SELECT *,items.notforloan as itemnotforloan
785                  FROM items, biblio, biblioitems
786                  LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype
787                 WHERE items.biblionumber = ?
788                     AND biblioitems.biblioitemnumber = items.biblioitemnumber
789                     AND biblio.biblionumber = items.biblionumber
790                 ORDER BY items.dateaccessioned desc
791                  ";
792     my $sth = $dbh->prepare($query);
793     $sth->execute($biblionumber);
794     my $i = 0;
795     my @results;
796     my ( $date_due, $count_reserves );
797
798     while ( my $data = $sth->fetchrow_hashref ) {
799         my $datedue = '';
800         my $isth    = $dbh->prepare(
801             "SELECT issues.*,borrowers.cardnumber
802             FROM   issues, borrowers
803             WHERE  itemnumber = ?
804                 AND returndate IS NULL
805                 AND issues.borrowernumber=borrowers.borrowernumber"
806         );
807         $isth->execute( $data->{'itemnumber'} );
808         if ( my $idata = $isth->fetchrow_hashref ) {
809             $data->{borrowernumber} = $idata->{borrowernumber};
810             $data->{cardnumber}     = $idata->{cardnumber};
811             $datedue                = format_date( $idata->{'date_due'} );
812         }
813         if ( $datedue eq '' ) {
814             #$datedue="Available";
815             my ( $restype, $reserves ) =
816               C4::Reserves::CheckReserves( $data->{'itemnumber'} );
817             if ($restype) {
818
819                 #$datedue=$restype;
820                 $count_reserves = $restype;
821             }
822         }
823         $isth->finish;
824
825         #get branch information.....
826         my $bsth = $dbh->prepare(
827             "SELECT * FROM branches WHERE branchcode = ?
828         "
829         );
830         $bsth->execute( $data->{'holdingbranch'} );
831         if ( my $bdata = $bsth->fetchrow_hashref ) {
832             $data->{'branchname'} = $bdata->{'branchname'};
833         }
834         my $date = format_date( $data->{'datelastseen'} );
835         $data->{'datelastseen'}   = $date;
836         $data->{'datedue'}        = $datedue;
837         $data->{'count_reserves'} = $count_reserves;
838
839         # get notforloan complete status if applicable
840         my $sthnflstatus = $dbh->prepare(
841             'SELECT authorised_value
842             FROM   marc_subfield_structure
843             WHERE  kohafield="items.notforloan"
844         '
845         );
846
847         $sthnflstatus->execute;
848         my ($authorised_valuecode) = $sthnflstatus->fetchrow;
849         if ($authorised_valuecode) {
850             $sthnflstatus = $dbh->prepare(
851                 "SELECT lib FROM authorised_values
852                  WHERE  category=?
853                  AND authorised_value=?"
854             );
855             $sthnflstatus->execute( $authorised_valuecode,
856                 $data->{itemnotforloan} );
857             my ($lib) = $sthnflstatus->fetchrow;
858             $data->{notforloan} = $lib;
859         }
860
861         # my stack procedures
862         my $stackstatus = $dbh->prepare(
863             'SELECT authorised_value
864              FROM   marc_subfield_structure
865              WHERE  kohafield="items.stack"
866         '
867         );
868         $stackstatus->execute;
869
870         ($authorised_valuecode) = $stackstatus->fetchrow;
871         if ($authorised_valuecode) {
872             $stackstatus = $dbh->prepare(
873                 "SELECT lib
874                  FROM   authorised_values
875                  WHERE  category=?
876                  AND    authorised_value=?
877             "
878             );
879             $stackstatus->execute( $authorised_valuecode, $data->{stack} );
880             my ($lib) = $stackstatus->fetchrow;
881             $data->{stack} = $lib;
882         }
883         $results[$i] = $data;
884         $i++;
885     }
886     $sth->finish;
887
888     return (@results);
889 }
890
891 =head2 getitemstatus
892
893 =over 4
894
895 $itemstatushash = &getitemstatus($fwkcode);
896 returns information about status.
897 Can be MARC dependant.
898 fwkcode is optional.
899 But basically could be can be loan or not
900 Create a status selector with the following code
901
902 =head3 in PERL SCRIPT
903
904 my $itemstatushash = getitemstatus;
905 my @itemstatusloop;
906 foreach my $thisstatus (keys %$itemstatushash) {
907     my %row =(value => $thisstatus,
908                 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
909             );
910     push @itemstatusloop, \%row;
911 }
912 $template->param(statusloop=>\@itemstatusloop);
913
914
915 =head3 in TEMPLATE
916
917             <select name="statusloop">
918                 <option value="">Default</option>
919             <!-- TMPL_LOOP name="statusloop" -->
920                 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
921             <!-- /TMPL_LOOP -->
922             </select>
923
924 =cut
925
926 sub GetItemStatus {
927
928     # returns a reference to a hash of references to status...
929     my ($fwk) = @_;
930     my %itemstatus;
931     my $dbh = C4::Context->dbh;
932     my $sth;
933     $fwk = '' unless ($fwk);
934     my ( $tag, $subfield ) =
935       GetMarcFromKohaField( "items.notforloan", $fwk );
936     if ( $tag and $subfield ) {
937         my $sth =
938           $dbh->prepare(
939 "select authorised_value from marc_subfield_structure where tagfield=? and tagsubfield=? and frameworkcode=?"
940           );
941         $sth->execute( $tag, $subfield, $fwk );
942         if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
943             my $authvalsth =
944               $dbh->prepare(
945 "select authorised_value, lib from authorised_values where category=? order by lib"
946               );
947             $authvalsth->execute($authorisedvaluecat);
948             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
949                 $itemstatus{$authorisedvalue} = $lib;
950             }
951             $authvalsth->finish;
952             return \%itemstatus;
953             exit 1;
954         }
955         else {
956
957             #No authvalue list
958             # build default
959         }
960         $sth->finish;
961     }
962
963     #No authvalue list
964     #build default
965     $itemstatus{"1"} = "Not For Loan";
966     return \%itemstatus;
967 }
968
969 =head2 getitemlocation
970
971 =over 4
972
973 $itemlochash = &getitemlocation($fwk);
974 returns informations about location.
975 where fwk stands for an optional framework code.
976 Create a location selector with the following code
977
978 =head3 in PERL SCRIPT
979
980 my $itemlochash = getitemlocation;
981 my @itemlocloop;
982 foreach my $thisloc (keys %$itemlochash) {
983     my $selected = 1 if $thisbranch eq $branch;
984     my %row =(locval => $thisloc,
985                 selected => $selected,
986                 locname => $itemlochash->{$thisloc},
987             );
988     push @itemlocloop, \%row;
989 }
990 $template->param(itemlocationloop => \@itemlocloop);
991
992 =head3 in TEMPLATE
993
994 <select name="location">
995     <option value="">Default</option>
996 <!-- TMPL_LOOP name="itemlocationloop" -->
997     <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
998 <!-- /TMPL_LOOP -->
999 </select>
1000
1001 =back
1002
1003 =cut
1004
1005 sub GetItemLocation {
1006
1007     # returns a reference to a hash of references to location...
1008     my ($fwk) = @_;
1009     my %itemlocation;
1010     my $dbh = C4::Context->dbh;
1011     my $sth;
1012     $fwk = '' unless ($fwk);
1013     my ( $tag, $subfield ) =
1014       GetMarcFromKohaField( "items.location", $fwk );
1015     if ( $tag and $subfield ) {
1016         my $sth =
1017           $dbh->prepare(
1018 "select authorised_value from marc_subfield_structure where tagfield=? and tagsubfield=? and frameworkcode=?"
1019           );
1020         $sth->execute( $tag, $subfield, $fwk );
1021         if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
1022             my $authvalsth =
1023               $dbh->prepare(
1024 "select authorised_value, lib from authorised_values where category=? order by lib"
1025               );
1026             $authvalsth->execute($authorisedvaluecat);
1027             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
1028                 $itemlocation{$authorisedvalue} = $lib;
1029             }
1030             $authvalsth->finish;
1031             return \%itemlocation;
1032             exit 1;
1033         }
1034         else {
1035
1036             #No authvalue list
1037             # build default
1038         }
1039         $sth->finish;
1040     }
1041
1042     #No authvalue list
1043     #build default
1044     $itemlocation{"1"} = "Not For Loan";
1045     return \%itemlocation;
1046 }
1047
1048 =head2 GetLostItems
1049
1050 $items = GetLostItems($where,$orderby);
1051
1052 This function get the items lost into C<$items>.
1053
1054 =over 2
1055
1056 =item input:
1057 C<$where> is a hashref. it containts a field of the items table as key
1058 and the value to match as value.
1059 C<$orderby> is a field of the items table.
1060
1061 =item return:
1062 C<$items> is a reference to an array full of hasref which keys are items' table column.
1063
1064 =item usage in the perl script:
1065
1066 my %where;
1067 $where{barcode} = 0001548;
1068 my $items = GetLostItems( \%where, "homebranch" );
1069 $template->param(itemsloop => $items);
1070
1071 =back
1072
1073 =cut
1074
1075 sub GetLostItems {
1076     # Getting input args.
1077     my $where   = shift;
1078     my $orderby = shift;
1079     my $dbh     = C4::Context->dbh;
1080
1081     my $query   = "
1082         SELECT *
1083         FROM   items
1084         WHERE  itemlost IS NOT NULL
1085           AND  itemlost <> 0
1086     ";
1087     foreach my $key (keys %$where) {
1088         $query .= " AND " . $key . " LIKE '%" . $where->{$key} . "%'";
1089     }
1090     $query .= " ORDER BY ".$orderby if defined $orderby;
1091
1092     my $sth = $dbh->prepare($query);
1093     $sth->execute;
1094     my @items;
1095     while ( my $row = $sth->fetchrow_hashref ){
1096         push @items, $row;
1097     }
1098     return \@items;
1099 }
1100
1101 =head2 GetItemsForInventory
1102
1103 $itemlist = GetItemsForInventory($minlocation,$maxlocation,$datelastseen,$offset,$size)
1104
1105 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
1106
1107 The sub returns a list of hashes, containing itemnumber, author, title, barcode & item callnumber.
1108 It is ordered by callnumber,title.
1109
1110 The minlocation & maxlocation parameters are used to specify a range of item callnumbers
1111 the datelastseen can be used to specify that you want to see items not seen since a past date only.
1112 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
1113
1114 =cut
1115
1116 sub GetItemsForInventory {
1117     my ( $minlocation, $maxlocation, $datelastseen, $branch, $offset, $size ) = @_;
1118     my $dbh = C4::Context->dbh;
1119     my $sth;
1120     if ($datelastseen) {
1121         my $query =
1122                 "SELECT itemnumber,barcode,itemcallnumber,title,author,datelastseen
1123                  FROM items
1124                    LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber 
1125                  WHERE itemcallnumber>= ?
1126                    AND itemcallnumber <=?
1127                    AND (datelastseen< ? OR datelastseen IS NULL)";
1128         $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
1129         $query .= " ORDER BY itemcallnumber,title";
1130         $sth = $dbh->prepare($query);
1131         $sth->execute( $minlocation, $maxlocation, $datelastseen );
1132     }
1133     else {
1134         my $query ="
1135                 SELECT itemnumber,barcode,itemcallnumber,title,author,datelastseen
1136                 FROM items 
1137                   LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber 
1138                 WHERE itemcallnumber>= ?
1139                   AND itemcallnumber <=?";
1140         $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
1141         $query .= " ORDER BY itemcallnumber,title";
1142         $sth = $dbh->prepare($query);
1143         $sth->execute( $minlocation, $maxlocation );
1144     }
1145     my @results;
1146     while ( my $row = $sth->fetchrow_hashref ) {
1147         $offset-- if ($offset);
1148         if ( ( !$offset ) && $size ) {
1149             push @results, $row;
1150             $size--;
1151         }
1152     }
1153     return \@results;
1154 }
1155
1156 =head2 &GetBiblioItemData
1157
1158 =over 4
1159
1160 $itemdata = &GetBiblioItemData($biblioitemnumber);
1161
1162 Looks up the biblioitem with the given biblioitemnumber. Returns a
1163 reference-to-hash. The keys are the fields from the C<biblio>,
1164 C<biblioitems>, and C<itemtypes> tables in the Koha database, except
1165 that C<biblioitems.notes> is given as C<$itemdata-E<gt>{bnotes}>.
1166
1167 =back
1168
1169 =cut
1170
1171 #'
1172 sub GetBiblioItemData {
1173     my ($bibitem) = @_;
1174     my $dbh       = C4::Context->dbh;
1175     my $sth       =
1176       $dbh->prepare(
1177 "Select *,biblioitems.notes as bnotes from biblioitems, biblio,itemtypes where biblio.biblionumber = biblioitems.biblionumber and biblioitemnumber = ? and biblioitems.itemtype = itemtypes.itemtype"
1178       );
1179     my $data;
1180
1181     $sth->execute($bibitem);
1182
1183     $data = $sth->fetchrow_hashref;
1184
1185     $sth->finish;
1186     return ($data);
1187 }    # sub &GetBiblioItemData
1188
1189 =head2 GetItemnumberFromBarcode
1190
1191 =over 4
1192
1193 $result = GetItemnumberFromBarcode($barcode);
1194
1195 =back
1196
1197 =cut
1198
1199 sub GetItemnumberFromBarcode {
1200     my ($barcode) = @_;
1201     my $dbh = C4::Context->dbh;
1202
1203     my $rq =
1204       $dbh->prepare("SELECT itemnumber from items where items.barcode=?");
1205     $rq->execute($barcode);
1206     my ($result) = $rq->fetchrow;
1207     return ($result);
1208 }
1209
1210 =head2 GetBiblioItemByBiblioNumber
1211
1212 =over 4
1213
1214 NOTE : This function has been copy/paste from C4/Biblio.pm from head before zebra integration.
1215
1216 =back
1217
1218 =cut
1219
1220 sub GetBiblioItemByBiblioNumber {
1221     my ($biblionumber) = @_;
1222     my $dbh = C4::Context->dbh;
1223     my $sth = $dbh->prepare("Select * from biblioitems where biblionumber = ?");
1224     my $count = 0;
1225     my @results;
1226
1227     $sth->execute($biblionumber);
1228
1229     while ( my $data = $sth->fetchrow_hashref ) {
1230         push @results, $data;
1231     }
1232
1233     $sth->finish;
1234     return @results;
1235 }
1236
1237 =head2 GetBiblioFromItemNumber
1238
1239 =over 4
1240
1241 $item = &GetBiblioFromItemNumber($itemnumber);
1242
1243 Looks up the item with the given itemnumber.
1244
1245 C<&itemnodata> returns a reference-to-hash whose keys are the fields
1246 from the C<biblio>, C<biblioitems>, and C<items> tables in the Koha
1247 database.
1248
1249 =back
1250
1251 =cut
1252
1253 #'
1254 sub GetBiblioFromItemNumber {
1255     my ( $itemnumber ) = @_;
1256     my $dbh = C4::Context->dbh;
1257     my $sth = $dbh->prepare(
1258         "SELECT * FROM biblio,items,biblioitems
1259          WHERE items.itemnumber = ?
1260            AND biblio.biblionumber = items.biblionumber
1261            AND biblioitems.biblioitemnumber = items.biblioitemnumber"
1262     );
1263
1264     $sth->execute($itemnumber);
1265     my $data = $sth->fetchrow_hashref;
1266     $sth->finish;
1267     return ($data);
1268 }
1269
1270 =head2 GetBiblio
1271
1272 =over 4
1273
1274 ( $count, @results ) = &GetBiblio($biblionumber);
1275
1276 =back
1277
1278 =cut
1279
1280 sub GetBiblio {
1281     my ($biblionumber) = @_;
1282     my $dbh = C4::Context->dbh;
1283     my $sth = $dbh->prepare("Select * from biblio where biblionumber = ?");
1284     my $count = 0;
1285     my @results;
1286     $sth->execute($biblionumber);
1287     while ( my $data = $sth->fetchrow_hashref ) {
1288         $results[$count] = $data;
1289         $count++;
1290     }    # while
1291     $sth->finish;
1292     return ( $count, @results );
1293 }    # sub GetBiblio
1294
1295 =head2 GetItem
1296
1297 =over 4
1298
1299 $data = &GetItem($itemnumber,$barcode);
1300
1301 return Item information, for a given itemnumber or barcode
1302
1303 =back
1304
1305 =cut
1306
1307 sub GetItem {
1308     my ($itemnumber,$barcode) = @_;
1309     my $dbh = C4::Context->dbh;
1310     if ($itemnumber) {
1311         my $sth = $dbh->prepare("
1312             SELECT * FROM items 
1313             WHERE itemnumber = ?");
1314         $sth->execute($itemnumber);
1315         my $data = $sth->fetchrow_hashref;
1316         return $data;
1317     } else {
1318         my $sth = $dbh->prepare("
1319             SELECT * FROM items 
1320             WHERE barcode = ?"
1321             );
1322         $sth->execute($barcode);
1323         my $data = $sth->fetchrow_hashref;
1324         return $data;
1325     }
1326 }    # sub GetItem
1327
1328 =head2 get_itemnumbers_of
1329
1330 =over 4
1331
1332 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1333
1334 Given a list of biblionumbers, return the list of corresponding itemnumbers
1335 for each biblionumber.
1336
1337 Return a reference on a hash where keys are biblionumbers and values are
1338 references on array of itemnumbers.
1339
1340 =back
1341
1342 =cut
1343
1344 sub get_itemnumbers_of {
1345     my @biblionumbers = @_;
1346
1347     my $dbh = C4::Context->dbh;
1348
1349     my $query = '
1350         SELECT itemnumber,
1351             biblionumber
1352         FROM items
1353         WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1354     ';
1355     my $sth = $dbh->prepare($query);
1356     $sth->execute(@biblionumbers);
1357
1358     my %itemnumbers_of;
1359
1360     while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1361         push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1362     }
1363
1364     return \%itemnumbers_of;
1365 }
1366
1367 =head2 GetItemInfosOf
1368
1369 =over 4
1370
1371 GetItemInfosOf(@itemnumbers);
1372
1373 =back
1374
1375 =cut
1376
1377 sub GetItemInfosOf {
1378     my @itemnumbers = @_;
1379
1380     my $query = '
1381         SELECT *
1382         FROM items
1383         WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1384     ';
1385     return get_infos_of( $query, 'itemnumber' );
1386 }
1387
1388 =head2 GetBiblioItemInfosOf
1389
1390 =over 4
1391
1392 GetBiblioItemInfosOf(@biblioitemnumbers);
1393
1394 =back
1395
1396 =cut
1397
1398 sub GetBiblioItemInfosOf {
1399     my @biblioitemnumbers = @_;
1400
1401     my $query = '
1402         SELECT biblioitemnumber,
1403             publicationyear,
1404             itemtype
1405         FROM biblioitems
1406         WHERE biblioitemnumber IN (' . join( ',', @biblioitemnumbers ) . ')
1407     ';
1408     return get_infos_of( $query, 'biblioitemnumber' );
1409 }
1410
1411 =head1 FUNCTIONS FOR HANDLING MARC MANAGEMENT
1412
1413 =head2 GetMarcStructure
1414
1415 =over 4
1416
1417 $res = GetMarcStructure($forlibrarian,$frameworkcode);
1418
1419 Returns a reference to a big hash of hash, with the Marc structure for the given frameworkcode
1420 $forlibrarian  :if set to 1, the MARC descriptions are the librarians ones, otherwise it's the public (OPAC) ones
1421 $frameworkcode : the framework code to read
1422
1423 =back
1424
1425 =cut
1426
1427 sub GetMarcStructure {
1428     my ( $forlibrarian, $frameworkcode ) = @_;
1429     my $dbh=C4::Context->dbh;
1430     $frameworkcode = "" unless $frameworkcode;
1431     my $sth;
1432     my $libfield = ( $forlibrarian eq 1 ) ? 'liblibrarian' : 'libopac';
1433
1434     # check that framework exists
1435     $sth =
1436       $dbh->prepare(
1437         "select count(*) from marc_tag_structure where frameworkcode=?");
1438     $sth->execute($frameworkcode);
1439     my ($total) = $sth->fetchrow;
1440     $frameworkcode = "" unless ( $total > 0 );
1441     $sth =
1442       $dbh->prepare(
1443 "select tagfield,liblibrarian,libopac,mandatory,repeatable from marc_tag_structure where frameworkcode=? order by tagfield"
1444       );
1445     $sth->execute($frameworkcode);
1446     my ( $liblibrarian, $libopac, $tag, $res, $tab, $mandatory, $repeatable );
1447
1448     while ( ( $tag, $liblibrarian, $libopac, $mandatory, $repeatable ) =
1449         $sth->fetchrow )
1450     {
1451         $res->{$tag}->{lib} =
1452           ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
1453           # why the hell do we need to explicitly decode utf8 ? 
1454           # that's a good question, but we must do it...
1455         $res->{$tab}->{tab}        = "";            # XXX
1456         $res->{$tag}->{mandatory}  = $mandatory;
1457         $res->{$tag}->{repeatable} = $repeatable;
1458     }
1459
1460     $sth =
1461       $dbh->prepare(
1462 "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"
1463       );
1464     $sth->execute($frameworkcode);
1465
1466     my $subfield;
1467     my $authorised_value;
1468     my $authtypecode;
1469     my $value_builder;
1470     my $kohafield;
1471     my $seealso;
1472     my $hidden;
1473     my $isurl;
1474     my $link;
1475     my $defaultvalue;
1476
1477     while (
1478         (
1479             $tag,          $subfield,      $liblibrarian,
1480             ,              $libopac,       $tab,
1481             $mandatory,    $repeatable,    $authorised_value,
1482             $authtypecode, $value_builder, $kohafield,
1483             $seealso,      $hidden,        $isurl,
1484             $link,$defaultvalue
1485         )
1486         = $sth->fetchrow
1487       )
1488     {
1489         $res->{$tag}->{$subfield}->{lib} =
1490           ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
1491         $res->{$tag}->{$subfield}->{tab}              = $tab;
1492         $res->{$tag}->{$subfield}->{mandatory}        = $mandatory;
1493         $res->{$tag}->{$subfield}->{repeatable}       = $repeatable;
1494         $res->{$tag}->{$subfield}->{authorised_value} = $authorised_value;
1495         $res->{$tag}->{$subfield}->{authtypecode}     = $authtypecode;
1496         $res->{$tag}->{$subfield}->{value_builder}    = $value_builder;
1497         $res->{$tag}->{$subfield}->{kohafield}        = $kohafield;
1498         $res->{$tag}->{$subfield}->{seealso}          = $seealso;
1499         $res->{$tag}->{$subfield}->{hidden}           = $hidden;
1500         $res->{$tag}->{$subfield}->{isurl}            = $isurl;
1501         $res->{$tag}->{$subfield}->{link}             = $link;
1502         $res->{$tag}->{$subfield}->{defaultvalue}     = $defaultvalue;
1503     }
1504     return $res;
1505 }
1506
1507 =head2 GetMarcFromKohaField
1508
1509 =over 4
1510
1511 ($MARCfield,$MARCsubfield)=GetMarcFromKohaField($kohafield,$frameworkcode);
1512 Returns the MARC fields & subfields mapped to the koha field 
1513 for the given frameworkcode
1514
1515 =back
1516
1517 =cut
1518
1519 sub GetMarcFromKohaField {
1520     my ( $kohafield, $frameworkcode ) = @_;
1521     return 0, 0 unless $kohafield;
1522     my $relations = C4::Context->marcfromkohafield;
1523     return (
1524         $relations->{$frameworkcode}->{$kohafield}->[0],
1525         $relations->{$frameworkcode}->{$kohafield}->[1]
1526     );
1527 }
1528
1529 =head2 GetMarcBiblio
1530
1531 =over 4
1532
1533 Returns MARC::Record of the biblionumber passed in parameter.
1534 the marc record contains both biblio & item datas
1535
1536 =back
1537
1538 =cut
1539
1540 sub GetMarcBiblio {
1541     my $biblionumber = shift;
1542     my $dbh          = C4::Context->dbh;
1543     my $sth          =
1544       $dbh->prepare("select marcxml from biblioitems where biblionumber=? ");
1545     $sth->execute($biblionumber);
1546     my ($marcxml) = $sth->fetchrow;
1547     MARC::File::XML->default_record_format(C4::Context->preference('marcflavour'));
1548     $marcxml =~ s/\x1e//g;
1549     $marcxml =~ s/\x1f//g;
1550     $marcxml =~ s/\x1d//g;
1551     $marcxml =~ s/\x0f//g;
1552     $marcxml =~ s/\x0c//g;
1553     my $record = MARC::Record->new();
1554     $record = MARC::Record::new_from_xml( $marcxml, "utf8",C4::Context->preference('marcflavour')) if $marcxml;
1555     return $record;
1556 }
1557
1558 =head2 GetXmlBiblio
1559
1560 =over 4
1561
1562 my $marcxml = GetXmlBiblio($biblionumber);
1563
1564 Returns biblioitems.marcxml of the biblionumber passed in parameter.
1565 The XML contains both biblio & item datas
1566
1567 =back
1568
1569 =cut
1570
1571 sub GetXmlBiblio {
1572     my ( $biblionumber ) = @_;
1573     my $dbh = C4::Context->dbh;
1574     my $sth =
1575       $dbh->prepare("select marcxml from biblioitems where biblionumber=? ");
1576     $sth->execute($biblionumber);
1577     my ($marcxml) = $sth->fetchrow;
1578     return $marcxml;
1579 }
1580
1581 =head2 GetAuthorisedValueDesc
1582
1583 =over 4
1584
1585 my $subfieldvalue =get_authorised_value_desc(
1586     $tag, $subf[$i][0],$subf[$i][1], '', $taglib);
1587 Retrieve the complete description for a given authorised value.
1588
1589 =back
1590
1591 =cut
1592
1593 sub GetAuthorisedValueDesc {
1594     my ( $tag, $subfield, $value, $framework, $tagslib ) = @_;
1595     my $dbh = C4::Context->dbh;
1596     
1597     #---- branch
1598     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
1599         return C4::Branch::GetBranchName($value);
1600     }
1601
1602     #---- itemtypes
1603     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) {
1604         return getitemtypeinfo($value);
1605     }
1606
1607     #---- "true" authorized value
1608     my $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'};
1609
1610     if ( $category ne "" ) {
1611         my $sth =
1612           $dbh->prepare(
1613             "select lib from authorised_values where category = ? and authorised_value = ?"
1614           );
1615         $sth->execute( $category, $value );
1616         my $data = $sth->fetchrow_hashref;
1617         return $data->{'lib'};
1618     }
1619     else {
1620         return $value;    # if nothing is found return the original value
1621     }
1622 }
1623
1624 =head2 GetMarcItem
1625
1626 =over 4
1627
1628 Returns MARC::Record of the item passed in parameter.
1629
1630 =back
1631
1632 =cut
1633
1634 sub GetMarcItem {
1635     my ( $biblionumber, $itemnumber ) = @_;
1636     my $dbh = C4::Context->dbh;
1637     my $newrecord = MARC::Record->new();
1638     my $marcflavour = C4::Context->preference('marcflavour');
1639     
1640     my $marcxml = GetXmlBiblio($biblionumber);
1641     my $record = MARC::Record->new();
1642     $record = MARC::Record::new_from_xml( $marcxml, "utf8", $marcflavour );
1643     # now, find where the itemnumber is stored & extract only the item
1644     my ( $itemnumberfield, $itemnumbersubfield ) =
1645       GetMarcFromKohaField( 'items.itemnumber', '' );
1646     my @fields = $record->field($itemnumberfield);
1647     foreach my $field (@fields) {
1648         if ( $field->subfield($itemnumbersubfield) eq $itemnumber ) {
1649             $newrecord->insert_fields_ordered($field);
1650         }
1651     }
1652     return $newrecord;
1653 }
1654
1655
1656
1657 =head2 GetMarcNotes
1658
1659 =over 4
1660
1661 $marcnotesarray = GetMarcNotes( $record, $marcflavour );
1662 Get all notes from the MARC record and returns them in an array.
1663 The note are stored in differents places depending on MARC flavour
1664
1665 =back
1666
1667 =cut
1668
1669 sub GetMarcNotes {
1670     my ( $record, $marcflavour ) = @_;
1671     my $scope;
1672     if ( $marcflavour eq "MARC21" ) {
1673         $scope = '5..';
1674     }
1675     else {    # assume unimarc if not marc21
1676         $scope = '3..';
1677     }
1678     my @marcnotes;
1679     my $note = "";
1680     my $tag  = "";
1681     my $marcnote;
1682     foreach my $field ( $record->field($scope) ) {
1683         my $value = $field->as_string();
1684         if ( $note ne "" ) {
1685             $marcnote = { marcnote => $note, };
1686             push @marcnotes, $marcnote;
1687             $note = $value;
1688         }
1689         if ( $note ne $value ) {
1690             $note = $note . " " . $value;
1691         }
1692     }
1693
1694     if ( $note ) {
1695         $marcnote = { marcnote => $note };
1696         push @marcnotes, $marcnote;    #load last tag into array
1697     }
1698     return \@marcnotes;
1699 }    # end GetMarcNotes
1700
1701 =head2 GetMarcSubjects
1702
1703 =over 4
1704
1705 $marcsubjcts = GetMarcSubjects($record,$marcflavour);
1706 Get all subjects from the MARC record and returns them in an array.
1707 The subjects are stored in differents places depending on MARC flavour
1708
1709 =back
1710
1711 =cut
1712
1713 sub GetMarcSubjects {
1714     my ( $record, $marcflavour ) = @_;
1715     my ( $mintag, $maxtag );
1716     if ( $marcflavour eq "MARC21" ) {
1717         $mintag = "600";
1718         $maxtag = "699";
1719     }
1720     else {    # assume unimarc if not marc21
1721         $mintag = "600";
1722         $maxtag = "611";
1723     }
1724
1725     my @marcsubjcts;
1726
1727     foreach my $field ( $record->fields ) {
1728         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1729         my @subfields = $field->subfields();
1730         my $link;
1731         my $label = "su:";
1732         my $flag = 0;
1733         for my $subject_subfield ( @subfields ) {
1734             if (
1735                 $marcflavour ne 'MARC21'
1736                 and (
1737                     ($subject_subfield->[0] eq '3') or
1738                     ($subject_subfield->[0] eq '4') or
1739                     ($subject_subfield->[0] eq '5')
1740                 )
1741             )
1742             {
1743                 next;
1744             }
1745             my $code = $subject_subfield->[0];
1746             $label .= $subject_subfield->[1] . " and su-to:" unless ( $code == 9 );
1747             if ( $code == 9 ) {
1748                 $link = "Koha-Auth-Number:".$subject_subfield->[1];
1749                 $flag = 1;
1750             }
1751             elsif ( ! $flag ) {
1752                 $link = $label;
1753                 $link =~ s/ and\ssu-to:$//;
1754             }
1755         }
1756         $label =~ s/su/ /g;
1757         $label =~ s/://g;
1758         $label =~ s/-to//g;
1759         $label =~ s/ and //g;
1760         push @marcsubjcts,
1761           {
1762             label => $label,
1763             link  => $link
1764           }
1765     }
1766     return \@marcsubjcts;
1767 }    #end GetMarcSubjects
1768
1769 =head2 GetMarcAuthors
1770
1771 =over 4
1772
1773 authors = GetMarcAuthors($record,$marcflavour);
1774 Get all authors from the MARC record and returns them in an array.
1775 The authors are stored in differents places depending on MARC flavour
1776
1777 =back
1778
1779 =cut
1780
1781 sub GetMarcAuthors {
1782     my ( $record, $marcflavour ) = @_;
1783     my ( $mintag, $maxtag );
1784     if ( $marcflavour eq "MARC21" ) {
1785         $mintag = "100";
1786         $maxtag = "111"; 
1787     }
1788     else {    # assume unimarc if not marc21
1789         $mintag = "701";
1790         $maxtag = "712";
1791     }
1792
1793     my @marcauthors;
1794
1795     foreach my $field ( $record->fields ) {
1796         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1797         my %hash;
1798         my @subfields = $field->subfields();
1799         my $count_auth = 0;
1800         my $and ;
1801         for my $authors_subfield (@subfields) {
1802             if (
1803                 $marcflavour ne 'MARC21'
1804                 and (
1805                     ($authors_subfield->[0] eq '3') or
1806                     ($authors_subfield->[0] eq '4') or
1807                     ($authors_subfield->[0] eq '5')
1808                 )
1809             )
1810             {
1811                 next;
1812             }
1813             if ($count_auth ne '0'){
1814                 $and = " and au:";
1815             }
1816             $count_auth++;
1817             my $subfieldcode = $authors_subfield->[0];
1818             my $value        = $authors_subfield->[1];
1819             $hash{tag}       = $field->tag;
1820             $hash{value}    .= $value . " " if ($subfieldcode != 9) ;
1821             $hash{link}     .= $value if ($subfieldcode eq 9);
1822         }
1823         push @marcauthors, \%hash;
1824     }
1825     return \@marcauthors;
1826 }
1827
1828 =head2 GetMarcSeries
1829
1830 =over 4
1831
1832 $marcseriessarray = GetMarcSeries($record,$marcflavour);
1833 Get all series from the MARC record and returns them in an array.
1834 The series are stored in differents places depending on MARC flavour
1835
1836 =back
1837
1838 =cut
1839
1840 sub GetMarcSeries {
1841     my ($record, $marcflavour) = @_;
1842     my ($mintag, $maxtag);
1843     if ($marcflavour eq "MARC21") {
1844         $mintag = "440";
1845         $maxtag = "490";
1846     } else {           # assume unimarc if not marc21
1847         $mintag = "600";
1848         $maxtag = "619";
1849     }
1850
1851     my @marcseries;
1852     my $subjct = "";
1853     my $subfield = "";
1854     my $marcsubjct;
1855
1856     foreach my $field ($record->field('440'), $record->field('490')) {
1857         my @subfields_loop;
1858         #my $value = $field->subfield('a');
1859         #$marcsubjct = {MARCSUBJCT => $value,};
1860         my @subfields = $field->subfields();
1861         #warn "subfields:".join " ", @$subfields;
1862         my $counter = 0;
1863         my @link_loop;
1864         for my $series_subfield (@subfields) {
1865                         my $volume_number;
1866                         undef $volume_number;
1867                         # see if this is an instance of a volume
1868                         if ($series_subfield->[0] eq 'v') {
1869                                 $volume_number=1;
1870                         }
1871
1872             my $code = $series_subfield->[0];
1873             my $value = $series_subfield->[1];
1874             my $linkvalue = $value;
1875             $linkvalue =~ s/(\(|\))//g;
1876             my $operator = " and " unless $counter==0;
1877             push @link_loop, {link => $linkvalue, operator => $operator };
1878             my $separator = C4::Context->preference("authoritysep") unless $counter==0;
1879                         if ($volume_number) {
1880                         push @subfields_loop, {volumenum => $value};
1881                         }
1882                         else {
1883             push @subfields_loop, {code => $code, value => $value, link_loop => \@link_loop, separator => $separator, volumenum => $volume_number};
1884                         }
1885             $counter++;
1886         }
1887         push @marcseries, { MARCSERIES_SUBFIELDS_LOOP => \@subfields_loop };
1888         #$marcsubjct = {MARCSUBJCT => $field->as_string(),};
1889         #push @marcsubjcts, $marcsubjct;
1890         #$subjct = $value;
1891
1892     }
1893     my $marcseriessarray=\@marcseries;
1894     return $marcseriessarray;
1895 }  #end getMARCseriess
1896
1897 =head2 GetFrameworkCode
1898
1899 =over 4
1900
1901 $frameworkcode = GetFrameworkCode( $biblionumber )
1902
1903 =back
1904
1905 =cut
1906
1907 sub GetFrameworkCode {
1908     my ( $biblionumber ) = @_;
1909     my $dbh = C4::Context->dbh;
1910     my $sth =
1911       $dbh->prepare("select frameworkcode from biblio where biblionumber=?");
1912     $sth->execute($biblionumber);
1913     my ($frameworkcode) = $sth->fetchrow;
1914     return $frameworkcode;
1915 }
1916
1917 =head2 TransformKohaToMarc
1918
1919 =over 4
1920
1921 $record = TransformKohaToMarc( $hash )
1922 This function builds partial MARC::Record from a hash
1923 Hash entries can be from biblio or biblioitems.
1924 This function is called in acquisition module, to create a basic catalogue entry from user entry
1925
1926 =back
1927
1928 =cut
1929
1930 sub TransformKohaToMarc {
1931
1932     my ( $hash ) = @_;
1933     my $dbh = C4::Context->dbh;
1934     my $sth =
1935     $dbh->prepare(
1936         "select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
1937     );
1938     my $record = MARC::Record->new();
1939     foreach (keys %{$hash}) {
1940         &TransformKohaToMarcOneField( $sth, $record, $_,
1941             $hash->{$_}, '' );
1942         }
1943     return $record;
1944 }
1945
1946 =head2 TransformKohaToMarcOneField
1947
1948 =over 4
1949
1950 $record = TransformKohaToMarcOneField( $sth, $record, $kohafieldname, $value, $frameworkcode );
1951
1952 =back
1953
1954 =cut
1955
1956 sub TransformKohaToMarcOneField {
1957     my ( $sth, $record, $kohafieldname, $value, $frameworkcode ) = @_;
1958     $frameworkcode='' unless $frameworkcode;
1959     my $tagfield;
1960     my $tagsubfield;
1961
1962     if ( !defined $sth ) {
1963         my $dbh = C4::Context->dbh;
1964         $sth =
1965           $dbh->prepare(
1966 "select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
1967           );
1968     }
1969     $sth->execute( $frameworkcode, $kohafieldname );
1970     if ( ( $tagfield, $tagsubfield ) = $sth->fetchrow ) {
1971         my $tag = $record->field($tagfield);
1972         if ($tag) {
1973             $tag->update( $tagsubfield => $value );
1974             $record->delete_field($tag);
1975             $record->insert_fields_ordered($tag);
1976         }
1977         else {
1978             $record->add_fields( $tagfield, " ", " ", $tagsubfield => $value );
1979         }
1980     }
1981     return $record;
1982 }
1983
1984 =head2 TransformHtmlToXml
1985
1986 =over 4
1987
1988 $xml = TransformHtmlToXml( $tags, $subfields, $values, $indicator, $ind_tag )
1989
1990 =back
1991
1992 =cut
1993
1994 sub TransformHtmlToXml {
1995     my ( $tags, $subfields, $values, $indicator, $ind_tag ) = @_;
1996     my $xml = MARC::File::XML::header('UTF-8');
1997     if ( C4::Context->preference('marcflavour') eq 'UNIMARC' ) {
1998         MARC::File::XML->default_record_format('UNIMARC');
1999         use POSIX qw(strftime);
2000         my $string = strftime( "%Y%m%d", localtime(time) );
2001         $string = sprintf( "%-*s", 35, $string );
2002         substr( $string, 22, 6, "frey50" );
2003         $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
2004         $xml .= "<subfield code=\"a\">$string</subfield>\n";
2005         $xml .= "</datafield>\n";
2006     }
2007     my $prevvalue;
2008     my $prevtag = -1;
2009     my $first   = 1;
2010     my $j       = -1;
2011     for ( my $i = 0 ; $i <= @$tags ; $i++ ) {
2012         @$values[$i] =~ s/&/&amp;/g;
2013         @$values[$i] =~ s/</&lt;/g;
2014         @$values[$i] =~ s/>/&gt;/g;
2015         @$values[$i] =~ s/"/&quot;/g;
2016         @$values[$i] =~ s/'/&apos;/g;
2017         if ( !utf8::is_utf8( @$values[$i] ) ) {
2018             utf8::decode( @$values[$i] );
2019         }
2020         if ( ( @$tags[$i] ne $prevtag ) ) {
2021             $j++ unless ( @$tags[$i] eq "" );
2022             if ( !$first ) {
2023                 $xml .= "</datafield>\n";
2024                 if (   ( @$tags[$i] && @$tags[$i] > 10 )
2025                     && ( @$values[$i] ne "" ) )
2026                 {
2027                     my $ind1 = substr( @$indicator[$j], 0, 1 );
2028                     my $ind2;
2029                     if ( @$indicator[$j] ) {
2030                         $ind2 = substr( @$indicator[$j], 1, 1 );
2031                     }
2032                     else {
2033                         warn "Indicator in @$tags[$i] is empty";
2034                         $ind2 = " ";
2035                     }
2036                     $xml .=
2037 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2038                     $xml .=
2039 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2040                     $first = 0;
2041                 }
2042                 else {
2043                     $first = 1;
2044                 }
2045             }
2046             else {
2047                 if ( @$values[$i] ne "" ) {
2048
2049                     # leader
2050                     if ( @$tags[$i] eq "000" ) {
2051                         $xml .= "<leader>@$values[$i]</leader>\n";
2052                         $first = 1;
2053
2054                         # rest of the fixed fields
2055                     }
2056                     elsif ( @$tags[$i] < 10 ) {
2057                         $xml .=
2058 "<controlfield tag=\"@$tags[$i]\">@$values[$i]</controlfield>\n";
2059                         $first = 1;
2060                     }
2061                     else {
2062                         my $ind1 = substr( @$indicator[$j], 0, 1 );
2063                         my $ind2 = substr( @$indicator[$j], 1, 1 );
2064                         $xml .=
2065 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2066                         $xml .=
2067 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2068                         $first = 0;
2069                     }
2070                 }
2071             }
2072         }
2073         else {    # @$tags[$i] eq $prevtag
2074             if ( @$values[$i] eq "" ) {
2075             }
2076             else {
2077                 if ($first) {
2078                     my $ind1 = substr( @$indicator[$j], 0, 1 );
2079                     my $ind2 = substr( @$indicator[$j], 1, 1 );
2080                     $xml .=
2081 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2082                     $first = 0;
2083                 }
2084                 $xml .=
2085 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2086             }
2087         }
2088         $prevtag = @$tags[$i];
2089     }
2090     $xml .= MARC::File::XML::footer();
2091
2092     return $xml;
2093 }
2094
2095 =head2 TransformHtmlToMarc
2096
2097 =over 4
2098
2099 $record = TransformHtmlToMarc( $dbh, $rtags, $rsubfields, $rvalues, %indicators )
2100
2101 =back
2102
2103 =cut
2104
2105 sub TransformHtmlToMarc {
2106     my ( $dbh, $rtags, $rsubfields, $rvalues, %indicators ) = @_;
2107     my $prevtag = -1;
2108     my $record  = MARC::Record->new();
2109
2110     #     my %subfieldlist=();
2111     my $prevvalue;    # if tag <10
2112     my $field;        # if tag >=10
2113     for ( my $i = 0 ; $i < @$rtags ; $i++ ) {
2114         next unless @$rvalues[$i];
2115
2116  # rebuild MARC::Record
2117  #             warn "0=>".@$rtags[$i].@$rsubfields[$i]." = ".@$rvalues[$i].": ";
2118         if ( @$rtags[$i] ne $prevtag ) {
2119             if ( $prevtag < 10 ) {
2120                 if ($prevvalue) {
2121
2122                     if ( $prevtag ne '000' ) {
2123                         $record->insert_fields_ordered(
2124                             ( sprintf "%03s", $prevtag ), $prevvalue );
2125                     }
2126                     else {
2127
2128                         $record->leader($prevvalue);
2129
2130                     }
2131                 }
2132             }
2133             else {
2134                 if ($field) {
2135                     $record->insert_fields_ordered($field);
2136                 }
2137             }
2138             $indicators{ @$rtags[$i] } .= '  ';
2139             if ( @$rtags[$i] < 10 ) {
2140                 $prevvalue = @$rvalues[$i];
2141                 undef $field;
2142             }
2143             else {
2144                 undef $prevvalue;
2145                 $field = MARC::Field->new(
2146                     ( sprintf "%03s", @$rtags[$i] ),
2147                     substr( $indicators{ @$rtags[$i] }, 0, 1 ),
2148                     substr( $indicators{ @$rtags[$i] }, 1, 1 ),
2149                     @$rsubfields[$i] => @$rvalues[$i]
2150                 );
2151             }
2152             $prevtag = @$rtags[$i];
2153         }
2154         else {
2155             if ( @$rtags[$i] < 10 ) {
2156                 $prevvalue = @$rvalues[$i];
2157             }
2158             else {
2159                 if ( length( @$rvalues[$i] ) > 0 ) {
2160                     $field->add_subfields( @$rsubfields[$i] => @$rvalues[$i] );
2161                 }
2162             }
2163             $prevtag = @$rtags[$i];
2164         }
2165     }
2166
2167     # the last has not been included inside the loop... do it now !
2168     $record->insert_fields_ordered($field) if $field;
2169
2170     $record->encoding('UTF-8');
2171
2172     #    $record->MARC::File::USMARC::update_leader();
2173     return $record;
2174 }
2175
2176 =head2 TransformMarcToKoha
2177
2178 =over 4
2179
2180 $result = TransformMarcToKoha( $dbh, $record, $frameworkcode )
2181
2182 =back
2183
2184 =cut
2185
2186 sub TransformMarcToKoha {
2187     my ( $dbh, $record, $frameworkcode ) = @_;
2188     my $sth =
2189       $dbh->prepare(
2190 "select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
2191       );
2192     my $result;
2193     my $sth2 = $dbh->prepare("SHOW COLUMNS from biblio");
2194     $sth2->execute;
2195     my $field;
2196     while ( ($field) = $sth2->fetchrow ) {
2197         $result =
2198           &TransformMarcToKohaOneField( "biblio", $field, $record, $result,
2199             $frameworkcode );
2200     }
2201     $sth2 = $dbh->prepare("SHOW COLUMNS from biblioitems");
2202     $sth2->execute;
2203     while ( ($field) = $sth2->fetchrow ) {
2204         if ( $field eq 'notes' ) { $field = 'bnotes'; }
2205         $result =
2206           &TransformMarcToKohaOneField( "biblioitems", $field, $record, $result,
2207             $frameworkcode );
2208     }
2209     $sth2 = $dbh->prepare("SHOW COLUMNS from items");
2210     $sth2->execute;
2211     while ( ($field) = $sth2->fetchrow ) {
2212         $result =
2213           &TransformMarcToKohaOneField( "items", $field, $record, $result,
2214             $frameworkcode );
2215     }
2216
2217     #
2218     # modify copyrightdate to keep only the 1st year found
2219     my $temp = $result->{'copyrightdate'};
2220     $temp =~ m/c(\d\d\d\d)/;    # search cYYYY first
2221     if ( $1 > 0 ) {
2222         $result->{'copyrightdate'} = $1;
2223     }
2224     else {                      # if no cYYYY, get the 1st date.
2225         $temp =~ m/(\d\d\d\d)/;
2226         $result->{'copyrightdate'} = $1;
2227     }
2228
2229     # modify publicationyear to keep only the 1st year found
2230     $temp = $result->{'publicationyear'};
2231     $temp =~ m/c(\d\d\d\d)/;    # search cYYYY first
2232     if ( $1 > 0 ) {
2233         $result->{'publicationyear'} = $1;
2234     }
2235     else {                      # if no cYYYY, get the 1st date.
2236         $temp =~ m/(\d\d\d\d)/;
2237         $result->{'publicationyear'} = $1;
2238     }
2239     return $result;
2240 }
2241
2242 =head2 TransformMarcToKohaOneField
2243
2244 =over 4
2245
2246 $result = TransformMarcToKohaOneField( $kohatable, $kohafield, $record, $result, $frameworkcode )
2247
2248 =back
2249
2250 =cut
2251
2252 sub TransformMarcToKohaOneField {
2253
2254 # FIXME ? if a field has a repeatable subfield that is used in old-db, only the 1st will be retrieved...
2255     my ( $kohatable, $kohafield, $record, $result, $frameworkcode ) = @_;
2256
2257     my $res = "";
2258     my ( $tagfield, $subfield ) =
2259       GetMarcFromKohaField( $kohatable . "." . $kohafield,
2260         $frameworkcode );
2261     foreach my $field ( $record->field($tagfield) ) {
2262         if ( $field->tag() < 10 ) {
2263             if ( $result->{$kohafield} ) {
2264                 $result->{$kohafield} .= " | " . $field->data();
2265             }
2266             else {
2267                 $result->{$kohafield} = $field->data();
2268             }
2269         }
2270         else {
2271             if ( $field->subfields ) {
2272                 my @subfields = $field->subfields();
2273                 foreach my $subfieldcount ( 0 .. $#subfields ) {
2274                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
2275                         if ( $result->{$kohafield} ) {
2276                             $result->{$kohafield} .=
2277                               " | " . $subfields[$subfieldcount][1];
2278                         }
2279                         else {
2280                             $result->{$kohafield} =
2281                               $subfields[$subfieldcount][1];
2282                         }
2283                     }
2284                 }
2285             }
2286         }
2287     }
2288     return $result;
2289 }
2290 =head1  OTHER FUNCTIONS
2291
2292 =head2 char_decode
2293
2294 =over 4
2295
2296 my $string = char_decode( $string, $encoding );
2297
2298 converts ISO 5426 coded string to UTF-8
2299 sloppy code : should be improved in next issue
2300
2301 =back
2302
2303 =cut
2304
2305 sub char_decode {
2306     my ( $string, $encoding ) = @_;
2307     $_ = $string;
2308
2309     $encoding = C4::Context->preference("marcflavour") unless $encoding;
2310     if ( $encoding eq "UNIMARC" ) {
2311
2312         #         s/\xe1/Æ/gm;
2313         s/\xe2/Ğ/gm;
2314         s/\xe9/Ø/gm;
2315         s/\xec/ş/gm;
2316         s/\xf1/æ/gm;
2317         s/\xf3/ğ/gm;
2318         s/\xf9/ø/gm;
2319         s/\xfb/ß/gm;
2320         s/\xc1\x61/à/gm;
2321         s/\xc1\x65/è/gm;
2322         s/\xc1\x69/ì/gm;
2323         s/\xc1\x6f/ò/gm;
2324         s/\xc1\x75/ù/gm;
2325         s/\xc1\x41/À/gm;
2326         s/\xc1\x45/È/gm;
2327         s/\xc1\x49/Ì/gm;
2328         s/\xc1\x4f/Ò/gm;
2329         s/\xc1\x55/Ù/gm;
2330         s/\xc2\x41/Á/gm;
2331         s/\xc2\x45/É/gm;
2332         s/\xc2\x49/Í/gm;
2333         s/\xc2\x4f/Ó/gm;
2334         s/\xc2\x55/Ú/gm;
2335         s/\xc2\x59/İ/gm;
2336         s/\xc2\x61/á/gm;
2337         s/\xc2\x65/é/gm;
2338         s/\xc2\x69/í/gm;
2339         s/\xc2\x6f/ó/gm;
2340         s/\xc2\x75/ú/gm;
2341         s/\xc2\x79/ı/gm;
2342         s/\xc3\x41/Â/gm;
2343         s/\xc3\x45/Ê/gm;
2344         s/\xc3\x49/Î/gm;
2345         s/\xc3\x4f/Ô/gm;
2346         s/\xc3\x55/Û/gm;
2347         s/\xc3\x61/â/gm;
2348         s/\xc3\x65/ê/gm;
2349         s/\xc3\x69/î/gm;
2350         s/\xc3\x6f/ô/gm;
2351         s/\xc3\x75/û/gm;
2352         s/\xc4\x41/Ã/gm;
2353         s/\xc4\x4e/Ñ/gm;
2354         s/\xc4\x4f/Õ/gm;
2355         s/\xc4\x61/ã/gm;
2356         s/\xc4\x6e/ñ/gm;
2357         s/\xc4\x6f/õ/gm;
2358         s/\xc8\x41/Ä/gm;
2359         s/\xc8\x45/Ë/gm;
2360         s/\xc8\x49/Ï/gm;
2361         s/\xc8\x61/ä/gm;
2362         s/\xc8\x65/ë/gm;
2363         s/\xc8\x69/ï/gm;
2364         s/\xc8\x6F/ö/gm;
2365         s/\xc8\x75/ü/gm;
2366         s/\xc8\x76/ÿ/gm;
2367         s/\xc9\x41/Ä/gm;
2368         s/\xc9\x45/Ë/gm;
2369         s/\xc9\x49/Ï/gm;
2370         s/\xc9\x4f/Ö/gm;
2371         s/\xc9\x55/Ü/gm;
2372         s/\xc9\x61/ä/gm;
2373         s/\xc9\x6f/ö/gm;
2374         s/\xc9\x75/ü/gm;
2375         s/\xca\x41/Å/gm;
2376         s/\xca\x61/å/gm;
2377         s/\xd0\x43/Ç/gm;
2378         s/\xd0\x63/ç/gm;
2379
2380         # this handles non-sorting blocks (if implementation requires this)
2381         $string = nsb_clean($_);
2382     }
2383     elsif ( $encoding eq "USMARC" || $encoding eq "MARC21" ) {
2384         ##MARC-8 to UTF-8
2385
2386         s/\xe1\x61/à/gm;
2387         s/\xe1\x65/è/gm;
2388         s/\xe1\x69/ì/gm;
2389         s/\xe1\x6f/ò/gm;
2390         s/\xe1\x75/ù/gm;
2391         s/\xe1\x41/À/gm;
2392         s/\xe1\x45/È/gm;
2393         s/\xe1\x49/Ì/gm;
2394         s/\xe1\x4f/Ò/gm;
2395         s/\xe1\x55/Ù/gm;
2396         s/\xe2\x41/Á/gm;
2397         s/\xe2\x45/É/gm;
2398         s/\xe2\x49/Í/gm;
2399         s/\xe2\x4f/Ó/gm;
2400         s/\xe2\x55/Ú/gm;
2401         s/\xe2\x59/İ/gm;
2402         s/\xe2\x61/á/gm;
2403         s/\xe2\x65/é/gm;
2404         s/\xe2\x69/í/gm;
2405         s/\xe2\x6f/ó/gm;
2406         s/\xe2\x75/ú/gm;
2407         s/\xe2\x79/ı/gm;
2408         s/\xe3\x41/Â/gm;
2409         s/\xe3\x45/Ê/gm;
2410         s/\xe3\x49/Î/gm;
2411         s/\xe3\x4f/Ô/gm;
2412         s/\xe3\x55/Û/gm;
2413         s/\xe3\x61/â/gm;
2414         s/\xe3\x65/ê/gm;
2415         s/\xe3\x69/î/gm;
2416         s/\xe3\x6f/ô/gm;
2417         s/\xe3\x75/û/gm;
2418         s/\xe4\x41/Ã/gm;
2419         s/\xe4\x4e/Ñ/gm;
2420         s/\xe4\x4f/Õ/gm;
2421         s/\xe4\x61/ã/gm;
2422         s/\xe4\x6e/ñ/gm;
2423         s/\xe4\x6f/õ/gm;
2424         s/\xe6\x41/Ă/gm;
2425         s/\xe6\x45/Ĕ/gm;
2426         s/\xe6\x65/ĕ/gm;
2427         s/\xe6\x61/ă/gm;
2428         s/\xe8\x45/Ë/gm;
2429         s/\xe8\x49/Ï/gm;
2430         s/\xe8\x65/ë/gm;
2431         s/\xe8\x69/ï/gm;
2432         s/\xe8\x76/ÿ/gm;
2433         s/\xe9\x41/A/gm;
2434         s/\xe9\x4f/O/gm;
2435         s/\xe9\x55/U/gm;
2436         s/\xe9\x61/a/gm;
2437         s/\xe9\x6f/o/gm;
2438         s/\xe9\x75/u/gm;
2439         s/\xea\x41/A/gm;
2440         s/\xea\x61/a/gm;
2441
2442         #Additional Turkish characters
2443         s/\x1b//gm;
2444         s/\x1e//gm;
2445         s/(\xf0)s/\xc5\x9f/gm;
2446         s/(\xf0)S/\xc5\x9e/gm;
2447         s/(\xf0)c/ç/gm;
2448         s/(\xf0)C/Ç/gm;
2449         s/\xe7\x49/\\xc4\xb0/gm;
2450         s/(\xe6)G/\xc4\x9e/gm;
2451         s/(\xe6)g/ğ\xc4\x9f/gm;
2452         s/\xB8/ı/gm;
2453         s/\xB9/£/gm;
2454         s/(\xe8|\xc8)o/ö/gm;
2455         s/(\xe8|\xc8)O/Ö/gm;
2456         s/(\xe8|\xc8)u/ü/gm;
2457         s/(\xe8|\xc8)U/Ü/gm;
2458         s/\xc2\xb8/\xc4\xb1/gm;
2459         s/¸/\xc4\xb1/gm;
2460
2461         # this handles non-sorting blocks (if implementation requires this)
2462         $string = nsb_clean($_);
2463     }
2464     return ($string);
2465 }
2466
2467 =head2 nsb_clean
2468
2469 =over 4
2470
2471 my $string = nsb_clean( $string, $encoding );
2472
2473 =back
2474
2475 =cut
2476
2477 sub nsb_clean {
2478     my $NSB      = '\x88';    # NSB : begin Non Sorting Block
2479     my $NSE      = '\x89';    # NSE : Non Sorting Block end
2480                               # handles non sorting blocks
2481     my ($string) = @_;
2482     $_ = $string;
2483     s/$NSB/(/gm;
2484     s/[ ]{0,1}$NSE/) /gm;
2485     $string = $_;
2486     return ($string);
2487 }
2488
2489 =head2 PrepareItemrecordDisplay
2490
2491 =over 4
2492
2493 PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber);
2494
2495 Returns a hash with all the fields for Display a given item data in a template
2496
2497 =back
2498
2499 =cut
2500
2501 sub PrepareItemrecordDisplay {
2502
2503     my ( $bibnum, $itemnum ) = @_;
2504
2505     my $dbh = C4::Context->dbh;
2506     my $frameworkcode = &GetFrameworkCode( $bibnum );
2507     my ( $itemtagfield, $itemtagsubfield ) =
2508       &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2509     my $tagslib = &GetMarcStructure( 1, $frameworkcode );
2510     my $itemrecord = GetMarcItem( $bibnum, $itemnum) if ($itemnum);
2511     my @loop_data;
2512     my $authorised_values_sth =
2513       $dbh->prepare(
2514 "select authorised_value,lib from authorised_values where category=? order by lib"
2515       );
2516     foreach my $tag ( sort keys %{$tagslib} ) {
2517         my $previous_tag = '';
2518         if ( $tag ne '' ) {
2519             # loop through each subfield
2520             my $cntsubf;
2521             foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
2522                 next if ( subfield_is_koha_internal_p($subfield) );
2523                 next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
2524                 my %subfield_data;
2525                 $subfield_data{tag}           = $tag;
2526                 $subfield_data{subfield}      = $subfield;
2527                 $subfield_data{countsubfield} = $cntsubf++;
2528                 $subfield_data{kohafield}     =
2529                   $tagslib->{$tag}->{$subfield}->{'kohafield'};
2530
2531          #        $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
2532                 $subfield_data{marc_lib} =
2533                     "<span id=\"error\" title=\""
2534                   . $tagslib->{$tag}->{$subfield}->{lib} . "\">"
2535                   . substr( $tagslib->{$tag}->{$subfield}->{lib}, 0, 12 )
2536                   . "</span>";
2537                 $subfield_data{mandatory} =
2538                   $tagslib->{$tag}->{$subfield}->{mandatory};
2539                 $subfield_data{repeatable} =
2540                   $tagslib->{$tag}->{$subfield}->{repeatable};
2541                 $subfield_data{hidden} = "display:none"
2542                   if $tagslib->{$tag}->{$subfield}->{hidden};
2543                 my ( $x, $value );
2544                 ( $x, $value ) = _find_value( $tag, $subfield, $itemrecord )
2545                   if ($itemrecord);
2546                 $value =~ s/"/&quot;/g;
2547
2548                 # search for itemcallnumber if applicable
2549                 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq
2550                     'items.itemcallnumber'
2551                     && C4::Context->preference('itemcallnumber') )
2552                 {
2553                     my $CNtag =
2554                       substr( C4::Context->preference('itemcallnumber'), 0, 3 );
2555                     my $CNsubfield =
2556                       substr( C4::Context->preference('itemcallnumber'), 3, 1 );
2557                     my $temp = $itemrecord->field($CNtag) if ($itemrecord);
2558                     if ($temp) {
2559                         $value = $temp->subfield($CNsubfield);
2560                     }
2561                 }
2562                 if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
2563                     my @authorised_values;
2564                     my %authorised_lib;
2565
2566                     # builds list, depending on authorised value...
2567                     #---- branch
2568                     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq
2569                         "branches" )
2570                     {
2571                         if ( ( C4::Context->preference("IndependantBranches") )
2572                             && ( C4::Context->userenv->{flags} != 1 ) )
2573                         {
2574                             my $sth =
2575                               $dbh->prepare(
2576 "select branchcode,branchname from branches where branchcode = ? order by branchname"
2577                               );
2578                             $sth->execute( C4::Context->userenv->{branch} );
2579                             push @authorised_values, ""
2580                               unless (
2581                                 $tagslib->{$tag}->{$subfield}->{mandatory} );
2582                             while ( my ( $branchcode, $branchname ) =
2583                                 $sth->fetchrow_array )
2584                             {
2585                                 push @authorised_values, $branchcode;
2586                                 $authorised_lib{$branchcode} = $branchname;
2587                             }
2588                         }
2589                         else {
2590                             my $sth =
2591                               $dbh->prepare(
2592 "select branchcode,branchname from branches order by branchname"
2593                               );
2594                             $sth->execute;
2595                             push @authorised_values, ""
2596                               unless (
2597                                 $tagslib->{$tag}->{$subfield}->{mandatory} );
2598                             while ( my ( $branchcode, $branchname ) =
2599                                 $sth->fetchrow_array )
2600                             {
2601                                 push @authorised_values, $branchcode;
2602                                 $authorised_lib{$branchcode} = $branchname;
2603                             }
2604                         }
2605
2606                         #----- itemtypes
2607                     }
2608                     elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq
2609                         "itemtypes" )
2610                     {
2611                         my $sth =
2612                           $dbh->prepare(
2613 "select itemtype,description from itemtypes order by description"
2614                           );
2615                         $sth->execute;
2616                         push @authorised_values, ""
2617                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2618                         while ( my ( $itemtype, $description ) =
2619                             $sth->fetchrow_array )
2620                         {
2621                             push @authorised_values, $itemtype;
2622                             $authorised_lib{$itemtype} = $description;
2623                         }
2624
2625                         #---- "true" authorised value
2626                     }
2627                     else {
2628                         $authorised_values_sth->execute(
2629                             $tagslib->{$tag}->{$subfield}->{authorised_value} );
2630                         push @authorised_values, ""
2631                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2632                         while ( my ( $value, $lib ) =
2633                             $authorised_values_sth->fetchrow_array )
2634                         {
2635                             push @authorised_values, $value;
2636                             $authorised_lib{$value} = $lib;
2637                         }
2638                     }
2639                     $subfield_data{marc_value} = CGI::scrolling_list(
2640                         -name     => 'field_value',
2641                         -values   => \@authorised_values,
2642                         -default  => "$value",
2643                         -labels   => \%authorised_lib,
2644                         -size     => 1,
2645                         -tabindex => '',
2646                         -multiple => 0,
2647                     );
2648                 }
2649                 elsif ( $tagslib->{$tag}->{$subfield}->{thesaurus_category} ) {
2650                     $subfield_data{marc_value} =
2651 "<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>";
2652
2653 #"
2654 # COMMENTED OUT because No $i is provided with this API.
2655 # And thus, no value_builder can be activated.
2656 # BUT could be thought over.
2657 #         } elsif ($tagslib->{$tag}->{$subfield}->{'value_builder'}) {
2658 #             my $plugin="value_builder/".$tagslib->{$tag}->{$subfield}->{'value_builder'};
2659 #             require $plugin;
2660 #             my $extended_param = plugin_parameters($dbh,$itemrecord,$tagslib,$i,0);
2661 #             my ($function_name,$javascript) = plugin_javascript($dbh,$record,$tagslib,$i,0);
2662 #             $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";
2663                 }
2664                 else {
2665                     $subfield_data{marc_value} =
2666 "<input type=\"text\" name=\"field_value\" value=\"$value\" size=50 maxlength=255>";
2667                 }
2668                 push( @loop_data, \%subfield_data );
2669             }
2670         }
2671     }
2672     my $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield )
2673       if ( $itemrecord && $itemrecord->field($itemtagfield) );
2674     return {
2675         'itemtagfield'    => $itemtagfield,
2676         'itemtagsubfield' => $itemtagsubfield,
2677         'itemnumber'      => $itemnumber,
2678         'iteminformation' => \@loop_data
2679     };
2680 }
2681 #"
2682
2683 #
2684 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2685 # at the same time
2686 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2687 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2688 # =head2 ModZebrafiles
2689
2690 # &ModZebrafiles( $dbh, $biblionumber, $record, $folder, $server );
2691
2692 # =cut
2693
2694 # sub ModZebrafiles {
2695
2696 #     my ( $dbh, $biblionumber, $record, $folder, $server ) = @_;
2697
2698 #     my $op;
2699 #     my $zebradir =
2700 #       C4::Context->zebraconfig($server)->{directory} . "/" . $folder . "/";
2701 #     unless ( opendir( DIR, "$zebradir" ) ) {
2702 #         warn "$zebradir not found";
2703 #         return;
2704 #     }
2705 #     closedir DIR;
2706 #     my $filename = $zebradir . $biblionumber;
2707
2708 #     if ($record) {
2709 #         open( OUTPUT, ">", $filename . ".xml" );
2710 #         print OUTPUT $record;
2711 #         close OUTPUT;
2712 #     }
2713 # }
2714
2715 =head2 ModZebra
2716
2717 =over 4
2718
2719 ModZebra( $biblionumber, $op, $server, $newRecord );
2720
2721     $biblionumber is the biblionumber we want to index
2722     $op is specialUpdate or delete, and is used to know what we want to do
2723     $server is the server that we want to update
2724     $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.
2725 =back
2726
2727 =cut
2728
2729 sub ModZebra {
2730 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
2731     my ( $biblionumber, $op, $server, $newRecord ) = @_;
2732 #     warn "ModZebra with : ( $biblionumber, $op, $server, $newRecord )";
2733     my $dbh=C4::Context->dbh;
2734     #warn "SERVER:".$server;
2735 #
2736 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2737 # at the same time
2738 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2739 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2740
2741     if (C4::Context->preference("NoZebra")) {
2742         # lock the nozebra table : we will read index lines, update them in Perl process
2743         # and write everything in 1 transaction.
2744         # lock the table to avoid someone else overwriting what we are doing
2745         $dbh->do('LOCK TABLES nozebra WRITE,biblio WRITE,biblioitems WRITE, systempreferences WRITE, auth_types WRITE, auth_header WRITE');
2746         my %result; # the result hash that will be builded by deletion / add, and written on mySQL at the end, to improve speed
2747         my $record;
2748         if ($server eq 'biblioserver') {
2749             $record= GetMarcBiblio($biblionumber);
2750         } else {
2751             $record= C4::AuthoritiesMarc::GetAuthority($biblionumber);
2752         }
2753         if ($op eq 'specialUpdate') {
2754             # OK, we have to add or update the record
2755             # 1st delete (virtually, in indexes) ...
2756             %result = _DelBiblioNoZebra($biblionumber,$record,$server);
2757             # ... add the record
2758             %result=_AddBiblioNoZebra($biblionumber,$newRecord, $server, %result);
2759         } else {
2760             # it's a deletion, delete the record...
2761 #             warn "DELETE the record $biblionumber on $server".$record->as_formatted;
2762             %result=_DelBiblioNoZebra($biblionumber,$record,$server);
2763         }
2764         # ok, now update the database...
2765         my $sth = $dbh->prepare("UPDATE nozebra SET biblionumbers=? WHERE server=? AND indexname=? AND value=?");
2766         foreach my $key (keys %result) {
2767             foreach my $index (keys %{$result{$key}}) {
2768 #                 warn "UPDATING : $server $key , $index with :".$result{$key}->{$index};
2769                 $sth->execute($result{$key}->{$index}, $server, $key, $index);
2770             }
2771         }
2772     $dbh->do('UNLOCK TABLES');
2773
2774     } else {
2775         #
2776         # we use zebra, just fill zebraqueue table
2777         #
2778         my $sth=$dbh->prepare("insert into zebraqueue  (biblio_auth_number ,server,operation) values(?,?,?)");
2779         $sth->execute($biblionumber,$server,$op);
2780         $sth->finish;
2781     }
2782 }
2783
2784 =head2 GetNoZebraIndexes
2785
2786 =cut
2787
2788 sub GetNoZebraIndexes {
2789     my $index = C4::Context->preference('NoZebraIndexes');
2790     my %indexes;
2791     foreach my $line (split /('|"),/,$index) {
2792         $line =~ /(.*)=>(.*)/;
2793         my $index = substr($1,1); # get the index, don't forget to remove initial ' or "
2794         my $fields = $2;
2795         $index =~ s/'|"| //g;
2796         $fields =~ s/'|"| //g;
2797         $indexes{$index}=$fields;
2798     }
2799     return %indexes;
2800 }
2801
2802 =head1 INTERNAL FUNCTIONS
2803
2804 =head2 _DelBiblioNoZebra($biblionumber,$record,$server);
2805
2806     function to delete a biblio in NoZebra indexes
2807     This function does NOT delete anything in database : it reads all the indexes entries
2808     that have to be deleted & delete them in the hash
2809     The SQL part is done either :
2810     - after the Add if we are modifying a biblio (delete + add again)
2811     - immediatly after this sub if we are doing a true deletion.
2812     $server can be 'biblioserver' or 'authorityserver' : it indexes biblios or authorities (in the same table, $server being part of the table itself
2813
2814 =cut
2815
2816
2817 sub _DelBiblioNoZebra {
2818     my ($biblionumber, $record, $server)=@_;
2819     
2820     # Get the indexes
2821     my $dbh = C4::Context->dbh;
2822     # Get the indexes
2823     my %index;
2824     my $title;
2825     if ($server eq 'biblioserver') {
2826         %index=GetNoZebraIndexes;
2827         # get title of the record (to store the 10 first letters with the index)
2828         my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title');
2829         $title = lc($record->subfield($titletag,$titlesubfield));
2830     } else {
2831         # for authorities, the "title" is the $a mainentry
2832         my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield(152,'b'));
2833         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
2834         $title = $record->subfield($authref->{auth_tag_to_report},'a');
2835         $index{'mainmainentry'}= $authref->{'auth_tag_to_report'}.'a';
2836         $index{'mainentry'}    = $authref->{'auth_tag_to_report'}.'*';
2837         $index{'auth_type'}    = '152b';
2838     }
2839     
2840     my %result;
2841     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2842     $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
2843     # limit to 10 char, should be enough, and limit the DB size
2844     $title = substr($title,0,10);
2845     #parse each field
2846     my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2847     foreach my $field ($record->fields()) {
2848         #parse each subfield
2849         next if $field->tag <10;
2850         foreach my $subfield ($field->subfields()) {
2851             my $tag = $field->tag();
2852             my $subfieldcode = $subfield->[0];
2853             my $indexed=0;
2854             # check each index to see if the subfield is stored somewhere
2855             # otherwise, store it in __RAW__ index
2856             foreach my $key (keys %index) {
2857 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2858                 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
2859                     $indexed=1;
2860                     my $line= lc $subfield->[1];
2861                     # remove meaningless value in the field...
2862                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2863                     # ... and split in words
2864                     foreach (split / /,$line) {
2865                         next unless $_; # skip  empty values (multiple spaces)
2866                         # if the entry is already here, do nothing, the biblionumber has already be removed
2867                         unless ($result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/) {
2868                             # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2869                             $sth2->execute($server,$key,$_);
2870                             my $existing_biblionumbers = $sth2->fetchrow;
2871                             # it exists
2872                             if ($existing_biblionumbers) {
2873 #                                 warn " existing for $key $_: $existing_biblionumbers";
2874                                 $result{$key}->{$_} =$existing_biblionumbers;
2875                                 $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2876                             }
2877                         }
2878                     }
2879                 }
2880             }
2881             # the subfield is not indexed, store it in __RAW__ index anyway
2882             unless ($indexed) {
2883                 my $line= lc $subfield->[1];
2884                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2885                 # ... and split in words
2886                 foreach (split / /,$line) {
2887                     next unless $_; # skip  empty values (multiple spaces)
2888                     # if the entry is already here, do nothing, the biblionumber has already be removed
2889                     unless ($result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/) {
2890                         # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2891                         $sth2->execute($server,'__RAW__',$_);
2892                         my $existing_biblionumbers = $sth2->fetchrow;
2893                         # it exists
2894                         if ($existing_biblionumbers) {
2895                             $result{'__RAW__'}->{$_} =$existing_biblionumbers;
2896                             $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2897                         }
2898                     }
2899                 }
2900             }
2901         }
2902     }
2903     return %result;
2904 }
2905
2906 =head2 _DelBiblioNoZebra($biblionumber,$record);
2907
2908     function to delete a biblio in NoZebra indexes
2909
2910 =cut
2911
2912
2913 sub _AddBiblioNoZebra {
2914     my ($biblionumber, $record, $server, %result)=@_;
2915     my $dbh = C4::Context->dbh;
2916     # Get the indexes
2917     my %index;
2918     my $title;
2919     if ($server eq 'biblioserver') {
2920         %index=GetNoZebraIndexes;
2921         # get title of the record (to store the 10 first letters with the index)
2922         my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title');
2923         $title = lc($record->subfield($titletag,$titlesubfield));
2924     } else {
2925 #     warn "server : $server";
2926         # for authorities, the "title" is the $a mainentry
2927         my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield(152,'b'));
2928         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
2929         $title = $record->subfield($authref->{auth_tag_to_report},'a');
2930         $index{'mainmainentry'}=$authref->{auth_tag_to_report}.'a';
2931         $index{'mainentry'}    = $authref->{auth_tag_to_report}.'*';
2932         $index{'auth_type'}    = '152b';
2933     }
2934
2935     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2936     $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
2937     # limit to 10 char, should be enough, and limit the DB size
2938     $title = substr($title,0,10);
2939     #parse each field
2940     my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2941     foreach my $field ($record->fields()) {
2942         #parse each subfield
2943         next if $field->tag <10;
2944         foreach my $subfield ($field->subfields()) {
2945             my $tag = $field->tag();
2946             my $subfieldcode = $subfield->[0];
2947             my $indexed=0;
2948             # check each index to see if the subfield is stored somewhere
2949             # otherwise, store it in __RAW__ index
2950             foreach my $key (keys %index) {
2951 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2952                 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
2953                     $indexed=1;
2954                     my $line= lc $subfield->[1];
2955                     # remove meaningless value in the field...
2956                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2957                     # ... and split in words
2958                     foreach (split / /,$line) {
2959                         next unless $_; # skip  empty values (multiple spaces)
2960                         # if the entry is already here, improve weight
2961 #                         warn "managing $_";
2962                         if ($result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/) {
2963                             my $weight=$1+1;
2964                             $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2965                             $result{$key}->{$_} .= "$biblionumber,$title-$weight;";
2966                         } else {
2967                             # get the value if it exist in the nozebra table, otherwise, create it
2968                             $sth2->execute($server,$key,$_);
2969                             my $existing_biblionumbers = $sth2->fetchrow;
2970                             # it exists
2971                             if ($existing_biblionumbers) {
2972                                 $result{$key}->{$_} =$existing_biblionumbers;
2973                                 my $weight=$1+1;
2974                                 $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2975                                 $result{$key}->{$_} .= "$biblionumber,$title-$weight;";
2976                             # create a new ligne for this entry
2977                             } else {
2978 #                             warn "INSERT : $server / $key / $_";
2979                                 $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).', indexname='.$dbh->quote($key).',value='.$dbh->quote($_));
2980                                 $result{$key}->{$_}.="$biblionumber,$title-1;";
2981                             }
2982                         }
2983                     }
2984                 }
2985             }
2986             # the subfield is not indexed, store it in __RAW__ index anyway
2987             unless ($indexed) {
2988                 my $line= lc $subfield->[1];
2989                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2990                 # ... and split in words
2991                 foreach (split / /,$line) {
2992                     next unless $_; # skip  empty values (multiple spaces)
2993                     # if the entry is already here, improve weight
2994                     if ($result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/) {
2995                         my $weight=$1+1;
2996                         $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2997                         $result{'__RAW__'}->{$_} .= "$biblionumber,$title-$weight;";
2998                     } else {
2999                         # get the value if it exist in the nozebra table, otherwise, create it
3000                         $sth2->execute($server,'__RAW__',$_);
3001                         my $existing_biblionumbers = $sth2->fetchrow;
3002                         # it exists
3003                         if ($existing_biblionumbers) {
3004                             $result{'__RAW__'}->{$_} =$existing_biblionumbers;
3005                             my $weight=$1+1;
3006                             $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
3007                             $result{'__RAW__'}->{$_} .= "$biblionumber,$title-$weight;";
3008                         # create a new ligne for this entry
3009                         } else {
3010                             $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).',  indexname="__RAW__",value='.$dbh->quote($_));
3011                             $result{'__RAW__'}->{$_}.="$biblionumber,$title-1;";
3012                         }
3013                     }
3014                 }
3015             }
3016         }
3017     }
3018     return %result;
3019 }
3020
3021
3022 =head2 MARCitemchange
3023
3024 =over 4
3025
3026 &MARCitemchange( $record, $itemfield, $newvalue )
3027
3028 Function to update a single value in an item field.
3029 Used twice, could probably be replaced by something else, but works well...
3030
3031 =back
3032
3033 =back
3034
3035 =cut
3036
3037 sub MARCitemchange {
3038     my ( $record, $itemfield, $newvalue ) = @_;
3039     my $dbh = C4::Context->dbh;
3040     
3041     my ( $tagfield, $tagsubfield ) =
3042       GetMarcFromKohaField( $itemfield, "" );
3043     if ( ($tagfield) && ($tagsubfield) ) {
3044         my $tag = $record->field($tagfield);
3045         if ($tag) {
3046             $tag->update( $tagsubfield => $newvalue );
3047             $record->delete_field($tag);
3048             $record->insert_fields_ordered($tag);
3049         }
3050     }
3051 }
3052
3053 =head2 _koha_add_biblio
3054
3055 =over 4
3056
3057 _koha_add_biblio($dbh,$biblioitem);
3058
3059 Internal function to add a biblio ($biblio is a hash with the values)
3060
3061 =back
3062
3063 =cut
3064
3065 sub _koha_add_biblio {
3066     my ( $dbh, $biblio, $frameworkcode ) = @_;
3067     my $sth = $dbh->prepare("Select max(biblionumber) from biblio");
3068     $sth->execute;
3069     my $data         = $sth->fetchrow_arrayref;
3070     my $biblionumber = $$data[0] + 1;
3071     my $series       = 0;
3072
3073     if ( $biblio->{'seriestitle'} ) { $series = 1 }
3074     $sth->finish;
3075     $sth = $dbh->prepare(
3076         "INSERT INTO biblio
3077     SET biblionumber  = ?, title = ?, author = ?, copyrightdate = ?, serial = ?, seriestitle = ?, notes = ?, abstract = ?, unititle = ?, frameworkcode = ? "
3078     );
3079     $sth->execute(
3080         $biblionumber,         $biblio->{'title'},
3081         $biblio->{'author'},   $biblio->{'copyrightdate'},
3082         $biblio->{'serial'},   $biblio->{'seriestitle'},
3083         $biblio->{'notes'},    $biblio->{'abstract'},
3084         $biblio->{'unititle'}, $frameworkcode
3085     );
3086
3087     $sth->finish;
3088     return ($biblionumber);
3089 }
3090
3091 =head2 _find_value
3092
3093 =over 4
3094
3095 ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
3096
3097 Find the given $subfield in the given $tag in the given
3098 MARC::Record $record.  If the subfield is found, returns
3099 the (indicators, value) pair; otherwise, (undef, undef) is
3100 returned.
3101
3102 PROPOSITION :
3103 Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
3104 I suggest we export it from this module.
3105
3106 =back
3107
3108 =cut
3109
3110 sub _find_value {
3111     my ( $tagfield, $insubfield, $record, $encoding ) = @_;
3112     my @result;
3113     my $indicator;
3114     if ( $tagfield < 10 ) {
3115         if ( $record->field($tagfield) ) {
3116             push @result, $record->field($tagfield)->data();
3117         }
3118         else {
3119             push @result, "";
3120         }
3121     }
3122     else {
3123         foreach my $field ( $record->field($tagfield) ) {
3124             my @subfields = $field->subfields();
3125             foreach my $subfield (@subfields) {
3126                 if ( @$subfield[0] eq $insubfield ) {
3127                     push @result, @$subfield[1];
3128                     $indicator = $field->indicator(1) . $field->indicator(2);
3129                 }
3130             }
3131         }
3132     }
3133     return ( $indicator, @result );
3134 }
3135
3136 =head2 _koha_modify_biblio
3137
3138 =over 4
3139
3140 Internal function for updating the biblio table
3141
3142 =back
3143
3144 =cut
3145
3146 sub _koha_modify_biblio {
3147     my ( $dbh, $biblio ) = @_;
3148
3149 # FIXME: this code could be made more portable by not hard-coding the values that are supposed to be in biblio table
3150     my $sth =
3151       $dbh->prepare(
3152 "Update biblio set title = ?, author = ?, abstract = ?, copyrightdate = ?, seriestitle = ?, serial = ?, unititle = ?, notes = ? where biblionumber = ?"
3153       );
3154     $sth->execute(
3155         $biblio->{'title'},       $biblio->{'author'},
3156         $biblio->{'abstract'},    $biblio->{'copyrightdate'},
3157         $biblio->{'seriestitle'}, $biblio->{'serial'},
3158         $biblio->{'unititle'},    $biblio->{'notes'},
3159         $biblio->{'biblionumber'}
3160     );
3161     $sth->finish;
3162     return ( $biblio->{'biblionumber'} );
3163 }
3164
3165 =head2 _koha_modify_biblioitem
3166
3167 =over 4
3168
3169 _koha_modify_biblioitem( $dbh, $biblioitem );
3170
3171 =back
3172
3173 =cut
3174
3175 sub _koha_modify_biblioitem {
3176     my ( $dbh, $biblioitem ) = @_;
3177     my $query;
3178 ##Recalculate LC in case it changed --TG
3179
3180     $biblioitem->{'itemtype'}      = $dbh->quote( $biblioitem->{'itemtype'} );
3181     $biblioitem->{'url'}           = $dbh->quote( $biblioitem->{'url'} );
3182     $biblioitem->{'isbn'}          = $dbh->quote( $biblioitem->{'isbn'} );
3183     $biblioitem->{'issn'}          = $dbh->quote( $biblioitem->{'issn'} );
3184     $biblioitem->{'publishercode'} =
3185       $dbh->quote( $biblioitem->{'publishercode'} );
3186     $biblioitem->{'publicationyear'} =
3187       $dbh->quote( $biblioitem->{'publicationyear'} );
3188     $biblioitem->{'classification'} =
3189       $dbh->quote( $biblioitem->{'classification'} );
3190     $biblioitem->{'dewey'}        = $dbh->quote( $biblioitem->{'dewey'} );
3191     $biblioitem->{'subclass'}     = $dbh->quote( $biblioitem->{'subclass'} );
3192     $biblioitem->{'illus'}        = $dbh->quote( $biblioitem->{'illus'} );
3193     $biblioitem->{'pages'}        = $dbh->quote( $biblioitem->{'pages'} );
3194     $biblioitem->{'volumeddesc'}  = $dbh->quote( $biblioitem->{'volumeddesc'} );
3195     $biblioitem->{'bnotes'}       = $dbh->quote( $biblioitem->{'bnotes'} );
3196     $biblioitem->{'size'}         = $dbh->quote( $biblioitem->{'size'} );
3197     $biblioitem->{'place'}        = $dbh->quote( $biblioitem->{'place'} );
3198     $biblioitem->{'ccode'}        = $dbh->quote( $biblioitem->{'ccode'} );
3199     $biblioitem->{'biblionumber'} =
3200       $dbh->quote( $biblioitem->{'biblionumber'} );
3201
3202     $query = "Update biblioitems set
3203         itemtype        = $biblioitem->{'itemtype'},
3204         url             = $biblioitem->{'url'},
3205         isbn            = $biblioitem->{'isbn'},
3206         issn            = $biblioitem->{'issn'},
3207         publishercode   = $biblioitem->{'publishercode'},
3208         publicationyear = $biblioitem->{'publicationyear'},
3209         classification  = $biblioitem->{'classification'},
3210         dewey           = $biblioitem->{'dewey'},
3211         subclass        = $biblioitem->{'subclass'},
3212         illus           = $biblioitem->{'illus'},
3213         pages           = $biblioitem->{'pages'},
3214         volumeddesc     = $biblioitem->{'volumeddesc'},
3215         notes           = $biblioitem->{'bnotes'},
3216         size            = $biblioitem->{'size'},
3217         place           = $biblioitem->{'place'},
3218         ccode           = $biblioitem->{'ccode'}
3219         where biblionumber = $biblioitem->{'biblionumber'}";
3220
3221     $dbh->do($query);
3222     if ( $dbh->errstr ) {
3223         warn "ERROR in _koha_modify_biblioitem $query";
3224     }
3225 }
3226
3227 =head2 _koha_add_biblioitem
3228
3229 =over 4
3230
3231 _koha_add_biblioitem( $dbh, $biblioitem );
3232
3233 Internal function to add a biblioitem
3234
3235 =back
3236
3237 =cut
3238
3239 sub _koha_add_biblioitem {
3240     my ( $dbh, $biblioitem ) = @_;
3241
3242     #  my $dbh   = C4Connect;
3243     my $sth = $dbh->prepare("SELECT max(biblioitemnumber) FROM biblioitems");
3244     my $data;
3245     my $bibitemnum;
3246
3247     $sth->execute;
3248     $data       = $sth->fetchrow_arrayref;
3249     $bibitemnum = $$data[0] + 1;
3250
3251     $sth->finish;
3252
3253     $sth = $dbh->prepare(
3254         "INSERT INTO biblioitems SET
3255             biblioitemnumber = ?, biblionumber    = ?,
3256             volume           = ?, number          = ?,
3257             classification   = ?, itemtype        = ?,
3258             url              = ?, isbn            = ?,
3259             issn             = ?, dewey           = ?,
3260             subclass         = ?, publicationyear = ?,
3261             publishercode    = ?, volumedate      = ?,
3262             volumeddesc      = ?, illus           = ?,
3263             pages            = ?, notes           = ?,
3264             size             = ?, lccn            = ?,
3265             marc             = ?, lcsort          =?,
3266             place            = ?, ccode           = ?
3267           "
3268     );
3269     my ($lcsort) =
3270       calculatelc( $biblioitem->{'classification'} )
3271       . $biblioitem->{'subclass'};
3272     $sth->execute(
3273         $bibitemnum,                     $biblioitem->{'biblionumber'},
3274         $biblioitem->{'volume'},         $biblioitem->{'number'},
3275         $biblioitem->{'classification'}, $biblioitem->{'itemtype'},
3276         $biblioitem->{'url'},            $biblioitem->{'isbn'},
3277         $biblioitem->{'issn'},           $biblioitem->{'dewey'},
3278         $biblioitem->{'subclass'},       $biblioitem->{'publicationyear'},
3279         $biblioitem->{'publishercode'},  $biblioitem->{'volumedate'},
3280         $biblioitem->{'volumeddesc'},    $biblioitem->{'illus'},
3281         $biblioitem->{'pages'},          $biblioitem->{'bnotes'},
3282         $biblioitem->{'size'},           $biblioitem->{'lccn'},
3283         $biblioitem->{'marc'},           $biblioitem->{'place'},
3284         $lcsort,                         $biblioitem->{'ccode'}
3285     );
3286     $sth->finish;
3287     return ($bibitemnum);
3288 }
3289
3290 =head2 _koha_new_items
3291
3292 =over 4
3293
3294 _koha_new_items( $dbh, $item, $barcode );
3295
3296 =back
3297
3298 =cut
3299
3300 sub _koha_new_items {
3301     my ( $dbh, $item, $barcode ) = @_;
3302
3303     #  my $dbh   = C4Connect;
3304     my $sth = $dbh->prepare("Select max(itemnumber) from items");
3305     my $data;
3306     my $itemnumber;
3307     my $error = "";
3308
3309     $sth->execute;
3310     $data       = $sth->fetchrow_hashref;
3311     $itemnumber = $data->{'max(itemnumber)'} + 1;
3312     $sth->finish;
3313 ## Now calculate lccalnumber
3314     my ($cutterextra) = itemcalculator(
3315         $dbh,
3316         $item->{'biblioitemnumber'},
3317         $item->{'itemcallnumber'}
3318     );
3319
3320 # FIXME the "notforloan" field seems to be named "loan" in some places. workaround bugfix.
3321     if ( $item->{'loan'} ) {
3322         $item->{'notforloan'} = $item->{'loan'};
3323     }
3324
3325     # if dateaccessioned is provided, use it. Otherwise, set to NOW()
3326     if ( $item->{'dateaccessioned'} eq '' || !$item->{'dateaccessioned'} ) {
3327
3328         $sth = $dbh->prepare(
3329             "Insert into items set
3330             itemnumber           = ?,     biblionumber     = ?,
3331             multivolumepart      = ?,
3332             biblioitemnumber     = ?,     barcode          = ?,
3333             booksellerid         = ?,     dateaccessioned  = NOW(),
3334             homebranch           = ?,     holdingbranch    = ?,
3335             price                = ?,     replacementprice = ?,
3336             replacementpricedate = NOW(), datelastseen     = NOW(),
3337             multivolume          = ?,     stack            = ?,
3338             itemlost             = ?,     wthdrawn         = ?,
3339             paidfor              = ?,     itemnotes        = ?,
3340             itemcallnumber       =?,      notforloan       = ?,
3341             location             = ?,     Cutterextra      = ?
3342           "
3343         );
3344         $sth->execute(
3345             $itemnumber,                $item->{'biblionumber'},
3346             $item->{'multivolumepart'}, $item->{'biblioitemnumber'},
3347             $barcode,                   $item->{'booksellerid'},
3348             $item->{'homebranch'},      $item->{'holdingbranch'},
3349             $item->{'price'},           $item->{'replacementprice'},
3350             $item->{multivolume},       $item->{stack},
3351             $item->{itemlost},          $item->{wthdrawn},
3352             $item->{paidfor},           $item->{'itemnotes'},
3353             $item->{'itemcallnumber'},  $item->{'notforloan'},
3354             $item->{'location'},        $cutterextra
3355         );
3356     }
3357     else {
3358         $sth = $dbh->prepare(
3359             "INSERT INTO items SET
3360             itemnumber           = ?,     biblionumber     = ?,
3361             multivolumepart      = ?,
3362             biblioitemnumber     = ?,     barcode          = ?,
3363             booksellerid         = ?,     dateaccessioned  = ?,
3364             homebranch           = ?,     holdingbranch    = ?,
3365             price                = ?,     replacementprice = ?,
3366             replacementpricedate = NOW(), datelastseen     = NOW(),
3367             multivolume          = ?,     stack            = ?,
3368             itemlost             = ?,     wthdrawn         = ?,
3369             paidfor              = ?,     itemnotes        = ?,
3370             itemcallnumber       = ?,     notforloan       = ?,
3371             location             = ?,
3372             Cutterextra          = ?
3373                             "
3374         );
3375         $sth->execute(
3376             $itemnumber,                 $item->{'biblionumber'},
3377             $item->{'multivolumepart'},  $item->{'biblioitemnumber'},
3378             $barcode,                    $item->{'booksellerid'},
3379             $item->{'dateaccessioned'},  $item->{'homebranch'},
3380             $item->{'holdingbranch'},    $item->{'price'},
3381             $item->{'replacementprice'}, $item->{multivolume},
3382             $item->{stack},              $item->{itemlost},
3383             $item->{wthdrawn},           $item->{paidfor},
3384             $item->{'itemnotes'},        $item->{'itemcallnumber'},
3385             $item->{'notforloan'},       $item->{'location'},
3386             $cutterextra
3387         );
3388     }
3389     if ( defined $sth->errstr ) {
3390         $error .= $sth->errstr;
3391     }
3392     return ( $itemnumber, $error );
3393 }
3394
3395 =head2 _koha_modify_item
3396
3397 =over 4
3398
3399 _koha_modify_item( $dbh, $item, $op );
3400
3401 =back
3402
3403 =cut
3404
3405 sub _koha_modify_item {
3406     my ( $dbh, $item, $op ) = @_;
3407     $item->{'itemnum'} = $item->{'itemnumber'} unless $item->{'itemnum'};
3408
3409     # if all we're doing is setting statuses, just update those and get out
3410     if ( $op eq "setstatus" ) {
3411         my $query =
3412           "UPDATE items SET itemlost=?,wthdrawn=?,binding=? WHERE itemnumber=?";
3413         my @bind = (
3414             $item->{'itemlost'}, $item->{'wthdrawn'},
3415             $item->{'binding'},  $item->{'itemnumber'}
3416         );
3417         my $sth = $dbh->prepare($query);
3418         $sth->execute(@bind);
3419         $sth->finish;
3420         return undef;
3421     }
3422 ## Now calculate lccalnumber
3423     my ($cutterextra) =
3424       itemcalculator( $dbh, $item->{'bibitemnum'}, $item->{'itemcallnumber'} );
3425
3426     my $query = "UPDATE items SET
3427 barcode=?,itemnotes=?,itemcallnumber=?,notforloan=?,location=?,multivolumepart=?,multivolume=?,stack=?,wthdrawn=?,holdingbranch=?,homebranch=?,cutterextra=?, onloan=?, binding=?";
3428
3429     my @bind = (
3430         $item->{'barcode'},        $item->{'notes'},
3431         $item->{'itemcallnumber'}, $item->{'notforloan'},
3432         $item->{'location'},       $item->{multivolumepart},
3433         $item->{multivolume},      $item->{stack},
3434         $item->{wthdrawn},         $item->{holdingbranch},
3435         $item->{homebranch},       $cutterextra,
3436         $item->{onloan},           $item->{binding}
3437     );
3438     if ( $item->{'lost'} ne '' ) {
3439         $query =
3440 "update items set biblioitemnumber=?,barcode=?,itemnotes=?,homebranch=?,
3441                             itemlost=?,wthdrawn=?,itemcallnumber=?,notforloan=?,
3442                              location=?,multivolumepart=?,multivolume=?,stack=?,wthdrawn=?,holdingbranch=?,cutterextra=?,onloan=?, binding=?";
3443         @bind = (
3444             $item->{'bibitemnum'},     $item->{'barcode'},
3445             $item->{'notes'},          $item->{'homebranch'},
3446             $item->{'lost'},           $item->{'wthdrawn'},
3447             $item->{'itemcallnumber'}, $item->{'notforloan'},
3448             $item->{'location'},       $item->{multivolumepart},
3449             $item->{multivolume},      $item->{stack},
3450             $item->{wthdrawn},         $item->{holdingbranch},
3451             $cutterextra,              $item->{onloan},
3452             $item->{binding}
3453         );
3454         if ( $item->{homebranch} ) {
3455             $query .= ",homebranch=?";
3456             push @bind, $item->{homebranch};
3457         }
3458         if ( $item->{holdingbranch} ) {
3459             $query .= ",holdingbranch=?";
3460             push @bind, $item->{holdingbranch};
3461         }
3462     }
3463     $query .= " where itemnumber=?";
3464     push @bind, $item->{'itemnum'};
3465     if ( $item->{'replacement'} ne '' ) {
3466         $query =~ s/ where/,replacementprice='$item->{'replacement'}' where/;
3467     }
3468     my $sth = $dbh->prepare($query);
3469     $sth->execute(@bind);
3470     $sth->finish;
3471 }
3472
3473 =head2 _koha_delete_biblio
3474
3475 =over 4
3476
3477 $error = _koha_delete_biblio($dbh,$biblionumber);
3478
3479 Internal sub for deleting from biblio table -- also saves to deletedbiblio
3480
3481 C<$dbh> - the database handle
3482 C<$biblionumber> - the biblionumber of the biblio to be deleted
3483
3484 =back
3485
3486 =cut
3487
3488 # FIXME: add error handling
3489
3490 sub _koha_delete_biblio {
3491     my ( $dbh, $biblionumber ) = @_;
3492
3493     # get all the data for this biblio
3494     my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
3495     $sth->execute($biblionumber);
3496
3497     if ( my $data = $sth->fetchrow_hashref ) {
3498
3499         # save the record in deletedbiblio
3500         # find the fields to save
3501         my $query = "INSERT INTO deletedbiblio SET ";
3502         my @bind  = ();
3503         foreach my $temp ( keys %$data ) {
3504             $query .= "$temp = ?,";
3505             push( @bind, $data->{$temp} );
3506         }
3507
3508         # replace the last , by ",?)"
3509         $query =~ s/\,$//;
3510         my $bkup_sth = $dbh->prepare($query);
3511         $bkup_sth->execute(@bind);
3512         $bkup_sth->finish;
3513
3514         # delete the biblio
3515         my $del_sth = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
3516         $del_sth->execute($biblionumber);
3517         $del_sth->finish;
3518     }
3519     $sth->finish;
3520     return undef;
3521 }
3522
3523 =head2 _koha_delete_biblioitems
3524
3525 =over 4
3526
3527 $error = _koha_delete_biblioitems($dbh,$biblioitemnumber);
3528
3529 Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems
3530
3531 C<$dbh> - the database handle
3532 C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted
3533
3534 =back
3535
3536 =cut
3537
3538 # FIXME: add error handling
3539
3540 sub _koha_delete_biblioitems {
3541     my ( $dbh, $biblioitemnumber ) = @_;
3542
3543     # get all the data for this biblioitem
3544     my $sth =
3545       $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
3546     $sth->execute($biblioitemnumber);
3547
3548     if ( my $data = $sth->fetchrow_hashref ) {
3549
3550         # save the record in deletedbiblioitems
3551         # find the fields to save
3552         my $query = "INSERT INTO deletedbiblioitems SET ";
3553         my @bind  = ();
3554         foreach my $temp ( keys %$data ) {
3555             $query .= "$temp = ?,";
3556             push( @bind, $data->{$temp} );
3557         }
3558
3559         # replace the last , by ",?)"
3560         $query =~ s/\,$//;
3561         my $bkup_sth = $dbh->prepare($query);
3562         $bkup_sth->execute(@bind);
3563         $bkup_sth->finish;
3564
3565         # delete the biblioitem
3566         my $del_sth =
3567           $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
3568         $del_sth->execute($biblioitemnumber);
3569         $del_sth->finish;
3570     }
3571     $sth->finish;
3572     return undef;
3573 }
3574
3575 =head2 _koha_delete_item
3576
3577 =over 4
3578
3579 _koha_delete_item( $dbh, $itemnum );
3580
3581 Internal function to delete an item record from the koha tables
3582
3583 =back
3584
3585 =cut
3586
3587 sub _koha_delete_item {
3588     my ( $dbh, $itemnum ) = @_;
3589
3590     my $sth = $dbh->prepare("select * from items where itemnumber=?");
3591     $sth->execute($itemnum);
3592     my $data = $sth->fetchrow_hashref;
3593     $sth->finish;
3594     my $query = "Insert into deleteditems set ";
3595     my @bind  = ();
3596     foreach my $temp ( keys %$data ) {
3597         $query .= "$temp = ?,";
3598         push( @bind, $data->{$temp} );
3599     }
3600     $query =~ s/\,$//;
3601
3602     #  print $query;
3603     $sth = $dbh->prepare($query);
3604     $sth->execute(@bind);
3605     $sth->finish;
3606     $sth = $dbh->prepare("Delete from items where itemnumber=?");
3607     $sth->execute($itemnum);
3608     $sth->finish;
3609 }
3610
3611 =head1 UNEXPORTED FUNCTIONS
3612
3613 =over 4
3614
3615 =head2 calculatelc
3616
3617 $lc = calculatelc($classification);
3618
3619 =back
3620
3621 =cut
3622
3623 sub calculatelc {
3624     my ($classification) = @_;
3625     $classification =~ s/^\s+|\s+$//g;
3626     my $i = 0;
3627     my $lc2;
3628     my $lc1;
3629
3630     for ( $i = 0 ; $i < length($classification) ; $i++ ) {
3631         my $c = ( substr( $classification, $i, 1 ) );
3632         if ( $c ge '0' && $c le '9' ) {
3633
3634             $lc2 = substr( $classification, $i );
3635             last;
3636         }
3637         else {
3638             $lc1 .= substr( $classification, $i, 1 );
3639
3640         }
3641     }    #while
3642
3643     my $other = length($lc1);
3644     if ( !$lc1 ) {
3645         $other = 0;
3646     }
3647
3648     my $extras;
3649     if ( $other < 4 ) {
3650         for ( 1 .. ( 4 - $other ) ) {
3651             $extras .= "0";
3652         }
3653     }
3654     $lc1 .= $extras;
3655     $lc2 =~ s/^ //g;
3656
3657     $lc2 =~ s/ //g;
3658     $extras = "";
3659     ##Find the decimal part of $lc2
3660     my $pos = index( $lc2, "." );
3661     if ( $pos < 0 ) { $pos = length($lc2); }
3662     if ( $pos >= 0 && $pos < 5 ) {
3663         ##Pad lc2 with zeros to create a 5digit decimal needed in marc record to sort as numeric
3664
3665         for ( 1 .. ( 5 - $pos ) ) {
3666             $extras .= "0";
3667         }
3668     }
3669     $lc2 = $extras . $lc2;
3670     return ( $lc1 . $lc2 );
3671 }
3672
3673 =head2 itemcalculator
3674
3675 =over 4
3676
3677 $cutterextra = itemcalculator( $dbh, $biblioitem, $callnumber );
3678
3679 =back
3680
3681 =cut
3682
3683 sub itemcalculator {
3684     my ( $dbh, $biblioitem, $callnumber ) = @_;
3685     my $sth =
3686       $dbh->prepare(
3687 "select classification, subclass from biblioitems where biblioitemnumber=?"
3688       );
3689
3690     $sth->execute($biblioitem);
3691     my ( $classification, $subclass ) = $sth->fetchrow;
3692     my $all         = $classification . " " . $subclass;
3693     my $total       = length($all);
3694     my $cutterextra = substr( $callnumber, $total - 1 );
3695
3696     return $cutterextra;
3697 }
3698
3699 =head2 ModBiblioMarc
3700
3701 =over 4
3702
3703 &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
3704
3705 Add MARC data for a biblio to koha 
3706
3707 Function exported, but should NOT be used, unless you really know what you're doing
3708
3709 =back
3710
3711 =cut
3712
3713 sub ModBiblioMarc {
3714
3715 # pass the MARC::Record to this function, and it will create the records in the marc field
3716     my ( $record, $biblionumber, $frameworkcode ) = @_;
3717     my $dbh = C4::Context->dbh;
3718     my @fields = $record->fields();
3719     if ( !$frameworkcode ) {
3720         $frameworkcode = "";
3721     }
3722     my $sth =
3723       $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
3724     $sth->execute( $frameworkcode, $biblionumber );
3725     $sth->finish;
3726     my $encoding = C4::Context->preference("marcflavour");
3727
3728 # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
3729     if ( $encoding eq "UNIMARC" ) {
3730         my $string;
3731         if ( length($record->subfield( 100, "a" )) == 35 ) {
3732             $string = $record->subfield( 100, "a" );
3733             my $f100 = $record->field(100);
3734             $record->delete_field($f100);
3735         }
3736         else {
3737             $string = POSIX::strftime( "%Y%m%d", localtime );
3738             $string =~ s/\-//g;
3739             $string = sprintf( "%-*s", 35, $string );
3740         }
3741         substr( $string, 22, 6, "frey50" );
3742         unless ( $record->subfield( 100, "a" ) ) {
3743             $record->insert_grouped_field(
3744                 MARC::Field->new( 100, "", "", "a" => $string ) );
3745         }
3746     }
3747 #     warn "biblionumber : ".$biblionumber;
3748     ModZebra($biblionumber,"specialUpdate","biblioserver",$record);
3749     $sth =
3750       $dbh->prepare(
3751         "update biblioitems set marc=?,marcxml=?  where biblionumber=?");
3752     $sth->execute( $record->as_usmarc(), $record->as_xml_record(),
3753         $biblionumber );
3754 #     warn $record->as_xml_record();
3755     $sth->finish;
3756     return $biblionumber;
3757 }
3758
3759 =head2 AddItemInMarc
3760
3761 =over 4
3762
3763 $newbiblionumber = AddItemInMarc( $record, $biblionumber, $frameworkcode );
3764
3765 Add an item in a MARC record and save the MARC record
3766
3767 Function exported, but should NOT be used, unless you really know what you're doing
3768
3769 =back
3770
3771 =cut
3772
3773 sub AddItemInMarc {
3774
3775 # pass the MARC::Record to this function, and it will create the records in the marc tables
3776     my ( $record, $biblionumber, $frameworkcode ) = @_;
3777     my $newrec = &GetMarcBiblio($biblionumber);
3778
3779     # create it
3780     my @fields = $record->fields();
3781     foreach my $field (@fields) {
3782         $newrec->append_fields($field);
3783     }
3784
3785     # FIXME: should we be making sure the biblionumbers are the same?
3786     my $newbiblionumber =
3787       &ModBiblioMarc( $newrec, $biblionumber, $frameworkcode );
3788     return $newbiblionumber;
3789 }
3790
3791 =head2 z3950_extended_services
3792
3793 z3950_extended_services($serviceType,$serviceOptions,$record);
3794
3795     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.
3796
3797 C<$serviceType> one of: itemorder,create,drop,commit,update,xmlupdate
3798
3799 C<$serviceOptions> a has of key/value pairs. For instance, if service_type is 'update', $service_options should contain:
3800
3801     action => update action, one of specialUpdate, recordInsert, recordReplace, recordDelete, elementUpdate.
3802
3803 and maybe
3804
3805     recordidOpaque => Opaque Record ID (user supplied) or recordidNumber => Record ID number (system number).
3806     syntax => the record syntax (transfer syntax)
3807     databaseName = Database from connection object
3808
3809     To set serviceOptions, call set_service_options($serviceType)
3810
3811 C<$record> the record, if one is needed for the service type
3812
3813     A record should be in XML. You can convert it to XML from MARC by running it through marc2xml().
3814
3815 =cut
3816
3817 sub z3950_extended_services {
3818     my ( $server, $serviceType, $action, $serviceOptions ) = @_;
3819
3820     # get our connection object
3821     my $Zconn = C4::Context->Zconn( $server, 0, 1 );
3822
3823     # create a new package object
3824     my $Zpackage = $Zconn->package();
3825
3826     # set our options
3827     $Zpackage->option( action => $action );
3828
3829     if ( $serviceOptions->{'databaseName'} ) {
3830         $Zpackage->option( databaseName => $serviceOptions->{'databaseName'} );
3831     }
3832     if ( $serviceOptions->{'recordIdNumber'} ) {
3833         $Zpackage->option(
3834             recordIdNumber => $serviceOptions->{'recordIdNumber'} );
3835     }
3836     if ( $serviceOptions->{'recordIdOpaque'} ) {
3837         $Zpackage->option(
3838             recordIdOpaque => $serviceOptions->{'recordIdOpaque'} );
3839     }
3840
3841  # this is an ILL request (Zebra doesn't support it, but Koha could eventually)
3842  #if ($serviceType eq 'itemorder') {
3843  #   $Zpackage->option('contact-name' => $serviceOptions->{'contact-name'});
3844  #   $Zpackage->option('contact-phone' => $serviceOptions->{'contact-phone'});
3845  #   $Zpackage->option('contact-email' => $serviceOptions->{'contact-email'});
3846  #   $Zpackage->option('itemorder-item' => $serviceOptions->{'itemorder-item'});
3847  #}
3848
3849     if ( $serviceOptions->{record} ) {
3850         $Zpackage->option( record => $serviceOptions->{record} );
3851
3852         # can be xml or marc
3853         if ( $serviceOptions->{'syntax'} ) {
3854             $Zpackage->option( syntax => $serviceOptions->{'syntax'} );
3855         }
3856     }
3857
3858     # send the request, handle any exception encountered
3859     eval { $Zpackage->send($serviceType) };
3860     if ( $@ && $@->isa("ZOOM::Exception") ) {
3861         return "error:  " . $@->code() . " " . $@->message() . "\n";
3862     }
3863
3864     # free up package resources
3865     $Zpackage->destroy();
3866 }
3867
3868 =head2 set_service_options
3869
3870 my $serviceOptions = set_service_options($serviceType);
3871
3872 C<$serviceType> itemorder,create,drop,commit,update,xmlupdate
3873
3874 Currently, we only support 'create', 'commit', and 'update'. 'drop' support will be added as soon as Zebra supports it.
3875
3876 =cut
3877
3878 sub set_service_options {
3879     my ($serviceType) = @_;
3880     my $serviceOptions;
3881
3882 # FIXME: This needs to be an OID ... if we ever need 'syntax' this sub will need to change
3883 #   $serviceOptions->{ 'syntax' } = ''; #zebra doesn't support syntaxes other than xml
3884
3885     if ( $serviceType eq 'commit' ) {
3886
3887         # nothing to do
3888     }
3889     if ( $serviceType eq 'create' ) {
3890
3891         # nothing to do
3892     }
3893     if ( $serviceType eq 'drop' ) {
3894         die "ERROR: 'drop' not currently supported (by Zebra)";
3895     }
3896     return $serviceOptions;
3897 }
3898
3899 =head2 GetItemsCount
3900
3901 $count = &GetItemsCount( $biblionumber);
3902 this function return count of item with $biblionumber
3903 =cut
3904
3905 sub GetItemsCount {
3906     my ( $biblionumber ) = @_;
3907     my $dbh = C4::Context->dbh;
3908     my $query = qq|SELECT count(*)
3909                   FROM  items 
3910                   WHERE biblionumber=?|;
3911     my $sth = $dbh->prepare($query);
3912     $sth->execute($biblionumber);
3913     my $count = $sth->fetchrow;  
3914     $sth->finish;
3915     return ($count);
3916 }
3917
3918
3919
3920
3921 END { }    # module clean-up code here (global destructor)
3922
3923 1;
3924
3925 __END__
3926
3927 =head1 AUTHOR
3928
3929 Koha Developement team <info@koha.org>
3930
3931 Paul POULAIN paul.poulain@free.fr
3932
3933 Joshua Ferraro jmf@liblime.com
3934
3935 =cut
3936
3937 # $Id$
3938 # $Log$
3939 # Revision 1.211  2007/06/15 09:40:06  toins
3940 # do not get $3 $4 and $5 on GetMarcSubjects GetMarcAuthors on unimarc flavour.
3941 #
3942 # Revision 1.210  2007/06/13 13:03:34  toins
3943 # removing warn compilation.
3944 #
3945 # Revision 1.209  2007/05/23 16:19:40  tipaul
3946 # various bugfixes (minor) and french translation updated
3947 #
3948 # Revision 1.208  2007/05/22 09:13:54  tipaul
3949 # Bugfixes & improvements (various and minor) :
3950 # - updating templates to have tmpl_process3.pl running without any errors
3951 # - adding a drupal-like css for prog templates (with 3 small images)
3952 # - fixing some bugs in circulation & other scripts
3953 # - updating french translation
3954 # - fixing some typos in templates
3955 #
3956 # Revision 1.207  2007/05/22 08:51:19  hdl
3957 # Changing GetMarcStructure signature.
3958 # Deleting first parameter $dbh
3959 #
3960 # Revision 1.206  2007/05/21 08:44:17  btoumi
3961 # add security when u delete biblio :
3962 # u must delete linked items before delete biblio
3963 #
3964 # Revision 1.205  2007/05/11 16:04:03  btoumi
3965 # bug fix:
3966 # problem in  displayed label link  with subject in detail.tmpl
3967 # ex: label random => rdom
3968 #
3969 # Revision 1.204  2007/05/10 14:45:15  tipaul
3970 # Koha NoZebra :
3971 # - support for authorities
3972 # - some bugfixes in ordering and "CCL" parsing
3973 # - support for authorities <=> biblios walking
3974 #
3975 # Seems I can do what I want now, so I consider its done, except for bugfixes that will be needed i m sure !
3976 #
3977 # Revision 1.203  2007/05/03 15:16:02  tipaul
3978 # BUGFIX for : NoZebra
3979 # - NoZebra features : seems they work fine now (adding, modifying, deleting)
3980 # - Biblio edition major bugfix : before this commit editing a biblio resulted in an item removal in marcxml field
3981 #
3982 # Revision 1.202  2007/05/02 16:44:31  tipaul
3983 # NoZebra SQL index management :
3984 # * adding 3 subs in Biblio.pm
3985 # - GetNoZebraIndexes, that get the index structure in a new systempreference (added with this commit)
3986 # - _DelBiblioNoZebra, that retrieve all index entries for a biblio and remove in a variable the biblio reference
3987 # - _AddBiblioNoZebra, that add index entries for a biblio.
3988 # 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).
3989 # I think the code has to be more deeply tested, but it works at least partially.
3990 #
3991 # Revision 1.201  2007/04/27 14:00:49  hdl
3992 # Removing $dbh from GetMarcFromKohaField (dbh is not used in this function.)
3993 #
3994 # Revision 1.200  2007/04/25 16:26:42  tipaul
3995 # 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 :
3996 # - add nozebra table management on biblio editing
3997 # - the index table content is hardcoded. I still have to add some specific systempref to let the library update it
3998 # - manage pagination (next/previous)
3999 # - manage facets
4000 # WHAT works :
4001 # - NZgetRecords : has exactly the same API & returns as zebra getQuery, except that some parameters are unused
4002 # - search & sort works quite good
4003 # - CQL parser is better that what I thought I could do : title="harry and sally" and publicationyear>2000 not itemtype=LIVR should work fine
4004 #
4005 # Revision 1.199  2007/04/24 09:07:53  tipaul
4006 # moving dotransfer to Biblio.pm::ModItemTransfer + some CheckReserves fixes
4007 #
4008 # Revision 1.198  2007/04/23 15:21:17  tipaul
4009 # renaming currenttransfers to transferstoreceive
4010 #
4011 # Revision 1.197  2007/04/18 17:00:14  tipaul
4012 # removing all useless %env / $env
4013 #
4014 # Revision 1.196  2007/04/17 08:48:00  tipaul
4015 # circulation cleaning continued: bufixing
4016 #
4017 # Revision 1.195  2007/04/04 16:46:22  tipaul
4018 # HUGE COMMIT : code cleaning circulation.
4019 #
4020 # some stuff to do, i'll write a mail on koha-devel NOW !
4021 #
4022 # Revision 1.194  2007/03/30 12:00:42  tipaul
4023 # 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...
4024 #
4025 # Revision 1.193  2007/03/29 16:45:53  tipaul
4026 # Code cleaning of Biblio.pm (continued)
4027 #
4028 # All subs have be cleaned :
4029 # - removed useless
4030 # - merged some
4031 # - reordering Biblio.pm completly
4032 # - using only naming conventions
4033 #
4034 # Seems to have broken nothing, but it still has to be heavily tested.
4035 # Note that Biblio.pm is now much more efficient than previously & probably more reliable as well.
4036 #
4037 # Revision 1.192  2007/03/29 13:30:31  tipaul
4038 # Code cleaning :
4039 # == Biblio.pm cleaning (useless) ==
4040 # * some sub declaration dropped
4041 # * removed modbiblio sub
4042 # * removed moditem sub
4043 # * removed newitems. It was used only in finishrecieve. Replaced by a TransformKohaToMarc+AddItem, that is better.
4044 # * removed MARCkoha2marcItem
4045 # * removed MARCdelsubfield declaration
4046 # * removed MARCkoha2marcBiblio
4047 #
4048 # == Biblio.pm cleaning (naming conventions) ==
4049 # * MARCgettagslib renamed to GetMarcStructure
4050 # * MARCgetitems renamed to GetMarcItem
4051 # * MARCfind_frameworkcode renamed to GetFrameworkCode
4052 # * MARCmarc2koha renamed to TransformMarcToKoha
4053 # * MARChtml2marc renamed to TransformHtmlToMarc
4054 # * MARChtml2xml renamed to TranformeHtmlToXml
4055 # * zebraop renamed to ModZebra
4056 #
4057 # == MARC=OFF ==
4058 # * removing MARC=OFF related scripts (in cataloguing directory)
4059 # * removed checkitems (function related to MARC=off feature, that is completly broken in head. If someone want to reintroduce it, hard work coming...)
4060 # * removed getitemsbybiblioitem (used only by MARC=OFF scripts, that is removed as well)
4061 #
4062 # Revision 1.191  2007/03/29 09:42:13  tipaul
4063 # adding default value new feature into cataloguing. The system (definition) part has already been added by toins
4064 #
4065 # Revision 1.190  2007/03/29 08:45:19  hdl
4066 # Deleting ignore_errors(1) pour MARC::Charset
4067 #
4068 # Revision 1.189  2007/03/28 10:39:16  hdl
4069 # removing $dbh as a parameter in AuthoritiesMarc functions
4070 # And reporting all differences into the scripts taht relies on those functions.