Bug 8215 - Course Reserves
[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 = @$cardnumbers if ( ref($cardnumbers) eq 'ARRAY' );
380     my @borrowernumbers = @$borrowernumbers
381       if ( ref($borrowernumbers) eq 'ARRAY' );
382
383     my $field  = (@cardnumbers) ? 'cardnumber' : 'borrowernumber';
384     my @fields = (@cardnumbers) ? @cardnumbers : @borrowernumbers;
385     my $placeholders = join( ',', ('?') x scalar @fields );
386
387     my $dbh = C4::Context->dbh;
388
389     $dbh->do( "DELETE FROM course_instructors WHERE course_id = ?",
390         undef, $course_id )
391       if ( $mode eq 'replace' );
392
393     my $query;
394
395     if ( $mode eq 'add' || $mode eq 'replace' ) {
396         $query = "
397             INSERT INTO course_instructors ( course_id, borrowernumber )
398             SELECT ?, borrowernumber
399             FROM borrowers
400             WHERE $field IN ( $placeholders )
401         ";
402     }
403     else {
404         $query = "
405             DELETE FROM course_instructors
406             WHERE course_id = ?
407             AND borrowernumber IN (
408                 SELECT borrowernumber FROM borrowers WHERE $field IN ( $placeholders )
409             )
410         ";
411     }
412
413     my $sth = $dbh->prepare($query);
414
415     $sth->execute( $course_id, @fields ) if (@fields);
416 }
417
418 =head2 GetCourseItem {
419
420   $course_item = GetCourseItem( itemnumber => $itemnumber [, ci_id => $ci_id );
421
422 =cut
423
424 sub GetCourseItem {
425     my (%params) = @_;
426     warn identify_myself(%params) if $DEBUG;
427
428     my $ci_id      = $params{'ci_id'};
429     my $itemnumber = $params{'itemnumber'};
430
431     return unless ( $itemnumber || $ci_id );
432
433     my $field = ($itemnumber) ? 'itemnumber' : 'ci_id';
434     my $value = ($itemnumber) ? $itemnumber  : $ci_id;
435
436     my $query = "SELECT * FROM course_items WHERE $field = ?";
437     my $dbh   = C4::Context->dbh;
438     my $sth   = $dbh->prepare($query);
439     $sth->execute($value);
440
441     my $course_item = $sth->fetchrow_hashref();
442
443     if ($course_item) {
444         $query = "SELECT * FROM course_reserves WHERE ci_id = ?";
445         $sth   = $dbh->prepare($query);
446         $sth->execute( $course_item->{'ci_id'} );
447         my $course_reserves = $sth->fetchall_arrayref( {} );
448
449         $course_item->{'course_reserves'} = $course_reserves
450           if ($course_reserves);
451     }
452     return $course_item;
453 }
454
455 =head2 ModCourseItem {
456
457   ModCourseItem( %params );
458
459   Creates or modifies an existing course item.
460
461 =cut
462
463 sub ModCourseItem {
464     my (%params) = @_;
465     warn identify_myself(%params) if $DEBUG;
466
467     my $itemnumber    = $params{'itemnumber'};
468     my $itype         = $params{'itype'};
469     my $ccode         = $params{'ccode'};
470     my $holdingbranch = $params{'holdingbranch'};
471     my $location      = $params{'location'};
472
473     return unless ($itemnumber);
474
475     my $course_item = GetCourseItem( itemnumber => $itemnumber );
476
477     my $ci_id;
478
479     if ($course_item) {
480         $ci_id = $course_item->{'ci_id'};
481
482         _UpdateCourseItem(
483             ci_id       => $ci_id,
484             course_item => $course_item,
485             %params
486         );
487     }
488     else {
489         $ci_id = _AddCourseItem(%params);
490     }
491
492     return $ci_id;
493
494 }
495
496 =head2 _AddCourseItem
497
498     my $ci_id = _AddCourseItem( %params );
499
500 =cut
501
502 sub _AddCourseItem {
503     my (%params) = @_;
504     warn identify_myself(%params) if $DEBUG;
505
506     my ( @fields, @values );
507
508     push( @fields, 'itemnumber = ?' );
509     push( @values, $params{'itemnumber'} );
510
511     foreach (@FIELDS) {
512         if ( $params{$_} ) {
513             push( @fields, "$_ = ?" );
514             push( @values, $params{$_} );
515         }
516     }
517
518     my $query = "INSERT INTO course_items SET " . join( ',', @fields );
519     my $dbh = C4::Context->dbh;
520     $dbh->do( $query, undef, @values );
521
522     my $ci_id = $dbh->last_insert_id( undef, undef, 'course_items', 'ci_id' );
523
524     return $ci_id;
525 }
526
527 =head2 _UpdateCourseItem
528
529   _UpdateCourseItem( %params );
530
531 =cut
532
533 sub _UpdateCourseItem {
534     my (%params) = @_;
535     warn identify_myself(%params) if $DEBUG;
536
537     my $ci_id         = $params{'ci_id'};
538     my $course_item   = $params{'course_item'};
539     my $itype         = $params{'itype'};
540     my $ccode         = $params{'ccode'};
541     my $holdingbranch = $params{'holdingbranch'};
542     my $location      = $params{'location'};
543
544     return unless ( $ci_id || $course_item );
545
546     $course_item = GetCourseItem( ci_id => $ci_id )
547       unless ($course_item);
548     $ci_id = $course_item->{'ci_id'} unless ($ci_id);
549
550     ## Revert fields that had an 'original' value, but now don't
551     ## Update the item fields to the stored values from course_items
552     ## and then set those fields in course_items to NULL
553     my @fields_to_revert;
554     foreach (@FIELDS) {
555         if ( !$params{$_} && $course_item->{$_} ) {
556             push( @fields_to_revert, $_ );
557         }
558     }
559     _RevertFields(
560         ci_id       => $ci_id,
561         fields      => \@fields_to_revert,
562         course_item => $course_item
563     ) if (@fields_to_revert);
564
565     ## Update fields that still have an original value, but it has changed
566     ## This necessitates only changing the current item values, as we still
567     ## have the original values stored in course_items
568     my %mod_params;
569     foreach (@FIELDS) {
570         if (   $params{$_}
571             && $course_item->{$_}
572             && $params{$_} ne $course_item->{$_} )
573         {
574             $mod_params{$_} = $params{$_};
575         }
576     }
577     ModItem( \%mod_params, undef, $course_item->{'itemnumber'} );
578
579     ## Update fields that didn't have an original value, but now do
580     ## We must save the original value in course_items, and also
581     ## update the item fields to the new value
582     my $item = GetItem( $course_item->{'itemnumber'} );
583     my %mod_params_new;
584     my %mod_params_old;
585     foreach (@FIELDS) {
586         if ( $params{$_} && !$course_item->{$_} ) {
587             $mod_params_new{$_} = $params{$_};
588             $mod_params_old{$_} = $item->{$_};
589         }
590     }
591     _ModStoredFields( 'ci_id' => $params{'ci_id'}, %mod_params_old );
592     ModItem( \%mod_params_new, undef, $course_item->{'itemnumber'} );
593
594 }
595
596 =head2 _ModStoredFields
597
598     _ModStoredFields( %params );
599
600     Updates the values for the 'original' fields in course_items
601     for a given ci_id
602
603 =cut
604
605 sub _ModStoredFields {
606     my (%params) = @_;
607     warn identify_myself(%params) if $DEBUG;
608
609     return unless ( $params{'ci_id'} );
610
611     my ( @fields_to_update, @values_to_update );
612
613     foreach (@FIELDS) {
614         if ( $params{$_} ) {
615             push( @fields_to_update, $_ );
616             push( @values_to_update, $params{$_} );
617         }
618     }
619
620     my $query =
621         "UPDATE course_items SET "
622       . join( ',', map { "$_=?" } @fields_to_update )
623       . " WHERE ci_id = ?";
624
625     C4::Context->dbh->do( $query, undef, @values_to_update, $params{'ci_id'} )
626       if (@values_to_update);
627
628 }
629
630 =head2 _RevertFields
631
632     _RevertFields( ci_id => $ci_id, fields => \@fields_to_revert );
633
634 =cut
635
636 sub _RevertFields {
637     my (%params) = @_;
638     warn identify_myself(%params) if $DEBUG;
639
640     my $ci_id       = $params{'ci_id'};
641     my $course_item = $params{'course_item'};
642     my $fields      = $params{'fields'};
643     my @fields      = @$fields;
644
645     return unless ($ci_id);
646
647     $course_item = GetCourseItem( ci_id => $params{'ci_id'} )
648       unless ($course_item);
649
650     my $mod_item_params;
651     my @fields_to_null;
652     foreach my $field (@fields) {
653         foreach (@FIELDS) {
654             if ( $field eq $_ && $course_item->{$_} ) {
655                 $mod_item_params->{$_} = $course_item->{$_};
656                 push( @fields_to_null, $_ );
657             }
658         }
659     }
660     ModItem( $mod_item_params, undef, $course_item->{'itemnumber'} );
661
662     my $query =
663         "UPDATE course_items SET "
664       . join( ',', map { "$_=NULL" } @fields_to_null )
665       . " WHERE ci_id = ?";
666
667     C4::Context->dbh->do( $query, undef, $ci_id ) if (@fields_to_null);
668 }
669
670 =head2 _SwapAllFields
671
672     _SwapAllFields( $ci_id );
673
674 =cut
675
676 sub _SwapAllFields {
677     my ($ci_id) = @_;
678     warn "C4::CourseReserves::_SwapFields( $ci_id )" if $DEBUG;
679
680     my $course_item = GetCourseItem( ci_id => $ci_id );
681     my $item = GetItem( $course_item->{'itemnumber'} );
682
683     my %course_item_fields;
684     my %item_fields;
685     foreach (@FIELDS) {
686         if ( $course_item->{$_} ) {
687             $course_item_fields{$_} = $course_item->{$_};
688             $item_fields{$_}        = $item->{$_};
689         }
690     }
691
692     ModItem( \%course_item_fields, undef, $course_item->{'itemnumber'} );
693     _ModStoredFields( %item_fields, ci_id => $ci_id );
694 }
695
696 =head2 GetCourseItems {
697
698   $course_items = GetCourseItems(
699       [course_id => $course_id]
700       [, itemnumber => $itemnumber ]
701   );
702
703 =cut
704
705 sub GetCourseItems {
706     my (%params) = @_;
707     warn identify_myself(%params) if $DEBUG;
708
709     my $course_id  = $params{'course_id'};
710     my $itemnumber = $params{'itemnumber'};
711
712     return unless ($course_id);
713
714     my @query_keys;
715     my @query_values;
716
717     my $query = "SELECT * FROM course_items";
718
719     if ( keys %params ) {
720
721         $query .= " WHERE ";
722
723         foreach my $key ( keys %params ) {
724             push( @query_keys,   " $key LIKE ? " );
725             push( @query_values, $params{$key} );
726         }
727
728         $query .= join( ' AND ', @query_keys );
729     }
730
731     my $dbh = C4::Context->dbh;
732     my $sth = $dbh->prepare($query);
733     $sth->execute(@query_values);
734
735     return $sth->fetchall_arrayref( {} );
736 }
737
738 =head2 DelCourseItem {
739
740   DelCourseItem( ci_id => $cr_id );
741
742 =cut
743
744 sub DelCourseItem {
745     my (%params) = @_;
746     warn identify_myself(%params) if $DEBUG;
747
748     my $ci_id = $params{'ci_id'};
749
750     return unless ($ci_id);
751
752     _RevertFields( ci_id => $ci_id, fields => \@FIELDS );
753
754     my $query = "
755         DELETE FROM course_items
756         WHERE ci_id = ?
757     ";
758     C4::Context->dbh->do( $query, undef, $ci_id );
759 }
760
761 =head2 GetCourseReserve {
762
763   $course_item = GetCourseReserve( %params );
764
765 =cut
766
767 sub GetCourseReserve {
768     my (%params) = @_;
769     warn identify_myself(%params) if $DEBUG;
770
771     my $cr_id     = $params{'cr_id'};
772     my $course_id = $params{'course_id'};
773     my $ci_id     = $params{'ci_id'};
774
775     return unless ( $cr_id || ( $course_id && $ci_id ) );
776
777     my $dbh = C4::Context->dbh;
778     my $sth;
779
780     if ($cr_id) {
781         my $query = "
782             SELECT * FROM course_reserves
783             WHERE cr_id = ?
784         ";
785         $sth = $dbh->prepare($query);
786         $sth->execute($cr_id);
787     }
788     else {
789         my $query = "
790             SELECT * FROM course_reserves
791             WHERE course_id = ? AND ci_id = ?
792         ";
793         $sth = $dbh->prepare($query);
794         $sth->execute( $course_id, $ci_id );
795     }
796
797     my $course_reserve = $sth->fetchrow_hashref();
798     return $course_reserve;
799 }
800
801 =head2 ModCourseReserve
802
803     $id = ModCourseReserve( %params );
804
805 =cut
806
807 sub ModCourseReserve {
808     my (%params) = @_;
809     warn identify_myself(%params) if $DEBUG;
810
811     my $course_id   = $params{'course_id'};
812     my $ci_id       = $params{'ci_id'};
813     my $staff_note  = $params{'staff_note'};
814     my $public_note = $params{'public_note'};
815
816     return unless ( $course_id && $ci_id );
817
818     my $course_reserve =
819       GetCourseReserve( course_id => $course_id, ci_id => $ci_id );
820     my $cr_id;
821
822     my $dbh = C4::Context->dbh;
823
824     if ($course_reserve) {
825         $cr_id = $course_reserve->{'cr_id'};
826
827         my $query = "
828             UPDATE course_reserves
829             SET staff_note = ?, public_note = ?
830             WHERE cr_id = ?
831         ";
832         $dbh->do( $query, undef, $staff_note, $public_note, $cr_id );
833     }
834     else {
835         my $query = "
836             INSERT INTO course_reserves SET
837             course_id = ?,
838             ci_id = ?,
839             staff_note = ?,
840             public_note = ?
841         ";
842         $dbh->do( $query, undef, $course_id, $ci_id, $staff_note,
843             $public_note );
844         $cr_id =
845           $dbh->last_insert_id( undef, undef, 'course_reserves', 'cr_id' );
846     }
847
848     my $course = GetCourse($course_id);
849     EnableOrDisableCourseItem(
850         ci_id   => $params{'ci_id'},
851         enabled => $course->{'enabled'}
852     );
853
854     return $cr_id;
855 }
856
857 =head2 GetCourseReserves {
858
859   $course_reserves = GetCourseReserves( %params );
860
861   Required:
862       course_id OR ci_id
863   Optional:
864       include_items   => 1,
865       include_count   => 1,
866       include_courses => 1,
867
868 =cut
869
870 sub GetCourseReserves {
871     my (%params) = @_;
872     warn identify_myself(%params) if $DEBUG;
873
874     my $course_id     = $params{'course_id'};
875     my $ci_id         = $params{'ci_id'};
876     my $include_items = $params{'include_items'};
877     my $include_count = $params{'include_count'};
878     my $include_courses = $params{'include_courses'};
879
880     return unless ( $course_id || $ci_id );
881
882     my $field = ($course_id) ? 'course_id' : 'ci_id';
883     my $value = ($course_id) ? $course_id  : $ci_id;
884
885     my $query = "
886         SELECT cr.*, ci.itemnumber
887         FROM course_reserves cr, course_items ci
888         WHERE cr.$field = ?
889         AND cr.ci_id = ci.ci_id
890     ";
891     my $dbh = C4::Context->dbh;
892     my $sth = $dbh->prepare($query);
893     $sth->execute($value);
894
895     my $course_reserves = $sth->fetchall_arrayref( {} );
896
897     if ($include_items) {
898         foreach my $cr (@$course_reserves) {
899             $cr->{'course_item'} = GetCourseItem( ci_id => $cr->{'ci_id'} );
900             $cr->{'item'} = GetBiblioFromItemNumber( $cr->{'itemnumber'} );
901             $cr->{'issue'} = GetOpenIssue( $cr->{'itemnumber'} );
902         }
903     }
904
905     if ($include_count) {
906         foreach my $cr (@$course_reserves) {
907             $cr->{'reserves_count'} =
908               CountCourseReservesForItem( ci_id => $cr->{'ci_id'} );
909         }
910     }
911
912     if ($include_courses) {
913         foreach my $cr (@$course_reserves) {
914             $cr->{'courses'} =
915               GetCourses( itemnumber => $cr->{'itemnumber'} );
916         }
917     }
918
919     return $course_reserves;
920 }
921
922 =head2 DelCourseReserve {
923
924   DelCourseReserve( cr_id => $cr_id );
925
926 =cut
927
928 sub DelCourseReserve {
929     my (%params) = @_;
930     warn identify_myself(%params) if $DEBUG;
931
932     my $cr_id = $params{'cr_id'};
933
934     return unless ($cr_id);
935
936     my $dbh = C4::Context->dbh;
937
938     my $course_reserve = GetCourseReserve( cr_id => $cr_id );
939
940     my $query = "
941         DELETE FROM course_reserves
942         WHERE cr_id = ?
943     ";
944     $dbh->do( $query, undef, $cr_id );
945
946     ## If there are no other course reserves for this item
947     ## delete the course_item as well
948     unless ( CountCourseReservesForItem( ci_id => $course_reserve->{'ci_id'} ) )
949     {
950         DelCourseItem( ci_id => $course_reserve->{'ci_id'} );
951     }
952
953 }
954
955 =head2 GetReservesInfo
956
957     my $arrayref = GetItemReservesInfo( itemnumber => $itemnumber );
958
959     For a given item, returns an arrayref of reserves hashrefs,
960     with a course hashref under the key 'course'
961
962 =cut
963
964 sub GetItemReservesInfo {
965     my (%params) = @_;
966     warn identify_myself(%params) if $DEBUG;
967
968     my $itemnumber = $params{'itemnumber'};
969
970     return unless ($itemnumber);
971
972     my $course_item = GetCourseItem( itemnumber => $itemnumber );
973
974     return unless ( keys %$course_item );
975
976     my $course_reserves = GetCourseReserves( ci_id => $course_item->{'ci_id'} );
977
978     foreach my $cr (@$course_reserves) {
979         $cr->{'course'} = GetCourse( $cr->{'course_id'} );
980     }
981
982     return $course_reserves;
983 }
984
985 =head2 CountCourseReservesForItem
986
987     $bool = CountCourseReservesForItem( %params );
988
989     ci_id - course_item id
990     OR
991     itemnumber - course_item itemnumber
992
993     enabled = 'yes' or 'no'
994     Optional, if not supplied, counts reserves
995     for both enabled and disabled courses
996
997 =cut
998
999 sub CountCourseReservesForItem {
1000     my (%params) = @_;
1001     warn identify_myself(%params) if $DEBUG;
1002
1003     my $ci_id      = $params{'ci_id'};
1004     my $itemnumber = $params{'itemnumber'};
1005     my $enabled    = $params{'enabled'};
1006
1007     return unless ( $ci_id || $itemnumber );
1008
1009     my $course_item =
1010       GetCourseItem( ci_id => $ci_id, itemnumber => $itemnumber );
1011
1012     my @params = ( $course_item->{'ci_id'} );
1013     push( @params, $enabled ) if ($enabled);
1014
1015     my $query = "
1016         SELECT COUNT(*) AS count
1017         FROM course_reserves cr
1018         LEFT JOIN courses c ON ( c.course_id = cr.course_id )
1019         WHERE ci_id = ?
1020     ";
1021     $query .= "AND c.enabled = ?" if ($enabled);
1022
1023     my $dbh = C4::Context->dbh;
1024     my $sth = $dbh->prepare($query);
1025     $sth->execute(@params);
1026
1027     my $row = $sth->fetchrow_hashref();
1028
1029     return $row->{'count'};
1030 }
1031
1032 =head2 SearchCourses
1033
1034     my $courses = SearchCourses( term => $search_term, enabled => 'yes' );
1035
1036 =cut
1037
1038 sub SearchCourses {
1039     my (%params) = @_;
1040     warn identify_myself(%params) if $DEBUG;
1041
1042     my $term = $params{'term'};
1043
1044     my $enabled = $params{'enabled'} || '%';
1045
1046     my @params;
1047     my $query = "SELECT c.* FROM courses c";
1048
1049     $query .= "
1050         LEFT JOIN course_instructors ci
1051             ON ( c.course_id = ci.course_id )
1052         LEFT JOIN borrowers b
1053             ON ( ci.borrowernumber = b.borrowernumber )
1054         LEFT JOIN authorised_values av
1055             ON ( c.department = av.authorised_value )
1056         WHERE
1057             ( av.category = 'DEPARTMENT' OR av.category = 'TERM' )
1058             AND
1059             (
1060                 department LIKE ? OR
1061                 course_number LIKE ? OR
1062                 section LIKE ? OR
1063                 course_name LIKE ? OR
1064                 term LIKE ? OR
1065                 public_note LIKE ? OR
1066                 CONCAT(surname,' ',firstname) LIKE ? OR
1067                 CONCAT(firstname,' ',surname) LIKE ? OR
1068                 lib LIKE ? OR
1069                 lib_opac LIKE ?
1070            )
1071            AND
1072            c.enabled LIKE ?
1073         GROUP BY c.course_id
1074     ";
1075
1076     $term   = "%$term%";
1077     @params = ($term) x 10;
1078
1079     $query .= " ORDER BY department, course_number, section, term, course_name ";
1080
1081     my $dbh = C4::Context->dbh;
1082     my $sth = $dbh->prepare($query);
1083
1084     $sth->execute(@params, $enabled);
1085
1086     my $courses = $sth->fetchall_arrayref( {} );
1087
1088     foreach my $c (@$courses) {
1089         $c->{'instructors'} = GetCourseInstructors( $c->{'course_id'} );
1090     }
1091
1092     return $courses;
1093 }
1094
1095 sub whoami  { ( caller(1) )[3] }
1096 sub whowasi { ( caller(2) )[3] }
1097
1098 sub stringify_params {
1099     my (%params) = @_;
1100
1101     my $string = "\n";
1102
1103     foreach my $key ( keys %params ) {
1104         $string .= "    $key => " . $params{$key} . "\n";
1105     }
1106
1107     return "( $string )";
1108 }
1109
1110 sub identify_myself {
1111     my (%params) = @_;
1112
1113     return whowasi() . stringify_params(%params);
1114 }
1115
1116 1;
1117
1118 =head1 AUTHOR
1119
1120 Kyle M Hall <kyle@bywatersolutions.com>
1121
1122 =cut