Bug 36563: Dirty fix
[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 from_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
38 my $format = $cgi->param('format');
39 my $template_name = 'catalogue/itemsearch.tt';
40
41 if (defined $format and $format eq 'json') {
42     $template_name = 'catalogue/itemsearch_json.tt';
43
44     # Map DataTables parameters with 'regular' parameters
45     $cgi->param('rows', scalar $cgi->param('length'));
46     $cgi->param('page', (scalar $cgi->param('start') / scalar $cgi->param('length')) + 1);
47     my @columns = @{ from_json $cgi->param('columns') };
48     $cgi->param('sortby', $columns[ $cgi->param('order[0][column]') ]->{name});
49     $cgi->param('sortorder', scalar $cgi->param('order[0][dir]'));
50
51     my @f = $cgi->multi_param('f');
52     my @q = $cgi->multi_param('q');
53
54     # If index indicates the value is a barcode, we need to preproccess it before searching
55     for ( my $i = 0; $i < @q; $i++ ) {
56         $q[$i] = barcodedecode($q[$i]) if $f[$i] eq 'barcode';
57     }
58
59     push @q, '' if @q == 0;
60     my @op = $cgi->multi_param('op');
61     my @c = $cgi->multi_param('c');
62     for my $column (@columns) {
63         my $search = $column->{search}->{value};
64         my $column_name = $column->{name};
65         if (defined $search and $search ne '') {
66             my @words = split /\s+/, $search;
67             foreach my $word (@words) {
68                 push @f, $column_name;
69                 push @c, 'and';
70
71                 if ( grep { $_ eq $column_name } qw( ccode homebranch holdingbranch location itype notforloan itemlost onloan ) ) {
72                     push @q, "$word";
73                     push @op, '=';
74                 } else {
75                     push @q, "%$word%";
76                     push @op, 'like';
77                 }
78             }
79         }
80     }
81     $cgi->param('f', @f);
82     $cgi->param('q', @q);
83     $cgi->param('op', @op);
84     $cgi->param('c', @c);
85 } elsif (defined $format and $format eq 'csv') {
86     $template_name = 'catalogue/itemsearch_csv.tt';
87
88     # Retrieve all results
89     $cgi->param('rows', 0);
90 } elsif (defined $format and $format eq 'barcodes') {
91     # Retrieve all results
92     $cgi->param('rows', 0);
93 } elsif (defined $format) {
94     die "Unsupported format $format";
95 }
96
97 my ($template, $borrowernumber, $cookie) = get_template_and_user({
98     template_name => $template_name,
99     query => $cgi,
100     type => 'intranet',
101     flagsrequired   => { catalogue => 1 },
102 });
103
104 my $mss = Koha::MarcSubfieldStructures->search({ frameworkcode => '', kohafield => 'items.itemlost', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] });
105 my $itemlost_values = $mss->count ? GetAuthorisedValues($mss->next->authorised_value) : [];
106
107 $mss = Koha::MarcSubfieldStructures->search({ frameworkcode => '', kohafield => 'items.withdrawn', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] });
108 my $withdrawn_values = $mss->count ? GetAuthorisedValues($mss->next->authorised_value) : [];
109
110 $mss = Koha::MarcSubfieldStructures->search(
111     {
112         frameworkcode    => '', kohafield => 'items.damaged',
113         authorised_value => [ -and => { '!=' => undef }, { '!=' => '' } ]
114     }
115 );
116 my $damaged_values = $mss->count ? GetAuthorisedValues( $mss->next->authorised_value ) : [];
117
118 if ( Koha::MarcSubfieldStructures->search( { frameworkcode => '', kohafield => 'items.new_status' } )->count ) {
119     $template->param( has_new_status => 1 );
120 }
121
122 if ( defined $format ) {
123     # Parameters given, it's a search
124
125     my $filter = {
126         conjunction => 'AND',
127         filters => [],
128     };
129
130     foreach my $p (
131         qw(homebranch holdingbranch location itype ccode issues datelastborrowed notforloan itemlost withdrawn damaged))
132     {
133         if ( my @q = $cgi->multi_param($p . "[]") ) {
134             if ( $q[0] ne '' ) {
135                 my $f = {
136                     field => $p,
137                     query => \@q,
138                 };
139                 if ( my $op = scalar $cgi->param( $p . '_op' ) ) {
140                     $f->{operator} = $op;
141                 }
142                 push @{ $filter->{filters} }, $f;
143             }
144         }
145     }
146
147     my @c = $cgi->multi_param('c');
148     my @fields = $cgi->multi_param('f');
149     my @q = $cgi->multi_param('q');
150     my @op = $cgi->multi_param('op');
151
152     my $f;
153     for (my $i = 0; $i < @fields; $i++) {
154         my $field = $fields[$i];
155         my $q = shift @q;
156         my $op = shift @op;
157         if (defined $q and $q ne '') {
158             if (C4::Context->preference("marcflavour") ne "UNIMARC" && $field eq 'publicationyear') {
159                 $field = 'copyrightdate';
160             }
161
162             if ($i == 0) {
163                 $f = {
164                     field => $field,
165                     query => $q,
166                     operator => $op,
167                 };
168             } else {
169                 my $c = shift @c;
170                 $f = {
171                     conjunction => $c,
172                     filters => [
173                         $f, {
174                             field => $field,
175                             query => $q,
176                             operator => $op,
177                         }
178                     ],
179                 };
180             }
181         }
182     }
183     push @{ $filter->{filters} }, $f;
184
185     # Yes/No parameters
186     foreach my $p (qw( new_status )) {
187         my $v = $cgi->param($p) // '';
188         my $f = {
189             field => $p,
190             query => 0,
191         };
192         if ( $p eq 'new_status' ) {
193             $f->{ifnull} = 0;
194         }
195         if ( $v eq 'yes' ) {
196             $f->{operator} = '!=';
197             push @{ $filter->{filters} }, $f;
198         } elsif ( $v eq 'no' ) {
199             $f->{operator} = '=';
200             push @{ $filter->{filters} }, $f;
201         }
202     }
203
204     # null/is not null parameters
205     foreach my $p (qw( onloan )) {
206         my $v = $cgi->param($p) // '';
207         my $f = {
208             field => $p,
209             operator => "is",
210         };
211         if ( $v eq 'IS NOT NULL' ) {
212             $f->{query} = "not null";
213         } elsif ( $v eq 'IS NULL' ) {
214             $f->{query} = "null";
215         }
216         push @{ $filter->{filters} }, $f unless ( $v eq "" );
217     }
218
219     if (my $itemcallnumber_from = scalar $cgi->param('itemcallnumber_from')) {
220         push @{ $filter->{filters} }, {
221             field => 'itemcallnumber',
222             query => $itemcallnumber_from,
223             operator => '>=',
224         };
225     }
226     if (my $itemcallnumber_to = scalar $cgi->param('itemcallnumber_to')) {
227         push @{ $filter->{filters} }, {
228             field => 'itemcallnumber',
229             query => $itemcallnumber_to,
230             operator => '<=',
231         };
232     }
233
234     my $sortby = $cgi->param('sortby') || 'itemnumber';
235     if (C4::Context->preference("marcflavour") ne "UNIMARC" && $sortby eq 'publicationyear') {
236         $sortby = 'copyrightdate';
237     }
238     my $search_params = {
239         rows => scalar $cgi->param('rows') // 20,
240         page => scalar $cgi->param('page') || 1,
241         sortby => $sortby,
242         sortorder => scalar $cgi->param('sortorder') || 'asc',
243     };
244
245     my ($results, $total_rows) = SearchItems($filter, $search_params);
246
247     if ($format eq 'barcodes') {
248         print $cgi->header({
249             type => 'text/plain',
250             attachment => 'barcodes.txt',
251         });
252
253         foreach my $item (@$results) {
254             print $item->{barcode} . "\n";
255         }
256         exit;
257     }
258
259     if ($results) {
260         foreach my $item (@$results) {
261             my $biblio = Koha::Biblios->find( $item->{biblionumber} );
262             $item->{biblio} = $biblio;
263             $item->{biblioitem} = $biblio->biblioitem->unblessed;
264             my $checkout = Koha::Checkouts->find({ itemnumber => $item->{itemnumber} });
265             $item->{checkout} = $checkout;
266         }
267     }
268
269     $template->param(
270         filter        => $filter,
271         search_params => $search_params,
272         results       => $results,
273         total_rows    => $total_rows,
274         user          => Koha::Patrons->find( $borrowernumber ),
275     );
276
277     if ($format eq 'csv') {
278         print $cgi->header({
279             type => 'text/csv',
280             attachment => 'items.csv',
281         });
282
283         for my $line ( split '\n', $template->output ) {
284             print "$line\n" unless $line =~ m|^\s*$|;
285         }
286     } elsif ($format eq 'json') {
287         $template->param(draw => scalar $cgi->param('draw'));
288         output_with_http_headers $cgi, $cookie, $template->output, 'json';
289     }
290
291     exit;
292 }
293
294 # Display the search form
295
296 my @branches = map { value => $_->branchcode, label => $_->branchname }, Koha::Libraries->search( {}, { order_by => 'branchname' } )->as_list;
297 my @itemtypes = map { value => $_->itemtype, label => $_->translated_description }, Koha::ItemTypes->search_with_localization->as_list;
298
299 my @ccodes = Koha::AuthorisedValues->get_descriptions_by_koha_field({ kohafield => 'items.ccode' });
300 foreach my $ccode (@ccodes) {
301     $ccode->{value} = $ccode->{authorised_value},
302     $ccode->{label} = $ccode->{lib},
303 }
304
305 my @itemlosts;
306 foreach my $value (@$itemlost_values) {
307     push @itemlosts, {
308         value => $value->{authorised_value},
309         label => $value->{lib},
310     };
311 }
312
313 my @withdrawns;
314 foreach my $value (@$withdrawn_values) {
315     push @withdrawns, {
316         value => $value->{authorised_value},
317         label => $value->{lib},
318     };
319 }
320
321 my @damageds;
322 foreach my $value (@$damaged_values) {
323     push @damageds, {
324         value => $value->{authorised_value},
325         label => $value->{lib},
326     };
327 }
328
329 my @items_search_fields = GetItemSearchFields();
330
331 my $authorised_values = {};
332 foreach my $field (@items_search_fields) {
333     if (my $category = ($field->{authorised_values_category})) {
334         $authorised_values->{$category} = GetAuthorisedValues($category);
335     }
336 }
337
338 $template->param(
339     branches => \@branches,
340     itemtypes => \@itemtypes,
341     ccodes => \@ccodes,
342     itemlosts => \@itemlosts,
343     withdrawns => \@withdrawns,
344     damageds => \@damageds,
345     items_search_fields => \@items_search_fields,
346     authorised_values_json => to_json($authorised_values),
347 );
348
349 output_html_with_http_headers $cgi, $cookie, $template->output;