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