3 # Copyright 2000-2009 Biblibre S.A
4 # John Soros <john.soros@biblibre.com>
6 # This file is part of Koha.
8 # Koha is free software; you can redistribute it and/or modify it under the
9 # terms of the GNU General Public License as published by the Free Software
10 # Foundation; either version 2 of the License, or (at your option) any later
13 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
14 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License along
18 # with Koha; if not, write to the Free Software Foundation, Inc.,
19 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #need to open cgi and get the fh before anything else opens a new cgi context (see C4::Auth)
27 my $uploadbarcodes = $input->param('uploadbarcodes');
35 use C4
::Branch
; # GetBranches
37 use C4
::Reports
::Guided
; #_get_column_defs
40 use List
::MoreUtils
qw( none );
43 my $minlocation=$input->param('minlocation') || '';
44 my $maxlocation=$input->param('maxlocation');
45 $maxlocation=$minlocation.'Z' unless ( $maxlocation || ! $minlocation );
46 my $location=$input->param('location') || '';
47 my $itemtype=$input->param('itemtype'); # FIXME note, template does not currently supply this
48 my $ignoreissued=$input->param('ignoreissued');
49 my $datelastseen = $input->param('datelastseen');
50 my $markseen = $input->param('markseen');
51 my $branchcode = $input->param('branchcode') || '';
52 my $branch = $input->param('branch');
53 my $op = $input->param('op');
54 my $compareinv2barcd = $input->param('compareinv2barcd');
56 my ( $template, $borrowernumber, $cookie ) = get_template_and_user
(
57 { template_name
=> "tools/inventory.tt",
61 flagsrequired
=> { tools
=> 'inventory' },
67 my $branches = GetBranches
();
69 for my $branch_hash (keys %$branches) {
70 push @branch_loop, {value
=> "$branch_hash",
71 branchname
=> $branches->{$branch_hash}->{'branchname'},
72 selected
=> ($branch_hash eq $branchcode?
1:0)};
75 @branch_loop = sort {$a->{branchname
} cmp $b->{branchname
}} @branch_loop;
76 my @authorised_value_list;
77 my $authorisedvalue_categories = '';
79 my $frameworks = getframeworks
();
80 $frameworks->{''} = {frameworkcode
=> ''}; # Add the default framework
82 for my $fwk (keys %$frameworks){
83 my $fwkcode = $frameworks->{$fwk}->{'frameworkcode'};
84 my $authcode = GetAuthValCode
('items.location', $fwkcode);
85 if ($authcode && $authorisedvalue_categories!~/\b$authcode\W/){
86 $authorisedvalue_categories.="$authcode ";
87 my $data=GetAuthorisedValues
($authcode);
88 foreach my $value (@
$data){
89 $value->{selected
}=1 if ($value->{authorised_value
} eq ($location));
91 push @authorised_value_list,@
$data;
96 for my $statfield (qw
/items.notforloan items.itemlost items.withdrawn items.damaged/){
98 $hash->{fieldname
} = $statfield;
99 $hash->{authcode
} = GetAuthValCode
($statfield);
100 if ($hash->{authcode
}){
101 my $arr = GetAuthorisedValues
($hash->{authcode
});
102 $hash->{values} = $arr;
103 push @
$statuses, $hash;
108 $template->param( statuses
=> $statuses );
109 my $staton = {}; #authorized values that are ticked
110 for my $authvfield (@
$statuses) {
111 $staton->{$authvfield->{fieldname
}} = [];
112 for my $authval (@
{$authvfield->{values}}){
113 if ( defined $input->param('status-' . $authvfield->{fieldname
} . '-' . $authval->{authorised_value
}) && $input->param('status-' . $authvfield->{fieldname
} . '-' . $authval->{authorised_value
}) eq 'on' ){
114 push @
{$staton->{$authvfield->{fieldname
}}}, $authval->{authorised_value
};
121 for my $authvfield (@
$statuses) {
122 if ( scalar @
{$staton->{$authvfield->{fieldname
}}} > 0 ){
123 my $joinedvals = join ',', @
{$staton->{$authvfield->{fieldname
}}};
124 $statussth .= "$authvfield->{fieldname} in ($joinedvals) and ";
125 $notforloanlist = $joinedvals if ($authvfield->{fieldname
} eq "items.notforloan");
128 $statussth =~ s
, and $,,g
;
130 branchloop
=> \
@branch_loop,
131 authorised_values
=> \
@authorised_value_list,
132 today
=> dt_from_string
,
133 minlocation
=> $minlocation,
134 maxlocation
=> $maxlocation,
135 location
=> $location,
136 ignoreissued
=> $ignoreissued,
137 branchcode
=> $branchcode,
139 datelastseen
=> $datelastseen,
140 compareinv2barcd
=> $compareinv2barcd,
141 notforloanlist
=> $notforloanlist
145 if (defined $notforloanlist) {
146 @notforloans = split(/,/, $notforloanlist);
151 if ( $uploadbarcodes && length($uploadbarcodes) > 0 ) {
152 my $dbh = C4
::Context
->dbh;
153 my $date = dt_from_string
( $input->param('setdate') );
154 $date = output_pref
( { dt
=> $date, dateformat
=> 'iso' } );
156 my $strsth = "select * from issues, items where items.itemnumber=issues.itemnumber and items.barcode =?";
157 my $qonloan = $dbh->prepare($strsth);
158 $strsth="select * from items where items.barcode =? and items.withdrawn = 1";
159 my $qwithdrawn = $dbh->prepare($strsth);
165 my $sth = $dbh->column_info(undef,undef,"items","barcode");
166 my $barcode_def = $sth->fetchall_hashref('COLUMN_NAME');
167 my $barcode_size = $barcode_def->{barcode
}->{COLUMN_SIZE
};
171 binmode($uploadbarcodes, ":encoding(UTF-8)");
172 while (my $barcode=<$uploadbarcodes>){
173 $barcode =~ s/\r?\n$//;
174 next unless $barcode;
176 if (length($barcode)>$barcode_size) {
179 my $check_barcode = $barcode;
180 $check_barcode =~ s/\p{Print}//g;
181 if (length($check_barcode)>0) { # Only printable unicode characters allowed.
184 next if length($barcode)>$barcode_size;
185 next if ( length($check_barcode)>0 );
186 push @barcodes,$barcode;
188 $template->param( LinesRead
=> $lines_read );
190 push @errorloop, {'barcode'=>'No valid barcodes!'};
191 $op=''; # force the initial inventory screen again.
194 $template->param( err_length
=> $err_length,
195 err_data
=> $err_data );
197 foreach my $barcode (@barcodes) {
198 if ( $qwithdrawn->execute($barcode) && $qwithdrawn->rows ) {
199 push @errorloop, { 'barcode' => $barcode, 'ERR_WTHDRAWN' => 1 };
201 my $item = GetItem
( '', $barcode );
202 if ( defined $item && $item->{'itemnumber'} ) {
203 ModItem
( { datelastseen
=> $date }, undef, $item->{'itemnumber'} );
204 push @scanned_items, $item;
206 $qonloan->execute($barcode);
208 my $data = $qonloan->fetchrow_hashref;
209 my ($doreturn, $messages, $iteminformation, $borrower) =AddReturn
($barcode, $data->{homebranch
});
211 push @errorloop, {'barcode'=>$barcode,'ERR_ONLOAN_RET'=>1}
213 push @errorloop, {'barcode'=>$barcode,'ERR_ONLOAN_NOT_RET'=>1}
217 push @errorloop, {'barcode'=>$barcode,'ERR_BARCODE'=>1};
223 $template->param( date
=> $date, Number
=> $count );
224 $template->param( errorloop
=> \
@errorloop ) if (@errorloop);
227 # now build the result list: inventoried items if requested, and mis-placed items -always-
229 my @items_with_problems;
230 if ( $markseen or $op ) {
231 # retrieve all items in this range.
233 ($inventorylist, $totalrecords) = GetItemsForInventory
($minlocation, $maxlocation, $location, $itemtype, $ignoreissued, '', $branchcode, $branch, 0, undef , $staton);
236 # If "compare barcodes list to results" has been checked, we want to alert for missing items
237 if ( $compareinv2barcd ) {
238 # set "missing" flags for all items with a datelastseen (dls) before the choosen datelastseen (cdls)
239 my $dls = output_pref
( { dt
=> dt_from_string
( $datelastseen ),
240 dateformat
=> 'iso' } );
241 foreach my $item ( @
$inventorylist ) {
242 my $cdls = output_pref
( { dt
=> dt_from_string
( $_->{datelastseen
} ),
243 dateformat
=> 'iso' } );
244 if ( $cdls lt $dls ) {
245 $item->{problem
} = 'missingitem';
246 # We have to push a copy of the item, not the reference
247 push @items_with_problems, { %$item };
254 # insert "wrongplace" to all scanned items that are not supposed to be in this range
255 # note this list is always displayed, whatever the librarian has choosen for comparison
256 my $moddatecount = 0;
257 foreach my $item ( @scanned_items ) {
259 # Saving notforloan code before it's replaced by it's authorised value for later comparison
260 $item->{notforloancode
} = $item->{notforloan
};
262 # Populating with authorised values
263 foreach my $field ( keys %$item ) {
264 # If the koha field is mapped to a marc field
265 my $fc = $item->{'frameworkcode'} || '';
266 my ($f, $sf) = GetMarcFromKohaField
("items.$field", $fc);
268 # We replace the code with it's description
269 my $authvals = C4
::Koha
::GetKohaAuthorisedValuesFromField
($f, $sf, $fc);
270 if ($authvals and defined $item->{$field} and defined $authvals->{$item->{$field}}) {
271 $item->{$field} = $authvals->{$item->{$field}};
276 next if $item->{onloan
}; # skip checked out items
278 # If we have scanned items with a non-matching notforloan value
279 if (none
{ $item->{'notforloancode'} eq $_ } @notforloans) {
280 $item->{problem
} = 'changestatus';
281 push @items_with_problems, { %$item };
283 if (none
{ $item->{barcode
} eq $_->{barcode
} && !$_->{'onloan'} } @
$inventorylist) {
284 $item->{problem
} = 'wrongplace';
285 push @items_with_problems, { %$item };
288 # Modify date last seen for scanned items
289 ModDateLastSeen
($_->{'itemnumber'});
293 if ( $compareinv2barcd ) {
294 my @scanned_barcodes = map {$_->{barcode
}} @scanned_items;
295 for my $should_be_scanned ( @
$inventorylist ) {
296 my $barcode = $should_be_scanned->{barcode
};
297 unless ( grep /^$barcode$/, @scanned_barcodes ) {
298 $should_be_scanned->{problem
} = 'not_scanned';
299 push @items_with_problems, { %$should_be_scanned };
304 for my $item ( @items_with_problems ) {
305 my $biblio = C4
::Biblio
::GetBiblioData
($item->{biblionumber
});
306 $item->{title
} = $biblio->{title
};
307 $item->{author
} = $biblio->{author
};
310 # If a barcode file is given, we want to show problems, else all items
312 @results = $uploadbarcodes
313 ?
@items_with_problems
319 moddatecount
=> $moddatecount,
324 if (defined $input->param('CSVexport') && $input->param('CSVexport') eq 'on'){
325 eval {use Text
::CSV
};
326 my $csv = Text
::CSV
->new or
327 die Text
::CSV
->error_diag ();
328 binmode STDOUT
, ":encoding(UTF-8)";
329 print $input->header(
331 -attachment
=> 'inventory.csv',
334 my $columns_def_hashref = C4
::Reports
::Guided
::_get_column_defs
($input);
335 foreach my $key ( keys %$columns_def_hashref ) {
337 $key =~ s/[^\.]*\.//;
338 $columns_def_hashref->{$initkey}=NormalizeString
($columns_def_hashref->{$initkey} // '');
339 $columns_def_hashref->{$key} = $columns_def_hashref->{$initkey};
343 for my $key (qw
/ biblioitems
.title biblio
.author
344 items
.barcode items
.itemnumber
345 items
.homebranch items
.location
346 items
.itemcallnumber items
.notforloan
347 items
.itemlost items
.damaged
348 items
.withdrawn items
.stocknumber
350 push @translated_keys, $columns_def_hashref->{$key};
352 push @translated_keys, 'problem' if $uploadbarcodes;
354 $csv->combine(@translated_keys);
355 print $csv->string, "\n";
357 my @keys = qw
/ title author barcode itemnumber homebranch location itemcallnumber notforloan lost damaged withdrawn stocknumber /;
358 for my $item ( @results ) {
360 for my $key (@keys) {
361 push @line, $item->{$key};
363 if ( defined $item->{problem
} ) {
364 if ( $item->{problem
} eq 'wrongplace' ) {
365 push @line, "wrong place";
366 } elsif ( $item->{problem
} eq 'missingitem' ) {
367 push @line, "missing item";
368 } elsif ( $item->{problem
} eq 'changestatus' ) {
369 push @line, "change item status";
370 } elsif ($item->{problem
} eq 'not_scanned' ) {
371 push @line, "item not scanned";
374 $csv->combine(@line);
375 print $csv->string, "\n";
377 # Adding not found barcodes
378 foreach my $error (@errorloop) {
380 if ($error->{'ERR_BARCODE'}) {
381 push @line, map { $_ eq 'barcode' ?
$error->{'barcode'} : ''} @keys;
382 push @line, "barcode not found";
383 $csv->combine(@line);
384 print $csv->string, "\n";
390 output_html_with_http_headers
$input, $cookie, $template->output;