+{
+ my $barcode = '1234567890';
+ my $branch = $library2->{branchcode};
+
+ my $biblio = MARC::Record->new();
+ my ($biblionumber, $biblioitemnumber) = AddBiblio($biblio, '');
+
+ #Create third item
+ my ( undef, undef, $itemnumber ) = AddItem(
+ {
+ homebranch => $branch,
+ holdingbranch => $branch,
+ barcode => $barcode
+ },
+ $biblionumber
+ );
+
+ # Create a borrower
+ my %a_borrower_data = (
+ firstname => 'Kyle',
+ surname => 'Hall',
+ categorycode => 'S',
+ branchcode => $branch,
+ );
+
+ my $borrowernumber = AddMember(%a_borrower_data);
+
+ my $issue = AddIssue( GetMember( borrowernumber => $borrowernumber ), $barcode );
+ UpdateFine(
+ {
+ issue_id => $issue->id(),
+ itemnumber => $itemnumber,
+ borrowernumber => $borrowernumber,
+ amount => 0
+ }
+ );
+
+ my $hr = $dbh->selectrow_hashref(q{SELECT COUNT(*) AS count FROM accountlines WHERE borrowernumber = ? AND itemnumber = ?}, undef, $borrowernumber, $itemnumber );
+ my $count = $hr->{count};
+
+ is ( $count, 0, "Calling UpdateFine on non-existant fine with an amount of 0 does not result in an empty fine" );
+}
+
+{
+ $dbh->do('DELETE FROM issues');
+ $dbh->do('DELETE FROM items');
+ $dbh->do('DELETE FROM issuingrules');
+ $dbh->do(
+ q{
+ INSERT INTO issuingrules ( categorycode, branchcode, itemtype, reservesallowed, maxissueqty, issuelength, lengthunit, renewalsallowed, renewalperiod,
+ norenewalbefore, auto_renew, fine, chargeperiod ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
+ },
+ {},
+ '*', '*', '*', 25,
+ 20, 14, 'days',
+ 1, 7,
+ undef, 0,
+ .10, 1
+ );
+ my $biblio = MARC::Record->new();
+ my ( $biblionumber, $biblioitemnumber ) = AddBiblio( $biblio, '' );
+
+ my $barcode1 = '1234';
+ my ( undef, undef, $itemnumber1 ) = AddItem(
+ {
+ homebranch => $library2->{branchcode},
+ holdingbranch => $library2->{branchcode},
+ barcode => $barcode1,
+ },
+ $biblionumber
+ );
+ my $barcode2 = '4321';
+ my ( undef, undef, $itemnumber2 ) = AddItem(
+ {
+ homebranch => $library2->{branchcode},
+ holdingbranch => $library2->{branchcode},
+ barcode => $barcode2,
+ },
+ $biblionumber
+ );
+
+ my $borrowernumber1 = AddMember(
+ firstname => 'Kyle',
+ surname => 'Hall',
+ categorycode => 'S',
+ branchcode => $library2->{branchcode},
+ );
+ my $borrowernumber2 = AddMember(
+ firstname => 'Chelsea',
+ surname => 'Hall',
+ categorycode => 'S',
+ branchcode => $library2->{branchcode},
+ );
+
+ my $borrower1 = GetMember( borrowernumber => $borrowernumber1 );
+ my $borrower2 = GetMember( borrowernumber => $borrowernumber2 );
+
+ my $issue = AddIssue( $borrower1, $barcode1 );
+
+ my ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $itemnumber1 );
+ is( $renewokay, 1, 'Bug 14337 - Verify the borrower can renew with no hold on the record' );
+
+ AddReserve(
+ $library2->{branchcode}, $borrowernumber2, $biblionumber,
+ '', 1, undef, undef, '',
+ undef, undef, undef
+ );
+
+ C4::Context->dbh->do("UPDATE issuingrules SET onshelfholds = 0");
+ t::lib::Mocks::mock_preference( 'AllowRenewalIfOtherItemsAvailable', 0 );
+ ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $itemnumber1 );
+ is( $renewokay, 0, 'Bug 14337 - Verify the borrower cannot renew with a hold on the record if AllowRenewalIfOtherItemsAvailable and onshelfholds are disabled' );
+
+ C4::Context->dbh->do("UPDATE issuingrules SET onshelfholds = 0");
+ t::lib::Mocks::mock_preference( 'AllowRenewalIfOtherItemsAvailable', 1 );
+ ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $itemnumber1 );
+ is( $renewokay, 0, 'Bug 14337 - Verify the borrower cannot renew with a hold on the record if AllowRenewalIfOtherItemsAvailable is enabled and onshelfholds is disabled' );
+
+ C4::Context->dbh->do("UPDATE issuingrules SET onshelfholds = 1");
+ t::lib::Mocks::mock_preference( 'AllowRenewalIfOtherItemsAvailable', 0 );
+ ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $itemnumber1 );
+ is( $renewokay, 0, 'Bug 14337 - Verify the borrower cannot renew with a hold on the record if AllowRenewalIfOtherItemsAvailable is disabled and onshelfhold is enabled' );
+
+ C4::Context->dbh->do("UPDATE issuingrules SET onshelfholds = 1");
+ t::lib::Mocks::mock_preference( 'AllowRenewalIfOtherItemsAvailable', 1 );
+ ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $itemnumber1 );
+ is( $renewokay, 1, 'Bug 14337 - Verify the borrower can renew with a hold on the record if AllowRenewalIfOtherItemsAvailable and onshelfhold are enabled' );
+
+ # Setting item not checked out to be not for loan but holdable
+ ModItem({ notforloan => -1 }, $biblionumber, $itemnumber2);
+
+ ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $itemnumber1 );
+ is( $renewokay, 0, 'Bug 14337 - Verify the borrower can not renew with a hold on the record if AllowRenewalIfOtherItemsAvailable is enabled but the only available item is notforloan' );
+}
+
+{
+ # Don't allow renewing onsite checkout
+ my $barcode = 'R00000XXX';
+ my $branch = $library->{branchcode};
+
+ #Create another record
+ my $biblio = MARC::Record->new();
+ $biblio->append_fields(
+ MARC::Field->new('100', ' ', ' ', a => 'Anonymous'),
+ MARC::Field->new('245', ' ', ' ', a => 'A title'),
+ );
+ my ($biblionumber, $biblioitemnumber) = AddBiblio($biblio, '');
+
+ my (undef, undef, $itemnumber) = AddItem(
+ {
+ homebranch => $branch,
+ holdingbranch => $branch,
+ barcode => $barcode,
+ },
+ $biblionumber
+ );
+
+ my $borrowernumber = AddMember(
+ firstname => 'fn',
+ surname => 'dn',
+ categorycode => 'S',
+ branchcode => $branch,
+ );
+
+ my $borrower = GetMember( borrowernumber => $borrowernumber );
+ my $issue = AddIssue( $borrower, $barcode, undef, undef, undef, undef, { onsite_checkout => 1 } );
+ my ( $renewed, $error ) = CanBookBeRenewed( $borrowernumber, $itemnumber );
+ is( $renewed, 0, 'CanBookBeRenewed should not allow to renew on-site checkout' );
+ is( $error, 'onsite_checkout', 'A correct error code should be returned by CanBookBeRenewed for on-site checkout' );
+}
+
+{
+ my $library = $builder->build({ source => 'Branch' });
+
+ my $biblio = MARC::Record->new();
+ my ($biblionumber, $biblioitemnumber) = AddBiblio($biblio, '');
+
+ my $barcode = 'just a barcode';
+ my ( undef, undef, $itemnumber ) = AddItem(
+ {
+ homebranch => $library->{branchcode},
+ holdingbranch => $library->{branchcode},
+ barcode => $barcode,
+ },
+ $biblionumber,
+ );
+
+ my $patron = $builder->build({ source => 'Borrower', value => { branchcode => $library->{branchcode} } } );
+
+ my $issue = AddIssue( GetMember( borrowernumber => $patron->{borrowernumber} ), $barcode );
+ UpdateFine(
+ {
+ issue_id => $issue->id(),
+ itemnumber => $itemnumber,
+ borrowernumber => $patron->{borrowernumber},
+ amount => 1,
+ }
+ );
+ UpdateFine(
+ {
+ issue_id => $issue->id(),
+ itemnumber => $itemnumber,
+ borrowernumber => $patron->{borrowernumber},
+ amount => 2,
+ }
+ );
+ is( Koha::Account::Lines->search({ issue_id => $issue->id })->count, 1, 'UpdateFine should not create a new accountline when updating an existing fine');
+}
+
+subtest 'CanBookBeIssued & AllowReturnToBranch' => sub {
+ plan tests => 23;
+
+ my $homebranch = $builder->build( { source => 'Branch' } );
+ my $holdingbranch = $builder->build( { source => 'Branch' } );
+ my $otherbranch = $builder->build( { source => 'Branch' } );
+ my $patron_1 = $builder->build( { source => 'Borrower' } );
+ my $patron_2 = $builder->build( { source => 'Borrower' } );
+
+ my $biblioitem = $builder->build( { source => 'Biblioitem' } );
+ my $item = $builder->build(
+ { source => 'Item',
+ value => {
+ homebranch => $homebranch->{branchcode},
+ holdingbranch => $holdingbranch->{branchcode},
+ notforloan => 0,
+ itemlost => 0,
+ withdrawn => 0,
+ biblionumber => $biblioitem->{biblionumber}
+ }
+ }
+ );
+
+ set_userenv($holdingbranch);
+
+ my $issue = AddIssue( $patron_1, $item->{barcode} );
+ is( ref($issue), 'Koha::Schema::Result::Issue' ); # FIXME Should be Koha::Issue
+
+ my ( $error, $question, $alerts );
+
+ # AllowReturnToBranch == anywhere
+ t::lib::Mocks::mock_preference( 'AllowReturnToBranch', 'anywhere' );
+ ## Can be issued from homebranch
+ set_userenv($homebranch);
+ ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
+ is( keys(%$error) + keys(%$alerts), 0 );
+ is( exists $question->{ISSUED_TO_ANOTHER}, 1 );
+ ## Can be issued from holdingbranch
+ set_userenv($holdingbranch);
+ ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
+ is( keys(%$error) + keys(%$alerts), 0 );
+ is( exists $question->{ISSUED_TO_ANOTHER}, 1 );
+ ## Can be issued from another branch
+ ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
+ is( keys(%$error) + keys(%$alerts), 0 );
+ is( exists $question->{ISSUED_TO_ANOTHER}, 1 );
+
+ # AllowReturnToBranch == holdingbranch
+ t::lib::Mocks::mock_preference( 'AllowReturnToBranch', 'holdingbranch' );
+ ## Cannot be issued from homebranch
+ set_userenv($homebranch);
+ ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
+ is( keys(%$question) + keys(%$alerts), 0 );
+ is( exists $error->{RETURN_IMPOSSIBLE}, 1 );
+ is( $error->{branch_to_return}, $holdingbranch->{branchcode} );
+ ## Can be issued from holdinbranch
+ set_userenv($holdingbranch);
+ ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
+ is( keys(%$error) + keys(%$alerts), 0 );
+ is( exists $question->{ISSUED_TO_ANOTHER}, 1 );
+ ## Cannot be issued from another branch
+ set_userenv($otherbranch);
+ ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
+ is( keys(%$question) + keys(%$alerts), 0 );
+ is( exists $error->{RETURN_IMPOSSIBLE}, 1 );
+ is( $error->{branch_to_return}, $holdingbranch->{branchcode} );
+
+ # AllowReturnToBranch == homebranch
+ t::lib::Mocks::mock_preference( 'AllowReturnToBranch', 'homebranch' );
+ ## Can be issued from holdinbranch
+ set_userenv($homebranch);
+ ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
+ is( keys(%$error) + keys(%$alerts), 0 );
+ is( exists $question->{ISSUED_TO_ANOTHER}, 1 );
+ ## Cannot be issued from holdinbranch
+ set_userenv($holdingbranch);
+ ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
+ is( keys(%$question) + keys(%$alerts), 0 );
+ is( exists $error->{RETURN_IMPOSSIBLE}, 1 );
+ is( $error->{branch_to_return}, $homebranch->{branchcode} );
+ ## Cannot be issued from holdinbranch
+ set_userenv($otherbranch);
+ ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
+ is( keys(%$question) + keys(%$alerts), 0 );
+ is( exists $error->{RETURN_IMPOSSIBLE}, 1 );
+ is( $error->{branch_to_return}, $homebranch->{branchcode} );
+
+ # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
+};
+
+subtest 'AddIssue & AllowReturnToBranch' => sub {
+ plan tests => 9;
+
+ my $homebranch = $builder->build( { source => 'Branch' } );
+ my $holdingbranch = $builder->build( { source => 'Branch' } );
+ my $otherbranch = $builder->build( { source => 'Branch' } );
+ my $patron_1 = $builder->build( { source => 'Borrower' } );
+ my $patron_2 = $builder->build( { source => 'Borrower' } );
+
+ my $biblioitem = $builder->build( { source => 'Biblioitem' } );
+ my $item = $builder->build(
+ { source => 'Item',
+ value => {
+ homebranch => $homebranch->{branchcode},
+ holdingbranch => $holdingbranch->{branchcode},
+ notforloan => 0,
+ itemlost => 0,
+ withdrawn => 0,
+ biblionumber => $biblioitem->{biblionumber}
+ }
+ }
+ );
+
+ set_userenv($holdingbranch);
+
+ my $ref_issue = 'Koha::Schema::Result::Issue'; # FIXME Should be Koha::Issue
+ my $issue = AddIssue( $patron_1, $item->{barcode} );
+
+ my ( $error, $question, $alerts );
+
+ # AllowReturnToBranch == homebranch
+ t::lib::Mocks::mock_preference( 'AllowReturnToBranch', 'anywhere' );
+ ## Can be issued from homebranch
+ set_userenv($homebranch);
+ is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), $ref_issue );
+ set_userenv($holdingbranch); AddIssue( $patron_1, $item->{barcode} ); # Reinsert the original issue
+ ## Can be issued from holdinbranch
+ set_userenv($holdingbranch);
+ is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), $ref_issue );
+ set_userenv($holdingbranch); AddIssue( $patron_1, $item->{barcode} ); # Reinsert the original issue
+ ## Can be issued from another branch
+ set_userenv($otherbranch);
+ is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), $ref_issue );
+ set_userenv($holdingbranch); AddIssue( $patron_1, $item->{barcode} ); # Reinsert the original issue
+
+ # AllowReturnToBranch == holdinbranch
+ t::lib::Mocks::mock_preference( 'AllowReturnToBranch', 'holdingbranch' );
+ ## Cannot be issued from homebranch
+ set_userenv($homebranch);
+ is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), '' );
+ ## Can be issued from holdingbranch
+ set_userenv($holdingbranch);
+ is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), $ref_issue );
+ set_userenv($holdingbranch); AddIssue( $patron_1, $item->{barcode} ); # Reinsert the original issue
+ ## Cannot be issued from another branch
+ set_userenv($otherbranch);
+ is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), '' );
+
+ # AllowReturnToBranch == homebranch
+ t::lib::Mocks::mock_preference( 'AllowReturnToBranch', 'homebranch' );
+ ## Can be issued from homebranch
+ set_userenv($homebranch);
+ is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), $ref_issue );
+ set_userenv($holdingbranch); AddIssue( $patron_1, $item->{barcode} ); # Reinsert the original issue
+ ## Cannot be issued from holdinbranch
+ set_userenv($holdingbranch);
+ is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), '' );
+ ## Cannot be issued from another branch
+ set_userenv($otherbranch);
+ is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), '' );
+ # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
+};
+
+subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
+ plan tests => 8;
+
+ my $library = $builder->build( { source => 'Branch' } );
+ my $patron = $builder->build( { source => 'Borrower' } );
+
+ my $biblioitem_1 = $builder->build( { source => 'Biblioitem' } );
+ my $item_1 = $builder->build(
+ { source => 'Item',
+ value => {
+ homebranch => $library->{branchcode},
+ holdingbranch => $library->{branchcode},
+ notforloan => 0,
+ itemlost => 0,
+ withdrawn => 0,
+ biblionumber => $biblioitem_1->{biblionumber}
+ }
+ }
+ );
+ my $biblioitem_2 = $builder->build( { source => 'Biblioitem' } );
+ my $item_2 = $builder->build(
+ { source => 'Item',
+ value => {
+ homebranch => $library->{branchcode},
+ holdingbranch => $library->{branchcode},
+ notforloan => 0,
+ itemlost => 0,
+ withdrawn => 0,
+ biblionumber => $biblioitem_2->{biblionumber}
+ }
+ }
+ );
+
+ my ( $error, $question, $alerts );
+
+ # Patron cannot issue item_1, he has overdues
+ my $yesterday = DateTime->today( time_zone => C4::Context->tz() )->add( days => -1 );
+ my $issue = AddIssue( $patron, $item_1->{barcode}, $yesterday ); # Add an overdue
+
+ t::lib::Mocks::mock_preference( 'OverduesBlockCirc', 'confirmation' );
+ ( $error, $question, $alerts ) = CanBookBeIssued( $patron, $item_2->{barcode} );
+ is( keys(%$error) + keys(%$alerts), 0 );
+ is( $question->{USERBLOCKEDOVERDUE}, 1 );
+
+ t::lib::Mocks::mock_preference( 'OverduesBlockCirc', 'block' );
+ ( $error, $question, $alerts ) = CanBookBeIssued( $patron, $item_2->{barcode} );
+ is( keys(%$question) + keys(%$alerts), 0 );
+ is( $error->{USERBLOCKEDOVERDUE}, 1 );
+
+ # Patron cannot issue item_1, he is debarred
+ my $tomorrow = DateTime->today( time_zone => C4::Context->tz() )->add( days => 1 );
+ Koha::Patron::Debarments::AddDebarment( { borrowernumber => $patron->{borrowernumber}, expiration => $tomorrow } );
+ ( $error, $question, $alerts ) = CanBookBeIssued( $patron, $item_2->{barcode} );
+ is( keys(%$question) + keys(%$alerts), 0 );
+ is( $error->{USERBLOCKEDWITHENDDATE}, output_pref( { dt => $tomorrow, dateformat => 'sql', dateonly => 1 } ) );
+
+ Koha::Patron::Debarments::AddDebarment( { borrowernumber => $patron->{borrowernumber} } );
+ ( $error, $question, $alerts ) = CanBookBeIssued( $patron, $item_2->{barcode} );
+ is( keys(%$question) + keys(%$alerts), 0 );
+ is( $error->{USERBLOCKEDNOENDDATE}, '9999-12-31' );
+};
+
+sub set_userenv {
+ my ( $library ) = @_;
+ C4::Context->set_userenv(0,0,0,'firstname','surname', $library->{branchcode}, $library->{branchname}, '', '', '');
+}
+
+1;