| Lines 1-7
          
      
      
        Link Here | 
        
          | 1 | #!/usr/bin/perl | 1 | #!/usr/bin/perl | 
        
          | 2 |  | 2 |  | 
        
          | 3 | # Copyright 2000-2009 Biblibre S.A | 3 | # Copyright 2000-2009 Biblibre S.A | 
          
            
              | 4 | #                                         John Soros <john.soros@biblibre.com> | 4 | # John Soros <john.soros@biblibre.com> | 
        
          | 5 | # | 5 | # | 
        
          | 6 | # This file is part of Koha. | 6 | # This file is part of Koha. | 
        
          | 7 | # | 7 | # | 
  
    | Lines 18-25
          
      
      
        Link Here | 
        
          | 18 | # You should have received a copy of the GNU General Public License | 18 | # You should have received a copy of the GNU General Public License | 
        
          | 19 | # along with Koha; if not, see <http://www.gnu.org/licenses>. | 19 | # along with Koha; if not, see <http://www.gnu.org/licenses>. | 
        
          | 20 |  | 20 |  | 
          
            
              | 21 | use strict; | 21 | use Modern::Perl; | 
            
              | 22 | use warnings; |  |  | 
        
          | 23 |  | 22 |  | 
        
          | 24 | #need to open cgi and get the fh before anything else opens a new cgi context (see C4::Auth) | 23 | #need to open cgi and get the fh before anything else opens a new cgi context (see C4::Auth) | 
        
          | 25 | use CGI qw ( -utf8 ); | 24 | use CGI qw ( -utf8 ); | 
  
    | Lines 40-54
          use Koha::AuthorisedValues;
      
      
        Link Here | 
        
          | 40 | use Koha::BiblioFrameworks; | 39 | use Koha::BiblioFrameworks; | 
        
          | 41 | use List::MoreUtils qw( none ); | 40 | use List::MoreUtils qw( none ); | 
        
          | 42 |  | 41 |  | 
            
              | 43 |  |  |  | 
        
          | 44 | my $minlocation=$input->param('minlocation') || ''; | 42 | my $minlocation=$input->param('minlocation') || ''; | 
        
          | 45 | my $maxlocation=$input->param('maxlocation'); | 43 | my $maxlocation=$input->param('maxlocation'); | 
        
          | 46 | $maxlocation=$minlocation.'Z' unless ( $maxlocation || ! $minlocation ); | 44 | $maxlocation=$minlocation.'Z' unless ( $maxlocation || ! $minlocation ); | 
        
          | 47 | my $location=$input->param('location') || ''; | 45 | my $location=$input->param('location') || ''; | 
            
              | 48 | my $itemtype=$input->param('itemtype'); # FIXME note, template does not currently supply this |  |  | 
        
          | 49 | my $ignoreissued=$input->param('ignoreissued'); | 46 | my $ignoreissued=$input->param('ignoreissued'); | 
          
            
              | 50 | my $datelastseen = $input->param('datelastseen'); | 47 | my $datelastseen = $input->param('datelastseen'); # last inventory date | 
            
              | 51 | my $markseen = $input->param('markseen'); |  |  | 
        
          | 52 | my $branchcode = $input->param('branchcode') || ''; | 48 | my $branchcode = $input->param('branchcode') || ''; | 
        
          | 53 | my $branch     = $input->param('branch'); | 49 | my $branch     = $input->param('branch'); | 
        
          | 54 | my $op         = $input->param('op'); | 50 | my $op         = $input->param('op'); | 
  
    | Lines 105-113
          for my $statfield (qw/items.notforloan items.itemlost items.withdrawn items.dama
      
      
        Link Here | 
        
          | 105 |     } | 101 |     } | 
        
          | 106 | } | 102 | } | 
        
          | 107 |  | 103 |  | 
            
              | 108 |  |  |  | 
        
          | 109 | $template->param( statuses => $statuses ); | 104 | $template->param( statuses => $statuses ); | 
          
            
              | 110 | my $staton = {};                                #authorized values that are ticked | 105 | my $staton = {}; #authorized values that are ticked | 
        
          | 111 | for my $authvfield (@$statuses) { | 106 | for my $authvfield (@$statuses) { | 
        
          | 112 |     $staton->{$authvfield->{fieldname}} = []; | 107 |     $staton->{$authvfield->{fieldname}} = []; | 
        
          | 113 |     for my $authval (@{$authvfield->{values}}){ | 108 |     for my $authval (@{$authvfield->{values}}){ | 
  
    | Lines 130-137
          $template->param(
      
      
        Link Here | 
        
          | 130 |     compareinv2barcd         => $compareinv2barcd, | 125 |     compareinv2barcd         => $compareinv2barcd, | 
        
          | 131 | ); | 126 | ); | 
        
          | 132 |  | 127 |  | 
            
              |  |  | 128 | # Walk through uploaded barcodes, report errors, mark as seen, check in | 
            
              | 129 | my $results = {}; | 
        
          | 133 | my @scanned_items; | 130 | my @scanned_items; | 
        
          | 134 | my @errorloop; | 131 | my @errorloop; | 
            
              |  |  | 132 | my $moddatecount = 0; | 
        
          | 135 | if ( $uploadbarcodes && length($uploadbarcodes) > 0 ) { | 133 | if ( $uploadbarcodes && length($uploadbarcodes) > 0 ) { | 
        
          | 136 |     my $dbh = C4::Context->dbh; | 134 |     my $dbh = C4::Context->dbh; | 
        
          | 137 |     my $date = dt_from_string( scalar $input->param('setdate') ); | 135 |     my $date = dt_from_string( scalar $input->param('setdate') ); | 
  
    | Lines 142-149
          if ( $uploadbarcodes && length($uploadbarcodes) > 0 ) {
      
      
        Link Here | 
        
          | 142 |     $strsth="select * from items where items.barcode =? and items.withdrawn = 1"; | 140 |     $strsth="select * from items where items.barcode =? and items.withdrawn = 1"; | 
        
          | 143 |     my $qwithdrawn = $dbh->prepare($strsth); | 141 |     my $qwithdrawn = $dbh->prepare($strsth); | 
        
          | 144 |  | 142 |  | 
            
              | 145 |     my $count = 0; |  |  | 
            
              | 146 |  | 
        
          | 147 |     my @barcodes; | 143 |     my @barcodes; | 
        
          | 148 |     my @uploadedbarcodes; | 144 |     my @uploadedbarcodes; | 
        
          | 149 |  | 145 |  | 
  
    | Lines 190-290
          if ( $uploadbarcodes && length($uploadbarcodes) > 0 ) {
      
      
        Link Here | 
        
          | 190 |         } else { | 186 |         } else { | 
        
          | 191 |             my $item = GetItem( '', $barcode ); | 187 |             my $item = GetItem( '', $barcode ); | 
        
          | 192 |             if ( defined $item && $item->{'itemnumber'} ) { | 188 |             if ( defined $item && $item->{'itemnumber'} ) { | 
          
            
              | 193 |                 ModItem( { datelastseen => $date }, undef, $item->{'itemnumber'} ); | 189 |                 # Modify date last seen for scanned items, remove lost status | 
            
              | 194 |                 push @scanned_items, $item; | 190 |                 ModItem( { itemlost => 0, datelastseen => $date }, undef, $item->{'itemnumber'} ); | 
            
              | 195 |                 $count++; | 191 |                 $moddatecount++; | 
            
              |  |  | 192 |                 # update item hash accordingly | 
            
              | 193 |                 $item->{itemlost} = 0; | 
            
              | 194 |                 $item->{datelastseen} = $date; | 
        
          | 196 |                 unless ( $dont_checkin ) { | 195 |                 unless ( $dont_checkin ) { | 
        
          | 197 |                     $qonloan->execute($barcode); | 196 |                     $qonloan->execute($barcode); | 
        
          | 198 |                     if ($qonloan->rows){ | 197 |                     if ($qonloan->rows){ | 
        
          | 199 |                         my $data = $qonloan->fetchrow_hashref; | 198 |                         my $data = $qonloan->fetchrow_hashref; | 
        
          | 200 |                         my ($doreturn, $messages, $iteminformation, $borrower) =AddReturn($barcode, $data->{homebranch}); | 199 |                         my ($doreturn, $messages, $iteminformation, $borrower) =AddReturn($barcode, $data->{homebranch}); | 
          
            
              | 201 |                         if ($doreturn){ | 200 |                         if( $doreturn ) { | 
            
              | 202 |                             push @errorloop, {'barcode'=>$barcode,'ERR_ONLOAN_RET'=>1} | 201 |                             $item->{onloan} = undef; | 
            
              |  |  | 202 |                             $item->{datelastseen} = dt_from_string; | 
        
          | 203 |                         } else { | 203 |                         } else { | 
          
            
              | 204 |                             push @errorloop, {'barcode'=>$barcode,'ERR_ONLOAN_NOT_RET'=>1} | 204 |                             push @errorloop, { barcode => $barcode, ERR_ONLOAN_NOT_RET => 1 }; | 
        
          | 205 |                         } | 205 |                         } | 
        
          | 206 |                     } | 206 |                     } | 
        
          | 207 |                 } | 207 |                 } | 
            
              |  |  | 208 |                 push @scanned_items, $item; | 
        
          | 208 |             } else { | 209 |             } else { | 
          
            
              | 209 |                 push @errorloop, {'barcode'=>$barcode,'ERR_BARCODE'=>1}; | 210 |                 push @errorloop, { barcode => $barcode, ERR_BARCODE => 1 }; | 
        
          | 210 |             } | 211 |             } | 
        
          | 211 |         } | 212 |         } | 
            
              | 212 |  |  |  | 
        
          | 213 |     } | 213 |     } | 
          
            
              | 214 |  | 214 |     $template->param( date => $date ); | 
            
              | 215 |     $template->param( date => $date, Number => $count ); |  |  | 
        
          | 216 |     $template->param( errorloop => \@errorloop ) if (@errorloop); | 215 |     $template->param( errorloop => \@errorloop ) if (@errorloop); | 
        
          | 217 | } | 216 | } | 
        
          | 218 |  | 217 |  | 
          
            
              | 219 | # now build the result list: inventoried items if requested, and mis-placed items -always- | 218 | # Build inventorylist: used as result list when you do not pass barcodes | 
            
              | 220 | my $inventorylist; | 219 | # This list is also used when you want to compare with barcodes | 
            
              | 221 | my $wrongplacelist; | 220 | my ( $inventorylist, $rightplacelist ); | 
            
              | 222 | my @items_with_problems; | 221 | if ( $op && ( !$uploadbarcodes || $compareinv2barcd )) { | 
            
              | 223 | if ( $markseen or $op ) { | 222 |     ( $inventorylist ) = GetItemsForInventory({ | 
            
              | 224 |     # retrieve all items in this range. |  |  | 
            
              | 225 |     my $totalrecords; | 
            
              | 226 |  | 
            
              | 227 |     # We use datelastseen only when comparing the results to the barcode file. | 
            
              | 228 |     my $paramdatelastseen = ($compareinv2barcd) ? $datelastseen : ''; | 
            
              | 229 |     ($inventorylist, $totalrecords) = GetItemsForInventory( { | 
        
          | 230 |       minlocation  => $minlocation, | 223 |       minlocation  => $minlocation, | 
        
          | 231 |       maxlocation  => $maxlocation, | 224 |       maxlocation  => $maxlocation, | 
        
          | 232 |       location     => $location, | 225 |       location     => $location, | 
            
              | 233 |       itemtype     => $itemtype, |  |  | 
        
          | 234 |       ignoreissued => $ignoreissued, | 226 |       ignoreissued => $ignoreissued, | 
          
            
              | 235 |       datelastseen => $paramdatelastseen, | 227 |       datelastseen => $datelastseen, | 
        
          | 236 |       branchcode   => $branchcode, | 228 |       branchcode   => $branchcode, | 
        
          | 237 |       branch       => $branch, | 229 |       branch       => $branch, | 
        
          | 238 |       offset       => 0, | 230 |       offset       => 0, | 
            
              | 239 |       size         => undef, |  |  | 
        
          | 240 |       statushash   => $staton, | 231 |       statushash   => $staton, | 
          
            
              | 241 |     } ); | 232 |     }); | 
            
              | 242 |  | 233 | } | 
            
              | 243 |     # For the items that may be marked as "wrong place", we only check the location (callnumbers, location and branch) | 234 | # Build rightplacelist used to check if a scanned item is in the right place. | 
            
              | 244 |     ($wrongplacelist, $totalrecords) = GetItemsForInventory( { | 235 | if( @scanned_items ) { | 
            
              |  |  | 236 |     ( $rightplacelist ) = GetItemsForInventory({ | 
        
          | 245 |       minlocation  => $minlocation, | 237 |       minlocation  => $minlocation, | 
        
          | 246 |       maxlocation  => $maxlocation, | 238 |       maxlocation  => $maxlocation, | 
        
          | 247 |       location     => $location, | 239 |       location     => $location, | 
            
              | 248 |       itemtype     => undef, |  |  | 
        
          | 249 |       ignoreissued => undef, | 240 |       ignoreissued => undef, | 
        
          | 250 |       datelastseen => undef, | 241 |       datelastseen => undef, | 
        
          | 251 |       branchcode   => $branchcode, | 242 |       branchcode   => $branchcode, | 
        
          | 252 |       branch       => $branch, | 243 |       branch       => $branch, | 
        
          | 253 |       offset       => 0, | 244 |       offset       => 0, | 
            
              | 254 |       size         => undef, |  |  | 
        
          | 255 |       statushash   => undef, | 245 |       statushash   => undef, | 
          
            
              | 256 |     } ); | 246 |     }); | 
            
              | 257 |  | 247 |     # Convert the structure to a hash on barcode | 
            
              | 258 | } | 248 |     $rightplacelist = { | 
            
              | 259 |  | 249 |         map { $_->{barcode} ? ( $_->{barcode}, $_ ) : (); } @$rightplacelist | 
            
              | 260 | # If "compare barcodes list to results" has been checked, we want to alert for missing items | 250 |     }; | 
            
              | 261 | if ( $compareinv2barcd ) { |  |  | 
            
              | 262 |     # set "missing" flags for all items with a datelastseen (dls) before the chosen datelastseen (cdls) | 
            
              | 263 |     my $dls = output_pref( { dt => dt_from_string( $datelastseen ), | 
            
              | 264 |                              dateformat => 'iso' } ); | 
            
              | 265 |     foreach my $item ( @$inventorylist ) { | 
            
              | 266 |         my $cdls = output_pref( { dt => dt_from_string( $item->{datelastseen} ), | 
            
              | 267 |                                   dateformat => 'iso' } ); | 
            
              | 268 |         if ( $cdls lt $dls ) { | 
            
              | 269 |             $item->{problem} = 'missingitem'; | 
            
              | 270 |             # We have to push a copy of the item, not the reference | 
            
              | 271 |             push @items_with_problems, { %$item }; | 
            
              | 272 |         } | 
            
              | 273 |     } | 
        
          | 274 | } | 251 | } | 
        
          | 275 |  | 252 |  | 
          
            
              | 276 |  | 253 | # Report scanned items that are on the wrong place, or have a wrong notforloan | 
            
              | 277 |  | 254 | # status, or are still checked out. | 
            
              | 278 | # insert "wrongplace" to all scanned items that are not supposed to be in this range |  |  | 
            
              | 279 | # note this list is always displayed, whatever the librarian has chosen for comparison | 
            
              | 280 | my $moddatecount = 0; | 
        
          | 281 | foreach my $item ( @scanned_items ) { | 255 | foreach my $item ( @scanned_items ) { | 
            
              |  |  | 256 |     $item->{notforloancode} = $item->{notforloan}; # save for later use | 
        
          | 282 |  | 257 |  | 
          
            
              | 283 |   # Saving notforloan code before it's replaced by it's authorised value for later comparison | 258 |     # Populating with authorised values | 
            
              | 284 |   $item->{notforloancode} = $item->{notforloan}; | 259 |     foreach my $field ( keys %$item ) { | 
            
              | 285 |  |  |  | 
            
              | 286 |   # Populating with authorised values | 
            
              | 287 |   foreach my $field ( keys %$item ) { | 
        
          | 288 |         # If the koha field is mapped to a marc field | 260 |         # If the koha field is mapped to a marc field | 
        
          | 289 |         my $fc = $item->{'frameworkcode'} || ''; | 261 |         my $fc = $item->{'frameworkcode'} || ''; | 
        
          | 290 |         my ($f, $sf) = GetMarcFromKohaField("items.$field", $fc); | 262 |         my ($f, $sf) = GetMarcFromKohaField("items.$field", $fc); | 
  
    | Lines 299-352
          foreach my $item ( @scanned_items ) {
      
      
        Link Here | 
        
          | 299 |         } | 271 |         } | 
        
          | 300 |     } | 272 |     } | 
        
          | 301 |  | 273 |  | 
            
              | 302 |     next if $item->{onloan}; # skip checked out items |  |  | 
            
              | 303 |  | 
        
          | 304 |     # If we have scanned items with a non-matching notforloan value | 274 |     # If we have scanned items with a non-matching notforloan value | 
          
            
              | 305 |     if (none { $item->{'notforloancode'} eq $_ } @notforloans) { | 275 |     if( none { $item->{'notforloancode'} eq $_ } @notforloans ) { | 
            
              | 306 |         $item->{problem} = 'changestatus'; | 276 |         $item->{problems}->{changestatus} = 1; | 
            
              | 307 |         push @items_with_problems, { %$item }; | 277 |         additemtoresults( $item, $results ); | 
            
              | 308 |     } |  |  | 
            
              | 309 |     if (none { $item->{barcode} eq $_->{barcode} && !$_->{'onloan'} } @$wrongplacelist) { | 
            
              | 310 |         $item->{problem} = 'wrongplace'; | 
            
              | 311 |         push @items_with_problems, { %$item }; | 
        
          | 312 |     } | 278 |     } | 
        
          | 313 |  | 279 |  | 
          
            
              | 314 |     # Modify date last seen for scanned items | 280 |     # Report an item that is checked out (unusual!) or wrongly placed | 
            
              | 315 |     ModDateLastSeen($item->{'itemnumber'}); | 281 |     if( $item->{onloan} ) { | 
            
              | 316 |     $moddatecount++; | 282 |         $item->{problems}->{checkedout} = 1; | 
            
              |  |  | 283 |         additemtoresults( $item, $results ); | 
            
              | 284 |         next; # do not modify item | 
            
              | 285 |     } elsif( !exists $rightplacelist->{ $item->{barcode} } ) { | 
            
              | 286 |         $item->{problems}->{wrongplace} = 1; | 
            
              | 287 |         additemtoresults( $item, $results ); | 
            
              | 288 |     } | 
        
          | 317 | } | 289 | } | 
        
          | 318 |  | 290 |  | 
            
              |  |  | 291 | # Compare barcodes with inventory list, report no_barcode and not_scanned. | 
            
              | 292 | # not_scanned can be interpreted as missing | 
        
          | 319 | if ( $compareinv2barcd ) { | 293 | if ( $compareinv2barcd ) { | 
        
          | 320 |     my @scanned_barcodes = map {$_->{barcode}} @scanned_items; | 294 |     my @scanned_barcodes = map {$_->{barcode}} @scanned_items; | 
          
            
              | 321 |     for my $should_be_scanned ( @$inventorylist ) { | 295 |     for my $item ( @$inventorylist ) { | 
            
              | 322 |         my $barcode = $should_be_scanned->{barcode}; | 296 |         my $barcode = $item->{barcode}; | 
            
              | 323 |         unless ( grep /^$barcode$/, @scanned_barcodes ) { | 297 |         if( !$barcode ) { | 
            
              | 324 |             $should_be_scanned->{problem} = 'not_scanned'; | 298 |             $item->{problems}->{no_barcode} = 1; | 
            
              | 325 |             push @items_with_problems, { %$should_be_scanned }; | 299 |         } elsif ( grep /^$barcode$/, @scanned_barcodes ) { | 
            
              |  |  | 300 |             next; | 
            
              | 301 |         } else { | 
            
              | 302 |             $item->{problems}->{not_scanned} = 1; | 
        
          | 326 |         } | 303 |         } | 
            
              |  |  | 304 |         additemtoresults( $item, $results ); | 
        
          | 327 |     } | 305 |     } | 
        
          | 328 | } | 306 | } | 
        
          | 329 |  | 307 |  | 
          
            
              | 330 | for my $item ( @items_with_problems ) { | 308 | # Construct final results, add biblio information | 
            
              |  |  | 309 | my $loop = $uploadbarcodes | 
            
              | 310 |     ? [ map { $results->{$_} } keys %$results ] | 
            
              | 311 |     : $inventorylist // []; | 
            
              | 312 | for my $item ( @$loop ) { | 
        
          | 331 |     my $biblio = C4::Biblio::GetBiblioData($item->{biblionumber}); | 313 |     my $biblio = C4::Biblio::GetBiblioData($item->{biblionumber}); | 
        
          | 332 |     $item->{title} = $biblio->{title}; | 314 |     $item->{title} = $biblio->{title}; | 
        
          | 333 |     $item->{author} = $biblio->{author}; | 315 |     $item->{author} = $biblio->{author}; | 
        
          | 334 | } | 316 | } | 
        
          | 335 |  | 317 |  | 
            
              | 336 | # If a barcode file is given, we want to show problems, else all items |  |  | 
            
              | 337 | my @results; | 
            
              | 338 | @results = $uploadbarcodes | 
            
              | 339 |             ? @items_with_problems | 
            
              | 340 |             : $op | 
            
              | 341 |                 ? @$inventorylist | 
            
              | 342 |                 : (); | 
            
              | 343 |  | 
        
          | 344 | $template->param( | 318 | $template->param( | 
        
          | 345 |     moddatecount => $moddatecount, | 319 |     moddatecount => $moddatecount, | 
          
            
              | 346 |     loop       => \@results, | 320 |     loop         => $loop, | 
            
              | 347 |     op         => $op | 321 |     op           => $op, | 
        
          | 348 | ); | 322 | ); | 
        
          | 349 |  | 323 |  | 
            
              |  |  | 324 | # Export to csv | 
        
          | 350 | if (defined $input->param('CSVexport') && $input->param('CSVexport') eq 'on'){ | 325 | if (defined $input->param('CSVexport') && $input->param('CSVexport') eq 'on'){ | 
        
          | 351 |     eval {use Text::CSV}; | 326 |     eval {use Text::CSV}; | 
        
          | 352 |     my $csv = Text::CSV->new or | 327 |     my $csv = Text::CSV->new or | 
  
    | Lines 381-402
          if (defined $input->param('CSVexport') && $input->param('CSVexport') eq 'on'){
      
      
        Link Here | 
        
          | 381 |     print $csv->string, "\n"; | 356 |     print $csv->string, "\n"; | 
        
          | 382 |  | 357 |  | 
        
          | 383 |     my @keys = qw / title author barcode itemnumber homebranch location itemcallnumber notforloan lost damaged withdrawn stocknumber /; | 358 |     my @keys = qw / title author barcode itemnumber homebranch location itemcallnumber notforloan lost damaged withdrawn stocknumber /; | 
          
            
              | 384 |     for my $item ( @results ) { | 359 |     for my $item ( @$loop ) { | 
        
          | 385 |         my @line; | 360 |         my @line; | 
        
          | 386 |         for my $key (@keys) { | 361 |         for my $key (@keys) { | 
        
          | 387 |             push @line, $item->{$key}; | 362 |             push @line, $item->{$key}; | 
        
          | 388 |         } | 363 |         } | 
          
            
              | 389 |         if ( defined $item->{problem} ) { | 364 |         my $errstr = ''; | 
            
              | 390 |             if ( $item->{problem} eq 'wrongplace' ) { | 365 |         foreach my $key ( keys %{$item->{problems}} ) { | 
            
              | 391 |                 push @line, "wrong place"; | 366 |             if( $key eq 'wrongplace' ) { | 
            
              | 392 |             } elsif ( $item->{problem} eq 'missingitem' ) { | 367 |                 $errstr .= "wrong place,"; | 
            
              | 393 |                 push @line, "missing item"; | 368 |             } elsif( $key eq 'changestatus' ) { | 
            
              | 394 |             } elsif ( $item->{problem} eq 'changestatus' ) { | 369 |                 $errstr .= "unknown notforloan status,"; | 
            
              | 395 |                 push @line, "change item status"; | 370 |             } elsif( $key eq 'not_scanned' ) { | 
            
              | 396 |             } elsif ($item->{problem} eq 'not_scanned' ) { | 371 |                 $errstr .= "missing,"; | 
            
              | 397 |                 push @line, "item not scanned"; | 372 |             } elsif( $key eq 'no_barcode' ) { | 
            
              |  |  | 373 |                 $errstr .= "no barcode,"; | 
            
              | 374 |             } elsif( $key eq 'checkedout' ) { | 
            
              | 375 |                 $errstr .= "checked out,"; | 
        
          | 398 |             } | 376 |             } | 
        
          | 399 |         } | 377 |         } | 
            
              |  |  | 378 |         $errstr =~ s/,$//; | 
            
              | 379 |         push @line, $errstr; | 
        
          | 400 |         $csv->combine(@line); | 380 |         $csv->combine(@line); | 
        
          | 401 |         print $csv->string, "\n"; | 381 |         print $csv->string, "\n"; | 
        
          | 402 |     } | 382 |     } | 
  
    | Lines 414-416
          if (defined $input->param('CSVexport') && $input->param('CSVexport') eq 'on'){
      
      
        Link Here | 
        
          | 414 | } | 394 | } | 
        
          | 415 |  | 395 |  | 
        
          | 416 | output_html_with_http_headers $input, $cookie, $template->output; | 396 | output_html_with_http_headers $input, $cookie, $template->output; | 
          
            
              | 417 | -  | 397 |  | 
            
              |  |  | 398 | sub additemtoresults { | 
            
              | 399 |     my ( $item, $results ) = @_; | 
            
              | 400 |     my $itemno = $item->{itemnumber}; | 
            
              | 401 |     # since the script appends to $item, we can just overwrite the hash entry | 
            
              | 402 |     $results->{$itemno} = $item; | 
            
              | 403 | } |