Bug 8215: Followup FIX QA issues
[koha.git] / C4 / CourseReserves.pm
1 package C4::CourseReserves;
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation; either version 2 of the License, or (at your option) any later
8 # version.
9 #
10 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License along with
15 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
16 # Suite 330, Boston, MA  02111-1307 USA
17
18 use Modern::Perl;
19
20 require Exporter;
21
22 use C4::Context;
23 use C4::Items qw(GetItem ModItem);
24 use C4::Biblio qw(GetBiblioFromItemNumber);
25 use C4::Circulation qw(GetOpenIssue);
26
27 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG @FIELDS);
28
29 BEGIN {
30     @ISA    = qw(Exporter);
31     @EXPORT = qw(
32       &GetCourse
33       &ModCourse
34       &GetCourses
35       &DelCourse
36
37       &GetCourseInstructors
38       &ModCourseInstructors
39
40       &GetCourseItem
41       &ModCourseItem
42
43       &GetCourseReserve
44       &ModCourseReserve
45       &GetCourseReserves
46       &DelCourseReserve
47
48       &SearchCourses
49
50       &GetItemReservesInfo
51     );
52
53     $DEBUG = 0;
54     @FIELDS = ( 'itype', 'ccode', 'holdingbranch', 'location' );
55 }
56
57 =head1 NAME
58
59 C4::CourseReserves - Koha course reserves module
60
61 =head1 SYNOPSIS
62
63 use C4::CourseReserves;
64
65 =head1 DESCRIPTION
66
67 This module deals with course reserves.
68
69 =head1 FUNCTIONS
70
71 =head2 GetCourse
72
73     $course = GetCourse( $course_id );
74
75 =cut
76
77 sub GetCourse {
78     my ($course_id) = @_;
79     warn whoami() . "( $course_id )" if $DEBUG;
80
81     my $query = "SELECT * FROM courses WHERE course_id = ?";
82     my $dbh   = C4::Context->dbh;
83     my $sth   = $dbh->prepare($query);
84     $sth->execute($course_id);
85
86     my $course = $sth->fetchrow_hashref();
87
88     $query = "
89         SELECT b.* FROM course_instructors ci
90         LEFT JOIN borrowers b ON ( ci.borrowernumber = b.borrowernumber )
91         WHERE course_id =  ?
92     ";
93     $sth = $dbh->prepare($query);
94     $sth->execute($course_id);
95     $course->{'instructors'} = $sth->fetchall_arrayref( {} );
96
97     return $course;
98 }
99
100 =head2 ModCourse
101
102     ModCourse( [ course_id => $id ] [, course_name => $course_name ] [etc...] );
103
104 =cut
105
106 sub ModCourse {
107     my (%params) = @_;
108     warn identify_myself(%params) if $DEBUG;
109
110     my $dbh = C4::Context->dbh;
111
112     my $course_id;
113     if ( defined $params{'course_id'} ) {
114         $course_id = $params{'course_id'};
115         delete $params{'course_id'};
116     }
117
118     my @query_keys;
119     my @query_values;
120
121     my $query;
122
123     $query .= ($course_id) ? ' UPDATE ' : ' INSERT ';
124     $query .= ' courses SET ';
125
126     foreach my $key ( keys %params ) {
127         push( @query_keys,   "$key=?" );
128         push( @query_values, $params{$key} );
129     }
130     $query .= join( ',', @query_keys );
131
132     if ($course_id) {
133         $query .= " WHERE course_id = ?";
134         push( @query_values, $course_id );
135     }
136
137     $dbh->do( $query, undef, @query_values );
138
139     $course_id = $course_id
140       || $dbh->last_insert_id( undef, undef, 'courses', 'course_id' );
141
142     EnableOrDisableCourseItems(
143         course_id => $course_id,
144         enabled   => $params{'enabled'}
145     );
146
147     return $course_id;
148 }
149
150 =head2 GetCourses
151
152   @courses = GetCourses( [ fieldname => $value ] [, fieldname2 => $value2 ] [etc...] );
153
154 =cut
155
156 sub GetCourses {
157     my (%params) = @_;
158     warn identify_myself(%params) if $DEBUG;
159
160     my @query_keys;
161     my @query_values;
162
163     my $query = "
164         SELECT courses.*
165         FROM courses
166         LEFT JOIN course_reserves ON course_reserves.course_id = courses.course_id
167         LEFT JOIN course_items ON course_items.ci_id = course_reserves.ci_id
168     ";
169
170     if ( keys %params ) {
171
172         $query .= " WHERE ";
173
174         foreach my $key ( keys %params ) {
175             push( @query_keys,   " $key LIKE ? " );
176             push( @query_values, $params{$key} );
177         }
178
179         $query .= join( ' AND ', @query_keys );
180     }
181
182     $query .= " GROUP BY courses.course_id ";
183
184     my $dbh = C4::Context->dbh;
185     my $sth = $dbh->prepare($query);
186     $sth->execute(@query_values);
187
188     my $courses = $sth->fetchall_arrayref( {} );
189
190     foreach my $c (@$courses) {
191         $c->{'instructors'} = GetCourseInstructors( $c->{'course_id'} );
192     }
193
194     return $courses;
195 }
196
197 =head2 DelCourse
198
199   DelCourse( $course_id );
200
201 =cut
202
203 sub DelCourse {
204     my ($course_id) = @_;
205
206     my $course_reserves = GetCourseReserves( course_id => $course_id );
207
208     foreach my $res (@$course_reserves) {
209         DelCourseReserve( cr_id => $res->{'cr_id'} );
210     }
211
212     my $query = "
213         DELETE FROM course_instructors
214         WHERE course_id = ?
215     ";
216     C4::Context->dbh->do( $query, undef, $course_id );
217
218     $query = "
219         DELETE FROM courses
220         WHERE course_id = ?
221     ";
222     C4::Context->dbh->do( $query, undef, $course_id );
223 }
224
225 =head2 EnableOrDisableCourseItems
226
227   EnableOrDisableCourseItems( course_id => $course_id, enabled => $enabled );
228
229   For each item on reserve for this course,
230   if the course item has no active course reserves,
231   swap the fields for the item to make it 'normal'
232   again.
233
234   enabled => 'yes' to enable course items
235   enabled => 'no' to disable course items
236
237 =cut
238
239 sub EnableOrDisableCourseItems {
240     my (%params) = @_;
241     warn identify_myself(%params) if $DEBUG;
242
243     my $course_id = $params{'course_id'};
244     my $enabled   = $params{'enabled'} || 0;
245
246     my $lookfor = ( $enabled eq 'yes' ) ? 'no' : 'yes';
247
248     return unless ( $course_id && $enabled );
249     return unless ( $enabled eq 'yes' || $enabled eq 'no' );
250
251     my $course_reserves = GetCourseReserves( course_id => $course_id );
252
253     if ( $enabled eq 'yes' ) {
254         foreach my $course_reserve (@$course_reserves) {
255             if (
256                 CountCourseReservesForItem(
257                     ci_id   => $course_reserve->{'ci_id'},
258                     enabled => 'yes'
259                 )
260               )
261             {
262                 EnableOrDisableCourseItem(
263                     ci_id   => $course_reserve->{'ci_id'},
264                     enabled => 'yes',
265                 );
266             }
267         }
268     }
269     if ( $enabled eq 'no' ) {
270         foreach my $course_reserve (@$course_reserves) {
271             unless (
272                 CountCourseReservesForItem(
273                     ci_id   => $course_reserve->{'ci_id'},
274                     enabled => 'yes'
275                 )
276               )
277             {
278                 EnableOrDisableCourseItem(
279                     ci_id   => $course_reserve->{'ci_id'},
280                     enabled => 'no',
281                 );
282             }
283         }
284     }
285 }
286
287 =head2 EnableOrDisableCourseItem
288
289     EnableOrDisableCourseItem( ci_id => $ci_id, enabled => $enabled );
290
291     enabled => 'yes' to enable course items
292     enabled => 'no' to disable course items
293
294 =cut
295
296 sub EnableOrDisableCourseItem {
297     my (%params) = @_;
298     warn identify_myself(%params) if $DEBUG;
299
300     my $ci_id   = $params{'ci_id'};
301     my $enabled = $params{'enabled'};
302
303     return unless ( $ci_id && $enabled );
304     return unless ( $enabled eq 'yes' || $enabled eq 'no' );
305
306     my $course_item = GetCourseItem( ci_id => $ci_id );
307
308     ## We don't want to 'enable' an already enabled item,
309     ## or disable and already disabled item,
310     ## as that would cause the fields to swap
311     if ( $course_item->{'enabled'} ne $enabled ) {
312         _SwapAllFields($ci_id);
313
314         my $query = "
315             UPDATE course_items
316             SET enabled = ?
317             WHERE ci_id = ?
318         ";
319
320         C4::Context->dbh->do( $query, undef, $enabled, $ci_id );
321
322     }
323
324 }
325
326 =head2 GetCourseInstructors
327
328     @$borrowers = GetCourseInstructors( $course_id );
329
330 =cut
331
332 sub GetCourseInstructors {
333     my ($course_id) = @_;
334     warn "C4::CourseReserves::GetCourseInstructors( $course_id )"
335       if $DEBUG;
336
337     my $query = "
338         SELECT * FROM borrowers
339         RIGHT JOIN course_instructors ON ( course_instructors.borrowernumber = borrowers.borrowernumber )
340         WHERE course_instructors.course_id = ?
341     ";
342
343     my $dbh = C4::Context->dbh;
344     my $sth = $dbh->prepare($query);
345     $sth->execute($course_id);
346
347     return $sth->fetchall_arrayref( {} );
348 }
349
350 =head2 ModCourseInstructors
351
352     ModCourseInstructors( mode => $mode, course_id => $course_id, [ cardnumbers => $cardnumbers ] OR [ borrowernumbers => $borrowernumbers  );
353
354     $mode can be 'replace', 'add', or 'delete'
355
356     $cardnumbers and $borrowernumbers are both references to arrays
357
358     Use either cardnumbers or borrowernumber, but not both.
359
360 =cut
361
362 sub ModCourseInstructors {
363     my (%params) = @_;
364     warn identify_myself(%params) if $DEBUG;
365
366     my $course_id       = $params{'course_id'};
367     my $mode            = $params{'mode'};
368     my $cardnumbers     = $params{'cardnumbers'};
369     my $borrowernumbers = $params{'borrowernumbers'};
370
371     return unless ($course_id);
372     return
373       unless ( $mode eq 'replace'
374         || $mode eq 'add'
375         || $mode eq 'delete' );
376     return unless ( $cardnumbers || $borrowernumbers );
377     return if ( $cardnumbers && $borrowernumbers );
378
379     my (@cardnumbers, @borrowernumbers);
380     @cardnumbers = @$cardnumbers if ( ref($cardnumbers) eq 'ARRAY' );
381     @borrowernumbers = @$borrowernumbers
382       if ( ref($borrowernumbers) eq 'ARRAY' );
383
384     my $field  = (@cardnumbers) ? 'cardnumber' : 'borrowernumber';
385     my @fields = (@cardnumbers) ? @cardnumbers : @borrowernumbers;
386     my $placeholders = join( ',', ('?') x scalar @fields );
387
388     my $dbh = C4::Context->dbh;
389
390     $dbh->do( "DELETE FROM course_instructors WHERE course_id = ?",
391         undef, $course_id )
392       if ( $mode eq 'replace' );
393
394     my $query;
395
396     if ( $mode eq 'add' || $mode eq 'replace' ) {
397         $query = "
398             INSERT INTO course_instructors ( course_id, borrowernumber )
399             SELECT ?, borrowernumber
400             FROM borrowers
401             WHERE $field IN ( $placeholders )
402         ";
403     }
404     else {
405         $query = "
406             DELETE FROM course_instructors
407             WHERE course_id = ?
408             AND borrowernumber IN (
409                 SELECT borrowernumber FROM borrowers WHERE $field IN ( $placeholders )
410             )
411         ";
412     }
413
414     my $sth = $dbh->prepare($query);
415
416     $sth->execute( $course_id, @fields ) if (@fields);
417 }
418
419 =head2 GetCourseItem {
420
421   $course_item = GetCourseItem( itemnumber => $itemnumber [, ci_id => $ci_id );
422
423 =cut
424
425 sub GetCourseItem {
426     my (%params) = @_;
427     warn identify_myself(%params) if $DEBUG;
428
429     my $ci_id      = $params{'ci_id'};
430     my $itemnumber = $params{'itemnumber'};
431
432     return unless ( $itemnumber || $ci_id );
433
434     my $field = ($itemnumber) ? 'itemnumber' : 'ci_id';
435     my $value = ($itemnumber) ? $itemnumber  : $ci_id;
436
437     my $query = "SELECT * FROM course_items WHERE $field = ?";
438     my $dbh   = C4::Context->dbh;
439     my $sth   = $dbh->prepare($query);
440     $sth->execute($value);
441
442     my $course_item = $sth->fetchrow_hashref();
443
444     if ($course_item) {
445         $query = "SELECT * FROM course_reserves WHERE ci_id = ?";
446         $sth   = $dbh->prepare($query);
447         $sth->execute( $course_item->{'ci_id'} );
448         my $course_reserves = $sth->fetchall_arrayref( {} );
449
450         $course_item->{'course_reserves'} = $course_reserves
451           if ($course_reserves);
452     }
453     return $course_item;
454 }
455
456 =head2 ModCourseItem {
457
458   ModCourseItem( %params );
459
460   Creates or modifies an existing course item.
461
462 =cut
463
464 sub ModCourseItem {
465     my (%params) = @_;
466     warn identify_myself(%params) if $DEBUG;
467
468     my $itemnumber    = $params{'itemnumber'};
469     my $itype         = $params{'itype'};
470     my $ccode         = $params{'ccode'};
471     my $holdingbranch = $params{'holdingbranch'};
472     my $location      = $params{'location'};
473
474     return unless ($itemnumber);
475
476     my $course_item = GetCourseItem( itemnumber => $itemnumber );
477
478     my $ci_id;
479
480     if ($course_item) {
481         $ci_id = $course_item->{'ci_id'};
482
483         _UpdateCourseItem(
484             ci_id       => $ci_id,
485             course_item => $course_item,
486             %params
487         );
488     }
489     else {
490         $ci_id = _AddCourseItem(%params);
491     }
492
493     return $ci_id;
494
495 }
496
497 =head2 _AddCourseItem
498
499     my $ci_id = _AddCourseItem( %params );
500
501 =cut
502
503 sub _AddCourseItem {
504     my (%params) = @_;
505     warn identify_myself(%params) if $DEBUG;
506
507     my ( @fields, @values );
508
509     push( @fields, 'itemnumber = ?' );
510     push( @values, $params{'itemnumber'} );
511
512     foreach (@FIELDS) {
513         if ( $params{$_} ) {
514             push( @fields, "$_ = ?" );
515             push( @values, $params{$_} );
516         }
517     }
518
519     my $query = "INSERT INTO course_items SET " . join( ',', @fields );
520     my $dbh = C4::Context->dbh;
521     $dbh->do( $query, undef, @values );
522
523     my $ci_id = $dbh->last_insert_id( undef, undef, 'course_items', 'ci_id' );
524
525     return $ci_id;
526 }
527
528 =head2 _UpdateCourseItem
529
530   _UpdateCourseItem( %params );
531
532 =cut
533
534 sub _UpdateCourseItem {
535     my (%params) = @_;
536     warn identify_myself(%params) if $DEBUG;
537
538     my $ci_id         = $params{'ci_id'};
539     my $course_item   = $params{'course_item'};
540     my $itype         = $params{'itype'};
541     my $ccode         = $params{'ccode'};
542     my $holdingbranch = $params{'holdingbranch'};
543     my $location      = $params{'location'};
544
545     return unless ( $ci_id || $course_item );
546
547     $course_item = GetCourseItem( ci_id => $ci_id )
548       unless ($course_item);
549     $ci_id = $course_item->{'ci_id'} unless ($ci_id);
550
551     ## Revert fields that had an 'original' value, but now don't
552     ## Update the item fields to the stored values from course_items
553     ## and then set those fields in course_items to NULL
554     my @fields_to_revert;
555     foreach (@FIELDS) {
556         if ( !$params{$_} && $course_item->{$_} ) {
557             push( @fields_to_revert, $_ );
558         }
559     }
560     _RevertFields(
561         ci_id       => $ci_id,
562         fields      => \@fields_to_revert,
563         course_item => $course_item
564     ) if (@fields_to_revert);
565
566     ## Update fields that still have an original value, but it has changed
567     ## This necessitates only changing the current item values, as we still
568     ## have the original values stored in course_items
569     my %mod_params;
570     foreach (@FIELDS) {
571         if (   $params{$_}
572             && $course_item->{$_}
573             && $params{$_} ne $course_item->{$_} )
574         {
575             $mod_params{$_} = $params{$_};
576         }
577     }
578     ModItem( \%mod_params, undef, $course_item->{'itemnumber'} );
579
580     ## Update fields that didn't have an original value, but now do
581     ## We must save the original value in course_items, and also
582     ## update the item fields to the new value
583     my $item = GetItem( $course_item->{'itemnumber'} );
584     my %mod_params_new;
585     my %mod_params_old;
586     foreach (@FIELDS) {
587         if ( $params{$_} && !$course_item->{$_} ) {
588             $mod_params_new{$_} = $params{$_};
589             $mod_params_old{$_} = $item->{$_};
590         }
591     }
592     _ModStoredFields( 'ci_id' => $params{'ci_id'}, %mod_params_old );
593     ModItem( \%mod_params_new, undef, $course_item->{'itemnumber'} );
594
595 }
596
597 =head2 _ModStoredFields
598
599     _ModStoredFields( %params );
600
601     Updates the values for the 'original' fields in course_items
602     for a given ci_id
603
604 =cut
605
606 sub _ModStoredFields {
607     my (%params) = @_;
608     warn identify_myself(%params) if $DEBUG;
609
610     return unless ( $params{'ci_id'} );
611
612     my ( @fields_to_update, @values_to_update );
613
614     foreach (@FIELDS) {
615         if ( $params{$_} ) {
616             push( @fields_to_update, $_ );
617             push( @values_to_update, $params{$_} );
618         }
619     }
620
621     my $query =
622         "UPDATE course_items SET "
623       . join( ',', map { "$_=?" } @fields_to_update )
624       . " WHERE ci_id = ?";
625
626     C4::Context->dbh->do( $query, undef, @values_to_update, $params{'ci_id'} )
627       if (@values_to_update);
628
629 }
630
631 =head2 _RevertFields
632
633     _RevertFields( ci_id => $ci_id, fields => \@fields_to_revert );
634
635 =cut
636
637 sub _RevertFields {
638     my (%params) = @_;
639     warn identify_myself(%params) if $DEBUG;
640
641     my $ci_id       = $params{'ci_id'};
642     my $course_item = $params{'course_item'};
643     my $fields      = $params{'fields'};
644     my @fields      = @$fields;
645
646     return unless ($ci_id);
647
648     $course_item = GetCourseItem( ci_id => $params{'ci_id'} )
649       unless ($course_item);
650
651     my $mod_item_params;
652     my @fields_to_null;
653     foreach my $field (@fields) {
654         foreach (@FIELDS) {
655             if ( $field eq $_ && $course_item->{$_} ) {
656                 $mod_item_params->{$_} = $course_item->{$_};
657                 push( @fields_to_null, $_ );
658             }
659         }
660     }
661     ModItem( $mod_item_params, undef, $course_item->{'itemnumber'} );
662
663     my $query =
664         "UPDATE course_items SET "
665       . join( ',', map { "$_=NULL" } @fields_to_null )
666       . " WHERE ci_id = ?";
667
668     C4::Context->dbh->do( $query, undef, $ci_id ) if (@fields_to_null);
669 }
670
671 =head2 _SwapAllFields
672
673     _SwapAllFields( $ci_id );
674
675 =cut
676
677 sub _SwapAllFields {
678     my ($ci_id) = @_;
679     warn "C4::CourseReserves::_SwapFields( $ci_id )" if $DEBUG;
680
681     my $course_item = GetCourseItem( ci_id => $ci_id );
682     my $item = GetItem( $course_item->{'itemnumber'} );
683
684     my %course_item_fields;
685     my %item_fields;
686     foreach (@FIELDS) {
687         if ( $course_item->{$_} ) {
688             $course_item_fields{$_} = $course_item->{$_};
689             $item_fields{$_}        = $item->{$_};
690         }
691     }
692
693     ModItem( \%course_item_fields, undef, $course_item->{'itemnumber'} );
694     _ModStoredFields( %item_fields, ci_id => $ci_id );
695 }
696
697 =head2 GetCourseItems {
698
699   $course_items = GetCourseItems(
700       [course_id => $course_id]
701       [, itemnumber => $itemnumber ]
702   );
703
704 =cut
705
706 sub GetCourseItems {
707     my (%params) = @_;
708     warn identify_myself(%params) if $DEBUG;
709
710     my $course_id  = $params{'course_id'};
711     my $itemnumber = $params{'itemnumber'};
712
713     return unless ($course_id);
714
715     my @query_keys;
716     my @query_values;
717
718     my $query = "SELECT * FROM course_items";
719
720     if ( keys %params ) {
721
722         $query .= " WHERE ";
723
724         foreach my $key ( keys %params ) {
725             push( @query_keys,   " $key LIKE ? " );
726             push( @query_values, $params{$key} );
727         }
728
729         $query .= join( ' AND ', @query_keys );
730     }
731
732     my $dbh = C4::Context->dbh;
733     my $sth = $dbh->prepare($query);
734     $sth->execute(@query_values);
735
736     return $sth->fetchall_arrayref( {} );
737 }
738
739 =head2 DelCourseItem {
740
741   DelCourseItem( ci_id => $cr_id );
742
743 =cut
744
745 sub DelCourseItem {
746     my (%params) = @_;
747     warn identify_myself(%params) if $DEBUG;
748
749     my $ci_id = $params{'ci_id'};
750
751     return unless ($ci_id);
752
753     _RevertFields( ci_id => $ci_id, fields => \@FIELDS );
754
755     my $query = "
756         DELETE FROM course_items
757         WHERE ci_id = ?
758     ";
759     C4::Context->dbh->do( $query, undef, $ci_id );
760 }
761
762 =head2 GetCourseReserve {
763
764   $course_item = GetCourseReserve( %params );
765
766 =cut
767
768 sub GetCourseReserve {
769     my (%params) = @_;
770     warn identify_myself(%params) if $DEBUG;
771
772     my $cr_id     = $params{'cr_id'};
773     my $course_id = $params{'course_id'};
774     my $ci_id     = $params{'ci_id'};
775
776     return unless ( $cr_id || ( $course_id && $ci_id ) );
777
778     my $dbh = C4::Context->dbh;
779     my $sth;
780
781     if ($cr_id) {
782         my $query = "
783             SELECT * FROM course_reserves
784             WHERE cr_id = ?
785         ";
786         $sth = $dbh->prepare($query);
787         $sth->execute($cr_id);
788     }
789     else {
790         my $query = "
791             SELECT * FROM course_reserves
792             WHERE course_id = ? AND ci_id = ?
793         ";
794         $sth = $dbh->prepare($query);
795         $sth->execute( $course_id, $ci_id );
796     }
797
798     my $course_reserve = $sth->fetchrow_hashref();
799     return $course_reserve;
800 }
801
802 =head2 ModCourseReserve
803
804     $id = ModCourseReserve( %params );
805
806 =cut
807
808 sub ModCourseReserve {
809     my (%params) = @_;
810     warn identify_myself(%params) if $DEBUG;
811
812     my $course_id   = $params{'course_id'};
813     my $ci_id       = $params{'ci_id'};
814     my $staff_note  = $params{'staff_note'};
815     my $public_note = $params{'public_note'};
816
817     return unless ( $course_id && $ci_id );
818
819     my $course_reserve =
820       GetCourseReserve( course_id => $course_id, ci_id => $ci_id );
821     my $cr_id;
822
823     my $dbh = C4::Context->dbh;
824
825     if ($course_reserve) {
826         $cr_id = $course_reserve->{'cr_id'};
827
828         my $query = "
829             UPDATE course_reserves
830             SET staff_note = ?, public_note = ?
831             WHERE cr_id = ?
832         ";
833         $dbh->do( $query, undef, $staff_note, $public_note, $cr_id );
834     }
835     else {
836         my $query = "
837             INSERT INTO course_reserves SET
838             course_id = ?,
839             ci_id = ?,
840             staff_note = ?,
841             public_note = ?
842         ";
843         $dbh->do( $query, undef, $course_id, $ci_id, $staff_note,
844             $public_note );
845         $cr_id =
846           $dbh->last_insert_id( undef, undef, 'course_reserves', 'cr_id' );
847     }
848
849     my $course = GetCourse($course_id);
850     EnableOrDisableCourseItem(
851         ci_id   => $params{'ci_id'},
852         enabled => $course->{'enabled'}
853     );
854
855     return $cr_id;
856 }
857
858 =head2 GetCourseReserves {
859
860   $course_reserves = GetCourseReserves( %params );
861
862   Required:
863       course_id OR ci_id
864   Optional:
865       include_items   => 1,
866       include_count   => 1,
867       include_courses => 1,
868
869 =cut
870
871 sub GetCourseReserves {
872     my (%params) = @_;
873     warn identify_myself(%params) if $DEBUG;
874
875     my $course_id     = $params{'course_id'};
876     my $ci_id         = $params{'ci_id'};
877     my $include_items = $params{'include_items'};
878     my $include_count = $params{'include_count'};
879     my $include_courses = $params{'include_courses'};
880
881     return unless ( $course_id || $ci_id );
882
883     my $field = ($course_id) ? 'course_id' : 'ci_id';
884     my $value = ($course_id) ? $course_id  : $ci_id;
885
886     my $query = "
887         SELECT cr.*, ci.itemnumber
888         FROM course_reserves cr, course_items ci
889         WHERE cr.$field = ?
890         AND cr.ci_id = ci.ci_id
891     ";
892     my $dbh = C4::Context->dbh;
893     my $sth = $dbh->prepare($query);
894     $sth->execute($value);
895
896     my $course_reserves = $sth->fetchall_arrayref( {} );
897
898     if ($include_items) {
899         foreach my $cr (@$course_reserves) {
900             $cr->{'course_item'} = GetCourseItem( ci_id => $cr->{'ci_id'} );
901             $cr->{'item'} = GetBiblioFromItemNumber( $cr->{'itemnumber'} );
902             $cr->{'issue'} = GetOpenIssue( $cr->{'itemnumber'} );
903         }
904     }
905
906     if ($include_count) {
907         foreach my $cr (@$course_reserves) {
908             $cr->{'reserves_count'} =
909               CountCourseReservesForItem( ci_id => $cr->{'ci_id'} );
910         }
911     }
912
913     if ($include_courses) {
914         foreach my $cr (@$course_reserves) {
915             $cr->{'courses'} =
916               GetCourses( itemnumber => $cr->{'itemnumber'} );
917         }
918     }
919
920     return $course_reserves;
921 }
922
923 =head2 DelCourseReserve {
924
925   DelCourseReserve( cr_id => $cr_id );
926
927 =cut
928
929 sub DelCourseReserve {
930     my (%params) = @_;
931     warn identify_myself(%params) if $DEBUG;
932
933     my $cr_id = $params{'cr_id'};
934
935     return unless ($cr_id);
936
937     my $dbh = C4::Context->dbh;
938
939     my $course_reserve = GetCourseReserve( cr_id => $cr_id );
940
941     my $query = "
942         DELETE FROM course_reserves
943         WHERE cr_id = ?
944     ";
945     $dbh->do( $query, undef, $cr_id );
946
947     ## If there are no other course reserves for this item
948     ## delete the course_item as well
949     unless ( CountCourseReservesForItem( ci_id => $course_reserve->{'ci_id'} ) )
950     {
951         DelCourseItem( ci_id => $course_reserve->{'ci_id'} );
952     }
953
954 }
955
956 =head2 GetReservesInfo
957
958     my $arrayref = GetItemReservesInfo( itemnumber => $itemnumber );
959
960     For a given item, returns an arrayref of reserves hashrefs,
961     with a course hashref under the key 'course'
962
963 =cut
964
965 sub GetItemReservesInfo {
966     my (%params) = @_;
967     warn identify_myself(%params) if $DEBUG;
968
969     my $itemnumber = $params{'itemnumber'};
970
971     return unless ($itemnumber);
972
973     my $course_item = GetCourseItem( itemnumber => $itemnumber );
974
975     return unless ( keys %$course_item );
976
977     my $course_reserves = GetCourseReserves( ci_id => $course_item->{'ci_id'} );
978
979     foreach my $cr (@$course_reserves) {
980         $cr->{'course'} = GetCourse( $cr->{'course_id'} );
981     }
982
983     return $course_reserves;
984 }
985
986 =head2 CountCourseReservesForItem
987
988     $bool = CountCourseReservesForItem( %params );
989
990     ci_id - course_item id
991     OR
992     itemnumber - course_item itemnumber
993
994     enabled = 'yes' or 'no'
995     Optional, if not supplied, counts reserves
996     for both enabled and disabled courses
997
998 =cut
999
1000 sub CountCourseReservesForItem {
1001     my (%params) = @_;
1002     warn identify_myself(%params) if $DEBUG;
1003
1004     my $ci_id      = $params{'ci_id'};
1005     my $itemnumber = $params{'itemnumber'};
1006     my $enabled    = $params{'enabled'};
1007
1008     return unless ( $ci_id || $itemnumber );
1009
1010     my $course_item =
1011       GetCourseItem( ci_id => $ci_id, itemnumber => $itemnumber );
1012
1013     my @params = ( $course_item->{'ci_id'} );
1014     push( @params, $enabled ) if ($enabled);
1015
1016     my $query = "
1017         SELECT COUNT(*) AS count
1018         FROM course_reserves cr
1019         LEFT JOIN courses c ON ( c.course_id = cr.course_id )
1020         WHERE ci_id = ?
1021     ";
1022     $query .= "AND c.enabled = ?" if ($enabled);
1023
1024     my $dbh = C4::Context->dbh;
1025     my $sth = $dbh->prepare($query);
1026     $sth->execute(@params);
1027
1028     my $row = $sth->fetchrow_hashref();
1029
1030     return $row->{'count'};
1031 }
1032
1033 =head2 SearchCourses
1034
1035     my $courses = SearchCourses( term => $search_term, enabled => 'yes' );
1036
1037 =cut
1038
1039 sub SearchCourses {
1040     my (%params) = @_;
1041     warn identify_myself(%params) if $DEBUG;
1042
1043     my $term = $params{'term'};
1044
1045     my $enabled = $params{'enabled'} || '%';
1046
1047     my @params;
1048     my $query = "SELECT c.* FROM courses c";
1049
1050     $query .= "
1051         LEFT JOIN course_instructors ci
1052             ON ( c.course_id = ci.course_id )
1053         LEFT JOIN borrowers b
1054             ON ( ci.borrowernumber = b.borrowernumber )
1055         LEFT JOIN authorised_values av
1056             ON ( c.department = av.authorised_value )
1057         WHERE
1058             ( av.category = 'DEPARTMENT' OR av.category = 'TERM' )
1059             AND
1060             (
1061                 department LIKE ? OR
1062                 course_number LIKE ? OR
1063                 section LIKE ? OR
1064                 course_name LIKE ? OR
1065                 term LIKE ? OR
1066                 public_note LIKE ? OR
1067                 CONCAT(surname,' ',firstname) LIKE ? OR
1068                 CONCAT(firstname,' ',surname) LIKE ? OR
1069                 lib LIKE ? OR
1070                 lib_opac LIKE ?
1071            )
1072            AND
1073            c.enabled LIKE ?
1074         GROUP BY c.course_id
1075     ";
1076
1077     $term   = "%$term%";
1078     @params = ($term) x 10;
1079
1080     $query .= " ORDER BY department, course_number, section, term, course_name ";
1081
1082     my $dbh = C4::Context->dbh;
1083     my $sth = $dbh->prepare($query);
1084
1085     $sth->execute(@params, $enabled);
1086
1087     my $courses = $sth->fetchall_arrayref( {} );
1088
1089     foreach my $c (@$courses) {
1090         $c->{'instructors'} = GetCourseInstructors( $c->{'course_id'} );
1091     }
1092
1093     return $courses;
1094 }
1095
1096 sub whoami  { ( caller(1) )[3] }
1097 sub whowasi { ( caller(2) )[3] }
1098
1099 sub stringify_params {
1100     my (%params) = @_;
1101
1102     my $string = "\n";
1103
1104     foreach my $key ( keys %params ) {
1105         $string .= "    $key => " . $params{$key} . "\n";
1106     }
1107
1108     return "( $string )";
1109 }
1110
1111 sub identify_myself {
1112     my (%params) = @_;
1113
1114     return whowasi() . stringify_params(%params);
1115 }
1116
1117 1;
1118
1119 =head1 AUTHOR
1120
1121 Kyle M Hall <kyle@bywatersolutions.com>
1122
1123 =cut