Revert "Bug 33497: (follow-up) Remove unecessary scalar"
[koha.git] / catalogue / itemsearch.pl
1 #!/usr/bin/perl
2 # Copyright 2013 BibLibre
3 #
4 # This file is part of Koha
5 #
6 # Koha is free software; you can redistribute it and/or modify it
7 # under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # Koha is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with Koha; if not, see <http://www.gnu.org/licenses>.
18
19 use Modern::Perl;
20 use CGI;
21
22 use JSON qw( to_json );
23
24 use C4::Auth qw( get_template_and_user );
25 use C4::Circulation qw( barcodedecode );
26 use C4::Output qw( output_with_http_headers output_html_with_http_headers );
27 use C4::Items qw( SearchItems );
28 use C4::Koha qw( GetAuthorisedValues );
29
30 use Koha::AuthorisedValues;
31 use Koha::Biblios;
32 use Koha::Item::Search::Field qw(GetItemSearchFields);
33 use Koha::ItemTypes;
34 use Koha::Libraries;
35
36 my $cgi = CGI->new;
37 my %params = $cgi->Vars;
38
39 my $format = $cgi->param('format');
40 my $template_name = 'catalogue/itemsearch.tt';
41
42 if (defined $format and $format eq 'json') {
43     $template_name = 'catalogue/itemsearch_json.tt';
44
45     # Map DataTables parameters with 'regular' parameters
46     $cgi->param('rows', scalar $cgi->param('iDisplayLength'));
47     $cgi->param('page', (scalar $cgi->param('iDisplayStart') / scalar $cgi->param('iDisplayLength')) + 1);
48     my @columns = split /,/, scalar $cgi->param('sColumns');
49     $cgi->param('sortby', $columns[ scalar $cgi->param('iSortCol_0') ]);
50     $cgi->param('sortorder', scalar $cgi->param('sSortDir_0'));
51
52     my @f = $cgi->multi_param('f');
53     my @q = $cgi->multi_param('q');
54
55     # If index indicates the value is a barcode, we need to preproccess it before searching
56     for ( my $i = 0; $i < @q; $i++ ) {
57         $q[$i] = barcodedecode($q[$i]) if $f[$i] eq 'barcode';
58     }
59
60     push @q, '' if @q == 0;
61     my @op = $cgi->multi_param('op');
62     my @c = $cgi->multi_param('c');
63     my $iColumns = $cgi->param('iColumns');
64     foreach my $i (0 .. ($iColumns - 1)) {
65         my $sSearch = $cgi->param("sSearch_$i");
66         if (defined $sSearch and $sSearch ne '') {
67             my @words = split /\s+/, $sSearch;
68             foreach my $word (@words) {
69                 push @f, $columns[$i];
70                 push @c, 'and';
71
72                 if ( grep { $_ eq $columns[$i] } qw( ccode homebranch holdingbranch location itype notforloan itemlost onloan ) ) {
73                     push @q, "$word";
74                     push @op, '=';
75                 } else {
76                     push @q, "%$word%";
77                     push @op, 'like';
78                 }
79             }
80         }
81     }
82     $cgi->param('f', @f);
83     $cgi->param('q', @q);
84     $cgi->param('op', @op);
85     $cgi->param('c', @c);
86 } elsif (defined $format and $format eq 'csv') {
87     $template_name = 'catalogue/itemsearch_csv.tt';
88
89     # Retrieve all results
90     $cgi->param('rows', 0);
91 } elsif (defined $format and $format eq 'barcodes') {
92     # Retrieve all results
93     $cgi->param('rows', 0);
94 } elsif (defined $format) {
95     die "Unsupported format $format";
96 }
97
98 my ($template, $borrowernumber, $cookie) = get_template_and_user({
99     template_name => $template_name,
100     query => $cgi,
101     type => 'intranet',
102     flagsrequired   => { catalogue => 1 },
103 });
104
105 my $mss = Koha::MarcSubfieldStructures->search({ frameworkcode => '', kohafield => 'items.itemlost', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] });
106 my $itemlost_values = $mss->count ? GetAuthorisedValues($mss->next->authorised_value) : [];
107
108 $mss = Koha::MarcSubfieldStructures->search({ frameworkcode => '', kohafield => 'items.withdrawn', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] });
109 my $withdrawn_values = $mss->count ? GetAuthorisedValues($mss->next->authorised_value) : [];
110
111 if ( Koha::MarcSubfieldStructures->search( { frameworkcode => '', kohafield => 'items.new_status' } )->count ) {
112     $template->param( has_new_status => 1 );
113 }
114
115 if ( defined $format ) {
116     # Parameters given, it's a search
117
118     my $filter = {
119         conjunction => 'AND',
120         filters => [],
121     };
122
123     foreach my $p (qw(homebranch holdingbranch location itype ccode issues datelastborrowed notforloan itemlost withdrawn)) {
124         if (my @q = $cgi->multi_param($p)) {
125             if ($q[0] ne '') {
126                 my $f = {
127                     field => $p,
128                     query => \@q,
129                 };
130                 if (my $op = scalar $cgi->param($p . '_op')) {
131                     $f->{operator} = $op;
132                 }
133                 push @{ $filter->{filters} }, $f;
134             }
135         }
136     }
137
138     my @c = $cgi->multi_param('c');
139     my @fields = $cgi->multi_param('f');
140     my @q = $cgi->multi_param('q');
141     my @op = $cgi->multi_param('op');
142
143     my $f;
144     for (my $i = 0; $i < @fields; $i++) {
145         my $field = $fields[$i];
146         my $q = shift @q;
147         my $op = shift @op;
148         if (defined $q and $q ne '') {
149             if (C4::Context->preference("marcflavour") ne "UNIMARC" && $field eq 'publicationyear') {
150                 $field = 'copyrightdate';
151             }
152
153             if ($i == 0) {
154                 $f = {
155                     field => $field,
156                     query => $q,
157                     operator => $op,
158                 };
159             } else {
160                 my $c = shift @c;
161                 $f = {
162                     conjunction => $c,
163                     filters => [
164                         $f, {
165                             field => $field,
166                             query => $q,
167                             operator => $op,
168                         }
169                     ],
170                 };
171             }
172         }
173     }
174     push @{ $filter->{filters} }, $f;
175
176     # Yes/No parameters
177     foreach my $p (qw( damaged new_status )) {
178         my $v = $cgi->param($p) // '';
179         my $f = {
180             field => $p,
181             query => 0,
182         };
183         if ( $p eq 'new_status' ) {
184             $f->{ifnull} = 0;
185         }
186         if ($v eq 'yes') {
187             $f->{operator} = '!=';
188             push @{ $filter->{filters} }, $f;
189         } elsif ($v eq 'no') {
190             $f->{operator} = '=';
191             push @{ $filter->{filters} }, $f;
192         }
193     }
194
195     # null/is not null parameters
196     foreach my $p (qw( onloan )) {
197         my $v = $cgi->param($p) // '';
198         my $f = {
199             field => $p,
200             operator => "is",
201         };
202         if ( $v eq 'IS NOT NULL' ) {
203             $f->{query} = "not null";
204         } elsif ( $v eq 'IS NULL' ) {
205             $f->{query} = "null";
206         }
207         push @{ $filter->{filters} }, $f unless ( $v eq "" );
208     }
209
210     if (my $itemcallnumber_from = scalar $cgi->param('itemcallnumber_from')) {
211         push @{ $filter->{filters} }, {
212             field => 'itemcallnumber',
213             query => $itemcallnumber_from,
214             operator => '>=',
215         };
216     }
217     if (my $itemcallnumber_to = scalar $cgi->param('itemcallnumber_to')) {
218         push @{ $filter->{filters} }, {
219             field => 'itemcallnumber',
220             query => $itemcallnumber_to,
221             operator => '<=',
222         };
223     }
224
225     my $sortby = $cgi->param('sortby') || 'itemnumber';
226     if (C4::Context->preference("marcflavour") ne "UNIMARC" && $sortby eq 'publicationyear') {
227         $sortby = 'copyrightdate';
228     }
229     my $search_params = {
230         rows => scalar $cgi->param('rows') // 20,
231         page => scalar $cgi->param('page') || 1,
232         sortby => $sortby,
233         sortorder => scalar $cgi->param('sortorder') || 'asc',
234     };
235
236     my ($results, $total_rows) = SearchItems($filter, $search_params);
237
238     if ($format eq 'barcodes') {
239         print $cgi->header({
240             type => 'text/plain',
241             attachment => 'barcodes.txt',
242         });
243
244         foreach my $item (@$results) {
245             print $item->{barcode} . "\n";
246         }
247         exit;
248     }
249
250     if ($results) {
251         foreach my $item (@$results) {
252             my $biblio = Koha::Biblios->find( $item->{biblionumber} );
253             $item->{biblio} = $biblio;
254             $item->{biblioitem} = $biblio->biblioitem->unblessed;
255             my $checkout = Koha::Checkouts->find({ itemnumber => $item->{itemnumber} });
256             $item->{checkout} = $checkout;
257         }
258     }
259
260     $template->param(
261         filter => $filter,
262         search_params => $search_params,
263         results => $results,
264         total_rows => $total_rows,
265     );
266
267     if ($format eq 'csv') {
268         print $cgi->header({
269             type => 'text/csv',
270             attachment => 'items.csv',
271         });
272
273         for my $line ( split '\n', $template->output ) {
274             print "$line\n" unless $line =~ m|^\s*$|;
275         }
276     } elsif ($format eq 'json') {
277         $template->param(sEcho => scalar $cgi->param('sEcho'));
278         output_with_http_headers $cgi, $cookie, $template->output, 'json';
279     }
280
281     exit;
282 }
283
284 # Display the search form
285
286 my @branches = map { value => $_->branchcode, label => $_->branchname }, Koha::Libraries->search( {}, { order_by => 'branchname' } )->as_list;
287 my @itemtypes = map { value => $_->itemtype, label => $_->translated_description }, Koha::ItemTypes->search_with_localization->as_list;
288
289 my @ccodes = Koha::AuthorisedValues->get_descriptions_by_koha_field({ kohafield => 'items.ccode' });
290 foreach my $ccode (@ccodes) {
291     $ccode->{value} = $ccode->{authorised_value},
292     $ccode->{label} = $ccode->{lib},
293 }
294
295 my @itemlosts;
296 foreach my $value (@$itemlost_values) {
297     push @itemlosts, {
298         value => $value->{authorised_value},
299         label => $value->{lib},
300     };
301 }
302
303 my @withdrawns;
304 foreach my $value (@$withdrawn_values) {
305     push @withdrawns, {
306         value => $value->{authorised_value},
307         label => $value->{lib},
308     };
309 }
310
311 my @items_search_fields = GetItemSearchFields();
312
313 my $authorised_values = {};
314 foreach my $field (@items_search_fields) {
315     if (my $category = ($field->{authorised_values_category})) {
316         $authorised_values->{$category} = GetAuthorisedValues($category);
317     }
318 }
319
320 $template->param(
321     branches => \@branches,
322     itemtypes => \@itemtypes,
323     ccodes => \@ccodes,
324     itemlosts => \@itemlosts,
325     withdrawns => \@withdrawns,
326     items_search_fields => \@items_search_fields,
327     authorised_values_json => to_json($authorised_values),
328 );
329
330 output_html_with_http_headers $cgi, $cookie, $template->output;