From 57743227d2a578537d7043a1aa473c202bbed04b Mon Sep 17 00:00:00 2001
From: Matthias Meusburger <matthias.meusburger@biblibre.com>
Date: Tue, 3 Nov 2020 14:49:31 +0000
Subject: [PATCH] Bug 26591: SIP option prevcheckout_block_checkout to block
 checkout of previously checked-out documents

This patchs adds a new SIP option to block checkout of previously
checked-out documents: prevcheckout_block_checkout

See the CheckPrevCheckout system preference to enable previously
checked-out verification in Koha.

Test plan:
  1) Apply this patch
  2) Prove t/db_dependent/SIP/Message.t

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
---
 C4/SIP/ILS.pm                      |  4 +-
 C4/SIP/ILS/Transaction/Checkout.pm |  3 ++
 C4/SIP/Sip/MsgType.pm              |  2 +-
 etc/SIPconfig.xml                  |  1 +
 t/db_dependent/SIP/Message.t       | 76 +++++++++++++++++++++++++++++-
 5 files changed, 82 insertions(+), 4 deletions(-)

diff --git a/C4/SIP/ILS.pm b/C4/SIP/ILS.pm
index d2a253873b..5ea4ea0546 100644
--- a/C4/SIP/ILS.pm
+++ b/C4/SIP/ILS.pm
@@ -126,7 +126,7 @@ sub offline_ok {
 # the response.
 #
 sub checkout {
-    my ( $self, $patron_id, $item_id, $sc_renew, $fee_ack ) = @_;
+    my ( $self, $patron_id, $item_id, $sc_renew, $fee_ack, $account ) = @_;
     my ( $patron, $item, $circ );
 
     $circ = C4::SIP::ILS::Transaction::Checkout->new();
@@ -153,7 +153,7 @@ sub checkout {
         $circ->screen_msg("Item checked out to another patron");
     }
     else {
-        $circ->do_checkout();
+        $circ->do_checkout($account);
         if ( $circ->ok ) {
             $debug and warn "circ is ok";
 
diff --git a/C4/SIP/ILS/Transaction/Checkout.pm b/C4/SIP/ILS/Transaction/Checkout.pm
index aabe5e6830..2b804338f4 100644
--- a/C4/SIP/ILS/Transaction/Checkout.pm
+++ b/C4/SIP/ILS/Transaction/Checkout.pm
@@ -47,11 +47,13 @@ sub new {
 
 sub do_checkout {
 	my $self = shift;
+	my $account = shift;
 	siplog('LOG_DEBUG', "ILS::Transaction::Checkout performing checkout...");
     my $shelf          = $self->{item}->hold_attached;
 	my $barcode        = $self->{item}->id;
     my $patron         = Koha::Patrons->find($self->{patron}->{borrowernumber});
     my $overridden_duedate; # usually passed as undef to AddIssue
+    my $prevcheckout_block_checkout  = $account->{prevcheckout_block_checkout};
     $debug and warn "do_checkout borrower: . " . $patron->borrowernumber;
     my ($issuingimpossible, $needsconfirmation) = _can_we_issue($patron, $barcode,
         C4::Context->preference("AllowItemsOnHoldCheckoutSIP")
@@ -95,6 +97,7 @@ sub do_checkout {
                 }
             } elsif ($confirmation eq 'PREVISSUE') {
                 $self->screen_msg("This item was previously checked out by you");
+                $noerror = 0 if ($prevcheckout_block_checkout);
                 last;
             } elsif ( $confirmation eq 'ADDITIONAL_MATERIALS' ) {
                 $self->screen_msg('Item must be checked out at a circulation desk');
diff --git a/C4/SIP/Sip/MsgType.pm b/C4/SIP/Sip/MsgType.pm
index e5c2b9edbc..aa8cdfcf1e 100644
--- a/C4/SIP/Sip/MsgType.pm
+++ b/C4/SIP/Sip/MsgType.pm
@@ -533,7 +533,7 @@ sub handle_checkout {
 
         # Does the transaction date really matter for items that are
         # checkout out while the terminal is online?  I'm guessing 'no'
-        $status = $ils->checkout( $patron_id, $item_id, $sc_renewal_policy, $fee_ack );
+        $status = $ils->checkout( $patron_id, $item_id, $sc_renewal_policy, $fee_ack, $account );
     }
 
     $item   = $status->item;
diff --git a/etc/SIPconfig.xml b/etc/SIPconfig.xml
index 43f34ee05b..8cec48c9f6 100644
--- a/etc/SIPconfig.xml
+++ b/etc/SIPconfig.xml
@@ -61,6 +61,7 @@
              av_field_template="[% accountline.description %] [% accountline.amountoutstanding | format('%.2f') %]"
              hide_fields="BD,BE,BF,PB"
              register_id=''
+             prevcheckout_block_checkout="0"
              holds_block_checkin="0">
           <screen_msg_regex find="Greetings from Koha." replace="Welcome to your library!" />
           <screen_msg_regex find="Invalid patron barcode." replace="Barcode not found, are you sure this is your library card?" />
diff --git a/t/db_dependent/SIP/Message.t b/t/db_dependent/SIP/Message.t
index 31baf30e3a..73da5c0226 100755
--- a/t/db_dependent/SIP/Message.t
+++ b/t/db_dependent/SIP/Message.t
@@ -21,7 +21,7 @@
 # along with Koha; if not, see <http://www.gnu.org/licenses>.
 
 use Modern::Perl;
-use Test::More tests => 9;
+use Test::More tests => 10;
 use Test::MockObject;
 use Test::MockModule;
 use Test::Warn;
@@ -30,6 +30,7 @@ use t::lib::Mocks;
 use t::lib::TestBuilder;
 
 use C4::Reserves qw(AddReserve);
+use C4::Circulation qw( AddReturn );
 use Koha::Database;
 use Koha::AuthUtils qw(hash_password);
 use Koha::DateUtils;
@@ -66,6 +67,15 @@ subtest 'Testing Patron Info Request V2' => sub {
     $schema->storage->txn_rollback;
 };
 
+subtest 'Checkout V2' => sub {
+    my $schema = Koha::Database->new->schema;
+    $schema->storage->txn_begin;
+    plan tests => 3;
+    $C4::SIP::Sip::protocol_version = 2;
+    test_checkout_v2();
+    $schema->storage->txn_rollback;
+};
+
 subtest 'Checkin V2' => sub {
     my $schema = Koha::Database->new->schema;
     $schema->storage->txn_begin;
@@ -486,6 +496,69 @@ sub test_request_patron_info_v2 {
     check_field( $respcode, $response, FID_SCREEN_MSG, '.+', 'But we have a screen msg', 'regex' );
 }
 
+sub test_checkout_v2 {
+    my $builder = t::lib::TestBuilder->new();
+    my $branchcode  = $builder->build({ source => 'Branch' })->{branchcode};
+    my $branchcode2 = $builder->build({ source => 'Branch' })->{branchcode};
+    my ( $response, $findpatron );
+    my $mocks = create_mocks( \$response, \$findpatron, \$branchcode );
+
+    # create some data
+    my $patron1 = $builder->build({
+        source => 'Borrower',
+        value  => {
+            password => hash_password( PATRON_PW ),
+        },
+    });
+    my $card1 = $patron1->{cardnumber};
+    my $sip_patron1 = C4::SIP::ILS::Patron->new( $card1 );
+    $findpatron = $sip_patron1;
+    my $item_object = $builder->build_sample_item({
+        damaged => 0,
+        withdrawn => 0,
+        itemlost => 0,
+        restricted => 0,
+        homebranch => $branchcode,
+        holdingbranch => $branchcode,
+    });
+
+    my $mockILS = $mocks->{ils};
+    my $server = { ils => $mockILS, account => {} };
+    $mockILS->mock( 'institution', sub { $branchcode; } );
+    $mockILS->mock( 'supports', sub { return; } );
+    $mockILS->mock( 'checkout', sub {
+        shift;
+        return C4::SIP::ILS->checkout(@_);
+    });
+    my $today = dt_from_string;
+    t::lib::Mocks::mock_userenv({ branchcode => $branchcode, flags => 1 });
+    t::lib::Mocks::mock_preference( 'CheckPrevCheckout',  'hardyes' );
+
+    my $issue = Koha::Checkout->new({ branchcode => $branchcode, borrowernumber => $patron1->{borrowernumber}, itemnumber => $item_object->itemnumber })->store;
+    my $return = AddReturn($item_object->barcode, $branchcode);
+
+    my $siprequest = CHECKOUT . 'YN' . siprequestdate($today) .
+    siprequestdate( $today->clone->add( days => 1) ) .
+    FID_INST_ID . $branchcode . '|'.
+    FID_PATRON_ID . $sip_patron1->id . '|' .
+    FID_ITEM_ID . $item_object->barcode . '|' .
+    FID_TERMINAL_PWD . 'ignored' . '|';
+    undef $response;
+
+    my $msg = C4::SIP::Sip::MsgType->new( $siprequest, 0 );
+    $server->{account}->{prevcheckout_block_checkout} = 1;
+    $msg->handle_checkout( $server );
+    my $respcode = substr( $response, 0, 2 );
+    check_field( $respcode, $response, FID_SCREEN_MSG, 'This item was previously checked out by you', 'Check screen msg', 'equals' );
+
+    is( Koha::Checkouts->search({ itemnumber => $item_object->id })->count, 0, "Item was not checked out (prevcheckout_block_checkout enabled)");
+
+    $server->{account}->{prevcheckout_block_checkout} = 0;
+    $msg->handle_checkout( $server );
+    $respcode = substr( $response, 0, 2 );
+    is( Koha::Checkouts->search({ itemnumber => $item_object->id })->count, 1, "Item was checked out (prevcheckout_block_checkout disabled)");
+}
+
 sub test_checkin_v2 {
     my $builder = t::lib::TestBuilder->new();
     my $branchcode  = $builder->build({ source => 'Branch' })->{branchcode};
@@ -753,5 +826,6 @@ sub fixed_length { #length of fixed fields including response code
       ( PATRON_STATUS_RESP )  => 37,
       ( PATRON_INFO_RESP )    => 61,
       ( CHECKIN_RESP )        => 24,
+      ( CHECKOUT_RESP )       => 24,
     }->{$_[0]};
 }
-- 
2.24.1 (Apple Git-126)