--- /dev/null
+#!/usr/bin/perl
+use Modern::Perl;
+
+use C4::Members;
+use C4::Circulation;
+use Koha::Database;
+use Koha::Patrons;
+use Koha::Patron;
+
+use Test::More tests => 59;
+
+use_ok('Koha::Patron');
+
+use t::lib::TestBuilder;
+use t::lib::Mocks;
+
+my $schema = Koha::Database->new->schema;
+$schema->storage->txn_begin;
+
+my $builder = t::lib::TestBuilder->new;
+my $yesCatCode = $builder->build({
+ source => 'Category',
+ value => {
+ categorycode => 'yesCat',
+ checkprevcheckout => 'yes',
+ },
+});
+
+my $noCatCode = $builder->build({
+ source => 'Category',
+ value => {
+ categorycode => 'noCat',
+ checkprevcheckout => 'no',
+ },
+});
+
+my $inheritCatCode = $builder->build({
+ source => 'Category',
+ value => {
+ categorycode => 'inheritCat',
+ checkprevcheckout => 'inherit',
+ },
+});
+
+# Create context for some tests late on in the file.
+my $staff = $builder->build({source => 'Borrower'});
+my @USERENV = (
+ $staff->{borrowernumber}, 'test', 'MASTERTEST', 'firstname', 'CPL',
+ 'CPL', 'email@example.org'
+);
+C4::Context->_new_userenv('DUMMY_SESSION_ID');
+C4::Context->set_userenv(@USERENV);
+BAIL_OUT("No userenv") unless C4::Context->userenv;
+
+
+# wantsCheckPrevCheckout
+
+# We expect the following result matrix:
+#
+# (1/0 indicates the return value of WantsCheckPrevCheckout; i.e. 1 says we
+# should check whether the item was previously issued)
+#
+# | System Preference | hardyes | softyes | softno | hardno |
+# |-------------------+-----------------------------------+-----------------------------------+-----------------------------------+-----------------------------------|
+# | Category Setting | yes | no | inherit | yes | no | inherit | yes | no | inherit | yes | no | inherit |
+# |-------------------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------|
+# | Patron Setting | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i | y | n | i |
+# |-------------------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+# | Expected Result | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+
+my $mappings = [
+ {
+ syspref => 'hardyes',
+ categories => [
+ {
+ setting => 'yes',
+ patrons => [
+ {setting => 'yes', result => 1},
+ {setting => 'no', result => 1},
+ {setting => 'inherit', result => 1},
+ ],
+ },
+ {
+ setting => 'no',
+ patrons => [
+ {setting => 'yes', result => 1},
+ {setting => 'no', result => 1},
+ {setting => 'inherit', result => 1},
+ ],
+ },
+ {
+ setting => 'inherit',
+ patrons => [
+ {setting => 'yes', result => 1},
+ {setting => 'no', result => 1},
+ {setting => 'inherit', result => 1},
+ ],
+ },
+ ],
+ },
+ {
+ syspref => 'softyes',
+ categories => [
+ {
+ setting => 'yes',
+ patrons => [
+ {setting => 'yes', result => 1},
+ {setting => 'no', result => 0},
+ {setting => 'inherit', result => 1},
+ ],
+ },
+ {
+ setting => 'no',
+ patrons => [
+ {setting => 'yes', result => 1},
+ {setting => 'no', result => 0},
+ {setting => 'inherit', result => 0},
+ ],
+ },
+ {
+ setting => 'inherit',
+ patrons => [
+ {setting => 'yes', result => 1},
+ {setting => 'no', result => 0},
+ {setting => 'inherit', result => 1},
+ ],
+ },
+ ],
+ },
+ {
+ syspref => 'softno',
+ categories => [
+ {
+ setting => 'yes',
+ patrons => [
+ {setting => 'yes', result => 1},
+ {setting => 'no', result => 0},
+ {setting => 'inherit', result => 1},
+ ],
+ },
+ {
+ setting => 'no',
+ patrons => [
+ {setting => 'yes', result => 1},
+ {setting => 'no', result => 0},
+ {setting => 'inherit', result => 0},
+ ],
+ },
+ {
+ setting => 'inherit',
+ patrons => [
+ {setting => 'yes', result => 1},
+ {setting => 'no', result => 0},
+ {setting => 'inherit', result => 0},
+ ],
+ },
+ ],
+ },
+ {
+ syspref => 'hardno',
+ categories => [
+ {
+ setting => 'yes',
+ patrons => [
+ {setting => 'yes', result => 0},
+ {setting => 'no', result => 0},
+ {setting => 'inherit', result => 0},
+ ],
+ },
+ {
+ setting => 'no',
+ patrons => [
+ {setting => 'yes', result => 0},
+ {setting => 'no', result => 0},
+ {setting => 'inherit', result => 0},
+ ],
+ },
+ {
+ setting => 'inherit',
+ patrons => [
+ {setting => 'yes', result => 0},
+ {setting => 'no', result => 0},
+ {setting => 'inherit', result => 0},
+ ],
+ },
+ ],
+ },
+];
+
+map {
+ my $syspref = $_->{syspref};
+ t::lib::Mocks::mock_preference('checkprevcheckout', $syspref);
+ map {
+ my $code = $_->{setting} . 'Cat';
+ map {
+ my $kpatron = $builder->build({
+ source => 'Borrower',
+ value => {
+ checkprevcheckout => $_->{setting},
+ categorycode => $code,
+ },
+ });
+ my $patron = Koha::Patrons->find($kpatron->{borrowernumber});
+ is(
+ $patron->wantsCheckPrevCheckout, $_->{result},
+ "Predicate with syspref " . $syspref . ", cat " . $code
+ . ", patron " . $_->{setting}
+ );
+ } @{$_->{patrons}};
+ } @{$_->{categories}};
+} @{$mappings};
+
+# doCheckPrevCheckout
+
+# We want to test:
+# - DESCRIPTION [RETURNVALUE (0/1)]
+## PreIssue (sanity checks)
+# - Item, patron [0]
+# - Diff item, same bib, same patron [0]
+# - Diff item, diff bib, same patron [0]
+# - Same item, diff patron [0]
+# - Diff item, same bib, diff patron [0]
+# - Diff item, diff bib, diff patron [0]
+## PostIssue
+# - Same item, same patron [1]
+# - Diff item, same bib, same patron [1]
+# - Diff item, diff bib, same patron [0]
+# - Same item, diff patron [0]
+# - Diff item, same bib, diff patron [0]
+# - Diff item, diff bib, diff patron [0]
+## PostReturn
+# - Same item, same patron [1]
+# - Diff item, same bib, same patron [1]
+# - Diff item, diff bib, same patron [0]
+# - Same item, diff patron [0]
+# - Diff item, same bib, diff patron [0]
+# - Diff item, diff bib, diff patron [0]
+
+# Requirements:
+# $patron, $different_patron, $items (same bib number), $different_item
+my $patron = $builder->build({source => 'Borrower'});
+my $patron_d = $builder->build({source => 'Borrower'});
+my $item_1 = $builder->build({source => 'Item'});
+my $item_2 = $builder->build({
+ source => 'Item',
+ value => { biblionumber => $item_1->{biblionumber} },
+});
+my $item_d = $builder->build({source => 'Item'});
+
+## Testing Sub
+sub test_it {
+ my ($mapping, $stage) = @_;
+ map {
+ my $patron = Koha::Patrons->find($_->{patron}->{borrowernumber});
+ is(
+ $patron->doCheckPrevCheckout($_->{item}),
+ $_->{result}, $stage . ": " . $_->{msg}
+ );
+ } @{$mapping};
+};
+
+## Initial Mappings
+my $cpvmappings = [
+ {
+ msg => "Item, patron [0]",
+ item => $item_1,
+ patron => $patron,
+ result => 0,
+ },
+ {
+ msg => "Diff item, same bib, same patron [0]",
+ item => $item_2,
+ patron => $patron,
+ result => 0,
+ },
+ {
+ msg => "Diff item, diff bib, same patron [0]",
+ item => $item_d,
+ patron => $patron,
+ result => 0,
+ },
+ {
+ msg => "Same item, diff patron [0]",
+ item => $item_1,
+ patron => $patron_d,
+ result => 0,
+ },
+ {
+ msg => "Diff item, same bib, diff patron [0]",
+ item => $item_2,
+ patron => $patron_d,
+ result => 0,
+ },
+ {
+ msg => "Diff item, diff bib, diff patron [0]",
+ item => $item_d,
+ patron => $patron_d,
+ result => 0,
+ },
+];
+
+test_it($cpvmappings, "PreIssue");
+
+# Issue item_1 to $patron:
+my $patron_get_mem =
+ GetMember(%{{borrowernumber => $patron->{borrowernumber}}});
+BAIL_OUT("Issue failed")
+ unless AddIssue($patron_get_mem, $item_1->{barcode});
+
+# Then test:
+my $cpvPmappings = [
+ {
+ msg => "Same item, same patron [1]",
+ item => $item_1,
+ patron => $patron,
+ result => 1,
+ },
+ {
+ msg => "Diff item, same bib, same patron [1]",
+ item => $item_2,
+ patron => $patron,
+ result => 1,
+ },
+ {
+ msg => "Diff item, diff bib, same patron [0]",
+ item => $item_d,
+ patron => $patron,
+ result => 0,
+ },
+ {
+ msg => "Same item, diff patron [0]",
+ item => $item_1,
+ patron => $patron_d,
+ result => 0,
+ },
+ {
+ msg => "Diff item, same bib, diff patron [0]",
+ item => $item_2,
+ patron => $patron_d,
+ result => 0,
+ },
+ {
+ msg => "Diff item, diff bib, diff patron [0]",
+ item => $item_d,
+ patron => $patron_d,
+ result => 0,
+ },
+];
+
+test_it($cpvPmappings, "PostIssue");
+
+# Return item_1 from patron:
+BAIL_OUT("Return Failed") unless AddReturn($item_1->{barcode}, $patron->{branchcode});
+
+# Then:
+test_it($cpvPmappings, "PostReturn");
+
+# Finally test C4::Circulation::CanBookBeIssued
+
+# We have already tested ->wantsCheckPrevCheckout and ->doCheckPrevCheckout,
+# so all that remains to be tested is whetherthe different combinational
+# outcomes of the above return values in CanBookBeIssued result in the
+# approriate $needsconfirmation.
+
+# We want to test:
+# - DESCRIPTION [RETURNVALUE (0/1)]
+# - patron, !wantsCheckPrevCheckout, !doCheckPrevCheckout
+# [!$issuingimpossible,!$needsconfirmation->{PREVISSUE}]
+# - patron, wantsCheckPrevCheckout, !doCheckPrevCheckout
+# [!$issuingimpossible,!$needsconfirmation->{PREVISSUE}]
+# - patron, !wantsCheckPrevCheckout, doCheckPrevCheckout
+# [!$issuingimpossible,!$needsconfirmation->{PREVISSUE}]
+# - patron, wantsCheckPrevCheckout, doCheckPrevCheckout
+# [!$issuingimpossible,$needsconfirmation->{PREVISSUE}]
+
+# Needs:
+# - $patron_from_GetMember
+# - $item objects (one not issued, another prevIssued)
+# - $checkprevcheckout pref (first hardno, then hardyes)
+
+# Our Patron
+my $CBBI_patron = $builder->build({source => 'Borrower'});
+my $p_from_GetMember =
+ GetMember(%{{borrowernumber => $CBBI_patron->{borrowernumber}}});
+# Our Items
+my $new_item = $builder->build({
+ source => 'Item',
+ value => {
+ notforloan => 0,
+ withdrawn => 0,
+ itemlost => 0,
+ },
+});
+my $prev_item = $builder->build({
+ source => 'Item',
+ value => {
+ notforloan => 0,
+ withdrawn => 0,
+ itemlost => 0,
+ },
+});
+# Second is Checked Out
+BAIL_OUT("CanBookBeIssued Issue failed")
+ unless AddIssue($p_from_GetMember, $prev_item->{barcode});
+
+# Mappings
+my $CBBI_mappings = [
+ {
+ syspref => 'hardno',
+ item => $new_item,
+ result => undef,
+ msg => "patron, !wantsCheckPrevCheckout, !doCheckPrevCheckout"
+
+ },
+ {
+ syspref => 'hardyes',
+ item => $new_item,
+ result => undef,
+ msg => "patron, wantsCheckPrevCheckout, !doCheckPrevCheckout"
+ },
+ {
+ syspref => 'hardno',
+ item => $prev_item,
+ result => undef,
+ msg => "patron, !wantsCheckPrevCheckout, doCheckPrevCheckout"
+ },
+ {
+ syspref => 'hardyes',
+ item => $prev_item,
+ result => 1,
+ msg => "patron, wantsCheckPrevCheckout, doCheckPrevCheckout"
+ },
+];
+
+# Tests
+map {
+ t::lib::Mocks::mock_preference('checkprevcheckout', $_->{syspref});
+ my ( $issuingimpossible, $needsconfirmation ) =
+ C4::Circulation::CanBookBeIssued(
+ $p_from_GetMember, $_->{item}->{barcode}
+ );
+ is($needsconfirmation->{PREVISSUE}, $_->{result}, $_->{msg});
+} @{$CBBI_mappings};
+
+$schema->storage->txn_rollback;
+
+1;