X-Git-Url: http://koha-dev.rot13.org:8081/gitweb/?a=blobdiff_plain;f=offline_circ%2Fprocess_koc.pl;h=75ff31950b125788870000beb4e747655286ac04;hb=4458ac5764126049d95e115adeacd1f335efdb8f;hp=2df542e3d4b5e6a2beba8996dc98553963b019ea;hpb=3d323db1feeb95abef29e37ef715605e87a5cfdf;p=koha_fer diff --git a/offline_circ/process_koc.pl b/offline_circ/process_koc.pl index 2df542e3d4..75ff31950b 100755 --- a/offline_circ/process_koc.pl +++ b/offline_circ/process_koc.pl @@ -13,13 +13,13 @@ # 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # use strict; -require Exporter; +use warnings; use CGI; use C4::Output; @@ -29,142 +29,363 @@ 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 Date::Calc qw( Add_Delta_Days Date_to_Days ); use constant DEBUG => 0; -our $query = new CGI; +# this is the file version number that we're coded against. +my $FILE_VERSION = '1.0'; -my ($template, $loggedinuser, $cookie) - = get_template_and_user( { template_name => "offline_circ/process_koc.tmpl", - query => $query, - type => "intranet", - authnotrequired => 1, - debug => 1, - }); +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 => "circulate_remaining_permissions" }, +}); + + +my $fileID=$query->param('uploadedfileid'); +my $runinbackground = $query->param('runinbackground'); +my $completedJobID = $query->param('completedJobID'); +my %cookies = parse CGI::Cookie($cookie); +my $sessionID = $cookies{'CGISESSID'}->value; ## 'Local' globals. our $dbh = C4::Context->dbh(); +our @output = (); ## For storing messages to be displayed to the user -our $branchcode = C4::Context->userenv->{branch}; -warn "Branchcode: $branchcode"; +if ($completedJobID) { + my $job = C4::BackgroundJob->fetch($sessionID, $completedJobID); + my $results = $job->results(); + $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>; -our @output; ## For storing messages to be displayed to the user + my $filename = $uploaded_file->name(); + my $job = undef; -$query::POST_MAX = 1024 * 10000; + if ($runinbackground) { + my $job_size = scalar(@input_lines); + $job = C4::BackgroundJob->new($sessionID, $filename, $ENV{'SCRIPT_NAME'}, $job_size); + my $jobID = $job->id(); -my $file = $query->param("kocfile"); -$file=~m/^.*(\\|\/)(.*)/; # strip the remote path and keep the filename -my $name = $file; + # fork off + if (my $pid = fork) { + # parent + # return job ID as JSON -my $header = <$file>; + # prevent parent exiting from + # destroying the kid's database handle + # FIXME: according to DBI doc, this may not work for Oracle + $dbh->{InactiveDestroy} = 1; -while ( my $line = <$file> ) { - my ( $type, $cardnumber, $barcode, $datetime ) = split( /\t/, $line ); - ( $datetime ) = split( /\+/, $datetime ); - my ( $date ) = split( / /, $datetime ); + my $reply = CGI->new(""); + print $reply->header(-type => 'text/html'); + print '{"jobID":"' . $jobID . '"}'; + exit 0; + } elsif (defined $pid) { + # child + # close STDOUT to signal to Apache that + # we're now running in the background + close STDOUT; + close STDERR; + } 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"; + exit 0; + } - my $circ; - $circ->{ 'type' } = $type; - $circ->{ 'cardnumber' } = $cardnumber; - $circ->{ 'barcode' } = $barcode; - $circ->{ 'datetime' } = $datetime; - $circ->{ 'date' } = $date; - - if ( $circ->{ 'type' } eq 'issue' ) { - kocIssueItem( $circ, $branchcode ); - } elsif ( $circ->{ 'type' } eq 'return' ) { - kocReturnItem( $circ ); - } elsif ( $circ->{ 'type' } eq 'payment' ) { - $circ->{'payment_amount'} = delete $circ->{'barcode'}; - kocMakePayment( $circ ); - } -} + # 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 + }; + } + + my $i = 0; + foreach my $line (@input_lines) { + $i++; + my $command_line = parse_command_line($line); -$template->param( - intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"), - intranetstylesheet => C4::Context->preference("intranetstylesheet"), - IntranetNav => C4::Context->preference("IntranetNav"), + # map command names in the file to subroutine names + my %dispatch_table = ( + issue => \&kocIssueItem, + 'return' => \&kocReturnItem, + payment => \&kocMakePayment, + ); + + # call the right sub name, passing the hashref of command_line to it. + if ( exists $dispatch_table{ $command_line->{'command'} } ) { + $dispatch_table{ $command_line->{'command'} }->($command_line); + } else { + warn "unknown command: '$command_line->{command}' not processed"; + } + + if ($runinbackground) { + $job->progress($i); + } + } + + if ($runinbackground) { + $job->finish({ results => \@output }) if defined($job); + } else { + $template->param(transactions_loaded => 1); + $template->param(messages => \@output); + } +} - messages => \@output, - ); output_html_with_http_headers $query, $cookie, $template->output; +=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 +for more information. + +pass in a string containing the header line (the first line from th +file). + +returns a hashref containing the information from the header. + +=cut + +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; +} + +=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 ); + + my %command = ( + date => $date, + time => $time, + id => $id, + command => $command, + ); + + # set the rest of the keys using a hash slice + my $argument_names = arguments_for_command($command); + @command{@$argument_names} = @args; + + return \%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 +command requires a C and C. In that case this +function returns a reference to the list C. + +parameters: the command name + +returns: listref of column names. + +=cut + +sub arguments_for_command { + my $command = shift; + + # define the fields for this version of the file. + my %format = ( + issue => [qw( cardnumber barcode )], + return => [qw( barcode )], + payment => [qw( cardnumber amount )], + ); + + return $format{$command}; +} + sub kocIssueItem { - my ( $circ, $branchcode ) = @_; - - 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, { message => "Renewed $item->{ 'title' } ( $item->{ 'barcode' } ) to $borrower->{ 'firstname' } $borrower->{ 'surename' } ( $borrower->{'cardnumber'} ) : $circ->{ 'datetime' }\n" } ); + my $circ = shift; - } 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'}, $date_due ) unless ( DEBUG ); - push( @output, { message => "Issued $item->{ 'title' } ( $item->{ 'barcode' } ) to $borrower->{ 'firstname' } $borrower->{ 'surename' } ( $borrower->{'cardnumber'} ) : $circ->{ 'datetime' }\n" } ); - - } 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 - } - + $circ->{ 'barcode' } = barcodedecode($circ->{'barcode'}) if( $circ->{'barcode'} && C4::Context->preference('itemBarcodeInputFilter')); + my $branchcode = C4::Context->userenv->{branch}; + my $borrower = GetMember( 'cardnumber'=>$circ->{ 'cardnumber' } ); + my $item = GetBiblioFromItemNumber( undef, $circ->{ 'barcode' } ); + my $issue = GetItemIssue( $item->{'itemnumber'} ); + + 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."; + + 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 => $item->{ 'title' }, + biblionumber => $item->{'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. + C4::Circulation::AddIssue( $borrower, $circ->{'barcode'}, undef, undef, $circ->{'date'} ) 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 { ## 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 => $item->{ 'title' }, + biblionumber => $item->{'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 - C4::Circulation::AddIssue( $borrower, $circ->{'barcode'}, $date_due ) unless ( DEBUG ); - push( @output, { message => "Issued $item->{ 'title' } ( $item->{ 'barcode' } ) to $borrower->{ 'firstname' } $borrower->{ 'surename' } ( $borrower->{'cardnumber'} ) : $circ->{ 'datetime' }\n" } ); - } } sub kocReturnItem { - my ( $circ ) = @_; - my $borrower = GetMember( $circ->{ 'cardnumber' }, 'cardnumber' ); - my $item = GetBiblioFromItemNumber( undef, $circ->{ 'barcode' } ); - C4::Circulation::MarkIssueReturned( $borrower->{'borrowernumber'}, - $item->{'itemnumber'}, - undef, - $circ->{'date'} ); - - push( @output, { message => "Returned $item->{ 'title' } ( $item->{ 'barcode' } ) From borrower number $borrower->{'borrowernumber'} : $circ->{ 'datetime' }\n" } ); + my ( $circ ) = @_; + $circ->{'barcode'} = barcodedecode($circ->{'barcode'}) if( $circ->{'barcode'} && C4::Context->preference('itemBarcodeInputFilter')); + 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'}, + $borrower->{'privacy'} + ); + + ModItem({ onloan => undef }, $item->{'biblionumber'}, $item->{'itemnumber'}); + ModDateLastSeen( $item->{'itemnumber'} ); + + 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'} + }; + } } sub kocMakePayment { - my ( $circ ) = @_; - my $borrower = GetMember( $circ->{ 'cardnumber' }, 'cardnumber' ); - recordpayment( $borrower->{'borrowernumber'}, $circ->{'payment_amount'} ); - push( @output, { message => "accepted payment ($circ->{'payment_amount'}) from cardnumber ($circ->{'cardnumber'}), borrower ($borrower->{'borrowernumber'})" } ); + my ( $circ ) = @_; + my $borrower = GetMember( 'cardnumber'=>$circ->{ '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'} + }; } +=head2 _get_borrowernumber_from_barcode + +pass in a barcode +get back the borrowernumber of the patron who has it checked out. +undef if that can't be found + +=cut + +sub _get_borrowernumber_from_barcode { + my $barcode = shift; + + 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'}; +}