@@ -, +, @@ inventory script Last inventory date is useless in this case. Errors wrongplace, checkedout and changestatus are reported. Use this scenario for partial scanned barcode lists (all but last). by an optional last inventory date. Apart from the errors mentioned under [2], this also reports not_scanned ("missing") and no_barcode. Use this scenario too for the last partial barcode file (together with inventory date). --- .../prog/en/modules/tools/inventory.tt | 31 ++-- tools/inventory.pl | 201 ++++++++++----------- 2 files changed, 112 insertions(+), 120 deletions(-) --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/inventory.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/inventory.tt @@ -125,7 +125,7 @@ $(document).ready(function(){ Use a barcode file
  1. -
  2. +
@@ -203,8 +203,9 @@ $(document).ready(function(){
    [% END %] -
  1. - +
  2. + + (Skip records marked as seen on or after this date.)
  3. [% IF (ignoreissued) %] @@ -235,7 +236,6 @@ $(document).ready(function(){ [% END %] [% IF (op) %]
    - @@ -277,7 +277,8 @@ $(document).ready(function(){ [% result.location | html %] [% IF ( result.itemcallnumber ) %][[% result.itemcallnumber | html %]][% ELSE %][% END %] -

    [% result.title | html %]

    [% result.author | html %]

    + [% result.title | html %] +

    [% result.author | html %]

    [% result.notforloan | html %] @@ -295,14 +296,18 @@ $(document).ready(function(){ [% result.datelastseen | $KohaDates | html %] - [% IF result.problem == 'wrongplace' %] -

    Item should not have been scanned

    - [% ELSIF result.problem == 'missingitem' %] -

    Item missing

    - [% ELSIF result.problem == 'changestatus' %] -

    Unknown not-for-loan status

    - [% ELSIF result.problem == 'not_scanned' %] -

    Item should have been scanned

    + [% FOREACH problem IN result.problems %] + [% IF problem.key == 'wrongplace' %] + Found in wrong place
    + [% ELSIF problem.key == 'changestatus' %] + Unknown not-for-loan status
    + [% ELSIF problem.key == 'not_scanned' %] + Missing (not scanned)
    + [% ELSIF problem.key == 'checkedout' %] + Still checked out
    + [% ELSIF problem.key == 'no_barcode' %] + No barcode
    + [% END %] [% END %] --- a/tools/inventory.pl +++ a/tools/inventory.pl @@ -1,7 +1,7 @@ #!/usr/bin/perl # Copyright 2000-2009 Biblibre S.A -# John Soros +# John Soros # # This file is part of Koha. # @@ -18,8 +18,7 @@ # You should have received a copy of the GNU General Public License # along with Koha; if not, see . -use strict; -use warnings; +use Modern::Perl; #need to open cgi and get the fh before anything else opens a new cgi context (see C4::Auth) use CGI qw ( -utf8 ); @@ -39,15 +38,12 @@ use C4::Charset; use Koha::DateUtils; use List::MoreUtils qw( none ); - my $minlocation=$input->param('minlocation') || ''; my $maxlocation=$input->param('maxlocation'); $maxlocation=$minlocation.'Z' unless ( $maxlocation || ! $minlocation ); my $location=$input->param('location') || ''; -my $itemtype=$input->param('itemtype'); # FIXME note, template does not currently supply this my $ignoreissued=$input->param('ignoreissued'); -my $datelastseen = $input->param('datelastseen'); -my $markseen = $input->param('markseen'); +my $datelastseen = $input->param('datelastseen'); # last inventory date my $branchcode = $input->param('branchcode') || ''; my $branch = $input->param('branch'); my $op = $input->param('op'); @@ -115,9 +111,8 @@ for my $statfield (qw/items.notforloan items.itemlost items.withdrawn items.dama } } - $template->param( statuses => $statuses ); -my $staton = {}; #authorized values that are ticked +my $staton = {}; #authorized values that are ticked for my $authvfield (@$statuses) { $staton->{$authvfield->{fieldname}} = []; for my $authval (@{$authvfield->{values}}){ @@ -141,8 +136,11 @@ $template->param( compareinv2barcd => $compareinv2barcd, ); +# Walk through uploaded barcodes, report errors, mark as seen, check in +my $results = {}; my @scanned_items; my @errorloop; +my $moddatecount = 0; if ( $uploadbarcodes && length($uploadbarcodes) > 0 ) { my $dbh = C4::Context->dbh; my $date = dt_from_string( scalar $input->param('setdate') ); @@ -153,8 +151,6 @@ if ( $uploadbarcodes && length($uploadbarcodes) > 0 ) { $strsth="select * from items where items.barcode =? and items.withdrawn = 1"; my $qwithdrawn = $dbh->prepare($strsth); - my $count = 0; - my @barcodes; my @uploadedbarcodes; @@ -201,103 +197,79 @@ if ( $uploadbarcodes && length($uploadbarcodes) > 0 ) { } else { my $item = GetItem( '', $barcode ); if ( defined $item && $item->{'itemnumber'} ) { - ModItem( { datelastseen => $date }, undef, $item->{'itemnumber'} ); - push @scanned_items, $item; - $count++; + # Modify date last seen for scanned items, remove lost status + ModItem( { itemlost => 0, datelastseen => $date }, undef, $item->{'itemnumber'} ); + $moddatecount++; + # update item hash accordingly + $item->{itemlost} = 0; + $item->{datelastseen} = $date; unless ( $dont_checkin ) { $qonloan->execute($barcode); if ($qonloan->rows){ my $data = $qonloan->fetchrow_hashref; my ($doreturn, $messages, $iteminformation, $borrower) =AddReturn($barcode, $data->{homebranch}); - if ($doreturn){ - push @errorloop, {'barcode'=>$barcode,'ERR_ONLOAN_RET'=>1} + if( $doreturn ) { + $item->{onloan} = undef; + $item->{datelastseen} = dt_from_string; } else { - push @errorloop, {'barcode'=>$barcode,'ERR_ONLOAN_NOT_RET'=>1} + push @errorloop, { barcode => $barcode, ERR_ONLOAN_NOT_RET => 1 }; } } } + push @scanned_items, $item; } else { - push @errorloop, {'barcode'=>$barcode,'ERR_BARCODE'=>1}; + push @errorloop, { barcode => $barcode, ERR_BARCODE => 1 }; } } - } - - $template->param( date => $date, Number => $count ); + $template->param( date => $date ); $template->param( errorloop => \@errorloop ) if (@errorloop); } -# now build the result list: inventoried items if requested, and mis-placed items -always- -my $inventorylist; -my $wrongplacelist; -my @items_with_problems; -if ( $markseen or $op ) { - # retrieve all items in this range. - my $totalrecords; - - # We use datelastseen only when comparing the results to the barcode file. - my $paramdatelastseen = ($compareinv2barcd) ? $datelastseen : ''; - ($inventorylist, $totalrecords) = GetItemsForInventory( { +# Build inventorylist: used as result list when you do not pass barcodes +# This list is also used when you want to compare with barcodes +my ( $inventorylist, $rightplacelist ); +if ( $op && ( !$uploadbarcodes || $compareinv2barcd )) { + ( $inventorylist ) = GetItemsForInventory({ minlocation => $minlocation, maxlocation => $maxlocation, location => $location, - itemtype => $itemtype, ignoreissued => $ignoreissued, - datelastseen => $paramdatelastseen, + datelastseen => $datelastseen, branchcode => $branchcode, branch => $branch, offset => 0, - size => undef, statushash => $staton, interface => 'staff', - } ); - - # For the items that may be marked as "wrong place", we only check the location (callnumbers, location and branch) - ($wrongplacelist, $totalrecords) = GetItemsForInventory( { + }); +} +# Build rightplacelist used to check if a scanned item is in the right place. +if( @scanned_items ) { + ( $rightplacelist ) = GetItemsForInventory({ minlocation => $minlocation, maxlocation => $maxlocation, location => $location, - itemtype => undef, ignoreissued => undef, datelastseen => undef, branchcode => $branchcode, branch => $branch, offset => 0, - size => undef, statushash => undef, interface => 'staff', - } ); - + }); + # Convert the structure to a hash on barcode + $rightplacelist = { + map { $_->{barcode} ? ( $_->{barcode}, $_ ) : (); } @$rightplacelist + }; } -# If "compare barcodes list to results" has been checked, we want to alert for missing items -if ( $compareinv2barcd ) { - # set "missing" flags for all items with a datelastseen (dls) before the chosen datelastseen (cdls) - my $dls = output_pref( { dt => dt_from_string( $datelastseen ), - dateformat => 'iso' } ); - foreach my $item ( @$inventorylist ) { - my $cdls = output_pref( { dt => dt_from_string( $item->{datelastseen} ), - dateformat => 'iso' } ); - if ( $cdls lt $dls ) { - $item->{problem} = 'missingitem'; - # We have to push a copy of the item, not the reference - push @items_with_problems, { %$item }; - } - } -} - - - -# insert "wrongplace" to all scanned items that are not supposed to be in this range -# note this list is always displayed, whatever the librarian has chosen for comparison -my $moddatecount = 0; +# Report scanned items that are on the wrong place, or have a wrong notforloan +# status, or are still checked out. foreach my $item ( @scanned_items ) { + $item->{notforloancode} = $item->{notforloan}; # save for later use - # Saving notforloan code before it's replaced by it's authorised value for later comparison - $item->{notforloancode} = $item->{notforloan}; - - # Populating with authorised values - foreach my $field ( keys %$item ) { + # Populating with authorised values + foreach my $field ( keys %$item ) { # If the koha field is mapped to a marc field my $fc = $item->{'frameworkcode'} || ''; my ($f, $sf) = GetMarcFromKohaField("items.$field", $fc); @@ -310,54 +282,57 @@ foreach my $item ( @scanned_items ) { } } - next if $item->{onloan}; # skip checked out items - # If we have scanned items with a non-matching notforloan value - if (none { $item->{'notforloancode'} eq $_ } @notforloans) { - $item->{problem} = 'changestatus'; - push @items_with_problems, { %$item }; - } - if (none { $item->{barcode} eq $_->{barcode} && !$_->{'onloan'} } @$wrongplacelist) { - $item->{problem} = 'wrongplace'; - push @items_with_problems, { %$item }; + if( none { $item->{'notforloancode'} eq $_ } @notforloans ) { + $item->{problems}->{changestatus} = 1; + additemtoresults( $item, $results ); } - # Modify date last seen for scanned items - ModDateLastSeen($item->{'itemnumber'}); - $moddatecount++; + # Report an item that is checked out (unusual!) or wrongly placed + if( $item->{onloan} ) { + $item->{problems}->{checkedout} = 1; + additemtoresults( $item, $results ); + next; # do not modify item + } elsif( !exists $rightplacelist->{ $item->{barcode} } ) { + $item->{problems}->{wrongplace} = 1; + additemtoresults( $item, $results ); + } } +# Compare barcodes with inventory list, report no_barcode and not_scanned. +# not_scanned can be interpreted as missing if ( $compareinv2barcd ) { my @scanned_barcodes = map {$_->{barcode}} @scanned_items; - for my $should_be_scanned ( @$inventorylist ) { - my $barcode = $should_be_scanned->{barcode}; - unless ( grep /^$barcode$/, @scanned_barcodes ) { - $should_be_scanned->{problem} = 'not_scanned'; - push @items_with_problems, { %$should_be_scanned }; + for my $item ( @$inventorylist ) { + my $barcode = $item->{barcode}; + if( !$barcode ) { + $item->{problems}->{no_barcode} = 1; + } elsif ( grep /^$barcode$/, @scanned_barcodes ) { + next; + } else { + $item->{problems}->{not_scanned} = 1; } + additemtoresults( $item, $results ); } } -for my $item ( @items_with_problems ) { +# Construct final results, add biblio information +my $loop = $uploadbarcodes + ? [ map { $results->{$_} } keys %$results ] + : $inventorylist // []; +for my $item ( @$loop ) { my $biblio = C4::Biblio::GetBiblioData($item->{biblionumber}); $item->{title} = $biblio->{title}; $item->{author} = $biblio->{author}; } -# If a barcode file is given, we want to show problems, else all items -my @results; -@results = $uploadbarcodes - ? @items_with_problems - : $op - ? @$inventorylist - : (); - $template->param( moddatecount => $moddatecount, - loop => \@results, - op => $op + loop => $loop, + op => $op, ); +# Export to csv if (defined $input->param('CSVexport') && $input->param('CSVexport') eq 'on'){ eval {use Text::CSV}; my $csv = Text::CSV->new or @@ -392,22 +367,27 @@ if (defined $input->param('CSVexport') && $input->param('CSVexport') eq 'on'){ print $csv->string, "\n"; my @keys = qw / title author barcode itemnumber homebranch location itemcallnumber notforloan lost damaged withdrawn stocknumber /; - for my $item ( @results ) { + for my $item ( @$loop ) { my @line; for my $key (@keys) { push @line, $item->{$key}; } - if ( defined $item->{problem} ) { - if ( $item->{problem} eq 'wrongplace' ) { - push @line, "wrong place"; - } elsif ( $item->{problem} eq 'missingitem' ) { - push @line, "missing item"; - } elsif ( $item->{problem} eq 'changestatus' ) { - push @line, "change item status"; - } elsif ($item->{problem} eq 'not_scanned' ) { - push @line, "item not scanned"; + my $errstr = ''; + foreach my $key ( keys %{$item->{problems}} ) { + if( $key eq 'wrongplace' ) { + $errstr .= "wrong place,"; + } elsif( $key eq 'changestatus' ) { + $errstr .= "unknown notforloan status,"; + } elsif( $key eq 'not_scanned' ) { + $errstr .= "missing,"; + } elsif( $key eq 'no_barcode' ) { + $errstr .= "no barcode,"; + } elsif( $key eq 'checkedout' ) { + $errstr .= "checked out,"; } } + $errstr =~ s/,$//; + push @line, $errstr; $csv->combine(@line); print $csv->string, "\n"; } @@ -425,3 +405,10 @@ if (defined $input->param('CSVexport') && $input->param('CSVexport') eq 'on'){ } output_html_with_http_headers $input, $cookie, $template->output; + +sub additemtoresults { + my ( $item, $results ) = @_; + my $itemno = $item->{itemnumber}; + # since the script appends to $item, we can just overwrite the hash entry + $results->{$itemno} = $item; +} --