Bug 23727: Editing course reserve items is broken
[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
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', '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{holdingbranch} ||= undef; # Can't be empty string, FK constraint
499
500     my %data = map { $_ => $params{$_} } @FIELDS;
501     my %enabled = map { $_ . "_enabled" => $params{ $_ . "_enabled" } } @FIELDS;
502
503     my $ci = Koha::Course::Item->new(
504         {
505             itemnumber => $params{itemnumber},
506             %data,
507             %enabled,
508         }
509     )->store();
510
511     return $ci->id;
512 }
513
514 =head2 _UpdateCourseItem
515
516   _UpdateCourseItem( %params );
517
518 =cut
519
520 sub _UpdateCourseItem {
521     my (%params) = @_;
522     warn identify_myself(%params) if $DEBUG;
523
524     my $ci_id         = $params{'ci_id'};
525     my $course_item   = $params{'course_item'};
526
527     $params{holdingbranch} ||= undef; # Can't be empty string, FK constraint
528
529     return unless ( $ci_id || $course_item );
530
531     $course_item = Koha::Course::Items->find( $ci_id || $course_item->{ci_id} );
532
533     my %data = map { $_ => $params{$_} } @FIELDS;
534     my %enabled = map { $_ . "_enabled" => $params{ $_ . "_enabled" } } @FIELDS;
535
536     $course_item->update( { %data, %enabled } );
537     if ( $course_item->is_enabled ) {
538         my $item_fields = {};
539         $item_fields->{itype}         = $course_item->itype         if $course_item->itype_enabled;
540         $item_fields->{ccode}         = $course_item->ccode         if $course_item->ccode_enabled;
541         $item_fields->{location}      = $course_item->location      if $course_item->location_enabled;
542         $item_fields->{holdingbranch} = $course_item->holdingbranch if $course_item->holdingbranch_enabled;
543
544         Koha::Items->find( $course_item->itemnumber )
545                    ->set( $item_fields )
546                    ->store
547             if keys %$item_fields;
548
549     }
550
551 }
552
553 =head2 _RevertFields
554
555     _RevertFields( ci_id => $ci_id, fields => \@fields_to_revert );
556
557     Copies fields from course item storage back to the actual item
558
559 =cut
560
561 sub _RevertFields {
562     my (%params) = @_;
563     warn identify_myself(%params) if $DEBUG;
564
565     my $ci_id = $params{'ci_id'};
566
567     return unless $ci_id;
568
569     my $course_item = Koha::Course::Items->find( $ci_id );
570
571     my $item_fields = {};
572     $item_fields->{itype}         = $course_item->itype_storage         if $course_item->itype_enabled;
573     $item_fields->{ccode}         = $course_item->ccode_storage         if $course_item->ccode_enabled;
574     $item_fields->{location}      = $course_item->location_storage      if $course_item->location_enabled;
575     $item_fields->{holdingbranch} = $course_item->holdingbranch_storage if $course_item->holdingbranch_enabled;
576
577     Koha::Items->find( $course_item->itemnumber )
578                ->set( $item_fields )
579                ->store
580         if keys %$item_fields;
581
582     $course_item->itype_storage(undef);
583     $course_item->ccode_storage(undef);
584     $course_item->location_storage(undef);
585     $course_item->holdingbranch_storage(undef);
586     $course_item->store();
587 }
588
589 =head2 _SwapAllFields
590
591     _SwapAllFields( $ci_id );
592
593 =cut
594
595 sub _SwapAllFields {
596     my ( $ci_id, $enabled ) = @_;
597     warn "C4::CourseReserves::_SwapFields( $ci_id )" if $DEBUG;
598
599     my $course_item = Koha::Course::Items->find( $ci_id );
600     my $item = Koha::Items->find( $course_item->itemnumber );
601
602     if ( $enabled eq 'yes' ) { # Copy item fields to course item storage, course item fields to item
603         $course_item->itype_storage( $item->effective_itemtype )    if $course_item->itype_enabled;
604         $course_item->ccode_storage( $item->ccode )                 if $course_item->ccode_enabled;
605         $course_item->location_storage( $item->location )           if $course_item->location_enabled;
606         $course_item->holdingbranch_storage( $item->holdingbranch ) if $course_item->holdingbranch_enabled;
607         $course_item->store();
608
609         my $item_fields = {};
610         $item_fields->{itype}         = $course_item->itype         if $course_item->itype_enabled;
611         $item_fields->{ccode}         = $course_item->ccode         if $course_item->ccode_enabled;
612         $item_fields->{location}      = $course_item->location      if $course_item->location_enabled;
613         $item_fields->{holdingbranch} = $course_item->holdingbranch if $course_item->holdingbranch_enabled;
614
615         Koha::Items->find( $course_item->itemnumber )
616                    ->set( $item_fields )
617                    ->store
618             if keys %$item_fields;
619
620     } else { # Copy course item storage to item
621         my $item_fields = {};
622         $item_fields->{itype}         = $course_item->itype_storage         if $course_item->itype_enabled;
623         $item_fields->{ccode}         = $course_item->ccode_storage         if $course_item->ccode_enabled;
624         $item_fields->{location}      = $course_item->location_storage      if $course_item->location_enabled;
625         $item_fields->{holdingbranch} = $course_item->holdingbranch_storage if $course_item->holdingbranch_enabled;
626
627         Koha::Items->find( $course_item->itemnumber )
628                    ->set( $item_fields )
629                    ->store
630             if keys %$item_fields;
631
632         $course_item->itype_storage(undef);
633         $course_item->ccode_storage(undef);
634         $course_item->location_storage(undef);
635         $course_item->holdingbranch_storage(undef);
636         $course_item->store();
637     }
638 }
639
640 =head2 GetCourseItems {
641
642   $course_items = GetCourseItems(
643       [course_id => $course_id]
644       [, itemnumber => $itemnumber ]
645   );
646
647 =cut
648
649 sub GetCourseItems {
650     my (%params) = @_;
651     warn identify_myself(%params) if $DEBUG;
652
653     my $course_id  = $params{'course_id'};
654     my $itemnumber = $params{'itemnumber'};
655
656     return unless ($course_id);
657
658     my @query_keys;
659     my @query_values;
660
661     my $query = "SELECT * FROM course_items";
662
663     if ( keys %params ) {
664
665         $query .= " WHERE ";
666
667         foreach my $key ( keys %params ) {
668             push( @query_keys,   " $key LIKE ? " );
669             push( @query_values, $params{$key} );
670         }
671
672         $query .= join( ' AND ', @query_keys );
673     }
674
675     my $dbh = C4::Context->dbh;
676     my $sth = $dbh->prepare($query);
677     $sth->execute(@query_values);
678
679     return $sth->fetchall_arrayref( {} );
680 }
681
682 =head2 DelCourseItem {
683
684   DelCourseItem( ci_id => $cr_id );
685
686 =cut
687
688 sub DelCourseItem {
689     my (%params) = @_;
690     warn identify_myself(%params) if $DEBUG;
691
692     my $ci_id = $params{'ci_id'};
693
694     return unless ($ci_id);
695
696     _RevertFields( ci_id => $ci_id );
697
698     my $query = "
699         DELETE FROM course_items
700         WHERE ci_id = ?
701     ";
702     C4::Context->dbh->do( $query, undef, $ci_id );
703 }
704
705 =head2 GetCourseReserve {
706
707   $course_item = GetCourseReserve( %params );
708
709 =cut
710
711 sub GetCourseReserve {
712     my (%params) = @_;
713     warn identify_myself(%params) if $DEBUG;
714
715     my $cr_id     = $params{'cr_id'};
716     my $course_id = $params{'course_id'};
717     my $ci_id     = $params{'ci_id'};
718
719     return unless ( $cr_id || ( $course_id && $ci_id ) );
720
721     my $dbh = C4::Context->dbh;
722     my $sth;
723
724     if ($cr_id) {
725         my $query = "
726             SELECT * FROM course_reserves
727             WHERE cr_id = ?
728         ";
729         $sth = $dbh->prepare($query);
730         $sth->execute($cr_id);
731     } else {
732         my $query = "
733             SELECT * FROM course_reserves
734             WHERE course_id = ? AND ci_id = ?
735         ";
736         $sth = $dbh->prepare($query);
737         $sth->execute( $course_id, $ci_id );
738     }
739
740     my $course_reserve = $sth->fetchrow_hashref();
741     return $course_reserve;
742 }
743
744 =head2 ModCourseReserve
745
746     $id = ModCourseReserve( %params );
747
748 =cut
749
750 sub ModCourseReserve {
751     my (%params) = @_;
752     warn identify_myself(%params) if $DEBUG;
753
754     my $course_id   = $params{'course_id'};
755     my $ci_id       = $params{'ci_id'};
756     my $staff_note  = $params{'staff_note'};
757     my $public_note = $params{'public_note'};
758
759     return unless ( $course_id && $ci_id );
760
761     my $course_reserve = GetCourseReserve( course_id => $course_id, ci_id => $ci_id );
762     my $cr_id;
763
764     my $dbh = C4::Context->dbh;
765
766     if ($course_reserve) {
767         $cr_id = $course_reserve->{'cr_id'};
768
769         my $query = "
770             UPDATE course_reserves
771             SET staff_note = ?, public_note = ?
772             WHERE cr_id = ?
773         ";
774         $dbh->do( $query, undef, $staff_note, $public_note, $cr_id );
775     } else {
776         my $query = "
777             INSERT INTO course_reserves SET
778             course_id = ?,
779             ci_id = ?,
780             staff_note = ?,
781             public_note = ?
782         ";
783         $dbh->do( $query, undef, $course_id, $ci_id, $staff_note, $public_note );
784         $cr_id = $dbh->last_insert_id( undef, undef, 'course_reserves', 'cr_id' );
785     }
786
787     EnableOrDisableCourseItem(
788         ci_id   => $params{'ci_id'},
789     );
790
791     return $cr_id;
792 }
793
794 =head2 GetCourseReserves {
795
796   $course_reserves = GetCourseReserves( %params );
797
798   Required:
799       course_id OR ci_id
800   Optional:
801       include_items   => 1,
802       include_count   => 1,
803       include_courses => 1,
804
805 =cut
806
807 sub GetCourseReserves {
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 $include_items   = $params{'include_items'};
814     my $include_count   = $params{'include_count'};
815     my $include_courses = $params{'include_courses'};
816
817     return unless ( $course_id || $ci_id );
818
819     my $field = ($course_id) ? 'course_id' : 'ci_id';
820     my $value = ($course_id) ? $course_id  : $ci_id;
821
822     my $query = "
823         SELECT cr.*, ci.itemnumber
824         FROM course_reserves cr, course_items ci
825         WHERE cr.$field = ?
826         AND cr.ci_id = ci.ci_id
827     ";
828     my $dbh = C4::Context->dbh;
829     my $sth = $dbh->prepare($query);
830     $sth->execute($value);
831
832     my $course_reserves = $sth->fetchall_arrayref( {} );
833
834     if ($include_items) {
835         foreach my $cr (@$course_reserves) {
836             my $item = Koha::Items->find( $cr->{itemnumber} );
837             my $biblio = $item->biblio;
838             my $biblioitem = $biblio->biblioitem;
839             $cr->{'course_item'} = GetCourseItem( ci_id => $cr->{'ci_id'} );
840             $cr->{'item'}        = $item;
841             $cr->{'biblio'}      = $biblio;
842             $cr->{'biblioitem'}  = $biblioitem;
843             $cr->{'issue'}       = GetOpenIssue( $cr->{'itemnumber'} );
844         }
845     }
846
847     if ($include_count) {
848         foreach my $cr (@$course_reserves) {
849             $cr->{'reserves_count'} = CountCourseReservesForItem( ci_id => $cr->{'ci_id'} );
850         }
851     }
852
853     if ($include_courses) {
854         foreach my $cr (@$course_reserves) {
855             $cr->{'courses'} = GetCourses( itemnumber => $cr->{'itemnumber'} );
856         }
857     }
858
859     return $course_reserves;
860 }
861
862 =head2 DelCourseReserve {
863
864   DelCourseReserve( cr_id => $cr_id );
865
866 =cut
867
868 sub DelCourseReserve {
869     my (%params) = @_;
870     warn identify_myself(%params) if $DEBUG;
871
872     my $cr_id = $params{'cr_id'};
873
874     return unless ($cr_id);
875
876     my $dbh = C4::Context->dbh;
877
878     my $course_reserve = GetCourseReserve( cr_id => $cr_id );
879
880     my $query = "
881         DELETE FROM course_reserves
882         WHERE cr_id = ?
883     ";
884     $dbh->do( $query, undef, $cr_id );
885
886     ## If there are no other course reserves for this item
887     ## delete the course_item as well
888     unless ( CountCourseReservesForItem( ci_id => $course_reserve->{'ci_id'} ) ) {
889         DelCourseItem( ci_id => $course_reserve->{'ci_id'} );
890     }
891
892 }
893
894 =head2 GetItemCourseReservesInfo
895
896     my $arrayref = GetItemCourseReservesInfo( itemnumber => $itemnumber );
897
898     For a given item, returns an arrayref of reserves hashrefs,
899     with a course hashref under the key 'course'
900
901 =cut
902
903 sub GetItemCourseReservesInfo {
904     my (%params) = @_;
905     warn identify_myself(%params) if $DEBUG;
906
907     my $itemnumber = $params{'itemnumber'};
908
909     return unless ($itemnumber);
910
911     my $course_item = GetCourseItem( itemnumber => $itemnumber );
912
913     return unless ( keys %$course_item );
914
915     my $course_reserves = GetCourseReserves( ci_id => $course_item->{'ci_id'} );
916
917     foreach my $cr (@$course_reserves) {
918         $cr->{'course'} = GetCourse( $cr->{'course_id'} );
919     }
920
921     return $course_reserves;
922 }
923
924 =head2 CountCourseReservesForItem
925
926     $bool = CountCourseReservesForItem( %params );
927
928     ci_id - course_item id
929     OR
930     itemnumber - course_item itemnumber
931
932     enabled = 'yes' or 'no'
933     Optional, if not supplied, counts reserves
934     for both enabled and disabled courses
935
936 =cut
937
938 sub CountCourseReservesForItem {
939     my (%params) = @_;
940     warn identify_myself(%params) if $DEBUG;
941
942     my $ci_id      = $params{'ci_id'};
943     my $itemnumber = $params{'itemnumber'};
944     my $enabled    = $params{'enabled'};
945
946     return unless ( $ci_id || $itemnumber );
947
948     my $course_item = GetCourseItem( ci_id => $ci_id, itemnumber => $itemnumber );
949
950     my @params = ( $course_item->{'ci_id'} );
951     push( @params, $enabled ) if ($enabled);
952
953     my $query = "
954         SELECT COUNT(*) AS count
955         FROM course_reserves cr
956         LEFT JOIN courses c ON ( c.course_id = cr.course_id )
957         WHERE ci_id = ?
958     ";
959     $query .= "AND c.enabled = ?" if ($enabled);
960
961     my $dbh = C4::Context->dbh;
962     my $sth = $dbh->prepare($query);
963     $sth->execute(@params);
964
965     my $row = $sth->fetchrow_hashref();
966
967     return $row->{'count'};
968 }
969
970 =head2 SearchCourses
971
972     my $courses = SearchCourses( term => $search_term, enabled => 'yes' );
973
974 =cut
975
976 sub SearchCourses {
977     my (%params) = @_;
978     warn identify_myself(%params) if $DEBUG;
979
980     my $term = $params{'term'};
981
982     my $enabled = $params{'enabled'} || '%';
983
984     my @params;
985     my $query = "
986         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
987         FROM courses c
988         LEFT JOIN course_instructors ci
989             ON ( c.course_id = ci.course_id )
990         LEFT JOIN borrowers b
991             ON ( ci.borrowernumber = b.borrowernumber )
992         LEFT JOIN authorised_values av
993             ON ( c.department = av.authorised_value )
994         WHERE
995             ( av.category = 'DEPARTMENT' OR av.category = 'TERM' )
996             AND
997             (
998                 department LIKE ? OR
999                 course_number LIKE ? OR
1000                 section LIKE ? OR
1001                 course_name LIKE ? OR
1002                 term LIKE ? OR
1003                 public_note LIKE ? OR
1004                 CONCAT(surname,' ',firstname) LIKE ? OR
1005                 CONCAT(firstname,' ',surname) LIKE ? OR
1006                 lib LIKE ? OR
1007                 lib_opac LIKE ?
1008            )
1009            AND
1010            c.enabled LIKE ?
1011         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
1012     ";
1013
1014     $term //= '';
1015     $term   = "%$term%";
1016     @params = ($term) x 10;
1017
1018     $query .= " ORDER BY department, course_number, section, term, course_name ";
1019
1020     my $dbh = C4::Context->dbh;
1021     my $sth = $dbh->prepare($query);
1022
1023     $sth->execute( @params, $enabled );
1024
1025     my $courses = $sth->fetchall_arrayref( {} );
1026
1027     foreach my $c (@$courses) {
1028         $c->{'instructors'} = GetCourseInstructors( $c->{'course_id'} );
1029     }
1030
1031     return $courses;
1032 }
1033
1034 sub whoami  { ( caller(1) )[3] }
1035 sub whowasi { ( caller(2) )[3] }
1036
1037 sub stringify_params {
1038     my (%params) = @_;
1039
1040     my $string = "\n";
1041
1042     foreach my $key ( keys %params ) {
1043         $string .= "    $key => " . $params{$key} . "\n";
1044     }
1045
1046     return "( $string )";
1047 }
1048
1049 sub identify_myself {
1050     my (%params) = @_;
1051
1052     return whowasi() . stringify_params(%params);
1053 }
1054
1055 1;
1056
1057 =head1 AUTHOR
1058
1059 Kyle M Hall <kyle@bywatersolutions.com>
1060
1061 =cut