Bug 35695: Remove useless JS from cataloging_additem.js
[koha.git] / svc / checkouts
1 #!/usr/bin/perl
2
3 # Copyright 2014 ByWater Solutions
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21
22 use CGI;
23 use JSON qw(to_json);
24
25 use C4::Auth qw(check_cookie_auth haspermission);
26 use C4::Circulation qw(GetIssuingCharges CanBookBeRenewed GetRenewCount );
27 use C4::Overdues qw(GetFine);
28 use C4::Context;
29
30 use Koha::AuthorisedValues;
31 use Koha::DateUtils qw( dt_from_string output_pref );
32 use Koha::Items;
33 use Koha::ItemTypes;
34
35 my $input = CGI->new;
36
37 my ( $auth_status, $session ) = check_cookie_auth( $input->cookie('CGISESSID'));
38 if( $auth_status ne 'ok' ) {
39     print CGI::header( '-status' => '401' );
40     exit 0;
41 }
42
43 my $userid   = $session->param('id');
44
45 unless (haspermission($userid, { circulate => 'circulate_remaining_permissions' })
46     || haspermission($userid, { borrowers => 'edit_borrowers' })) {
47     exit 0;
48 }
49
50 my @sort_columns = qw/date_due title itype issuedate branchcode itemcallnumber/;
51
52 my @borrowernumber   = $input->multi_param('borrowernumber');
53 my $offset           = $input->param('iDisplayStart');
54 my $results_per_page = $input->param('iDisplayLength') || -1;
55
56 my $sorting_column = $input->param('iSortCol_0') || q{};
57 $sorting_column = ( $sorting_column && $sort_columns[$sorting_column] ) ? $sort_columns[$sorting_column] : 'issuedate';
58
59 my $sorting_direction = $input->param('sSortDir_0') || q{};
60 $sorting_direction = $sorting_direction eq 'asc' ? 'asc' : 'desc';
61
62 $results_per_page = undef if ( $results_per_page == -1 );
63
64 binmode STDOUT, ":encoding(UTF-8)";
65 print $input->header( -type => 'text/plain', -charset => 'UTF-8' );
66
67 my @parameters;
68 my $sql = '
69     SELECT
70         issues.issue_id,
71         issues.issuedate,
72         issues.date_due,
73         issues.date_due < now() as date_due_overdue,
74         issues.timestamp,
75         issues.auto_renew,
76
77         issues.onsite_checkout,
78
79         biblio.biblionumber,
80         biblio.title,
81         biblio.subtitle,
82         biblio.medium,
83         biblio.part_number,
84         biblio.part_name,
85         biblio.author,
86
87         items.itemnumber,
88         items.barcode,
89         branches2.branchname AS homebranch,
90         items.itemnotes,
91         items.itemnotes_nonpublic,
92         items.itemcallnumber,
93         items.copynumber,
94         items.replacementprice,
95
96         issues.branchcode,
97         branches.branchname,
98
99         items.itype,
100         biblioitems.itemtype,
101
102         items.ccode AS collection,
103
104         borrowers.borrowernumber,
105         borrowers.surname,
106         borrowers.firstname,
107         borrowers.cardnumber,
108
109         items.itemlost,
110         items.damaged,
111         items.location,
112         items.enumchron,
113         items.materials,
114
115         DATEDIFF( issues.issuedate, CURRENT_DATE() ) AS not_issued_today,
116
117         return_claims.id AS return_claim_id,
118         return_claims.notes AS return_claim_notes,
119         return_claims.created_on AS return_claim_created_on,
120         return_claims.updated_on AS return_claim_updated_on
121
122     FROM issues
123         LEFT JOIN items USING ( itemnumber )
124         LEFT JOIN biblio USING ( biblionumber )
125         LEFT JOIN biblioitems USING ( biblionumber )
126         LEFT JOIN borrowers USING ( borrowernumber )
127         LEFT JOIN branches ON ( issues.branchcode = branches.branchcode )
128         LEFT JOIN branches branches2 ON ( items.homebranch = branches2.branchcode )
129         LEFT JOIN return_claims USING ( issue_id )
130     WHERE issues.borrowernumber
131 ';
132
133 if ( @borrowernumber == 1 ) {
134     $sql .= '= ?';
135 }
136 else {
137     $sql .= ' IN (' . join( ',', ('?') x @borrowernumber ) . ') ';
138 }
139 push( @parameters, @borrowernumber );
140
141 $sql .= " ORDER BY $sorting_column $sorting_direction ";
142
143 my $dbh = C4::Context->dbh();
144 my $sth = $dbh->prepare($sql);
145 $sth->execute(@parameters);
146
147 my $item_level_itypes = C4::Context->preference('item-level_itypes');
148 my $claims_returned_lost_value = C4::Context->preference('ClaimReturnedLostValue');
149 my $confirm_parts_required = C4::Context->preference("CircConfirmItemParts");
150
151 my $itemtypes = { map { $_->{itemtype} => $_->{translated_description} } @{ Koha::ItemTypes->search_with_localization->unblessed } };
152
153 my @checkouts_today;
154 my @checkouts_previous;
155 while ( my $c = $sth->fetchrow_hashref() ) {
156     my ($charge) = GetIssuingCharges( $c->{itemnumber}, $c->{borrowernumber} );
157     my $fine = GetFine( $c->{itemnumber}, $c->{borrowernumber} );
158
159     my $checkout_obj = Koha::Checkouts->find( $c->{issue_id} );
160     my ( $can_renew, $can_renew_error, $info ) =
161       CanBookBeRenewed( $checkout_obj->patron, $checkout_obj );
162     my $can_renew_date =
163       $can_renew_error && $can_renew_error eq 'too_soon'
164       ? output_pref(
165         {
166             dt => $info->{soonest_renew_date},
167             as_due_date => 1
168         }
169       )
170       : undef;
171
172     my (
173         $renewals_count,
174         $renewals_allowed,
175         $renewals_remaining,
176         $unseen_count,
177         $unseen_allowed,
178         $unseen_remaining
179     ) =
180       GetRenewCount( $c->{borrowernumber}, $c->{itemnumber} );
181
182     my ( $itemtype, $recordtype, $type_for_stat );
183     $itemtype      = $itemtypes->{ $c->{itype} }    if $c->{itype};
184     $recordtype    = $itemtypes->{ $c->{itemtype} } if $c->{itemtype};
185     $type_for_stat = $item_level_itypes ? $itemtype : $recordtype;
186
187     my $location;
188     if ( $c->{location} ) {
189         my $av = Koha::AuthorisedValues->get_description_by_koha_field(
190             { kohafield => 'items.location', authorised_value => $c->{location} } );
191         $location = $av->{lib} ? $av->{lib} : '';
192     }
193     my $collection;
194     if ( $c->{collection} ) {
195         my $av = Koha::AuthorisedValues->get_description_by_koha_field(
196             { kohafield => 'items.ccode', authorised_value => $c->{collection} } );
197         $collection = $av->{lib} ? $av->{lib} : '';
198     }
199     my $lost;
200     my $claims_returned;
201     if ( $c->{itemlost} ) {
202         my $av = Koha::AuthorisedValues->get_description_by_koha_field(
203             { kohafield => 'items.itemlost', authorised_value => $c->{itemlost} } );
204         $lost            = $av->{lib} ? $av->{lib} : '';
205         $claims_returned = $c->{itemlost} eq $claims_returned_lost_value;
206     }
207     my $damaged;
208     if ( $c->{damaged} ) {
209         my $av = Koha::AuthorisedValues->get_description_by_koha_field(
210             { kohafield => 'items.damaged', authorised_value => $c->{damaged} } );
211         $damaged = $av->{lib} ? $av->{lib} : '';
212     }
213     my $materials;
214     if ( $c->{materials} && $confirm_parts_required ) {
215         my $descriptions = Koha::AuthorisedValues->get_description_by_koha_field({frameworkcode => '', kohafield =>'items.materials', authorised_value => $c->{materials} });
216         $materials = $descriptions->{lib} // $c->{materials};
217     }
218     my @subtitles = split(/ \| /, $c->{'subtitle'} // '' );
219
220     my $recalled = 0;
221     if ( C4::Context->preference('UseRecalls') ) {
222         my $item = Koha::Items->find( $c->{itemnumber} );
223         my $recall = undef;
224         $recall = $item->check_recalls if $item->can_be_waiting_recall;
225         if ( defined $recall ) {
226             if ( $recall->item_level ) {
227                 if ( $recall->item_id == $c->{itemnumber} ) {
228                     # item-level recall on this item
229                     $recalled = 1;
230                 } else {
231                     $recalled = 0;
232                 }
233             } else {
234                 # biblio-level recall, but don't want to mark recalled if the recall has been allocated a different item
235                 if ( !$recall->waiting ) {
236                     $recalled = 1;
237                 }
238             }
239         }
240     }
241
242     my $checkout = {
243         DT_RowId               => $c->{itemnumber} . '-' . $c->{borrowernumber},
244         title                  => $c->{title},
245         subtitle               => \@subtitles,
246         medium                 => $c->{medium} // '',
247         part_number            => $c->{part_number} // '',
248         part_name              => $c->{part_name} // '',
249         author                 => $c->{author},
250         barcode                => $c->{barcode},
251         type_for_stat          => $type_for_stat || q{},
252         itemtype_description   => $itemtype || q{},
253         recordtype_description => $recordtype || q{},
254         collection             => $collection,
255         location               => $location,
256         homebranch             => $c->{homebranch},
257         itemnotes              => $c->{itemnotes},
258         itemnotes_nonpublic    => $c->{itemnotes_nonpublic},
259         branchcode             => $c->{branchcode},
260         branchname             => $c->{branchname},
261         itemcallnumber         => $c->{itemcallnumber} || q{},
262         copynumber             => $c->{copynumber} || q{},
263         charge                 => $charge,
264         fine                   => $fine,
265         price                  => $c->{replacementprice} || q{},
266         can_renew              => $can_renew,
267         can_renew_error        => $can_renew_error,
268         can_renew_date         => $can_renew_date,
269         itemnumber             => $c->{itemnumber},
270         borrowernumber         => $c->{borrowernumber},
271         biblionumber           => $c->{biblionumber},
272         issuedate              => $c->{issuedate},
273         date_due               => $c->{date_due},
274         date_due_overdue       => $c->{date_due_overdue} ? JSON::true : JSON::false,
275         timestamp              => $c->{timestamp},
276         auto_renew             => $c->{auto_renew},
277         onsite_checkout        => $c->{onsite_checkout},
278         enumchron              => $c->{enumchron},
279         renewals_count         => $renewals_count,
280         renewals_allowed       => $renewals_allowed || 0,
281         renewals_remaining     => $renewals_remaining,
282         unseen_count           => $unseen_count,
283         unseen_allowed         => $unseen_allowed,
284         unseen_remaining       => $unseen_remaining,
285
286         return_claim_id                   => $c->{return_claim_id},
287         return_claim_notes                => $c->{return_claim_notes},
288         return_claim_created_on           => $c->{return_claim_created_on},
289         return_claim_updated_on           => $c->{return_claim_updated_on},
290         return_claim_created_on_formatted => $c->{return_claim_created_on}
291         ? output_pref( { dt => dt_from_string( $c->{return_claim_created_on} ) } )
292         : undef,
293         return_claim_updated_on_formatted => $c->{return_claim_updated_on}
294         ? output_pref( { dt => dt_from_string( $c->{return_claim_updated_on} ) } )
295         : undef,
296
297         lost            => $lost,
298         claims_returned => $claims_returned,
299         damaged         => $damaged,
300         materials       => $materials,
301         borrower        => {
302             surname    => $c->{surname},
303             firstname  => $c->{firstname},
304             cardnumber => $c->{cardnumber},
305         },
306         issued_today => !$c->{not_issued_today},
307         recalled     => $recalled,
308     };
309
310     if ( $c->{not_issued_today} ) {
311         push( @checkouts_previous, $checkout );
312     }
313     else {
314         push( @checkouts_today, $checkout );
315     }
316 }
317
318
319 @checkouts_today = sort { $a->{timestamp} cmp $b->{timestamp} } @checkouts_today;    # latest to earliest
320 @checkouts_today = reverse(@checkouts_today)
321   if ( C4::Context->preference('todaysIssuesDefaultSortOrder') eq 'desc' );      # earliest to latest
322
323 @checkouts_previous =
324   sort { $a->{date_due} cmp $b->{date_due} || $a->{timestamp} cmp $b->{timestamp} }
325   @checkouts_previous;                                                               # latest to earliest
326 @checkouts_previous = reverse(@checkouts_previous)
327   if ( C4::Context->preference('previousIssuesDefaultSortOrder') eq 'desc' );    # earliest to latest
328
329 my @checkouts = ( @checkouts_today, @checkouts_previous );
330
331 my $i = 1;
332 map { $_->{sort_order} = $i++ } @checkouts;
333
334
335 my $data;
336 $data->{'iTotalRecords'}        = scalar @checkouts;
337 $data->{'iTotalDisplayRecords'} = scalar @checkouts;
338 $data->{'sEcho'}                = $input->param('sEcho') || undef;
339 $data->{'aaData'}               = \@checkouts;
340
341 print to_json($data);