Bug 8220 - Allow koc uploads to go to process queue instead of being applied directly.
authorKyle M Hall <kyle@bywatersolutions.com>
Sat, 9 Jun 2012 15:07:39 +0000 (11:07 -0400)
committerJared Camins-Esakov <jcamins@cpbibliography.com>
Fri, 22 Mar 2013 00:35:37 +0000 (20:35 -0400)
The primary advantage to the Firefox offline cirulation plugin when compared
to the offline circulation desktop application, is the ability to add offline
circulation actions to a queue so that multiple machines running offline
circ can have their circ actions combined and ordered chronologically before
being executed. This commit adds the ability to put actions from uploaded
KOC files into this queue. In this way, both the FF plugina and the desktop
application can be run side by side with no ill effects.

Signed-off-by: Bob Birchall <bob@calyx.net.au>
Signed-off-by: Katrin Fischer <Katrin.Fischer.83@web.de>
Signed-off-by: Jared Camins-Esakov <jcamins@cpbibliography.com>

C4/Circulation.pm
installer/data/mysql/kohastructure.sql
installer/data/mysql/updatedatabase.pl
koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation-home.tt
koha-tmpl/intranet-tmpl/prog/en/modules/offline_circ/enqueue_koc.tt [new file with mode: 0644]
koha-tmpl/intranet-tmpl/prog/en/modules/offline_circ/list.tt
koha-tmpl/intranet-tmpl/prog/en/modules/offline_circ/process_koc.tt
offline_circ/enqueue_koc.pl [new file with mode: 0755]
offline_circ/list.pl

index b5a5172..bb5b718 100644 (file)
@@ -3336,9 +3336,10 @@ sub GetOfflineOperation {
 }
 
 sub AddOfflineOperation {
+    my ( $userid, $branchcode, $timestamp, $action, $barcode, $cardnumber, $amount ) = @_;
     my $dbh = C4::Context->dbh;
-    my $sth = $dbh->prepare("INSERT INTO pending_offline_operations (userid, branchcode, timestamp, action, barcode, cardnumber) VALUES(?,?,?,?,?,?)");
-    $sth->execute( @_ );
+    my $sth = $dbh->prepare("INSERT INTO pending_offline_operations (userid, branchcode, timestamp, action, barcode, cardnumber, amount) VALUES(?,?,?,?,?,?,?)");
+    $sth->execute( $userid, $branchcode, $timestamp, $action, $barcode, $cardnumber, $amount );
     return "Added.";
 }
 
@@ -3357,6 +3358,8 @@ sub ProcessOfflineOperation {
         $report = ProcessOfflineReturn( $operation );
     } elsif ( $operation->{action} eq 'issue' ) {
         $report = ProcessOfflineIssue( $operation );
+    } elsif ( $operation->{action} eq 'payment' ) {
+        $report = ProcessOfflinePayment( $operation );
     }
 
     DeleteOfflineOperation( $operation->{operationid} ) if $operation->{operationid};
@@ -3426,6 +3429,16 @@ sub ProcessOfflineIssue {
     }
 }
 
+sub ProcessOfflinePayment {
+    my $operation = shift;
+
+    my $borrower = C4::Members::GetMemberDetails( undef, $operation->{cardnumber} ); # Get borrower from operation cardnumber
+    my $amount = $operation->{amount};
+
+    recordpayment( $borrower->{borrowernumber}, $amount );
+
+    return "Success."
+}
 
 
 =head2 TransferSlip
index c74c39b..15eb4d7 100644 (file)
@@ -1559,17 +1559,17 @@ CREATE TABLE `patronimage` ( -- information related to patron images
 -- so MyISAM is better in this case
 
 DROP TABLE IF EXISTS `pending_offline_operations`;
-CREATE TABLE `pending_offline_operations` (
-  `operationid` int(11) NOT NULL AUTO_INCREMENT,
-  `userid` varchar(30) NOT NULL,
-  `branchcode` varchar(10) NOT NULL,
+CREATE TABLE pending_offline_operations (
+  operationid int(11) NOT NULL AUTO_INCREMENT,
+  userid varchar(30) NOT NULL,
+  branchcode varchar(10) NOT NULL,
   `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
   `action` varchar(10) NOT NULL,
-  `barcode` varchar(20) NOT NULL,
-  `cardnumber` varchar(16) DEFAULT NULL,
-  PRIMARY KEY (`operationid`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-
+  barcode varchar(20) DEFAULT NULL,
+  cardnumber varchar(16) DEFAULT NULL,
+  amount decimal(28,6) DEFAULT NULL,
+  PRIMARY KEY (operationid)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
 
 
 --
index 8a6a5b8..8cb0539 100755 (executable)
@@ -5828,8 +5828,6 @@ if(C4::Context->preference("Version") < TransformToNum($DBversion) ) {
     SetVersion($DBversion);
 }
 
-
-
 $DBversion = "3.09.00.050";
 if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
     $dbh->do("ALTER TABLE authorised_values MODIFY category varchar(16) NOT NULL DEFAULT '';");
@@ -6528,6 +6526,13 @@ if ( CheckVersion($DBversion) ) {
 $DBversion = "3.11.00.100";
 if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
     print "Upgrade to $DBversion done (3.12-alpha release)\n";
+}
+
+$DBversion = "3.11.00.XXX";
+if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
+    $dbh->do("ALTER TABLE `pending_offline_operations` CHANGE `barcode` `barcode` VARCHAR( 20 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL");
+    $dbh->do("ALTER TABLE `pending_offline_operations` ADD `amount` DECIMAL( 28, 6 ) NULL DEFAULT NULL");
+    print "Upgrade to $DBversion done (Bug 8220 - Allow koc uploads to go to process queue)\n";
     SetVersion ($DBversion);
 }
 
index cb0662a..f5e82b4 100644 (file)
        <div class="yui-u">
         <h5>Offline circulation</h5>
                <ul>
-            <li><a href="/cgi-bin/koha/offline_circ/process_koc.pl">Offline circulation file (.koc) uploader</a></li>
-            <li><a href="/cgi-bin/koha/offline_circ/list.pl">Offline circulation</a> (<a href="https://addons.mozilla.org/[% lang %]/firefox/addon/koct/">Firefox add-on</a>)</li>
+                    <li><a href="/cgi-bin/koha/offline_circ/process_koc.pl">Upload offline circulation file (.koc)</a></li>
+                    <li><a href="/cgi-bin/koha/offline_circ/list.pl">Pending offline circulation actions</a>
+                    <ul>
+                        <li><a href="http://kylehall.info/index.php/projects/koha/koha-offline-circulation/">Get desktop application</a></li>
+                        <li><a href="https://addons.mozilla.org/[% lang %]/firefox/addon/koct/">Get Firefox add-on</a></li>
+                    </ul>
                </ul>
        </div>
 </div>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/offline_circ/enqueue_koc.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/offline_circ/enqueue_koc.tt
new file mode 100644 (file)
index 0000000..8c836e3
--- /dev/null
@@ -0,0 +1,31 @@
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha &rsaquo; Circulation &rsaquo; Add offline circulations to queue</title>
+[% INCLUDE 'doc-head-close.inc' %]
+</head>
+<body>
+[% INCLUDE 'header.inc' %]
+[% INCLUDE 'circ-search.inc' %]
+
+<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/circ/circulation-home.pl">Circulation</a> &rsaquo; <a href="/cgi-bin/koha/offline_circ/process_koc.pl">Add offline circulations to queue</a></div>
+
+<div id="doc" class="yui-t7">
+
+   <div id="bd">
+
+  <h2>Koha offline circulation</h2>
+  <p>Your file was processed.</p>
+
+[% FOREACH message IN messages %]
+  [% IF ( message.message ) %]
+      [% IF ( message.ERROR_file_version ) %]
+          <div class="dialog alert"><p>Warning: This file is version [% message.upload_version %], but I only know how to import version [% message.current_version %]. I'll try my best.</p>
+      [% END %]
+  [% END %]
+[% END %]
+
+<p><a href="process_koc.pl">Upload another KOC file</a></p>
+
+<p><a href="list.pl">View pending offline circulation actions</a></p>
+
+</div>
+[% INCLUDE 'intranet-bottom.inc' %]
index 8e14d07..eb19589 100644 (file)
@@ -78,7 +78,8 @@
                                    <th>Date</th>
                                    <th>Action</th>
                                    <th>Barcode</th>
-                        <th>Card number</th>
+                                    <th>Card number</th>
+                                    <th>Amount</th>
                            </tr>
                        </thead>
                        <tbody>
@@ -95,7 +96,7 @@
                                            [% END %]
                                        </td>
                                        <td>
-                                       [% IF ( operation.actionissue ) %]
+                                       [% IF ( operation.actionissue || operation.actionpayment) %]
                                        [% IF ( operation.borrowernumber ) %]
                                            <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% operation.borrowernumber %]" title="[% operation.borrower %]">[% operation.cardnumber %]</a>
                                        [% ELSE %]
                                        [% END %]
                                        [% END %]
                                        </td>
+                                        <td>[% operation.amount %]</td>
                                </tr>
                            [% END %]
                        </tbody>
index 0aae78b..5c9da7e 100644 (file)
@@ -6,22 +6,25 @@
 <script type="text/javascript">
 //<![CDATA[
 $(document).ready(function(){
-       $("#processfile").hide();
+    $("#enqueuefile").hide();
+    $("#processfile").hide();
 });
+
 function CheckUpload(f){
-       if(f.fileToUpload.value == ""){
-               alert(_("Please choose a file to upload"));
-       } else {
-               return ajaxFileUpload()
-       }
-               return false;
+    if (f.fileToUpload.value == ""){
+        alert(_("Please choose a file to upload"));
+    } else {
+        return ajaxFileUpload()
+    }
+    return false;
 }
+
 function CheckForm(f) {
     if (f.uploadedfileid.value == '') {
         alert(_("Please upload a file first."));
     } else {
-               $("#fileuploadstatus").hide();
-               $("#fileuploadform").slideUp();
+        $("#fileuploadstatus").hide();
+        $("#fileuploadform").slideUp();
         return submitBackgroundJob(f);
     }
     return false;
@@ -60,7 +63,8 @@ function CheckForm(f) {
        [% IF ( message.payment ) %]<p>Accepted payment ([% message.amount %]) from <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% message.borrowernumber %]">[% message.firstname %] [% message.surname %]</a> ([% message.cardnumber %]): [% message.datetime %]</p>[% END %]
   [% END %]
 [% ELSE %]
-  <h2>Upload offline circulation data</h2>
+   <h2>Upload offline circulation data</h2>
+
    <div id="fileuploadform">
      <form method="post" action="[% SCRIPT_NAME %]" enctype="multipart/form-data">
                <fieldset class="brief">
@@ -72,11 +76,17 @@ function CheckForm(f) {
      <div id="fileuploadstatus" style="display:none">Upload progress: <div id="fileuploadprogress"></div> <span id="fileuploadpercent">0</span>%</div>
      <div id="fileuploadfailed" style="display:none"></div>
    </div>
-   <form action="process_koc.pl" id="processfile" method="post" enctype="multipart/form-data">
+
+   <form action="enqueue_koc.pl" id="processfile" method="post" enctype="multipart/form-data">
+     <input type="hidden" name="uploadedfileid" id="uploadedfileid" value="" />
+     <input type="submit" value="Add to offline circulation queue" onclick="return CheckForm(this.form);" id="queueformsubmit" />
+   </form>
+
+   <form action="process_koc.pl" id="enqueuefile" method="post" enctype="multipart/form-data">
      <input type="hidden" name="uploadedfileid" id="uploadedfileid" value="" />
      <input type="hidden" name="runinbackground" id="runinbackground" value="" />
      <input type="hidden" name="completedJobID" id="completedJobID" value="" />
-     <input type="submit" value="Process offline circulation file" onclick="return CheckForm(this.form);" id="mainformsubmit" />
+     <input type="submit" value="Apply directly" onclick="return CheckForm(this.form);" id="mainformsubmit" />
      <div id="jobstatus" style="display:none">Job progress: <div id="jobprogress"></div> <span id="jobprogresspercent">0</span>%</div>
      <div id="jobfailed" style="display:none"></div>
    </form>
diff --git a/offline_circ/enqueue_koc.pl b/offline_circ/enqueue_koc.pl
new file mode 100755 (executable)
index 0000000..bfc1e35
--- /dev/null
@@ -0,0 +1,197 @@
+#!/usr/bin/perl
+
+# 2008 Kyle Hall <kyle.m.hall@gmail.com>
+
+# 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 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.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+use strict;
+use warnings;
+
+use CGI;
+use C4::Output;
+use C4::Auth;
+use C4::Koha;
+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 Date::Calc qw( Add_Delta_Days Date_to_Days );
+
+use constant DEBUG => 0;
+
+# this is the file version number that we're coded against.
+my $FILE_VERSION = '1.0';
+
+my $query = CGI->new;
+my @output;
+
+my ($template, $loggedinuser, $cookie) = get_template_and_user({
+    template_name => "offline_circ/enqueue_koc.tmpl",
+    query => $query,
+    type => "intranet",
+    authnotrequired => 0,
+     flagsrequired   => { circulate => "circulate_remaining_permissions" },
+});
+
+
+my $fileID=$query->param('uploadedfileid');
+my %cookies = parse CGI::Cookie($cookie);
+my $sessionID = $cookies{'CGISESSID'}->value;
+## 'Local' globals.
+our $dbh = C4::Context->dbh();
+
+if ($fileID) {
+    my $uploaded_file = C4::UploadedFile->fetch($sessionID, $fileID);
+    my $fh = $uploaded_file->fh();
+    my @input_lines = <$fh>;
+
+    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 $userid = C4::Context->userenv->{id};
+    my $branchcode = C4::Context->userenv->{branch};
+
+    foreach  my $line (@input_lines)  {
+        my $command_line = parse_command_line($line);
+        my $timestamp = $command_line->{'date'} . ' ' . $command_line->{'time'};
+        my $action = $command_line->{'command'};
+        my $barcode = $command_line->{'barcode'};
+        my $cardnumber = $command_line->{'cardnumber'};
+        my $amount = $command_line->{'amount'};
+
+        AddOfflineOperation( $userid, $branchcode, $timestamp, $action, $barcode, $cardnumber, $amount );
+    }
+
+}
+
+$template->param( 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<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
+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<issue>
+command requires a C<cardnumber> and C<barcode>. In that case this
+function returns a reference to the list C<qw( cardnumber barcode )>.
+
+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};
+}
+
+=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'};
+}
index de129ff..c503948 100755 (executable)
@@ -54,6 +54,7 @@ for (@$operations) {
     }
     $_->{'actionissue'}    = $_->{'action'} eq 'issue';
     $_->{'actionreturn'}   = $_->{'action'} eq 'return';
+    $_->{'actionpayment'}  = $_->{'action'} eq 'payment';
 }
 $template->param(pending_operations => $operations);