Bug 26468: Add damaged as a searchable status in item search
[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 $mss = Koha::MarcSubfieldStructures->search(
112     {
113         frameworkcode    => '', kohafield => 'items.damaged',
114         authorised_value => [ -and => { '!=' => undef }, { '!=' => '' } ]
115     }
116 );
117 my $damaged_values = $mss->count ? GetAuthorisedValues( $mss->next->authorised_value ) : [];
118
119 if ( Koha::MarcSubfieldStructures->search( { frameworkcode => '', kohafield => 'items.new_status' } )->count ) {
120     $template->param( has_new_status => 1 );
121 }
122
123 if ( defined $format ) {
124     # Parameters given, it's a search
125
126     my $filter = {
127         conjunction => 'AND',
128         filters => [],
129     };
130
131     foreach my $p (
132         qw(homebranch holdingbranch location itype ccode issues datelastborrowed notforloan itemlost withdrawn damaged))
133     {
134         if ( my @q = $cgi->multi_param($p) ) {
135             if ( $q[0] ne '' ) {
136                 my $f = {
137                     field => $p,
138                     query => \@q,
139                 };
140                 if ( my $op = scalar $cgi->param( $p . '_op' ) ) {
141                     $f->{operator} = $op;
142                 }
143                 push @{ $filter->{filters} }, $f;
144             }
145         }
146     }
147
148     my @c = $cgi->multi_param('c');
149     my @fields = $cgi->multi_param('f');
150     my @q = $cgi->multi_param('q');
151     my @op = $cgi->multi_param('op');
152
153     my $f;
154     for (my $i = 0; $i < @fields; $i++) {
155         my $field = $fields[$i];
156         my $q = shift @q;
157         my $op = shift @op;
158         if (defined $q and $q ne '') {
159             if (C4::Context->preference("marcflavour") ne "UNIMARC" && $field eq 'publicationyear') {
160                 $field = 'copyrightdate';
161             }
162
163             if ($i == 0) {
164                 $f = {
165                     field => $field,
166                     query => $q,
167                     operator => $op,
168                 };
169             } else {
170                 my $c = shift @c;
171                 $f = {
172                     conjunction => $c,
173                     filters => [
174                         $f, {
175                             field => $field,
176                             query => $q,
177                             operator => $op,
178                         }
179                     ],
180                 };
181             }
182         }
183     }
184     push @{ $filter->{filters} }, $f;
185
186     # Yes/No parameters
187     foreach my $p (qw( new_status )) {
188         my $v = $cgi->param($p) // '';
189         my $f = {
190             field => $p,
191             query => 0,
192         };
193         if ( $p eq 'new_status' ) {
194             $f->{ifnull} = 0;
195         }
196         if ( $v eq 'yes' ) {
197             $f->{operator} = '!=';
198             push @{ $filter->{filters} }, $f;
199         } elsif ( $v eq 'no' ) {
200             $f->{operator} = '=';
201             push @{ $filter->{filters} }, $f;
202         }
203     }
204
205     # null/is not null parameters
206     foreach my $p (qw( onloan )) {
207         my $v = $cgi->param($p) // '';
208         my $f = {
209             field => $p,
210             operator => "is",
211         };
212         if ( $v eq 'IS NOT NULL' ) {
213             $f->{query} = "not null";
214         } elsif ( $v eq 'IS NULL' ) {
215             $f->{query} = "null";
216         }
217         push @{ $filter->{filters} }, $f unless ( $v eq "" );
218     }
219
220     if (my $itemcallnumber_from = scalar $cgi->param('itemcallnumber_from')) {
221         push @{ $filter->{filters} }, {
222             field => 'itemcallnumber',
223             query => $itemcallnumber_from,
224             operator => '>=',
225         };
226     }
227     if (my $itemcallnumber_to = scalar $cgi->param('itemcallnumber_to')) {
228         push @{ $filter->{filters} }, {
229             field => 'itemcallnumber',
230             query => $itemcallnumber_to,
231             operator => '<=',
232         };
233     }
234
235     my $sortby = $cgi->param('sortby') || 'itemnumber';
236     if (C4::Context->preference("marcflavour") ne "UNIMARC" && $sortby eq 'publicationyear') {
237         $sortby = 'copyrightdate';
238     }
239     my $search_params = {
240         rows => scalar $cgi->param('rows') // 20,
241         page => scalar $cgi->param('page') || 1,
242         sortby => $sortby,
243         sortorder => scalar $cgi->param('sortorder') || 'asc',
244     };
245
246     my ($results, $total_rows) = SearchItems($filter, $search_params);
247
248     if ($format eq 'barcodes') {
249         print $cgi->header({
250             type => 'text/plain',
251             attachment => 'barcodes.txt',
252         });
253
254         foreach my $item (@$results) {
255             print $item->{barcode} . "\n";
256         }
257         exit;
258     }
259
260     if ($results) {
261         foreach my $item (@$results) {
262             my $biblio = Koha::Biblios->find( $item->{biblionumber} );
263             $item->{biblio} = $biblio;
264             $item->{biblioitem} = $biblio->biblioitem->unblessed;
265             my $checkout = Koha::Checkouts->find({ itemnumber => $item->{itemnumber} });
266             $item->{checkout} = $checkout;
267         }
268     }
269
270     $template->param(
271         filter        => $filter,
272         search_params => $search_params,
273         results       => $results,
274         total_rows    => $total_rows,
275         user          => Koha::Patrons->find( $borrowernumber ),
276     );
277
278     if ($format eq 'csv') {
279         print $cgi->header({
280             type => 'text/csv',
281             attachment => 'items.csv',
282         });
283
284         for my $line ( split '\n', $template->output ) {
285             print "$line\n" unless $line =~ m|^\s*$|;
286         }
287     } elsif ($format eq 'json') {
288         $template->param(sEcho => scalar $cgi->param('sEcho'));
289         output_with_http_headers $cgi, $cookie, $template->output, 'json';
290     }
291
292     exit;
293 }
294
295 # Display the search form
296
297 my @branches = map { value => $_->branchcode, label => $_->branchname }, Koha::Libraries->search( {}, { order_by => 'branchname' } )->as_list;
298 my @itemtypes = map { value => $_->itemtype, label => $_->translated_description }, Koha::ItemTypes->search_with_localization->as_list;
299
300 my @ccodes = Koha::AuthorisedValues->get_descriptions_by_koha_field({ kohafield => 'items.ccode' });
301 foreach my $ccode (@ccodes) {
302     $ccode->{value} = $ccode->{authorised_value},
303     $ccode->{label} = $ccode->{lib},
304 }
305
306 my @itemlosts;
307 foreach my $value (@$itemlost_values) {
308     push @itemlosts, {
309         value => $value->{authorised_value},
310         label => $value->{lib},
311     };
312 }
313
314 my @withdrawns;
315 foreach my $value (@$withdrawn_values) {
316     push @withdrawns, {
317         value => $value->{authorised_value},
318         label => $value->{lib},
319     };
320 }
321
322 my @damageds;
323 foreach my $value (@$damaged_values) {
324     push @damageds, {
325         value => $value->{authorised_value},
326         label => $value->{lib},
327     };
328 }
329
330 my @items_search_fields = GetItemSearchFields();
331
332 my $authorised_values = {};
333 foreach my $field (@items_search_fields) {
334     if (my $category = ($field->{authorised_values_category})) {
335         $authorised_values->{$category} = GetAuthorisedValues($category);
336     }
337 }
338
339 $template->param(
340     branches => \@branches,
341     itemtypes => \@itemtypes,
342     ccodes => \@ccodes,
343     itemlosts => \@itemlosts,
344     withdrawns => \@withdrawns,
345     damageds => \@damageds,
346     items_search_fields => \@items_search_fields,
347     authorised_values_json => to_json($authorised_values),
348 );
349
350 output_html_with_http_headers $cgi, $cookie, $template->output;