bugfix #168 + "breedingsearch" sub added+ "isbnsearch" (used only in acqui.simple...
[koha.git] / C4 / Search.pm
1 package C4::Search;
2 #require '/u/acli/lib/cvs.pl'; # XXX
3
4 # Copyright 2000-2002 Katipo Communications
5 #
6 # This file is part of Koha.
7 #
8 # Koha is free software; you can redistribute it and/or modify it under the
9 # terms of the GNU General Public License as published by the Free Software
10 # Foundation; either version 2 of the License, or (at your option) any later
11 # version.
12 #
13 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
14 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License along with
18 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
19 # Suite 330, Boston, MA  02111-1307 USA
20
21 use strict;
22 require Exporter;
23 use DBI;
24 use C4::Context;
25 use C4::Reserves2;
26         # FIXME - C4::Search uses C4::Reserves2, which uses C4::Search.
27         # So Perl complains that all of the functions here get redefined.
28
29 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
30
31 # set the version for version checking
32 $VERSION = 0.02;
33
34 =head1 NAME
35
36 C4::Search - Functions for searching the Koha catalog and other databases
37
38 =head1 SYNOPSIS
39
40   use C4::Search;
41
42   my ($count, @results) = catalogsearch($env, $type, $search, $num, $offset);
43
44 =head1 DESCRIPTION
45
46 This module provides the searching facilities for the Koha catalog and
47 other databases.
48
49 C<&catalogsearch> is a front end to all the other searches. Depending
50 on what is passed to it, it calls the appropriate search function.
51
52 =head1 FUNCTIONS
53
54 =over 2
55
56 =cut
57
58 @ISA = qw(Exporter);
59 @EXPORT = qw(&CatSearch &BornameSearch &ItemInfo &KeywordSearch &subsearch
60 &itemdata &bibdata &GetItems &borrdata &itemnodata &itemcount
61 &borrdata2 &NewBorrowerNumber &bibitemdata &borrissues
62 &getboracctrecord &ItemType &itemissues &subject &subtitle
63 &addauthor &bibitems &barcodes &findguarantees &allissues
64 &findguarantor &getwebsites &getwebbiblioitems &catalogsearch &itemcount2
65 &isbnsearch &breedingsearch);
66 # make all your functions, whether exported or not;
67
68 =item findguarantees
69
70   ($num_children, $children_arrayref) = &findguarantees($parent_borrno);
71   $child0_cardno = $children_arrayref->[0]{"cardnumber"};
72   $child0_borrno = $children_arrayref->[0]{"borrowernumber"};
73
74 C<&findguarantees> takes a borrower number (e.g., that of a patron
75 with children) and looks up the borrowers who are guaranteed by that
76 borrower (i.e., the patron's children).
77
78 C<&findguarantees> returns two values: an integer giving the number of
79 borrowers guaranteed by C<$parent_borrno>, and a reference to an array
80 of references to hash, which gives the actual results.
81
82 =cut
83 #'
84 sub findguarantees{
85   my ($bornum)=@_;
86   my $dbh = C4::Context->dbh;
87   my $query="select cardnumber,borrowernumber from borrowers where
88   guarantor='$bornum'";
89   my $sth=$dbh->prepare($query);
90   $sth->execute;
91
92   my @dat;
93   while (my $data = $sth->fetchrow_hashref)
94   {
95     push @dat, $data;
96   }
97   $sth->finish;
98   return (scalar(@dat), \@dat);
99 }
100
101 =item findguarantor
102
103   $guarantor = &findguarantor($borrower_no);
104   $guarantor_cardno = $guarantor->{"cardnumber"};
105   $guarantor_surname = $guarantor->{"surname"};
106   ...
107
108 C<&findguarantor> takes a borrower number (presumably that of a child
109 patron), finds the guarantor for C<$borrower_no> (the child's parent),
110 and returns the record for the guarantor.
111
112 C<&findguarantor> returns a reference-to-hash. Its keys are the fields
113 from the C<borrowers> database table;
114
115 =cut
116 #'
117 sub findguarantor{
118   my ($bornum)=@_;
119   my $dbh = C4::Context->dbh;
120   my $query="select guarantor from borrowers where
121   borrowernumber='$bornum'";
122   my $sth=$dbh->prepare($query);
123   $sth->execute;
124   my $data=$sth->fetchrow_hashref;
125   $sth->finish;
126   $query="Select * from borrowers where
127   borrowernumber='$data->{'guarantor'}'";
128   $sth=$dbh->prepare($query);
129   $sth->execute;
130   $data=$sth->fetchrow_hashref;
131   $sth->finish;
132   return($data);
133 }
134
135 =item NewBorrowerNumber
136
137   $num = &NewBorrowerNumber();
138
139 Allocates a new, unused borrower number, and returns it.
140
141 =cut
142 #'
143 # FIXME - This is identical to C4::Circulation::Borrower::NewBorrowerNumber.
144 # Pick one and stick with it. Preferably use the other one. This function
145 # doesn't belong in C4::Search.
146 sub NewBorrowerNumber {
147   my $dbh = C4::Context->dbh;
148   my $sth=$dbh->prepare("Select max(borrowernumber) from borrowers");
149   $sth->execute;
150   my $data=$sth->fetchrow_hashref;
151   $sth->finish;
152   $data->{'max(borrowernumber)'}++;
153   return($data->{'max(borrowernumber)'});
154 }
155
156 =item catalogsearch
157
158   ($count, @results) = &catalogsearch($env, $type, $search, $num, $offset);
159
160 This is primarily a front-end to other, more specialized catalog
161 search functions: if C<$search-E<gt>{itemnumber}> or
162 C<$search-E<gt>{isbn}> is given, C<&catalogsearch> uses a precise
163 C<&CatSearch>. If $search->{subject} is given, it runs a subject
164 C<&CatSearch>. If C<$search-E<gt>{keyword}> is given, it runs a
165 C<&KeywordSearch>. Otherwise, it runs a loose C<&CatSearch>.
166
167 If C<$env-E<gt>{itemcount}> is 1, then C<&catalogsearch> also counts
168 the items for each result, and adds several keys:
169
170 =over 4
171
172 =item C<itemcount>
173
174 The total number of copies of this book.
175
176 =item C<locationhash>
177
178 This is a reference-to-hash; the keys are the names of branches where
179 this book may be found, and the values are the number of copies at
180 that branch.
181
182 =item C<location>
183
184 A descriptive string saying where the book is located, and how many
185 copies there are, if greater than 1.
186
187 =item C<subject2>
188
189 The book's subject, with spaces replaced with C<%20>, presumably for
190 HTML.
191
192 =back
193
194 =cut
195 #'
196 sub catalogsearch {
197         my ($env,$type,$search,$num,$offset)=@_;
198         my $dbh = C4::Context->dbh;
199         #  foreach my $key (%$search){
200         #    $search->{$key}=$dbh->quote($search->{$key});
201         #  }
202         my ($count,@results);
203         #  print STDERR "Doing a search \n";
204         # FIXME - Use "elsif" to avoid this sort of deep nesting
205         if ($search->{'itemnumber'} ne '' || $search->{'isbn'} ne ''){
206                 print STDERR "Doing a precise search\n";
207                 ($count,@results)=CatSearch($env,'precise',$search,$num,$offset);
208         } else {
209                 if ($search->{'subject'} ne ''){
210                         ($count,@results)=CatSearch($env,'subject',$search,$num,$offset);
211                 } else {
212                         if ($search->{'keyword'} ne ''){
213                                 ($count,@results)=&KeywordSearch($env,'keyword',$search,$num,$offset);
214                         } else {
215                                 ($count,@results)=CatSearch($env,'loose',$search,$num,$offset);
216
217                         }
218                 }
219         }
220         if ($env->{itemcount} eq '1') {
221                 foreach my $data (@results){
222                         my ($counts) = itemcount2($env, $data->{'biblionumber'}, 'intra');
223                         my $subject2=$data->{'subject'};
224                         $subject2=~ s/ /%20/g;
225                         $data->{'itemcount'}=$counts->{'total'};
226                         my $totalitemcounts=0;
227                         foreach my $key (keys %$counts){
228                                 if ($key ne 'total'){   # FIXME - Should ignore 'order', too.
229                                         #$data->{'location'}.="$key $counts->{$key} ";
230                                         $totalitemcounts+=$counts->{$key};
231                                         $data->{'locationhash'}->{$key}=$counts->{$key};
232                                 }
233                         }
234                         my $locationtext='';
235                         my $notavailabletext='';
236                         foreach (sort keys %{$data->{'locationhash'}}) {
237                                 if ($_ eq 'notavailable') {
238                                         $notavailabletext="Not available";
239                                         my $c=$data->{'locationhash'}->{$_};
240                                         if ($totalitemcounts>1) {
241                                         $notavailabletext.=" ($c)";
242                                         }
243                                 } else {
244                                         $locationtext.="$_";
245                                         my $c=$data->{'locationhash'}->{$_};
246                                         if ($totalitemcounts>1) {
247                                         $locationtext.=" ($c), ";
248                                         }
249                                 }
250                         }
251                         if ($notavailabletext) {
252                                 $locationtext.=$notavailabletext;
253                         } else {
254                                 $locationtext=~s/, $//;
255                         }
256                         $data->{'location'}=$locationtext;
257                         $data->{'subject2'}=$subject2;
258                 }
259         }
260         return ($count,@results);
261 }
262
263 =item KeywordSearch
264
265   $search = { "keyword" => "One or more keywords",
266               "class"   => "VID|CD",    # Limit search to fiction and CDs
267               "dewey"   => "813",
268          };
269   ($count, @results) = &KeywordSearch($env, $type, $search, $num, $offset);
270
271 C<&KeywordSearch> searches the catalog by keyword: given a string
272 (C<$search-E<gt>{"keyword"}> consisting of a space-separated list of
273 keywords, it looks for books that contain any of those keywords in any
274 of a number of places.
275
276 C<&KeywordSearch> looks for keywords in the book title (and subtitle),
277 series name, notes (both C<biblio.notes> and C<biblioitems.notes>),
278 and subjects.
279
280 C<$search-E<gt>{"class"}> can be set to a C<|> (pipe)-separated list of
281 item class codes (e.g., "F" for fiction, "JNF" for junior nonfiction,
282 etc.). In this case, the search will be restricted to just those
283 classes.
284
285 If C<$search-E<gt>{"class"}> is not specified, you may specify
286 C<$search-E<gt>{"dewey"}>. This will restrict the search to that
287 particular Dewey Decimal Classification category. Setting
288 C<$search-E<gt>{"dewey"}> to "513" will return books about arithmetic,
289 whereas setting it to "5" will return all books with Dewey code 5I<xx>
290 (Science and Mathematics).
291
292 C<$env> and C<$type> are ignored.
293
294 C<$offset> and C<$num> specify the subset of results to return.
295 C<$num> specifies the number of results to return, and C<$offset> is
296 the number of the first result. Thus, setting C<$offset> to 100 and
297 C<$num> to 5 will return results 100 through 104 inclusive.
298
299 =cut
300 #'
301 sub KeywordSearch {
302   my ($env,$type,$search,$num,$offset)=@_;
303   my $dbh = C4::Context->dbh;
304   $search->{'keyword'}=~ s/ +$//;
305   $search->{'keyword'}=~ s/'/\\'/;
306   my @key=split(' ',$search->{'keyword'});
307                 # FIXME - Naive users might enter comma-separated
308                 # words, e.g., "training, animal". Ought to cope with
309                 # this.
310   my $count=@key;
311   my $i=1;
312   my %biblionumbers;            # Set of biblionumbers returned by the
313                                 # various searches.
314
315   # FIXME - Ought to filter the stopwords out of the list of keywords.
316   #     @key = map { !defined($stopwords{$_}) } @key;
317
318   # FIXME - The way this code is currently set up, it looks for all of
319   # the keywords first in (title, notes, seriestitle), then in the
320   # subtitle, then in the subject. Thus, if you look for keywords
321   # "science fiction", this search won't find a book with
322   #     title    = "How to write fiction"
323   #     subtitle = "A science-based approach"
324   # Is this the desired effect? If not, then the first SQL query
325   # should look in the biblio, subtitle, and subject tables all at
326   # once. The way the first query is built can accomodate this easily.
327
328   # Look for keywords in table 'biblio'.
329
330   # Build an SQL query that finds each of the keywords in any of the
331   # title, biblio.notes, or seriestitle. To do this, we'll build up an
332   # array of clauses, one for each keyword.
333   my $query;                    # The SQL query
334   my @clauses = ();             # The search clauses
335
336   $query = <<EOT;               # Beginning of the query
337         SELECT  biblionumber
338         FROM    biblio
339         WHERE
340 EOT
341   foreach my $keyword (@key)
342   {
343     my @subclauses = ();        # Subclauses, one for each field we're
344                                 # searching on
345
346     # For each field we're searching on, create a subclause that'll
347     # match the current keyword in the current field.
348     foreach my $field (qw(title notes seriestitle))
349     {
350       push @subclauses,
351         "$field LIKE '\Q$keyword\E%' OR $field LIKE '% \Q$keyword\E%'";
352     }
353     # (Yes, this could have been done as
354     #   @subclauses = map {...} qw(field1 field2 ...)
355     # )but I think this way is more readable.
356
357     # Construct the current clause by joining the subclauses.
358     push @clauses, "(" . join(")\n\tOR (", @subclauses) . ")";
359   }
360   # Now join all of the clauses together and append to the query.
361   $query .= "(" . join(")\nAND (", @clauses) . ")";
362
363   # FIXME - Perhaps use $sth->bind_columns() ? Documented as the most
364   # efficient way to fetch data.
365   my $sth=$dbh->prepare($query);
366   $sth->execute;
367   while (my @res = $sth->fetchrow_array) {
368     for (@res)
369     {
370         $biblionumbers{$_} = 1;         # Add these results to the set
371     }
372   }
373   $sth->finish;
374
375   # Now look for keywords in the 'bibliosubtitle' table.
376
377   # Again, we build a list of clauses from the keywords.
378   @clauses = ();
379   $query = "SELECT biblionumber FROM bibliosubtitle WHERE ";
380   foreach my $keyword (@key)
381   {
382     push @clauses,
383         "subtitle LIKE '\Q$keyword\E%' OR subtitle like '% \Q$keyword\E%'";
384   }
385   $query .= "(" . join(") AND (", @clauses) . ")";
386
387   $sth=$dbh->prepare($query);
388   $sth->execute;
389   while (my @res = $sth->fetchrow_array) {
390     for (@res)
391     {
392         $biblionumbers{$_} = 1;         # Add these results to the set
393     }
394   }
395   $sth->finish;
396
397   # Look for the keywords in the notes for individual items
398   # ('biblioitems.notes')
399
400   # Again, we build a list of clauses from the keywords.
401   @clauses = ();
402   $query = "SELECT biblionumber FROM biblioitems WHERE ";
403   foreach my $keyword (@key)
404   {
405     push @clauses,
406         "notes LIKE '\Q$keyword\E%' OR notes like '% \Q$keyword\E%'";
407   }
408   $query .= "(" . join(") AND (", @clauses) . ")";
409
410   $sth=$dbh->prepare($query);
411   $sth->execute;
412   while (my @res = $sth->fetchrow_array) {
413     for (@res)
414     {
415         $biblionumbers{$_} = 1;         # Add these results to the set
416     }
417   }
418   $sth->finish;
419
420   # Look for keywords in the 'bibliosubject' table.
421
422   # FIXME - The other queries look for words in the desired field that
423   # begin with the individual keywords the user entered. This one
424   # searches for the literal string the user entered. Is this the
425   # desired effect?
426   # Note in particular that spaces are retained: if the user typed
427   #     science  fiction
428   # (with two spaces), this won't find the subject "science fiction"
429   # (one space). Likewise, a search for "%" will return absolutely
430   # everything.
431   # If this isn't the desired effect, see the previous searches for
432   # how to do it.
433
434   $sth=$dbh->prepare("Select biblionumber from bibliosubject where subject
435   like '%$search->{'keyword'}%' group by biblionumber");
436   $sth->execute;
437
438   while (my @res = $sth->fetchrow_array) {
439     for (@res)
440     {
441         $biblionumbers{$_} = 1;         # Add these results to the set
442     }
443   }
444   $sth->finish;
445
446   my $i2=0;
447   my $i3=0;
448   my $i4=0;
449
450   my @res2;
451   my @res = keys %biblionumbers;
452   $count=@res;
453
454   $i=0;
455 #  print "count $count";
456   if ($search->{'class'} ne ''){
457     while ($i2 <$count){
458       my $query="select * from biblio,biblioitems where
459       biblio.biblionumber='$res[$i2]' and
460       biblio.biblionumber=biblioitems.biblionumber ";
461       if ($search->{'class'} ne ''){    # FIXME - Redundant
462       my @temp=split(/\|/,$search->{'class'});
463       my $count=@temp;
464       $query.= "and ( itemtype='$temp[0]'";
465       for (my $i=1;$i<$count;$i++){
466         $query.=" or itemtype='$temp[$i]'";
467       }
468       $query.=")";
469       }
470        my $sth=$dbh->prepare($query);
471        #    print $query;
472        $sth->execute;
473        if (my $data2=$sth->fetchrow_hashref){
474          my $dewey= $data2->{'dewey'};
475          my $subclass=$data2->{'subclass'};
476          # FIXME - This next bit is bogus, because it assumes that the
477          # Dewey code is a floating-point number. It isn't. It's
478          # actually a string that mainly consists of numbers. In
479          # particular, "4" is not a valid Dewey code, although "004"
480          # is ("Data processing; Computer science"). Likewise, zeros
481          # after the decimal are significant ("575" is not the same as
482          # "575.0"; the latter is more specific). And "000" is a
483          # perfectly good Dewey code ("General works; computer
484          # science") and should not be interpreted to mean "this
485          # database entry does not have a Dewey code". That's what
486          # NULL is for.
487          $dewey=~s/\.*0*$//;
488          ($dewey == 0) && ($dewey='');
489          ($dewey) && ($dewey.=" $subclass") ;
490           $sth->finish;
491           my $end=$offset +$num;
492           if ($i4 <= $offset){
493             $i4++;
494           }
495 #         print $i4;
496           if ($i4 <=$end && $i4 > $offset){
497             $data2->{'dewey'}=$dewey;
498             $res2[$i3]=$data2;
499
500 #           $res2[$i3]="$data2->{'author'}\t$data2->{'title'}\t$data2->{'biblionumber'}\t$data2->{'copyrightdate'}\t$dewey";
501             $i3++;
502             $i4++;
503 #           print "in here $i3<br>";
504           } else {
505 #           print $end;
506           }
507           $i++;
508         }
509      $i2++;
510      }
511      $count=$i;
512
513    } else {
514   # $search->{'class'} was not specified
515
516   # FIXME - This is bogus: it makes a separate query for each
517   # biblioitem, and returns results in apparently random order. It'd
518   # be much better to combine all of the previous queries into one big
519   # one (building it up a little at a time, of course), and have that
520   # big query select all of the desired fields, instead of just
521   # 'biblionumber'.
522
523   while ($i2 < $num && $i2 < $count){
524     my $query="select * from biblio,biblioitems where
525     biblio.biblionumber='$res[$i2+$offset]' and
526     biblio.biblionumber=biblioitems.biblionumber ";
527
528     if ($search->{'dewey'} ne ''){
529       $query.= "and (dewey like '$search->{'dewey'}%') ";
530     }
531
532     my $sth=$dbh->prepare($query);
533 #    print $query;
534     $sth->execute;
535     if (my $data2=$sth->fetchrow_hashref){
536         my $dewey= $data2->{'dewey'};
537         my $subclass=$data2->{'subclass'};
538         $dewey=~s/\.*0*$//;
539         ($dewey == 0) && ($dewey='');
540         ($dewey) && ($dewey.=" $subclass") ;
541         $sth->finish;
542         $data2->{'dewey'}=$dewey;
543
544         $res2[$i]=$data2;
545 #       $res2[$i]="$data2->{'author'}\t$data2->{'title'}\t$data2->{'biblionumber'}\t$data2->{'copyrightdate'}\t$dewey";
546         $i++;
547     }
548     $i2++;
549
550   }
551   }
552
553   #$count=$i;
554   return($count,@res2);
555 }
556
557 sub KeywordSearch2 {
558   my ($env,$type,$search,$num,$offset)=@_;
559   my $dbh = C4::Context->dbh;
560   $search->{'keyword'}=~ s/ +$//;
561   $search->{'keyword'}=~ s/'/\\'/;
562   my @key=split(' ',$search->{'keyword'});
563   my $count=@key;
564   my $i=1;
565   my @results;
566   my $query ="Select * from biblio,bibliosubtitle,biblioitems where
567   biblio.biblionumber=biblioitems.biblionumber and
568   biblio.biblionumber=bibliosubtitle.biblionumber and
569   (((title like '$key[0]%' or title like '% $key[0]%')";
570   while ($i < $count){
571     $query .= " and (title like '$key[$i]%' or title like '% $key[$i]%')";
572     $i++;
573   }
574   $query.= ") or ((subtitle like '$key[0]%' or subtitle like '% $key[0]%')";
575   for ($i=1;$i<$count;$i++){
576     $query.= " and (subtitle like '$key[$i]%' or subtitle like '% $key[$i]%')";
577   }
578   $query.= ") or ((seriestitle like '$key[0]%' or seriestitle like '% $key[0]%')";
579   for ($i=1;$i<$count;$i++){
580     $query.=" and (seriestitle like '$key[$i]%' or seriestitle like '% $key[$i]%')";
581   }
582   $query.= ") or ((biblio.notes like '$key[0]%' or biblio.notes like '% $key[0]%')";
583   for ($i=1;$i<$count;$i++){
584     $query.=" and (biblio.notes like '$key[$i]%' or biblio.notes like '% $key[$i]%')";
585   }
586   $query.= ") or ((biblioitems.notes like '$key[0]%' or biblioitems.notes like '% $key[0]%')";
587   for ($i=1;$i<$count;$i++){
588     $query.=" and (biblioitems.notes like '$key[$i]%' or biblioitems.notes like '% $key[$i]%')";
589   }
590   if ($search->{'keyword'} =~ /new zealand/i){
591     $query.= "or (title like 'nz%' or title like '% nz %' or title like '% nz' or subtitle like 'nz%'
592     or subtitle like '% nz %' or subtitle like '% nz' or author like 'nz %'
593     or author like '% nz %' or author like '% nz')"
594   }
595   if ($search->{'keyword'} eq  'nz' || $search->{'keyword'} eq 'NZ' ||
596   $search->{'keyword'} =~ /nz /i || $search->{'keyword'} =~ / nz /i ||
597   $search->{'keyword'} =~ / nz/i){
598     $query.= "or (title like 'new zealand%' or title like '% new zealand %'
599     or title like '% new zealand' or subtitle like 'new zealand%' or
600     subtitle like '% new zealand %'
601     or subtitle like '% new zealand' or author like 'new zealand%'
602     or author like '% new zealand %' or author like '% new zealand' or
603     seriestitle like 'new zealand%' or seriestitle like '% new zealand %'
604     or seriestitle like '% new zealand')"
605   }
606   $query .= "))";
607   if ($search->{'class'} ne ''){
608     my @temp=split(/\|/,$search->{'class'});
609     my $count=@temp;
610     $query.= "and ( itemtype='$temp[0]'";
611     for (my $i=1;$i<$count;$i++){
612       $query.=" or itemtype='$temp[$i]'";
613      }
614   $query.=")";
615   }
616   if ($search->{'dewey'} ne ''){
617     $query.= "and (dewey like '$search->{'dewey'}%') ";
618   }
619    $query.="group by biblio.biblionumber";
620    #$query.=" order by author,title";
621 #  print $query;
622   my $sth=$dbh->prepare($query);
623   $sth->execute;
624   $i=0;
625   while (my $data=$sth->fetchrow_hashref){
626 #    my $sti=$dbh->prepare("select dewey,subclass from biblioitems where biblionumber=$data->{'biblionumber'}
627 #    ");
628 #    $sti->execute;
629 #    my ($dewey, $subclass) = $sti->fetchrow;
630     my $dewey=$data->{'dewey'};
631     my $subclass=$data->{'subclass'};
632     $dewey=~s/\.*0*$//;
633     ($dewey == 0) && ($dewey='');
634     ($dewey) && ($dewey.=" $subclass");
635 #    $sti->finish;
636     $results[$i]="$data->{'author'}\t$data->{'title'}\t$data->{'biblionumber'}\t$data->{'copyrightdate'}\t$dewey";
637 #      print $results[$i];
638     $i++;
639   }
640   $sth->finish;
641   $sth=$dbh->prepare("Select biblionumber from bibliosubject where subject
642   like '%$search->{'keyword'}%' group by biblionumber");
643   $sth->execute;
644   while (my $data=$sth->fetchrow_hashref){
645     $query="Select * from biblio,biblioitems where
646     biblio.biblionumber=$data->{'biblionumber'} and
647     biblio.biblionumber=biblioitems.biblionumber ";
648     if ($search->{'class'} ne ''){
649       my @temp=split(/\|/,$search->{'class'});
650       my $count=@temp;
651       $query.= " and ( itemtype='$temp[0]'";
652       for (my $i=1;$i<$count;$i++){
653         $query.=" or itemtype='$temp[$i]'";
654       }
655       $query.=")";
656
657     }
658     if ($search->{'dewey'} ne ''){
659       $query.= "and (dewey like '$search->{'dewey'}%') ";
660     }
661     my $sth2=$dbh->prepare($query);
662     $sth2->execute;
663 #    print $query;
664     while (my $data2=$sth2->fetchrow_hashref){
665       my $dewey= $data2->{'dewey'};
666       my $subclass=$data2->{'subclass'};
667       $dewey=~s/\.*0*$//;
668       ($dewey == 0) && ($dewey='');
669       ($dewey) && ($dewey.=" $subclass") ;
670 #      $sti->finish;
671        $results[$i]="$data2->{'author'}\t$data2->{'title'}\t$data2->{'biblionumber'}\t$data2->{'copyrightdate'}\t$dewey";
672 #      print $results[$i];
673       $i++;
674     }
675     $sth2->finish;
676   }
677   my $i2=1;
678   @results=sort @results;
679   my @res;
680   $count=@results;
681   $i=1;
682   if ($count > 0){
683     $res[0]=$results[0];
684   }
685   while ($i2 < $count){
686     if ($results[$i2] ne $res[$i-1]){
687       $res[$i]=$results[$i2];
688       $i++;
689     }
690     $i2++;
691   }
692   $i2=0;
693   my @res2;
694   $count=@res;
695   while ($i2 < $num && $i2 < $count){
696     $res2[$i2]=$res[$i2+$offset];
697 #    print $res2[$i2];
698     $i2++;
699   }
700   $sth->finish;
701 #  $i--;
702 #  $i++;
703   return($i,@res2);
704 }
705
706 =item CatSearch
707
708   ($count, @results) = &CatSearch($env, $type, $search, $num, $offset);
709
710 C<&CatSearch> searches the Koha catalog. It returns a list whose first
711 element is the number of returned results, and whose subsequent
712 elements are the results themselves.
713
714 Each returned element is a reference-to-hash. Most of the keys are
715 simply the fields from the C<biblio> table in the Koha database, but
716 the following keys may also be present:
717
718 =over 4
719
720 =item C<illustrator>
721
722 The book's illustrator.
723
724 =item C<publisher>
725
726 The publisher.
727
728 =back
729
730 C<$env> is ignored.
731
732 C<$type> may be C<subject>, C<loose>, or C<precise>. This controls the
733 high-level behavior of C<&CatSearch>, as described below.
734
735 In many cases, the description below says that a certain field in the
736 database must match the search string. In these cases, it means that
737 the beginning of some word in the field must match the search string.
738 Thus, an author search for "sm" will return books whose author is
739 "John Smith" or "Mike Smalls", but not "Paul Grossman", since the "sm"
740 does not occur at the beginning of a word.
741
742 Note that within each search mode, the criteria are and-ed together.
743 That is, if you perform a loose search on the author "Jerome" and the
744 title "Boat", the search will only return books by Jerome containing
745 "Boat" in the title.
746
747 It is not possible to cross modes, e.g., set the author to "Asimov"
748 and the subject to "Math" in hopes of finding books on math by Asimov.
749
750 =head2 Loose search
751
752 If C<$type> is set to C<loose>, the following search criteria may be
753 used:
754
755 =over 4
756
757 =item C<$search-E<gt>{author}>
758
759 The search string is a space-separated list of words. Each word must
760 match either the C<author> or C<additionalauthors> field.
761
762 =item C<$search-E<gt>{title}>
763
764 Each word in the search string must match the book title. If no author
765 is specified, the book subtitle will also be searched.
766
767 =item C<$search-E<gt>{abstract}>
768
769 Searches for the given search string in the book's abstract.
770
771 =item C<$search-E<gt>{'date-before'}>
772
773 Searches for books whose copyright date matches the search string.
774 That is, setting C<$search-E<gt>{'date-before'}> to "1985" will find
775 books written in 1985, and setting it to "198" will find books written
776 between 1980 and 1989.
777
778 =item C<$search-E<gt>{title}>
779
780 Searches by title are also affected by the value of
781 C<$search-E<gt>{"ttype"}>; if it is set to C<exact>, then the book
782 title, (one of) the series titleZ<>(s), or (one of) the unititleZ<>(s) must
783 match the search string exactly (the subtitle is not searched).
784
785 If C<$search-E<gt>{"ttype"}> is set to anything other than C<exact>,
786 each word in the search string must match the title, subtitle,
787 unititle, or series title.
788
789 =item C<$search-E<gt>{class}>
790
791 Restricts the search to certain item classes. The value of
792 C<$search-E<gt>{"class"}> is a | (pipe)-separated list of item types.
793 Thus, setting it to "F" restricts the search to fiction, and setting
794 it to "CD|CAS" will only look in compact disks and cassettes.
795
796 =item C<$search-E<gt>{dewey}>
797
798 Searches for books whose Dewey Decimal Classification code matches the
799 search string. That is, setting C<$search-E<gt>{"dewey"}> to "5" will
800 search for all books in 5I<xx> (Science and mathematics), setting it
801 to "54" will search for all books in 54I<x> (Chemistry), and setting
802 it to "546" will search for books on inorganic chemistry.
803
804 =item C<$search-E<gt>{publisher}>
805
806 Searches for books whose publisher contains the search string (unlike
807 other search criteria, C<$search-E<gt>{publisher}> is a string, not a
808 set of words.
809
810 =back
811
812 =head2 Subject search
813
814 If C<$type> is set to C<subject>, the following search criterion may
815 be used:
816
817 =over 4
818
819 =item C<$search-E<gt>{subject}>
820
821 The search string is a space-separated list of words, each of which
822 must match the book's subject.
823
824 Special case: if C<$search-E<gt>{subject}> is set to C<nz>,
825 C<&CatSearch> will search for books whose subject is "New Zealand".
826 However, setting C<$search-E<gt>{subject}> to C<"nz football"> will
827 search for books on "nz" and "football", not books on "New Zealand"
828 and "football".
829
830 =back
831
832 =head2 Precise search
833
834 If C<$type> is set to C<precise>, the following search criteria may be
835 used:
836
837 =over 4
838
839 =item C<$search-E<gt>{item}>
840
841 Searches for books whose barcode exactly matches the search string.
842
843 =item C<$search-E<gt>{isbn}>
844
845 Searches for books whose ISBN exactly matches the search string.
846
847 =back
848
849 For a loose search, if an author was specified, the results are
850 ordered by author and title. If no author was specified, the results
851 are ordered by title.
852
853 For other (non-loose) searches, if a subject was specified, the
854 results are ordered alphabetically by subject.
855
856 In all other cases (e.g., loose search by keyword), the results are
857 not ordered.
858
859 =cut
860 #'
861 sub CatSearch  {
862         my ($env,$type,$search,$num,$offset)=@_;
863         warn "type = $type";
864         my $dbh = C4::Context->dbh;
865         my $query = '';
866         my @results;
867         # FIXME - Why not just
868         #       $search->{'title'} = quotemeta($search->{'title'})
869         # to escape all questionable characters, not just single-quotes?
870         $search->{'title'}=~ s/'/\\'/g;
871         $search->{'author'}=~ s/'/\\'/g;
872         $search->{'illustrator'}=~ s/'/\\'/g;
873         my $title = lc($search->{'title'});
874         if ($type eq 'loose') {
875                 if ($search->{'author'} ne ''){
876                         my @key=split(' ',$search->{'author'});
877                         my $count=@key;
878                         my $i=1;
879                         $query="select *,biblio.author,biblio.biblionumber from
880                                                         biblio
881                                                         left join additionalauthors
882                                                         on additionalauthors.biblionumber =biblio.biblionumber
883                                                         where
884                                                         ((biblio.author like '$key[0]%' or biblio.author like '% $key[0]%' or
885                                                         additionalauthors.author like '$key[0]%' or additionalauthors.author
886                                                         like '% $key[0]%'
887                                                                 )";
888                         while ($i < $count){
889                                         $query .= " and (
890                                                                         biblio.author like '$key[$i]%' or biblio.author like '% $key[$i]%' or
891                                                                         additionalauthors.author like '$key[$i]%' or additionalauthors.author like '% $key[$i]%'
892                                                                         )";
893                                 $i++;
894                         }
895                         $query .= ")";
896                         if ($search->{'title'} ne ''){
897                                 my @key=split(' ',$search->{'title'});
898                                 my $count=@key;
899                                 my $i=0;
900                                 $query.= " and (((title like '$key[0]%' or title like '% $key[0]%' or title like '% $key[0]')";
901                                 while ($i<$count){
902                                         $query .= " and (title like '$key[$i]%' or title like '% $key[$i]%' or title like '% $key[$i]')";
903                                         $i++;
904                                 }
905                                 $query.=") or ((seriestitle like '$key[0]%' or seriestitle like '% $key[0]%' or seriestitle like '% $key[0]')";
906                                 for ($i=1;$i<$count;$i++){
907                                         $query.=" and (seriestitle like '$key[$i]%' or seriestitle like '% $key[$i]%')";
908                                         }
909                                 $query.=") or ((unititle like '$key[0]%' or unititle like '% $key[0]%' or unititle like '% $key[0]')";
910                                 for ($i=1;$i<$count;$i++){
911                                         $query.=" and (unititle like '$key[$i]%' or unititle like '% $key[$i]%')";
912                                         }
913                                 $query .= "))";
914                                 #$query=$query. " and (title like '%$search->{'title'}%'
915                                 #or seriestitle like '%$search->{'title'}%')";
916                         }
917                         if ($search->{'abstract'} ne ''){
918                                 $query.= " and (abstract like '%$search->{'abstract'}%')";
919                         }
920                         if ($search->{'date-before'} ne ''){
921                                 $query.= " and (copyrightdate like '%$search->{'date-before'}%')";
922                         }
923                         $query.=" group by biblio.biblionumber";
924                 } else {
925                         if ($search->{'title'} ne '') {
926                                 if ($search->{'ttype'} eq 'exact'){
927                                         $query="select * from biblio
928                                         where
929                                         (biblio.title='$search->{'title'}' or (biblio.unititle = '$search->{'title'}'
930                                         or biblio.unititle like '$search->{'title'} |%' or
931                                         biblio.unititle like '%| $search->{'title'} |%' or
932                                         biblio.unititle like '%| $search->{'title'}') or
933                                         (biblio.seriestitle = '$search->{'title'}' or
934                                         biblio.seriestitle like '$search->{'title'} |%' or
935                                         biblio.seriestitle like '%| $search->{'title'} |%' or
936                                         biblio.seriestitle like '%| $search->{'title'}')
937                                         )";
938                                 } else {
939                                         my @key=split(' ',$search->{'title'});
940                                         my $count=@key;
941                                         my $i=1;
942                                         $query="select biblio.biblionumber,author,title,unititle,notes,abstract,serial,seriestitle,copyrightdate,timestamp,subtitle from biblio
943                                         left join bibliosubtitle on
944                                         biblio.biblionumber=bibliosubtitle.biblionumber
945                                         where
946                                         (((title like '$key[0]%' or title like '% $key[0]%' or title like '% $key[0]')";
947                                         while ($i<$count){
948                                                 $query .= " and (title like '$key[$i]%' or title like '% $key[$i]%' or title like '% $key[$i]')";
949                                                 $i++;
950                                         }
951                                         $query.=") or ((subtitle like '$key[0]%' or subtitle like '% $key[0]%' or subtitle like '% $key[0]')";
952                                         for ($i=1;$i<$count;$i++){
953                                                 $query.=" and (subtitle like '$key[$i]%' or subtitle like '% $key[$i]%' or subtitle like '% $key[$i]')";
954                                         }
955                                         $query.=") or ((seriestitle like '$key[0]%' or seriestitle like '% $key[0]%' or seriestitle like '% $key[0]')";
956                                         for ($i=1;$i<$count;$i++){
957                                                 $query.=" and (seriestitle like '$key[$i]%' or seriestitle like '% $key[$i]%')";
958                                         }
959                                         $query.=") or ((unititle like '$key[0]%' or unititle like '% $key[0]%' or unititle like '% $key[0]')";
960                                         for ($i=1;$i<$count;$i++){
961                                                 $query.=" and (unititle like '$key[$i]%' or unititle like '% $key[$i]%')";
962                                         }
963                                         $query .= "))";
964                                 }
965                                 if ($search->{'abstract'} ne ''){
966                                         $query.= " and (abstract like '%$search->{'abstract'}%')";
967                                 }
968                                 if ($search->{'date-before'} ne ''){
969                                         $query.= " and (copyrightdate like '%$search->{'date-before'}%')";
970                                 }
971                         } elsif ($search->{'class'} ne ''){
972                                 $query="select * from biblioitems,biblio where biblio.biblionumber=biblioitems.biblionumber";
973                                 my @temp=split(/\|/,$search->{'class'});
974                                 my $count=@temp;
975                                 $query.= " and ( itemtype='$temp[0]'";
976                                 for (my $i=1;$i<$count;$i++){
977                                         $query.=" or itemtype='$temp[$i]'";
978                                 }
979                                 $query.=")";
980                                 if ($search->{'illustrator'} ne ''){
981                                         $query.=" and illus like '%".$search->{'illustrator'}."%' ";
982                                 }
983                                 if ($search->{'dewey'} ne ''){
984                                         $query.=" and biblioitems.dewey like '$search->{'dewey'}%'";
985                                 }
986                         } elsif ($search->{'dewey'} ne ''){
987                                 $query="select * from biblioitems,biblio
988                                 where biblio.biblionumber=biblioitems.biblionumber
989                                 and biblioitems.dewey like '$search->{'dewey'}%'";
990                         } elsif ($search->{'illustrator'} ne '') {
991                                         $query="select * from biblioitems,biblio
992                                 where biblio.biblionumber=biblioitems.biblionumber
993                                 and biblioitems.illus like '%".$search->{'illustrator'}."%'";
994                         } elsif ($search->{'publisher'} ne ''){
995                                 $query.= "Select * from biblio,biblioitems where biblio.biblionumber
996                                 =biblioitems.biblionumber and (publishercode like '%$search->{'publisher'}%')";
997                         } elsif ($search->{'abstract'} ne ''){
998                                 $query.= "Select * from biblio where abstract like '%$search->{'abstract'}%'";
999                         } elsif ($search->{'date-before'} ne ''){
1000                                 $query.= "Select * from biblio where copyrightdate like '%$search->{'date-before'}%'";
1001                         }
1002                         $query .=" group by biblio.biblionumber";
1003                 }
1004         }
1005         if ($type eq 'subject'){
1006                 # FIXME - Subject search is badly broken. The query defined by
1007                 # $query returns a single item (the subject), but later code
1008                 # expects a ref-to-hash with all sorts of stuff in it.
1009                 # Also, the count of items (biblios?) with the given subject is
1010                 # wrong.
1011
1012                 my @key=split(' ',$search->{'subject'});
1013                 my $count=@key;
1014                 my $i=1;
1015                 $query="select distinct(subject) from bibliosubject where( subject like
1016                 '$key[0]%' or subject like '% $key[0]%' or subject like '% $key[0]' or subject like '%($key[0])%')";
1017                 while ($i<$count){
1018                         $query.=" and (subject like '$key[$i]%' or subject like '% $key[$i]%'
1019                         or subject like '% $key[$i]'
1020                         or subject like '%($key[$i])%')";
1021                         $i++;
1022                 }
1023
1024                 # FIXME - Wouldn't it be better to fix the database so that if a
1025                 # book has a subject "NZ", then it also gets added the subject
1026                 # "New Zealand"?
1027                 # This can also be generalized by adding a table of subject
1028                 # synonyms to the database: just declare "NZ" to be a synonym for
1029                 # "New Zealand", "SF" a synonym for both "Science fiction" and
1030                 # "Fantastic fiction", etc.
1031
1032                 # FIXME - This can be rewritten as
1033                 #       if (lc($search->{"subject"}) eq "nz") {
1034                 if ($search->{'subject'} eq 'NZ' || $search->{'subject'} eq 'nz'){
1035                         $query.= " or (subject like 'NEW ZEALAND %' or subject like '% NEW ZEALAND %'
1036                         or subject like '% NEW ZEALAND' or subject like '%(NEW ZEALAND)%' ) ";
1037                 } elsif ( $search->{'subject'} =~ /^nz /i || $search->{'subject'} =~ / nz /i || $search->{'subject'} =~ / nz$/i){
1038                         $query=~ s/ nz/ NEW ZEALAND/ig;
1039                         $query=~ s/nz /NEW ZEALAND /ig;
1040                         $query=~ s/\(nz\)/\(NEW ZEALAND\)/gi;
1041                 }
1042         }
1043         if ($type eq 'precise'){
1044                 if ($search->{'itemnumber'} ne ''){
1045                         $query="select * from items,biblio ";
1046                         my $search2=uc $search->{'itemnumber'};
1047                         $query=$query." where
1048                         items.biblionumber=biblio.biblionumber
1049                         and barcode='$search2'";
1050                                         # FIXME - .= <<EOT;
1051                 }
1052                 if ($search->{'isbn'} ne ''){
1053                         my $search2=uc $search->{'isbn'};
1054                         my $query1 = "select * from biblioitems where isbn='$search2'";
1055                         my $sth1=$dbh->prepare($query1);
1056                 #       print STDERR "$query1\n";
1057                         $sth1->execute;
1058                         my $i2=0;
1059                         while (my $data=$sth1->fetchrow_hashref) {
1060                                 $query="select * from biblioitems,biblio where
1061                                         biblio.biblionumber = $data->{'biblionumber'}
1062                                         and biblioitems.biblionumber = biblio.biblionumber";
1063                                 my $sth=$dbh->prepare($query);
1064                                 $sth->execute;
1065                                 # FIXME - There's already a $data in this scope.
1066                                 my $data=$sth->fetchrow_hashref;
1067                                 my ($dewey, $subclass) = ($data->{'dewey'}, $data->{'subclass'});
1068                                 # FIXME - The following assumes that the Dewey code is a
1069                                 # floating-point number. It isn't: it's a string.
1070                                 $dewey=~s/\.*0*$//;
1071                                 ($dewey == 0) && ($dewey='');
1072                                 ($dewey) && ($dewey.=" $subclass");
1073                                 $data->{'dewey'}=$dewey;
1074                                 $results[$i2]=$data;
1075                         #           $results[$i2]="$data->{'author'}\t$data->{'title'}\t$data->{'biblionumber'}\t$data->{'copyrightdate'}\t$dewey\t$data->{'isbn'}\t$data->{'itemtype'}";
1076                                 $i2++;
1077                                 $sth->finish;
1078                         }
1079                         $sth1->finish;
1080                 }
1081         }
1082         if ($type ne 'precise' && $type ne 'subject'){
1083                 if ($search->{'author'} ne ''){
1084                         $query .= " order by biblio.author,title";
1085                 } else {
1086                         $query .= " order by title";
1087                 }
1088         } else {
1089                 if ($type eq 'subject'){
1090                         $query .= " order by subject";
1091                 }
1092         }
1093         my $sth=$dbh->prepare($query);
1094         $sth->execute;
1095         my $count=1;
1096         my $i=0;
1097         my $limit= $num+$offset;
1098         while (my $data=$sth->fetchrow_hashref){
1099                 my $query="select dewey,subclass,publishercode from biblioitems where biblionumber=$data->{'biblionumber'}";
1100                 if ($search->{'class'} ne ''){
1101                         my @temp=split(/\|/,$search->{'class'});
1102                         my $count=@temp;
1103                         $query.= " and ( itemtype='$temp[0]'";
1104                         for (my $i=1;$i<$count;$i++){
1105                         $query.=" or itemtype='$temp[$i]'";
1106                         }
1107                         $query.=")";
1108                 }
1109                 if ($search->{'dewey'} ne ''){
1110                         $query.=" and dewey='$search->{'dewey'}' ";
1111                 }
1112                 if ($search->{'illustrator'} ne ''){
1113                         $query.=" and illus like '%".$search->{'illustrator'}."%' ";
1114                 }
1115                 if ($search->{'publisher'} ne ''){
1116                         $query.= " and (publishercode like '%$search->{'publisher'}%')";
1117                 }
1118                 warn $query;
1119                 my $sti=$dbh->prepare($query);
1120                 $sti->execute;
1121                 my $dewey;
1122                 my $subclass;
1123                 my $true=0;
1124                 my $publishercode;
1125                 my $bibitemdata;
1126                 if ($bibitemdata = $sti->fetchrow_hashref() || $type eq 'subject'){
1127                         $true=1;
1128                         $dewey=$bibitemdata->{'dewey'};
1129                         $subclass=$bibitemdata->{'subclass'};
1130                         $publishercode=$bibitemdata->{'publishercode'};
1131                 }
1132                 #  print STDERR "$dewey $subclass $publishercode\n";
1133                 # FIXME - The Dewey code is a string, not a number.
1134                 $dewey=~s/\.*0*$//;
1135                 ($dewey == 0) && ($dewey='');
1136                 ($dewey) && ($dewey.=" $subclass");
1137                 $data->{'dewey'}=$dewey;
1138                 $data->{'publishercode'}=$publishercode;
1139                 $sti->finish;
1140                 if ($true == 1){
1141                         if ($count > $offset && $count <= $limit){
1142                                 $results[$i]=$data;
1143                                 $i++;
1144                         }
1145                         $count++;
1146                 }
1147         }
1148         $sth->finish;
1149         $count--;
1150         return($count,@results);
1151 }
1152
1153 sub updatesearchstats{
1154   my ($dbh,$query)=@_;
1155
1156 }
1157
1158 =item subsearch
1159
1160   @results = &subsearch($env, $subject);
1161
1162 Searches for books that have a subject that exactly matches
1163 C<$subject>.
1164
1165 C<&subsearch> returns an array of results. Each element of this array
1166 is a string, containing the book's title, author, and biblionumber,
1167 separated by tabs.
1168
1169 C<$env> is ignored.
1170
1171 =cut
1172 #'
1173 sub subsearch {
1174   my ($env,$subject)=@_;
1175   my $dbh = C4::Context->dbh;
1176   $subject=$dbh->quote($subject);
1177   my $query="Select * from biblio,bibliosubject where
1178   biblio.biblionumber=bibliosubject.biblionumber and
1179   bibliosubject.subject=$subject group by biblio.biblionumber
1180   order by biblio.title";
1181   my $sth=$dbh->prepare($query);
1182   $sth->execute;
1183   my $i=0;
1184 #  print $query;
1185   my @results;
1186   while (my $data=$sth->fetchrow_hashref){
1187     $results[$i]="$data->{'title'}\t$data->{'author'}\t$data->{'biblionumber'}";
1188     $i++;
1189   }
1190   $sth->finish;
1191   return(@results);
1192 }
1193
1194 =item ItemInfo
1195
1196   @results = &ItemInfo($env, $biblionumber, $type);
1197
1198 Returns information about books with the given biblionumber.
1199
1200 C<$type> may be either C<intra> or anything else. If it is not set to
1201 C<intra>, then the search will exclude lost, very overdue, and
1202 withdrawn items.
1203
1204 C<$env> is ignored.
1205
1206 C<&ItemInfo> returns a list of references-to-hash. Each element
1207 contains a number of keys. Most of them are table items from the
1208 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
1209 Koha database. Other keys include:
1210
1211 =over 4
1212
1213 =item C<$data-E<gt>{branchname}>
1214
1215 The name (not the code) of the branch to which the book belongs.
1216
1217 =item C<$data-E<gt>{datelastseen}>
1218
1219 This is simply C<items.datelastseen>, except that while the date is
1220 stored in YYYY-MM-DD format in the database, here it is converted to
1221 DD/MM/YYYY format. A NULL date is returned as C<//>.
1222
1223 =item C<$data-E<gt>{datedue}>
1224
1225 =item C<$data-E<gt>{class}>
1226
1227 This is the concatenation of C<biblioitems.classification>, the book's
1228 Dewey code, and C<biblioitems.subclass>.
1229
1230 =item C<$data-E<gt>{ocount}>
1231
1232 I think this is the number of copies of the book available.
1233
1234 =item C<$data-E<gt>{order}>
1235
1236 If this is set, it is set to C<One Order>.
1237
1238 =back
1239
1240 =cut
1241 #'
1242 sub ItemInfo {
1243     my ($env,$biblionumber,$type) = @_;
1244     my $dbh   = C4::Context->dbh;
1245     my $query = "SELECT * FROM items, biblio, biblioitems, itemtypes
1246                   WHERE items.biblionumber = ?
1247                     AND biblioitems.biblioitemnumber = items.biblioitemnumber
1248                     AND biblioitems.itemtype = itemtypes.itemtype
1249                     AND biblio.biblionumber = items.biblionumber";
1250   if ($type ne 'intra'){
1251     $query .= " and ((items.itemlost<>1 and items.itemlost <> 2)
1252     or items.itemlost is NULL)
1253     and (wthdrawn <> 1 or wthdrawn is NULL)";
1254   }
1255   $query .= " order by items.dateaccessioned desc";
1256     #warn $query;
1257   my $sth=$dbh->prepare($query);
1258   $sth->execute($biblionumber);
1259   my $i=0;
1260   my @results;
1261 #  print $query;
1262   while (my $data=$sth->fetchrow_hashref){
1263     my $iquery = "Select * from issues
1264     where itemnumber = '$data->{'itemnumber'}'
1265     and returndate is null";
1266     my $datedue = '';
1267     my $isth=$dbh->prepare($iquery);
1268     $isth->execute;
1269     if (my $idata=$isth->fetchrow_hashref){
1270       # FIXME - The date ought to be properly parsed, and printed
1271       # according to local convention.
1272       my @temp=split('-',$idata->{'date_due'});
1273       $datedue = "$temp[2]/$temp[1]/$temp[0]";
1274     }
1275     if ($data->{'itemlost'} eq '2'){
1276         $datedue='Very Overdue';
1277     }
1278     if ($data->{'itemlost'} eq '1'){
1279         $datedue='Lost';
1280     }
1281     if ($data->{'wthdrawn'} eq '1'){
1282         $datedue="Cancelled";
1283     }
1284     if ($datedue eq ''){
1285         $datedue="Available";
1286         my ($restype,$reserves)=CheckReserves($data->{'itemnumber'});
1287         if ($restype){
1288             $datedue=$restype;
1289         }
1290     }
1291     $isth->finish;
1292 #get branch information.....
1293     my $bquery = "SELECT * FROM branches
1294                           WHERE branchcode = '$data->{'holdingbranch'}'";
1295     my $bsth=$dbh->prepare($bquery);
1296     $bsth->execute;
1297     if (my $bdata=$bsth->fetchrow_hashref){
1298         $data->{'branchname'} = $bdata->{'branchname'};
1299     }
1300
1301     my $class = $data->{'classification'};
1302     my $dewey = $data->{'dewey'};
1303     $dewey =~ s/0+$//;
1304     if ($dewey eq "000.") { $dewey = "";};      # FIXME - "000" is general
1305                                                 # books about computer science
1306     if ($dewey < 10){$dewey='00'.$dewey;}
1307     if ($dewey < 100 && $dewey > 10){$dewey='0'.$dewey;}
1308     if ($dewey <= 0){
1309       $dewey='';
1310     }
1311     $dewey=~ s/\.$//;
1312     $class .= $dewey;
1313     if ($dewey ne ''){
1314       $class .= $data->{'subclass'};
1315     }
1316  #   $results[$i]="$data->{'title'}\t$data->{'barcode'}\t$datedue\t$data->{'branchname'}\t$data->{'dewey'}";
1317     # FIXME - If $data->{'datelastseen'} is NULL, perhaps it'd be prettier
1318     # to leave it empty, rather than convert it to "//".
1319     # Also ideally this should use the local format for displaying dates.
1320     my @temp=split('-',$data->{'datelastseen'});
1321     my $date="$temp[2]/$temp[1]/$temp[0]";
1322     $data->{'datelastseen'}=$date;
1323     $data->{'datedue'}=$datedue;
1324     $data->{'class'}=$class;
1325     $results[$i]=$data;
1326     $i++;
1327   }
1328  $sth->finish;
1329   my $query2="Select * from aqorders where biblionumber=$biblionumber";
1330   my $sth2=$dbh->prepare($query2);
1331   $sth2->execute;
1332   my $data;
1333   my $ocount;
1334   if ($data=$sth2->fetchrow_hashref){
1335     $ocount=$data->{'quantity'} - $data->{'quantityreceived'};
1336     if ($ocount > 0){
1337       $data->{'ocount'}=$ocount;
1338       $data->{'order'}="One Order";
1339       $results[$i]=$data;
1340     }
1341   }
1342   $sth2->finish;
1343
1344   return(@results);
1345 }
1346
1347 =item GetItems
1348
1349   @results = &GetItems($env, $biblionumber);
1350
1351 Returns information about books with the given biblionumber.
1352
1353 C<$env> is ignored.
1354
1355 C<&GetItems> returns an array of strings. Each element is a
1356 tab-separated list of values: biblioitemnumber, itemtype,
1357 classification, Dewey number, subclass, ISBN, volume, number, and
1358 itemdata.
1359
1360 Itemdata, in turn, is a string of the form
1361 "I<barcode>C<[>I<holdingbranch>C<[>I<flags>" where I<flags> contains
1362 the string C<NFL> if the item is not for loan, and C<LOST> if the item
1363 is lost.
1364
1365 =cut
1366 #'
1367 sub GetItems {
1368    my ($env,$biblionumber)=@_;
1369    #debug_msg($env,"GetItems");
1370    my $dbh = C4::Context->dbh;
1371    my $query = "Select * from biblioitems where (biblionumber = $biblionumber)";
1372    #debug_msg($env,$query);
1373    my $sth=$dbh->prepare($query);
1374    $sth->execute;
1375    #debug_msg($env,"executed query");
1376    my $i=0;
1377    my @results;
1378    while (my $data=$sth->fetchrow_hashref) {
1379       #debug_msg($env,$data->{'biblioitemnumber'});
1380       my $dewey = $data->{'dewey'};
1381       $dewey =~ s/0+$//;
1382       my $line = $data->{'biblioitemnumber'}."\t".$data->{'itemtype'};
1383       $line .= "\t$data->{'classification'}\t$dewey";
1384       $line .= "\t$data->{'subclass'}\t$data->{isbn}";
1385       $line .= "\t$data->{'volume'}\t$data->{number}";
1386       my $isth= $dbh->prepare("select * from items where biblioitemnumber = $data->{'biblioitemnumber'}");
1387       $isth->execute;
1388       while (my $idata = $isth->fetchrow_hashref) {
1389         my $iline = $idata->{'barcode'}."[".$idata->{'holdingbranch'}."[";
1390         if ($idata->{'notforloan'} == 1) {
1391           $iline .= "NFL ";
1392         }
1393         if ($idata->{'itemlost'} == 1) {
1394           $iline .= "LOST ";
1395         }
1396         $line .= "\t$iline";
1397       }
1398       $isth->finish;
1399       $results[$i] = $line;
1400       $i++;
1401    }
1402    $sth->finish;
1403    return(@results);
1404 }
1405
1406 =item itemdata
1407
1408   $item = &itemdata($barcode);
1409
1410 Looks up the item with the given barcode, and returns a
1411 reference-to-hash containing information about that item. The keys of
1412 the hash are the fields from the C<items> and C<biblioitems> tables in
1413 the Koha database.
1414
1415 =cut
1416 #'
1417 sub itemdata {
1418   my ($barcode)=@_;
1419   my $dbh = C4::Context->dbh;
1420   my $query="Select * from items,biblioitems where barcode='$barcode'
1421   and items.biblioitemnumber=biblioitems.biblioitemnumber";
1422 #  print $query;
1423   my $sth=$dbh->prepare($query);
1424   $sth->execute;
1425   my $data=$sth->fetchrow_hashref;
1426   $sth->finish;
1427   return($data);
1428 }
1429
1430 =item bibdata
1431
1432   $data = &bibdata($biblionumber, $type);
1433
1434 Returns information about the book with the given biblionumber.
1435
1436 C<$type> is ignored.
1437
1438 C<&bibdata> returns a reference-to-hash. The keys are the fields in
1439 the C<biblio>, C<biblioitems>, and C<bibliosubtitle> tables in the
1440 Koha database.
1441
1442 In addition, C<$data-E<gt>{subject}> is the list of the book's
1443 subjects, separated by C<" , "> (space, comma, space).
1444
1445 If there are multiple biblioitems with the given biblionumber, only
1446 the first one is considered.
1447
1448 =cut
1449 #'
1450 sub bibdata {
1451     my ($bibnum, $type) = @_;
1452     my $dbh   = C4::Context->dbh;
1453     my $query = "Select *, biblio.notes
1454     from biblio, biblioitems
1455     left join bibliosubtitle on
1456     biblio.biblionumber = bibliosubtitle.biblionumber
1457     where biblio.biblionumber = $bibnum
1458     and biblioitems.biblionumber = $bibnum";
1459     my $sth   = $dbh->prepare($query);
1460     my $data;
1461
1462     $sth->execute;
1463     $data  = $sth->fetchrow_hashref;
1464     $sth->finish;
1465
1466     $query = "Select * from bibliosubject where biblionumber = '$bibnum'";
1467     $sth   = $dbh->prepare($query);
1468     $sth->execute;
1469     while (my $dat = $sth->fetchrow_hashref){
1470         $data->{'subject'} .= " , $dat->{'subject'}";
1471     } # while
1472
1473     $sth->finish;
1474     return($data);
1475 } # sub bibdata
1476
1477 =item bibitemdata
1478
1479   $itemdata = &bibitemdata($biblioitemnumber);
1480
1481 Looks up the biblioitem with the given biblioitemnumber. Returns a
1482 reference-to-hash. The keys are the fields from the C<biblio>,
1483 C<biblioitems>, and C<itemtypes> tables in the Koha database, except
1484 that C<biblioitems.notes> is given as C<$itemdata-E<gt>{bnotes}>.
1485
1486 =cut
1487 #'
1488 sub bibitemdata {
1489     my ($bibitem) = @_;
1490     my $dbh   = C4::Context->dbh;
1491     my $query = "Select *,biblioitems.notes as bnotes from biblio, biblioitems,itemtypes
1492 where biblio.biblionumber = biblioitems.biblionumber
1493 and biblioitemnumber = $bibitem
1494 and biblioitems.itemtype = itemtypes.itemtype";
1495     my $sth   = $dbh->prepare($query);
1496     my $data;
1497
1498     $sth->execute;
1499
1500     $data = $sth->fetchrow_hashref;
1501
1502     $sth->finish;
1503     return($data);
1504 } # sub bibitemdata
1505
1506 =item subject
1507
1508   ($count, $subjects) = &subject($biblionumber);
1509
1510 Looks up the subjects of the book with the given biblionumber. Returns
1511 a two-element list. C<$subjects> is a reference-to-array, where each
1512 element is a subject of the book, and C<$count> is the number of
1513 elements in C<$subjects>.
1514
1515 =cut
1516 #'
1517 sub subject {
1518   my ($bibnum)=@_;
1519   my $dbh = C4::Context->dbh;
1520   my $query="Select * from bibliosubject where biblionumber=$bibnum";
1521   my $sth=$dbh->prepare($query);
1522   $sth->execute;
1523   my @results;
1524   my $i=0;
1525   while (my $data=$sth->fetchrow_hashref){
1526     $results[$i]=$data;
1527     $i++;
1528   }
1529   $sth->finish;
1530   return($i,\@results);
1531 }
1532
1533 =item addauthor
1534
1535   ($count, $authors) = &addauthors($biblionumber);
1536
1537 Looks up the additional authors for the book with the given
1538 biblionumber.
1539
1540 Returns a two-element list. C<$authors> is a reference-to-array, where
1541 each element is an additional author, and C<$count> is the number of
1542 elements in C<$authors>.
1543
1544 =cut
1545 #'
1546 sub addauthor {
1547   my ($bibnum)=@_;
1548   my $dbh = C4::Context->dbh;
1549   my $query="Select * from additionalauthors where biblionumber=$bibnum";
1550   my $sth=$dbh->prepare($query);
1551   $sth->execute;
1552   my @results;
1553   my $i=0;
1554   while (my $data=$sth->fetchrow_hashref){
1555     $results[$i]=$data;
1556     $i++;
1557   }
1558   $sth->finish;
1559   return($i,\@results);
1560 }
1561
1562 =item subtitle
1563
1564   ($count, $subtitles) = &subtitle($biblionumber);
1565
1566 Looks up the subtitles for the book with the given biblionumber.
1567
1568 Returns a two-element list. C<$subtitles> is a reference-to-array,
1569 where each element is a subtitle, and C<$count> is the number of
1570 elements in C<$subtitles>.
1571
1572 =cut
1573 #'
1574 sub subtitle {
1575   my ($bibnum)=@_;
1576   my $dbh = C4::Context->dbh;
1577   my $query="Select * from bibliosubtitle where biblionumber=$bibnum";
1578   my $sth=$dbh->prepare($query);
1579   $sth->execute;
1580   my @results;
1581   my $i=0;
1582   while (my $data=$sth->fetchrow_hashref){
1583     $results[$i]=$data;
1584     $i++;
1585   }
1586   $sth->finish;
1587   return($i,\@results);
1588 }
1589
1590 =item itemissues
1591
1592   @issues = &itemissues($biblioitemnumber, $biblio);
1593
1594 Looks up information about who has borrowed the bookZ<>(s) with the
1595 given biblioitemnumber.
1596
1597 C<$biblio> is ignored.
1598
1599 C<&itemissues> returns an array of references-to-hash. The keys
1600 include the fields from the C<items> table in the Koha database.
1601 Additional keys include:
1602
1603 =over 4
1604
1605 =item C<date_due>
1606
1607 If the item is currently on loan, this gives the due date.
1608
1609 If the item is not on loan, then this is either "Available" or
1610 "Cancelled", if the item has been withdrawn.
1611
1612 =item C<card>
1613
1614 If the item is currently on loan, this gives the card number of the
1615 patron who currently has the item.
1616
1617 =item C<timestamp0>, C<timestamp1>, C<timestamp2>
1618
1619 These give the timestamp for the last three times the item was
1620 borrowed.
1621
1622 =item C<card0>, C<card1>, C<card2>
1623
1624 The card number of the last three patrons who borrowed this item.
1625
1626 =item C<borrower0>, C<borrower1>, C<borrower2>
1627
1628 The borrower number of the last three patrons who borrowed this item.
1629
1630 =back
1631
1632 =cut
1633 #'
1634 sub itemissues {
1635     my ($bibitem, $biblio)=@_;
1636     my $dbh   = C4::Context->dbh;
1637     my $query = "Select * from items where
1638 items.biblioitemnumber = '$bibitem'";
1639     # FIXME - If this function die()s, the script will abort, and the
1640     # user won't get anything; depending on how far the script has
1641     # gotten, the user might get a blank page. It would be much better
1642     # to at least print an error message. The easiest way to do this
1643     # is to set $SIG{__DIE__}.
1644     my $sth   = $dbh->prepare($query)
1645       || die $dbh->errstr;
1646     my $i     = 0;
1647     my @results;
1648
1649     $sth->execute
1650       || die $sth->errstr;
1651
1652     while (my $data = $sth->fetchrow_hashref) {
1653         # Find out who currently has this item.
1654         # FIXME - Wouldn't it be better to do this as a left join of
1655         # some sort? Currently, this code assumes that if
1656         # fetchrow_hashref() fails, then the book is on the shelf.
1657         # fetchrow_hashref() can fail for any number of reasons (e.g.,
1658         # database server crash), not just because no items match the
1659         # search criteria.
1660         my $query2 = "select * from issues,borrowers
1661 where itemnumber = $data->{'itemnumber'}
1662 and returndate is NULL
1663 and issues.borrowernumber = borrowers.borrowernumber";
1664         my $sth2   = $dbh->prepare($query2);
1665
1666         $sth2->execute;
1667         if (my $data2 = $sth2->fetchrow_hashref) {
1668             $data->{'date_due'} = $data2->{'date_due'};
1669             $data->{'card'}     = $data2->{'cardnumber'};
1670         } else {
1671             if ($data->{'wthdrawn'} eq '1') {
1672                 $data->{'date_due'} = 'Cancelled';
1673             } else {
1674                 $data->{'date_due'} = 'Available';
1675             } # else
1676         } # else
1677
1678         $sth2->finish;
1679
1680         # Find the last 3 people who borrowed this item.
1681         $query2 = "select * from issues, borrowers
1682                                                 where itemnumber = ?
1683                                                                         and issues.borrowernumber = borrowers.borrowernumber
1684                                                                         and returndate is not NULL
1685                                                                         order by returndate desc,timestamp desc";
1686 warn "$query2";
1687         $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1688         $sth2->execute($data->{'itemnumber'}) || die $sth2->errstr;
1689         for (my $i2 = 0; $i2 < 2; $i2++) { # FIXME : error if there is less than 3 pple borrowing this item
1690             if (my $data2 = $sth2->fetchrow_hashref) {
1691                 $data->{"timestamp$i2"} = $data2->{'timestamp'};
1692                 $data->{"card$i2"}      = $data2->{'cardnumber'};
1693                 $data->{"borrower$i2"}  = $data2->{'borrowernumber'};
1694             } # if
1695         } # for
1696
1697         $sth2->finish;
1698         $results[$i] = $data;
1699         $i++;
1700     }
1701
1702     $sth->finish;
1703     return(@results);
1704 }
1705
1706 =item itemnodata
1707
1708   $item = &itemnodata($env, $dbh, $biblioitemnumber);
1709
1710 Looks up the item with the given biblioitemnumber.
1711
1712 C<$env> and C<$dbh> are ignored.
1713
1714 C<&itemnodata> returns a reference-to-hash whose keys are the fields
1715 from the C<biblio>, C<biblioitems>, and C<items> tables in the Koha
1716 database.
1717
1718 =cut
1719 #'
1720 sub itemnodata {
1721   my ($env,$dbh,$itemnumber) = @_;
1722   $dbh = C4::Context->dbh;
1723   my $query="Select * from biblio,items,biblioitems
1724     where items.itemnumber = '$itemnumber'
1725     and biblio.biblionumber = items.biblionumber
1726     and biblioitems.biblioitemnumber = items.biblioitemnumber";
1727   my $sth=$dbh->prepare($query);
1728 #  print $query;
1729   $sth->execute;
1730   my $data=$sth->fetchrow_hashref;
1731   $sth->finish;
1732   return($data);
1733 }
1734
1735 =item BornameSearch
1736
1737   ($count, $borrowers) = &BornameSearch($env, $searchstring, $type);
1738
1739 Looks up patrons (borrowers) by name.
1740
1741 C<$env> and C<$type> are ignored.
1742
1743 C<$searchstring> is a space-separated list of search terms. Each term
1744 must match the beginning a borrower's surname, first name, or other
1745 name.
1746
1747 C<&BornameSearch> returns a two-element list. C<$borrowers> is a
1748 reference-to-array; each element is a reference-to-hash, whose keys
1749 are the fields of the C<borrowers> table in the Koha database.
1750 C<$count> is the number of elements in C<$borrowers>.
1751
1752 =cut
1753 #'
1754 #used by member enquiries from the intranet
1755 #called by member.pl
1756 sub BornameSearch  {
1757   my ($env,$searchstring,$type)=@_;
1758   my $dbh = C4::Context->dbh;
1759   $searchstring=~ s/\'/\\\'/g;
1760   my @data=split(' ',$searchstring);
1761   my $count=@data;
1762   my $query="Select * from borrowers
1763   where ((surname like \"$data[0]%\" or surname like \"% $data[0]%\"
1764   or firstname  like \"$data[0]%\" or firstname like \"% $data[0]%\"
1765   or othernames like \"$data[0]%\" or othernames like \"% $data[0]%\")
1766   ";
1767   for (my $i=1;$i<$count;$i++){
1768     $query=$query." and (surname like \"$data[$i]%\" or surname like \"% $data[$i]%\"
1769     or firstname  like \"$data[$i]%\" or firstname like \"% $data[$i]%\"
1770     or othernames like \"$data[$i]%\" or othernames like \"% $data[$i]%\")";
1771                         # FIXME - .= <<EOT;
1772   }
1773   $query=$query.") or cardnumber = \"$searchstring\"
1774   order by surname,firstname";
1775                         # FIXME - .= <<EOT;
1776 #  print $query,"\n";
1777   my $sth=$dbh->prepare($query);
1778   $sth->execute;
1779   my @results;
1780   my $cnt=0;
1781   while (my $data=$sth->fetchrow_hashref){
1782     push(@results,$data);
1783     $cnt ++;
1784   }
1785 #  $sth->execute;
1786   $sth->finish;
1787   return ($cnt,\@results);
1788 }
1789
1790 =item borrdata
1791
1792   $borrower = &borrdata($cardnumber, $borrowernumber);
1793
1794 Looks up information about a patron (borrower) by either card number
1795 or borrower number. If $borrowernumber is specified, C<&borrdata>
1796 searches by borrower number; otherwise, it searches by card number.
1797
1798 C<&borrdata> returns a reference-to-hash whose keys are the fields of
1799 the C<borrowers> table in the Koha database.
1800
1801 =cut
1802 #'
1803 sub borrdata {
1804   my ($cardnumber,$bornum)=@_;
1805   $cardnumber = uc $cardnumber;
1806   my $dbh = C4::Context->dbh;
1807   my $query;
1808   if ($bornum eq ''){
1809     $query="Select * from borrowers where cardnumber='$cardnumber'";
1810   } else {
1811       $query="Select * from borrowers where borrowernumber='$bornum'";
1812   }
1813   #print $query;
1814   my $sth=$dbh->prepare($query);
1815   $sth->execute;
1816   my $data=$sth->fetchrow_hashref;
1817   $sth->finish;
1818   return($data);
1819 }
1820
1821 =item borrissues
1822
1823   ($count, $issues) = &borrissues($borrowernumber);
1824
1825 Looks up what the patron with the given borrowernumber has borrowed.
1826
1827 C<&borrissues> returns a two-element array. C<$issues> is a
1828 reference-to-array, where each element is a reference-to-hash; the
1829 keys are the fields from the C<issues>, C<biblio>, and C<items> tables
1830 in the Koha database. C<$count> is the number of elements in
1831 C<$issues>.
1832
1833 =cut
1834 #'
1835 sub borrissues {
1836   my ($bornum)=@_;
1837   my $dbh = C4::Context->dbh;
1838   my $query;
1839   $query="Select * from issues,biblio,items where borrowernumber='$bornum' and
1840 items.itemnumber=issues.itemnumber and
1841 items.biblionumber=biblio.biblionumber and issues.returndate is NULL order
1842 by date_due";
1843   #print $query;
1844   my $sth=$dbh->prepare($query);
1845     $sth->execute;
1846   my @result;
1847   while (my $data = $sth->fetchrow_hashref) {
1848     push @result, $data;
1849   }
1850   $sth->finish;
1851   return(scalar(@result), \@result);
1852 }
1853
1854 =item allissues
1855
1856   ($count, $issues) = &allissues($borrowernumber, $sortkey, $limit);
1857
1858 Looks up what the patron with the given borrowernumber has borrowed,
1859 and sorts the results.
1860
1861 C<$sortkey> is the name of a field on which to sort the results. This
1862 should be the name of a field in the C<issues>, C<biblio>,
1863 C<biblioitems>, or C<items> table in the Koha database.
1864
1865 C<$limit> is the maximum number of results to return.
1866
1867 C<&allissues> returns a two-element array. C<$issues> is a
1868 reference-to-array, where each element is a reference-to-hash; the
1869 keys are the fields from the C<issues>, C<biblio>, C<biblioitems>, and
1870 C<items> tables of the Koha database. C<$count> is the number of
1871 elements in C<$issues>
1872
1873 =cut
1874 #'
1875 sub allissues {
1876   my ($bornum,$order,$limit)=@_;
1877   my $dbh = C4::Context->dbh;
1878   my $query;
1879   $query="Select * from issues,biblio,items,biblioitems
1880   where borrowernumber='$bornum' and
1881   items.biblioitemnumber=biblioitems.biblioitemnumber and
1882   items.itemnumber=issues.itemnumber and
1883   items.biblionumber=biblio.biblionumber";
1884   $query.=" order by $order";
1885   if ($limit !=0){
1886     $query.=" limit $limit";
1887   }
1888   #print $query;
1889   my $sth=$dbh->prepare($query);
1890   $sth->execute;
1891   my @result;
1892   my $i=0;
1893   while (my $data=$sth->fetchrow_hashref){
1894     $result[$i]=$data;;
1895     $i++;
1896   }
1897   $sth->finish;
1898   return($i,\@result);
1899 }
1900
1901 =item borrdata2
1902
1903   ($borrowed, $due, $fine) = &borrdata2($env, $borrowernumber);
1904
1905 Returns aggregate data about items borrowed by the patron with the
1906 given borrowernumber.
1907
1908 C<$env> is ignored.
1909
1910 C<&borrdata2> returns a three-element array. C<$borrowed> is the
1911 number of books the patron currently has borrowed. C<$due> is the
1912 number of overdue items the patron currently has borrowed. C<$fine> is
1913 the total fine currently due by the borrower.
1914
1915 =cut
1916 #'
1917 sub borrdata2 {
1918   my ($env,$bornum)=@_;
1919   my $dbh = C4::Context->dbh;
1920   my $query="Select count(*) from issues where borrowernumber='$bornum' and
1921     returndate is NULL";
1922     # print $query;
1923   my $sth=$dbh->prepare($query);
1924   $sth->execute;
1925   my $data=$sth->fetchrow_hashref;
1926   $sth->finish;
1927   $sth=$dbh->prepare("Select count(*) from issues where
1928     borrowernumber='$bornum' and date_due < now() and returndate is NULL");
1929   $sth->execute;
1930   my $data2=$sth->fetchrow_hashref;
1931   $sth->finish;
1932   $sth=$dbh->prepare("Select sum(amountoutstanding) from accountlines where
1933     borrowernumber='$bornum'");
1934   $sth->execute;
1935   my $data3=$sth->fetchrow_hashref;
1936   $sth->finish;
1937
1938 return($data2->{'count(*)'},$data->{'count(*)'},$data3->{'sum(amountoutstanding)'});
1939 }
1940
1941 =item getboracctrecord
1942
1943   ($count, $acctlines, $total) = &getboracctrecord($env, $borrowernumber);
1944
1945 Looks up accounting data for the patron with the given borrowernumber.
1946
1947 C<$env> is ignored.
1948
1949 (FIXME - I'm not at all sure what this is about.)
1950
1951 C<&getboracctrecord> returns a three-element array. C<$acctlines> is a
1952 reference-to-array, where each element is a reference-to-hash; the
1953 keys are the fields of the C<accountlines> table in the Koha database.
1954 C<$count> is the number of elements in C<$acctlines>. C<$total> is the
1955 total amount outstanding for all of the account lines.
1956
1957 =cut
1958 #'
1959 sub getboracctrecord {
1960    my ($env,$params) = @_;
1961    my $dbh = C4::Context->dbh;
1962    my @acctlines;
1963    my $numlines=0;
1964    my $query= "Select * from accountlines where
1965 borrowernumber=? order by date desc,timestamp desc";
1966    my $sth=$dbh->prepare($query);
1967 #   print $query;
1968    $sth->execute($params->{'borrowernumber'});
1969    my $total=0;
1970    while (my $data=$sth->fetchrow_hashref){
1971 #      if ($data->{'itemnumber'} ne ''){
1972 #        $query="Select * from items,biblio where items.itemnumber=
1973 #       '$data->{'itemnumber'}' and biblio.biblionumber=items.biblionumber";
1974 #       my $sth2=$dbh->prepare($query);
1975 #       $sth2->execute;
1976 #       my $data2=$sth2->fetchrow_hashref;
1977 #       $sth2->finish;
1978 #       $data=$data2;
1979  #     }
1980       $acctlines[$numlines] = $data;
1981       $numlines++;
1982       $total += $data->{'amountoutstanding'};
1983    }
1984    $sth->finish;
1985    return ($numlines,\@acctlines,$total);
1986 }
1987
1988 =item itemcount
1989
1990   ($count, $lcount, $nacount, $fcount, $scount, $lostcount,
1991   $mending, $transit,$ocount) =
1992     &itemcount($env, $biblionumber, $type);
1993
1994 Counts the number of items with the given biblionumber, broken down by
1995 category.
1996
1997 C<$env> is ignored.
1998
1999 If C<$type> is not set to C<intra>, lost, very overdue, and withdrawn
2000 items will not be counted.
2001
2002 C<&itemcount> returns a nine-element list:
2003
2004 C<$count> is the total number of items with the given biblionumber.
2005
2006 C<$lcount> is the number of items at the Levin branch.
2007
2008 C<$nacount> is the number of items that are neither borrowed, lost,
2009 nor withdrawn (and are therefore presumably on a shelf somewhere).
2010
2011 C<$fcount> is the number of items at the Foxton branch.
2012
2013 C<$scount> is the number of items at the Shannon branch.
2014
2015 C<$lostcount> is the number of lost and very overdue items.
2016
2017 C<$mending> is the number of items at the Mending branch (being
2018 mended?).
2019
2020 C<$transit> is the number of items at the Transit branch (in transit
2021 between branches?).
2022
2023 C<$ocount> is the number of items that haven't arrived yet
2024 (aqorders.quantity - aqorders.quantityreceived).
2025
2026 =cut
2027 #'
2028
2029 # FIXME - There's also a &C4::Biblio::itemcount.
2030 # Since they're all exported, acqui/acquire.pl doesn't compile with -w.
2031 sub itemcount {
2032   my ($env,$bibnum,$type)=@_;
2033   my $dbh = C4::Context->dbh;
2034   my $query="Select * from items where
2035   biblionumber=$bibnum ";
2036   if ($type ne 'intra'){
2037     $query.=" and ((itemlost <>1 and itemlost <> 2) or itemlost is NULL) and
2038     (wthdrawn <> 1 or wthdrawn is NULL)";
2039   }
2040   my $sth=$dbh->prepare($query);
2041   #  print $query;
2042   $sth->execute;
2043   my $count=0;
2044   my $lcount=0;
2045   my $nacount=0;
2046   my $fcount=0;
2047   my $scount=0;
2048   my $lostcount=0;
2049   my $mending=0;
2050   my $transit=0;
2051   my $ocount=0;
2052   while (my $data=$sth->fetchrow_hashref){
2053     $count++;
2054     my $query2="select * from issues,items where issues.itemnumber=
2055     '$data->{'itemnumber'}' and returndate is NULL
2056     and items.itemnumber=issues.itemnumber and ((items.itemlost <>1 and
2057     items.itemlost <> 2) or items.itemlost is NULL)
2058     and (wthdrawn <> 1 or wthdrawn is NULL)";
2059
2060     my $sth2=$dbh->prepare($query2);
2061     $sth2->execute;
2062     if (my $data2=$sth2->fetchrow_hashref){
2063        $nacount++;
2064     } else {
2065       if ($data->{'holdingbranch'} eq 'C' || $data->{'holdingbranch'} eq 'LT'){
2066         $lcount++;
2067       }
2068       if ($data->{'holdingbranch'} eq 'F' || $data->{'holdingbranch'} eq 'FP'){
2069         $fcount++;
2070       }
2071       if ($data->{'holdingbranch'} eq 'S' || $data->{'holdingbranch'} eq 'SP'){
2072         $scount++;
2073       }
2074       if ($data->{'itemlost'} eq '1'){
2075         $lostcount++;
2076       }
2077       if ($data->{'itemlost'} eq '2'){
2078         $lostcount++;
2079       }
2080       if ($data->{'holdingbranch'} eq 'FM'){
2081         $mending++;
2082       }
2083       if ($data->{'holdingbranch'} eq 'TR'){
2084         $transit++;
2085       }
2086     }
2087     $sth2->finish;
2088   }
2089 #  if ($count == 0){
2090     my $query2="Select * from aqorders where biblionumber=$bibnum";
2091     my $sth2=$dbh->prepare($query2);
2092     $sth2->execute;
2093     if (my $data=$sth2->fetchrow_hashref){
2094       $ocount=$data->{'quantity'} - $data->{'quantityreceived'};
2095     }
2096 #    $count+=$ocount;
2097     $sth2->finish;
2098   $sth->finish;
2099   return ($count,$lcount,$nacount,$fcount,$scount,$lostcount,$mending,$transit,$ocount);
2100 }
2101
2102 =item itemcount2
2103
2104   $counts = &itemcount2($env, $biblionumber, $type);
2105
2106 Counts the number of items with the given biblionumber, broken down by
2107 category.
2108
2109 C<$env> is ignored.
2110
2111 C<$type> may be either C<intra> or anything else. If it is not set to
2112 C<intra>, then the search will exclude lost, very overdue, and
2113 withdrawn items.
2114
2115 C<$&itemcount2> returns a reference-to-hash, with the following fields:
2116
2117 =over 4
2118
2119 =item C<total>
2120
2121 The total number of items with this biblionumber.
2122
2123 =item C<order>
2124
2125 The number of items on order (aqorders.quantity -
2126 aqorders.quantityreceived).
2127
2128 =item I<branchname>
2129
2130 For each branch that has at least one copy of the book, C<$counts>
2131 will have a key with the branch name, giving the number of copies at
2132 that branch.
2133
2134 =back
2135
2136 =cut
2137 #'
2138 sub itemcount2 {
2139   my ($env,$bibnum,$type)=@_;
2140   my $dbh = C4::Context->dbh;
2141   my $query="Select * from items,branches where
2142   biblionumber=$bibnum and items.holdingbranch=branches.branchcode";
2143   if ($type ne 'intra'){
2144     $query.=" and ((itemlost <>1 and itemlost <> 2) or itemlost is NULL) and
2145     (wthdrawn <> 1 or wthdrawn is NULL)";
2146   }
2147   my $sth=$dbh->prepare($query);
2148   #  print $query;
2149   $sth->execute;
2150   my %counts;
2151   $counts{'total'}=0;
2152   while (my $data=$sth->fetchrow_hashref){
2153     $counts{'total'}++;
2154     my $query2="select * from issues,items where issues.itemnumber=
2155     '$data->{'itemnumber'}' and returndate is NULL
2156     and items.itemnumber=issues.itemnumber and ((items.itemlost <>1 and
2157     items.itemlost <> 2) or items.itemlost is NULL)
2158     and (wthdrawn <> 1 or wthdrawn is NULL)";
2159
2160     my $sth2=$dbh->prepare($query2);
2161     $sth2->execute;
2162     # FIXME - fetchrow_hashref() can fail for any number of reasons
2163     # (e.g., a database server crash). Perhaps use a left join of some
2164     # sort for this?
2165     if (my $data2=$sth2->fetchrow_hashref){
2166        $counts{'not available'}++;
2167        #my $x = "Not available, data2=" . (defined $data2? CGI::escapeHTML(cvs($data2)): "undef");
2168        #$counts{$x}++; #XXX
2169     } else {
2170        $counts{$data->{'branchname'}}++;
2171     }
2172     $sth2->finish;
2173   }
2174   my $query2="Select * from aqorders where biblionumber=$bibnum and
2175   datecancellationprinted is NULL and quantity > quantityreceived";
2176   my $sth2=$dbh->prepare($query2);
2177   $sth2->execute;
2178   if (my $data=$sth2->fetchrow_hashref){
2179       $counts{'order'}=$data->{'quantity'} - $data->{'quantityreceived'};
2180   }
2181   $sth2->finish;
2182   $sth->finish;
2183   return (\%counts);
2184 }
2185
2186 =item ItemType
2187
2188   $description = &ItemType($itemtype);
2189
2190 Given an item type code, returns the description for that type.
2191
2192 =cut
2193 #'
2194
2195 # FIXME - I'm pretty sure that after the initial setup, the list of
2196 # item types doesn't change very often. Hence, it seems slow and
2197 # inefficient to make yet another database call to look up information
2198 # that'll only change every few months or years.
2199 #
2200 # Much better, I think, to automatically build a Perl file that can be
2201 # included in those scripts that require it, e.g.:
2202 #       @itemtypes = qw( ART BCD CAS CD F ... );
2203 #       %itemtypedesc = (
2204 #               ART     => "Art Prints",
2205 #               BCD     => "CD-ROM from book",
2206 #               CD      => "Compact disc (WN)",
2207 #               F       => "Free Fiction",
2208 #               ...
2209 #       );
2210 # The web server can then run a cron job to rebuild this file from the
2211 # database every hour or so.
2212 #
2213 # The same thing goes for branches, book funds, book sellers, currency
2214 # rates, printers, stopwords, and perhaps others.
2215 sub ItemType {
2216   my ($type)=@_;
2217   my $dbh = C4::Context->dbh;
2218   my $query="select description from itemtypes where itemtype='$type'";
2219   my $sth=$dbh->prepare($query);
2220   $sth->execute;
2221   my $dat=$sth->fetchrow_hashref;
2222   $sth->finish;
2223   return ($dat->{'description'});
2224 }
2225
2226 =item bibitems
2227
2228   ($count, @results) = &bibitems($biblionumber);
2229
2230 Given the biblionumber for a book, C<&bibitems> looks up that book's
2231 biblioitems (different publications of the same book, the audio book
2232 and film versions, etc.).
2233
2234 C<$count> is the number of elements in C<@results>.
2235
2236 C<@results> is an array of references-to-hash; the keys are the fields
2237 of the C<biblioitems> and C<itemtypes> tables of the Koha database. In
2238 addition, C<itemlost> indicates the availability of the item: if it is
2239 "2", then all copies of the item are long overdue; if it is "1", then
2240 all copies are lost; otherwise, there is at least one copy available.
2241
2242 =cut
2243 #'
2244 sub bibitems {
2245     my ($bibnum) = @_;
2246     my $dbh   = C4::Context->dbh;
2247     my $query = "SELECT biblioitems.*,
2248                         itemtypes.*,
2249                         MIN(items.itemlost)        as itemlost,
2250                         MIN(items.dateaccessioned) as dateaccessioned
2251                           FROM biblioitems, itemtypes, items
2252                          WHERE biblioitems.biblionumber     = ?
2253                            AND biblioitems.itemtype         = itemtypes.itemtype
2254                            AND biblioitems.biblioitemnumber = items.biblioitemnumber
2255                       GROUP BY items.biblioitemnumber";
2256     my $sth   = $dbh->prepare($query);
2257     my $count = 0;
2258     my @results;
2259     $sth->execute($bibnum);
2260     while (my $data = $sth->fetchrow_hashref) {
2261         $results[$count] = $data;
2262         $count++;
2263     } # while
2264     $sth->finish;
2265     return($count, @results);
2266 } # sub bibitems
2267
2268 =item barcodes
2269
2270   @barcodes = &barcodes($biblioitemnumber);
2271
2272 Given a biblioitemnumber, looks up the corresponding items.
2273
2274 Returns an array of references-to-hash; the keys are C<barcode> and
2275 C<itemlost>.
2276
2277 The returned items include very overdue items, but not lost ones.
2278
2279 =cut
2280 #'
2281 sub barcodes{
2282     #called from request.pl
2283     my ($biblioitemnumber)=@_;
2284     my $dbh = C4::Context->dbh;
2285     my $query="SELECT barcode, itemlost, holdingbranch FROM items
2286                            WHERE biblioitemnumber = ?
2287                              AND (wthdrawn <> 1 OR wthdrawn IS NULL)";
2288     my $sth=$dbh->prepare($query);
2289     $sth->execute($biblioitemnumber);
2290     my @barcodes;
2291     my $i=0;
2292     while (my $data=$sth->fetchrow_hashref){
2293         $barcodes[$i]=$data;
2294         $i++;
2295     }
2296     $sth->finish;
2297     return(@barcodes);
2298 }
2299
2300 =item getwebsites
2301
2302   ($count, @websites) = &getwebsites($biblionumber);
2303
2304 Looks up the web sites pertaining to the book with the given
2305 biblionumber.
2306
2307 C<$count> is the number of elements in C<@websites>.
2308
2309 C<@websites> is an array of references-to-hash; the keys are the
2310 fields from the C<websites> table in the Koha database.
2311
2312 =cut
2313 #'
2314 sub getwebsites {
2315     my ($biblionumber) = @_;
2316     my $dbh   = C4::Context->dbh;
2317     my $query = "Select * from websites where biblionumber = $biblionumber";
2318     my $sth   = $dbh->prepare($query);
2319     my $count = 0;
2320     my @results;
2321
2322     $sth->execute;
2323     while (my $data = $sth->fetchrow_hashref) {
2324         # FIXME - The URL scheme shouldn't be stripped off, at least
2325         # not here, since it's part of the URL, and will be useful in
2326         # constructing a link to the site. If you don't want the user
2327         # to see the "http://" part, strip that off when building the
2328         # HTML code.
2329         $data->{'url'} =~ s/^http:\/\///;       # FIXME - Leaning toothpick
2330                                                 # syndrome
2331         $results[$count] = $data;
2332         $count++;
2333     } # while
2334
2335     $sth->finish;
2336     return($count, @results);
2337 } # sub getwebsites
2338
2339 =item getwebbiblioitems
2340
2341   ($count, @results) = &getwebbiblioitems($biblionumber);
2342
2343 Given a book's biblionumber, looks up the web versions of the book
2344 (biblioitems with itemtype C<WEB>).
2345
2346 C<$count> is the number of items in C<@results>. C<@results> is an
2347 array of references-to-hash; the keys are the items from the
2348 C<biblioitems> table of the Koha database.
2349
2350 =cut
2351 #'
2352 sub getwebbiblioitems {
2353     my ($biblionumber) = @_;
2354     my $dbh   = C4::Context->dbh;
2355     my $query = "Select * from biblioitems where biblionumber = $biblionumber
2356 and itemtype = 'WEB'";
2357     my $sth   = $dbh->prepare($query);
2358     my $count = 0;
2359     my @results;
2360
2361     $sth->execute;
2362     while (my $data = $sth->fetchrow_hashref) {
2363         $data->{'url'} =~ s/^http:\/\///;
2364         $results[$count] = $data;
2365         $count++;
2366     } # while
2367
2368     $sth->finish;
2369     return($count, @results);
2370 } # sub getwebbiblioitems
2371
2372
2373 =item breedingsearch
2374
2375   ($count, @results) = &breedingsearch($title);
2376
2377 C<$count> is the number of items in C<@results>. C<@results> is an
2378 array of references-to-hash; the keys are the items from the
2379 C<marc_breeding> table of the Koha database.
2380
2381 =cut
2382
2383 sub breedingsearch {
2384         my ($title,$isbn) = @_;
2385         my $dbh   = C4::Context->dbh;
2386         my $count = 0;
2387         my $query;
2388         my $sth;
2389         my @results;
2390
2391         $query = "Select id,file,isbn,title,author from marc_breeding where ";
2392         if ($title) {
2393                 $query .= "title like \"$title%\"";
2394         }
2395         if ($title && $isbn) {
2396                 $query .= " and ";
2397         }
2398         if ($isbn) {
2399                 $query .= "isbn like \"$isbn%\"";
2400         }
2401         $sth   = $dbh->prepare($query);
2402         $sth->execute;
2403         while (my $data = $sth->fetchrow_hashref) {
2404                         $results[$count] = $data;
2405                         $count++;
2406         } # while
2407
2408         $sth->finish;
2409         return($count, @results);
2410 } # sub breedingsearch
2411
2412 =item isbnsearch
2413
2414   ($count, @results) = &isbnsearch($isbn,$title);
2415
2416 Given an isbn and/or a title, returns the biblios having it.
2417 Used in acqui.simple, isbnsearch.pl only
2418
2419 C<$count> is the number of items in C<@results>. C<@results> is an
2420 array of references-to-hash; the keys are the items from the
2421 C<biblioitems> table of the Koha database.
2422
2423 =cut
2424
2425 sub isbnsearch {
2426     my ($isbn,$title) = @_;
2427     my $dbh   = C4::Context->dbh;
2428     my $count = 0;
2429     my $query;
2430     my $sth;
2431     my @results;
2432
2433     $query = "Select distinct biblio.* from biblio, biblioitems where
2434                                 biblio.biblionumber = biblioitems.biblionumber";
2435         if ($isbn) {
2436                 $query .= " and isbn=".$dbh->quote($isbn);
2437         }
2438         if ($title) {
2439                 $query .= " and title like ".$dbh->quote($title."%");
2440         }
2441         warn $query;
2442     $sth   = $dbh->prepare($query);
2443
2444     $sth->execute;
2445     while (my $data = $sth->fetchrow_hashref) {
2446         $results[$count] = $data;
2447         $count++;
2448     } # while
2449
2450     $sth->finish;
2451     return($count, @results);
2452 } # sub isbnsearch
2453
2454 END { }       # module clean-up code here (global destructor)
2455
2456 1;
2457 __END__
2458
2459 =back
2460
2461 =head1 AUTHOR
2462
2463 Koha Developement team <info@koha.org>
2464
2465 =cut