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