Bug 26265: (QA follow-up) Remove g option from regex, add few dirs
[koha-equinox.git] / offline_circ / process_koc.pl
index 71effd5..935440b 100755 (executable)
@@ -4,24 +4,25 @@
 
 # This file is part of Koha.
 #
-# Koha is free software; you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.
+# Koha is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
 #
-# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+# Koha is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
 #
-# You should have received a copy of the GNU General Public License along with
-# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
-# Suite 330, Boston, MA  02111-1307 USA
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
 #
 
-use strict;
-use warnings;
+use Modern::Perl;
+
+use CGI qw ( -utf8 );
+use Carp;
 
-use CGI;
 use C4::Output;
 use C4::Auth;
 use C4::Koha;
@@ -29,10 +30,14 @@ use C4::Context;
 use C4::Biblio;
 use C4::Accounts;
 use C4::Circulation;
+use C4::Items;
 use C4::Members;
 use C4::Stats;
-use C4::UploadedFile;
 use C4::BackgroundJob;
+use Koha::UploadedFiles;
+use Koha::Account;
+use Koha::Checkouts;
+use Koha::Patrons;
 
 use Date::Calc qw( Add_Delta_Days Date_to_Days );
 
@@ -43,13 +48,13 @@ my $FILE_VERSION = '1.0';
 
 our $query = CGI->new;
 
-my ($template, $loggedinuser, $cookie)
-  = get_template_and_user( { template_name => "offline_circ/process_koc.tmpl",
-                               query => $query,
-                               type => "intranet",
-                               authnotrequired => 0,
-                                flagsrequired   => { circulate => 1 },
-                               });
+my ($template, $loggedinuser, $cookie) = get_template_and_user({
+    template_name => "offline_circ/process_koc.tt",
+    query => $query,
+    type => "intranet",
+    authnotrequired => 0,
+     flagsrequired   => { circulate => "circulate_remaining_permissions" },
+});
 
 
 my $fileID=$query->param('uploadedfileid');
@@ -68,16 +73,17 @@ if ($completedJobID) {
     $template->param(transactions_loaded => 1);
     $template->param(messages => $results->{results});
 } elsif ($fileID) {
-    my $uploaded_file = C4::UploadedFile->fetch($sessionID, $fileID);
-    my $fh = $uploaded_file->fh();
-    my @input_lines = <$fh>;
-  
-    my $filename = $uploaded_file->name(); 
+    my $upload = Koha::UploadedFiles->find( $fileID );
+    my $fh = $upload? $upload->file_handle: undef;
+    my $filename = $upload? $upload->filename: undef;
+    my @input_lines = $fh? <$fh>: ();
+    $fh->close if $fh;
+
     my $job = undef;
 
     if ($runinbackground) {
         my $job_size = scalar(@input_lines);
-        $job = C4::BackgroundJob->new($sessionID, $filename, $ENV{'SCRIPT_NAME'}, $job_size);
+        $job = C4::BackgroundJob->new($sessionID, $filename, '/cgi-bin/koha/offline_circ/process_koc.pl', $job_size);
         my $jobID = $job->id();
 
         # fork off
@@ -92,7 +98,7 @@ if ($completedJobID) {
 
             my $reply = CGI->new("");
             print $reply->header(-type => 'text/html');
-            print "{ jobID: '$jobID' }";
+            print '{"jobID":"' . $jobID . '"}';
             exit 0;
         } elsif (defined $pid) {
             # child
@@ -103,32 +109,31 @@ if ($completedJobID) {
         } else {
             # fork failed, so exit immediately
             # fork failed, so exit immediately
-            warn "fork failed while attempting to run $ENV{'SCRIPT_NAME'} as a background job";
+            warn "fork failed while attempting to run offline_circ/process_koc.pl as a background job";
             exit 0;
         }
 
         # if we get here, we're a child that has detached
         # itself from Apache
 
-    }     
+    }
 
     my $header_line = shift @input_lines;
     my $file_info   = parse_header_line($header_line);
     if ($file_info->{'Version'} ne $FILE_VERSION) {
-      push( @output, { message => 1,
-      ERROR_file_version => 1,
-      upload_version => $file_info->{'Version'},
-      current_version => $FILE_VERSION
-      } );
+        push @output, {
+            message => 1,
+            ERROR_file_version => 1,
+            upload_version => $file_info->{'Version'},
+            current_version => $FILE_VERSION
+        };
     }
-    
-    
+
     my $i = 0;
     foreach  my $line (@input_lines)  {
-    
         $i++;
         my $command_line = parse_command_line($line);
-        
+
         # map command names in the file to subroutine names
         my %dispatch_table = (
             issue     => \&kocIssueItem,
@@ -158,12 +163,14 @@ if ($completedJobID) {
 
 output_html_with_http_headers $query, $cookie, $template->output;
 
-=head3 parse_header_line
+=head1 FUNCTIONS
+
+=head2 parse_header_line
 
 parses the header line from a .koc file. This is the line that
 specifies things such as the file version, and the name and version of
 the offline circulation tool that generated the file. See
-L<http://wiki.koha.org/doku.php?id=koha_offline_circulation_file_format>
+L<http://wiki.koha-community.org/wiki/Koha_offline_circulation_file_format>
 for more information.
 
 pass in a string containing the header line (the first line from th
@@ -176,19 +183,21 @@ returns a hashref containing the information from the header.
 sub parse_header_line {
     my $header_line = shift;
     chomp($header_line);
+    $header_line =~ s/\r//g;
 
     my @fields = split( /\t/, $header_line );
     my %header_info = map { split( /=/, $_ ) } @fields;
     return \%header_info;
 }
 
-=head3 parse_command_line
+=head2 parse_command_line
 
 =cut
 
 sub parse_command_line {
     my $command_line = shift;
     chomp($command_line);
+    $command_line =~ s/\r//g;
 
     my ( $timestamp, $command, @args ) = split( /\t/, $command_line );
     my ( $date,      $time,    $id )   = split( /\s/, $timestamp );
@@ -208,7 +217,7 @@ sub parse_command_line {
 
 }
 
-=head3 arguments_for_command
+=head2 arguments_for_command
 
 fetches the names of the columns (and function arguments) found in the
 .koc file for a particular command name. For instance, the C<issue>
@@ -235,135 +244,147 @@ sub arguments_for_command {
 }
 
 sub kocIssueItem {
-  my $circ = shift;
-
-  my $branchcode = C4::Context->userenv->{branch};
-  my $borrower = GetMember( $circ->{ 'cardnumber' }, 'cardnumber' );
-  my $item = GetBiblioFromItemNumber( undef, $circ->{ 'barcode' } );
-  my $issue = GetItemIssue( $item->{'itemnumber'} );
-
-  my $issuingrule = GetIssuingRule( $borrower->{ 'categorycode' }, $item->{ 'itemtype' }, $branchcode );
-  my $issuelength = $issuingrule->{ 'issuelength' };
-  my ( $year, $month, $day ) = split( /-/, $circ->{'date'} );
-  ( $year, $month, $day ) = Add_Delta_Days( $year, $month, $day, $issuelength );
-  my $date_due = sprintf("%04d-%02d-%02d", $year, $month, $day);
-  
-  if ( $issue->{ 'date_due' } ) { ## Item is currently checked out to another person.
-#warn "Item Currently Issued.";
-    my $issue = GetOpenIssue( $item->{'itemnumber'} );
-
-    if ( $issue->{'borrowernumber'} eq $borrower->{'borrowernumber'} ) { ## Issued to this person already, renew it.
-#warn "Item issued to this member already, renewing.";
-    
-    my $date_due_object = C4::Dates->new($date_due ,'iso');
-    C4::Circulation::AddRenewal(
-        $issue->{'borrowernumber'},    # borrowernumber
-        $item->{'itemnumber'},         # itemnumber
-        undef,                         # branch
-        $date_due_object,              # datedue
-        $circ->{'date'},               # issuedate
-    ) unless ($DEBUG);
-
-      push( @output, { renew => 1,
-    title => $item->{ 'title' },
-    biblionumber => $item->{'biblionumber'},
-    barcode => $item->{ 'barcode' },
-    firstname => $borrower->{ 'firstname' },
-    surname => $borrower->{ 'surname' },
-    borrowernumber => $borrower->{'borrowernumber'},
-    cardnumber => $borrower->{'cardnumber'},
-    datetime => $circ->{ 'datetime' }
-    } );
+    my $circ = shift;
+
+    $circ->{ 'barcode' } = barcodedecode($circ->{'barcode'}) if( $circ->{'barcode'} && C4::Context->preference('itemBarcodeInputFilter'));
+    my $branchcode = C4::Context->userenv->{branch};
+    my $patron = Koha::Patrons->find( { cardnumber => $circ->{cardnumber} } );
+    my $borrower = $patron->unblessed;
+    my $item = Koha::Items->find({ barcode => $circ->{barcode} });
+    my $issue = Koha::Checkouts->find( { itemnumber => $item->itemnumber } );
+    my $biblio = $item->biblio;
+
+    if ( $issue ) { ## Item is currently checked out to another person.
+        #warn "Item Currently Issued.";
+        my $issue = GetOpenIssue( $item->itemnumber ); # FIXME Hum? That does not make sense, if it's in the issue table, the issue is open (i.e. returndate is null)
+
+        if ( $issue->{'borrowernumber'} eq $borrower->{'borrowernumber'} ) { ## Issued to this person already, renew it.
+            #warn "Item issued to this member already, renewing.";
+
+            C4::Circulation::AddRenewal(
+                $issue->{'borrowernumber'},    # borrowernumber
+                $item->itemnumber,             # itemnumber
+                undef,                         # branch
+                undef,                         # datedue - let AddRenewal calculate it automatically
+                $circ->{'date'},               # issuedate
+            ) unless ($DEBUG);
+
+            push @output, {
+                renew => 1,
+                title => $biblio->title,
+                biblionumber => $biblio->biblionumber,
+                barcode => $item->barcode,
+                firstname => $borrower->{ 'firstname' },
+                surname => $borrower->{ 'surname' },
+                borrowernumber => $borrower->{'borrowernumber'},
+                cardnumber => $borrower->{'cardnumber'},
+                datetime => $circ->{ 'datetime' }
+            };
 
-    } else {
-#warn "Item issued to a different member.";
-#warn "Date of previous issue: $issue->{'issuedate'}";
-#warn "Date of this issue: $circ->{'date'}";
-      my ( $i_y, $i_m, $i_d ) = split( /-/, $issue->{'issuedate'} );
-      my ( $c_y, $c_m, $c_d ) = split( /-/, $circ->{'date'} );
-      
-      if ( Date_to_Days( $i_y, $i_m, $i_d ) < Date_to_Days( $c_y, $c_m, $c_d ) ) { ## Current issue to a different persion is older than this issue, return and issue.
-        my $date_due_object = C4::Dates->new($date_due ,'iso');
-        C4::Circulation::AddIssue( $borrower, $circ->{'barcode'}, $date_due_object ) unless ( DEBUG );
-        push( @output, { issue => 1,
-    title => $item->{ 'title' },
-    biblionumber => $item->{'biblionumber'},
-    barcode => $item->{ 'barcode' },
-    firstname => $borrower->{ 'firstname' },
-    surname => $borrower->{ 'surname' },
-    borrowernumber => $borrower->{'borrowernumber'},
-    cardnumber => $borrower->{'cardnumber'},
-    datetime => $circ->{ 'datetime' }
-    } );
-
-      } else { ## Current issue is *newer* than this issue, write a 'returned' issue, as the item is most likely in the hands of someone else now.
-#warn "Current issue to another member is newer. Doing nothing";
-        ## This situation should only happen of the Offline Circ data is *really* old.
-        ## FIXME: write line to old_issues and statistics
-      }
-    
+        } else {
+            #warn "Item issued to a different member.";
+            #warn "Date of previous issue: $issue->{'issuedate'}";
+            #warn "Date of this issue: $circ->{'date'}";
+            my ( $i_y, $i_m, $i_d ) = split( /-/, $issue->{'issuedate'} );
+            my ( $c_y, $c_m, $c_d ) = split( /-/, $circ->{'date'} );
+
+            if ( Date_to_Days( $i_y, $i_m, $i_d ) < Date_to_Days( $c_y, $c_m, $c_d ) ) { ## Current issue to a different persion is older than this issue, return and issue.
+                C4::Circulation::AddIssue( $borrower, $circ->{'barcode'}, undef, undef, $circ->{'date'} ) unless ( DEBUG );
+                push @output, {
+                    issue => 1,
+                    title => $biblio->title,
+                    biblionumber => $biblio->biblionumber,
+                    barcode => $item->barcode,
+                    firstname => $borrower->{ 'firstname' },
+                    surname => $borrower->{ 'surname' },
+                    borrowernumber => $borrower->{'borrowernumber'},
+                    cardnumber => $borrower->{'cardnumber'},
+                    datetime => $circ->{ 'datetime' }
+                };
+
+            } else { ## Current issue is *newer* than this issue, write a 'returned' issue, as the item is most likely in the hands of someone else now.
+                #warn "Current issue to another member is newer. Doing nothing";
+                ## This situation should only happen of the Offline Circ data is *really* old.
+                ## FIXME: write line to old_issues and statistics
+            }
+        }
+    } else { ## Item is not checked out to anyone at the moment, go ahead and issue it
+        C4::Circulation::AddIssue( $borrower, $circ->{'barcode'}, undef, undef, $circ->{'date'} ) unless ( DEBUG );
+        push @output, {
+            issue => 1,
+            title => $biblio->title,
+            biblionumber => $biblio->biblionumber,
+            barcode => $item->barcode,
+            firstname => $borrower->{ 'firstname' },
+            surname => $borrower->{ 'surname' },
+            borrowernumber => $borrower->{'borrowernumber'},
+            cardnumber => $borrower->{'cardnumber'},
+            datetime =>$circ->{ 'datetime' }
+        };
     }
-  } else { ## Item is not checked out to anyone at the moment, go ahead and issue it
-      my $date_due_object = C4::Dates->new($date_due ,'iso');
-      C4::Circulation::AddIssue( $borrower, $circ->{'barcode'}, $date_due_object ) unless ( DEBUG );
-    push( @output, { issue => 1,
-    title => $item->{ 'title' },
-    biblionumber => $item->{'biblionumber'},
-    barcode => $item->{ 'barcode' },
-    firstname => $borrower->{ 'firstname' },
-    surname => $borrower->{ 'surname' },
-    borrowernumber => $borrower->{'borrowernumber'},
-    cardnumber => $borrower->{'cardnumber'},
-    datetime =>$circ->{ 'datetime' }
-    } );
-        }  
 }
 
 sub kocReturnItem {
-  my ( $circ ) = @_;
-  my $item = GetBiblioFromItemNumber( undef, $circ->{ 'barcode' } );
-  #warn( Data::Dumper->Dump( [ $circ, $item ], [ qw( circ item ) ] ) );
-  my $borrowernumber = _get_borrowernumber_from_barcode( $circ->{'barcode'} );
-  if ( $borrowernumber ) {
-  my $borrower = GetMember( $borrowernumber, 'borrowernumber' );
-    C4::Circulation::MarkIssueReturned( $borrowernumber,
-                                      $item->{'itemnumber'},
-                                      undef,
-                                      $circ->{'date'} );
-  
-  push( @output, { return => 1,
-    title => $item->{ 'title' },
-    biblionumber => $item->{'biblionumber'},
-    barcode => $item->{ 'barcode' },
-    borrowernumber => $borrower->{'borrowernumber'},
-    firstname => $borrower->{'firstname'},
-    surname => $borrower->{'surname'},
-    cardnumber => $borrower->{'cardnumber'},
-    datetime => $circ->{ 'datetime' }
-    } ); 
-  } else {
-    push( @output, { ERROR_no_borrower_from_item => 1,
-    badbarcode => $circ->{'barcode'}
-    } );
-  
-  }
+    my ( $circ ) = @_;
+    $circ->{'barcode'} = barcodedecode($circ->{'barcode'}) if( $circ->{'barcode'} && C4::Context->preference('itemBarcodeInputFilter'));
+    my $item = Koha::Items->find({ barcode => $circ->{barcode} });
+    my $biblio = $item->biblio;
+    my $borrowernumber = _get_borrowernumber_from_barcode( $circ->{'barcode'} );
+    if ( $borrowernumber ) {
+        my $patron = Koha::Patrons->find( $borrowernumber );
+        C4::Circulation::MarkIssueReturned(
+            $borrowernumber,
+            $item->itemnumber,
+            $circ->{'date'},
+            $patron->privacy
+        );
 
+        $item->onloadn(undef)->store;
+        ModDateLastSeen( $item->itemnumber );
+
+        push @output,
+          {
+            return         => 1,
+            title          => $biblio->title,
+            biblionumber   => $biblio->biblionumber,
+            barcode        => $item->barcode,
+            borrowernumber => $patron->borrowernumber,
+            firstname      => $patron->firstname,
+            surname        => $patron->surname,
+            cardnumber     => $patron->cardnumber,
+            datetime       => $circ->{'datetime'}
+          };
+    } else {
+        push @output, {
+            ERROR_no_borrower_from_item => 1,
+            badbarcode => $circ->{'barcode'}
+        };
+    }
 }
 
 sub kocMakePayment {
-  my ( $circ ) = @_;
-  my $borrower = GetMember( $circ->{ 'cardnumber' }, 'cardnumber' );
-  recordpayment( $borrower->{'borrowernumber'}, $circ->{'amount'} );
-  push( @output, { payment => 1,
-    amount => $circ->{'amount'},
-    firstname => $borrower->{'firstname'},
-    surname => $borrower->{'surname'},
-    cardnumber => $circ->{'cardnumber'},
-    borrower => $borrower->{'borrowernumber'}
-    } );
+    my ($circ) = @_;
+
+    my $cardnumber = $circ->{cardnumber};
+    my $amount = $circ->{amount};
+
+    my $patron = Koha::Patrons->find( { cardnumber => $cardnumber } );
+
+    Koha::Account->new( { patron_id => $patron->id } )
+      ->pay( { amount => $amount, interface => C4::Context->interface } );
+
+    push @output,
+      {
+        payment    => 1,
+        amount     => $circ->{'amount'},
+        firstname  => $patron->firstname,
+        surname    => $patron->surname,
+        cardnumber => $patron->cardnumber,
+        borrower   => $patron->id,
+      };
 }
 
-=head3 _get_borrowernumber_from_barcode
+=head2 _get_borrowernumber_from_barcode
 
 pass in a barcode
 get back the borrowernumber of the patron who has it checked out.
@@ -376,11 +397,10 @@ sub _get_borrowernumber_from_barcode {
 
     return unless $barcode;
 
-    my $item = GetBiblioFromItemNumber( undef, $barcode );
-    return unless $item->{'itemnumber'};
-    
-    my $issue = C4::Circulation::GetItemIssue( $item->{'itemnumber'} );
-    return unless $issue->{'borrowernumber'};
-    return $issue->{'borrowernumber'};
-    
+    my $item = Koha::Items->find({ barcode => $barcode });
+    return unless $item;
+
+    my $issue = Koha::Checkouts->find( { itemnumber => $item->itemnumber } );
+    return unless $issue;
+    return $issue->borrowernumber;
 }