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