@@ -, +, @@ --- C4/Reserves.pm | 418 +++++++++----------- circ/circulation.pl | 2 +- installer/data/mysql/sysprefs.sql | 1 + installer/data/mysql/updatedatabase.pl | 7 + .../en/modules/admin/preferences/circulation.pref | 7 +- .../prog/en/modules/reserve/request.tt | 25 +- koha-tmpl/opac-tmpl/prog/en/modules/opac-user.tt | 2 +- opac/opac-modrequest.pl | 6 +- opac/opac-reserve.pl | 19 +- reserve/modrequest.pl | 21 +- reserve/request.pl | 68 ++-- 11 files changed, 278 insertions(+), 298 deletions(-) --- a/C4/Reserves.pm +++ a/C4/Reserves.pm @@ -93,7 +93,8 @@ BEGIN { @ISA = qw(Exporter); @EXPORT = qw( &AddReserve - + + &GetReserve &GetReservesFromItemnumber &GetReservesFromBiblionumber &GetReservesFromBorrowernumber @@ -244,6 +245,24 @@ sub AddReserve { return; # FIXME: why not have a useful return value? } +=head2 GetReserve + + $res = GetReserve( $reserve_id ); + +=cut + +sub GetReserve { + my ($reserve_id) = @_; + #warn "C4::Reserves::GetReserve( $reserve_id )"; + + my $dbh = C4::Context->dbh; + my $query = "SELECT * FROM reserves WHERE reserve_id = ?"; + my $sth = $dbh->prepare( $query ); + $sth->execute( $reserve_id ); + my $res = $sth->fetchrow_hashref(); + return $res; +} + =head2 GetReservesFromBiblionumber ($count, $title_reserves) = &GetReserves($biblionumber); @@ -260,7 +279,8 @@ sub GetReservesFromBiblionumber { # Find the desired items in the reserves my $query = " - SELECT branchcode, + SELECT reserve_id, + branchcode, timestamp AS rtimestamp, priority, biblionumber, @@ -328,7 +348,7 @@ sub GetReservesFromBiblionumber { =head2 GetReservesFromItemnumber - ( $reservedate, $borrowernumber, $branchcode ) = GetReservesFromItemnumber($itemnumber); + ( $reservedate, $borrowernumber, $branchcode, $reserve_id ) = GetReservesFromItemnumber($itemnumber); TODO :: Description here @@ -338,7 +358,7 @@ sub GetReservesFromItemnumber { my ( $itemnumber, $all_dates ) = @_; my $dbh = C4::Context->dbh; my $query = " - SELECT reservedate,borrowernumber,branchcode + SELECT reservedate,borrowernumber,branchcode,reserve_id FROM reserves WHERE itemnumber=? "; @@ -418,8 +438,10 @@ This function return 1 if an item can be issued by this borrower. sub CanItemBeReserved{ my ($borrowernumber, $itemnumber) = @_; - my $dbh = C4::Context->dbh; + my $dbh = C4::Context->dbh; my $allowedreserves = 0; + my $multi_holds_allowed_same = 0; + my $multi_holds_allowed_diff = 0; my $controlbranch = C4::Context->preference('ReservesControlBranch'); my $itype = C4::Context->preference('item-level_itypes') ? "itype" : "itemtype"; @@ -428,27 +450,14 @@ sub CanItemBeReserved{ my $item = GetItem($itemnumber); my $borrower = C4::Members::GetMember('borrowernumber'=>$borrowernumber); - # we retrieve user rights on this itemtype and branchcode - my $sth = $dbh->prepare("SELECT categorycode, itemtype, branchcode, reservesallowed - FROM issuingrules - WHERE (categorycode in (?,'*') ) - AND (itemtype IN (?,'*')) - AND (branchcode IN (?,'*')) - ORDER BY - categorycode DESC, - itemtype DESC, - branchcode DESC;" - ); - - my $querycount ="SELECT - count(*) as count - FROM reserves - LEFT JOIN items USING (itemnumber) - LEFT JOIN biblioitems ON (reserves.biblionumber=biblioitems.biblionumber) - LEFT JOIN borrowers USING (borrowernumber) - WHERE borrowernumber = ? - "; - + my $querycount = " + SELECT count(*) AS count + FROM reserves + LEFT JOIN items USING (itemnumber) + LEFT JOIN biblioitems ON (reserves.biblionumber=biblioitems.biblionumber) + LEFT JOIN borrowers USING (borrowernumber) + WHERE borrowernumber = ? + "; my $itemtype = $item->{$itype}; my $categorycode = $borrower->{categorycode}; @@ -464,14 +473,19 @@ sub CanItemBeReserved{ } # we retrieve rights - $sth->execute($categorycode, $itemtype, $branchcode); - if(my $rights = $sth->fetchrow_hashref()){ + my $rights = C4::Circulation::GetIssuingRule( $categorycode, $itemtype, $branchcode ); + if ( defined $rights ) { $itemtype = $rights->{itemtype}; $allowedreserves = $rights->{reservesallowed}; }else{ $itemtype = '*'; } + my $multi_holds_same = GetMultiHoldsCountSameEnum( + borrowernumber => $borrowernumber, + itemnumber => $itemnumber + ); + # we retrieve count $querycount .= "AND $branchfield = ?"; @@ -491,10 +505,10 @@ sub CanItemBeReserved{ } # we check if it's ok or not - if( $reservecount < $allowedreserves ){ - return 1; - }else{ + if( $reservecount >= $allowedreserves ){ ## too many reserves return 0; + }else{ + return 1; } } #-------------------------------------------------------------------------------- @@ -511,11 +525,11 @@ sub GetReserveCount { my $dbh = C4::Context->dbh; - my $query = ' + my $query = " SELECT COUNT(*) AS counter FROM reserves - WHERE borrowernumber = ? - '; + WHERE borrowernumber = ? + "; my $sth = $dbh->prepare($query); $sth->execute($borrowernumber); my $row = $sth->fetchrow_hashref; @@ -687,7 +701,7 @@ sub GetReservesToBranch { my ( $frombranch ) = @_; my $dbh = C4::Context->dbh; my $sth = $dbh->prepare( - "SELECT borrowernumber,reservedate,itemnumber,timestamp + "SELECT reserve_id,borrowernumber,reservedate,itemnumber,timestamp FROM reserves WHERE priority='0' AND branchcode=?" @@ -710,22 +724,24 @@ sub GetReservesToBranch { sub GetReservesForBranch { my ($frombranch) = @_; - my $dbh = C4::Context->dbh; - my $query = "SELECT borrowernumber,reservedate,itemnumber,waitingdate + my $dbh = C4::Context->dbh; + + my $query = " + SELECT reserve_id,borrowernumber,reservedate,itemnumber,waitingdate FROM reserves WHERE priority='0' - AND found='W' "; - if ($frombranch){ - $query .= " AND branchcode=? "; - } + AND found='W' + "; + $query .= " AND branchcode=? " if ( $frombranch ); $query .= "ORDER BY waitingdate" ; + my $sth = $dbh->prepare($query); if ($frombranch){ - $sth->execute($frombranch); - } - else { - $sth->execute(); - } + $sth->execute($frombranch); + } else { + $sth->execute(); + } + my @transreserv; my $i = 0; while ( my $data = $sth->fetchrow_hashref ) { @@ -923,109 +939,49 @@ sub AutoUnsuspendReserves { =head2 CancelReserve - &CancelReserve($biblionumber, $itemnumber, $borrowernumber); + &CancelReserve( $reserve_id ); Cancels a reserve. -Use either C<$biblionumber> or C<$itemnumber> to specify the item to -cancel, but not both: if both are given, C<&CancelReserve> uses -C<$itemnumber>. - -C<$borrowernumber> is the borrower number of the patron on whose -behalf the book was reserved. - -If C<$biblionumber> was given, C<&CancelReserve> also adjusts the -priorities of the other people who are waiting on the book. - =cut sub CancelReserve { - my ( $biblio, $item, $borr ) = @_; + my ( $reserve_id ) = @_; my $dbh = C4::Context->dbh; - if ( $item and $borr ) { - # removing a waiting reserve record.... - # update the database... - my $query = " - UPDATE reserves - SET cancellationdate = now(), - found = Null, - priority = 0 - WHERE itemnumber = ? - AND borrowernumber = ? - "; - my $sth = $dbh->prepare($query); - $sth->execute( $item, $borr ); - $sth->finish; - $query = " - INSERT INTO old_reserves - SELECT * FROM reserves - WHERE itemnumber = ? - AND borrowernumber = ? - "; - $sth = $dbh->prepare($query); - $sth->execute( $item, $borr ); - $query = " - DELETE FROM reserves - WHERE itemnumber = ? - AND borrowernumber = ? - "; - $sth = $dbh->prepare($query); - $sth->execute( $item, $borr ); - } - else { - # removing a reserve record.... - # get the prioritiy on this record.... - my $priority; - my $query = qq/ - SELECT priority FROM reserves - WHERE biblionumber = ? - AND borrowernumber = ? - AND cancellationdate IS NULL - AND itemnumber IS NULL - /; - my $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borr ); - ($priority) = $sth->fetchrow_array; - $sth->finish; - $query = qq/ - UPDATE reserves - SET cancellationdate = now(), - found = Null, - priority = 0 - WHERE biblionumber = ? - AND borrowernumber = ? - /; - - # update the database, removing the record... - $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borr ); - $sth->finish; - $query = qq/ - INSERT INTO old_reserves - SELECT * FROM reserves - WHERE biblionumber = ? - AND borrowernumber = ? - /; - $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borr ); + my $query = " + UPDATE reserves + SET cancellationdate = now(), + found = Null, + priority = 0 + WHERE reserve_id = ? + "; + my $sth = $dbh->prepare($query); + $sth->execute( $reserve_id ); + $sth->finish; - $query = qq/ - DELETE FROM reserves - WHERE biblionumber = ? - AND borrowernumber = ? - /; - $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borr ); + $query = " + INSERT INTO old_reserves + SELECT * FROM reserves + WHERE reserve_id = ? + "; + $sth = $dbh->prepare($query); + $sth->execute( $reserve_id ); - # now fix the priority on the others.... - _FixPriority( $biblio, $borr ); - } + $query = " + DELETE FROM reserves + WHERE reserve_id = ? + "; + $sth = $dbh->prepare($query); + $sth->execute( $reserve_id ); + + # now fix the priority on the others.... + _FixPriority( $reserve_id ); } =head2 ModReserve - ModReserve($rank, $biblio, $borrower, $branch[, $itemnumber]) + ModReserve($rank, $reserve_id, $branch[, $itemnumber]) Change a hold request's priority or cancel it. @@ -1056,58 +1012,54 @@ itemnumber and supplying itemnumber. sub ModReserve { #subroutine to update a reserve - my ( $rank, $biblio, $borrower, $branch , $itemnumber, $suspend_until) = @_; + my ( $rank, $reserve_id, $branch , $itemnumber, $suspend_until) = @_; return if $rank eq "W"; return if $rank eq "n"; my $dbh = C4::Context->dbh; if ( $rank eq "del" ) { - my $query = qq/ + my $query = " UPDATE reserves SET cancellationdate=now() - WHERE biblionumber = ? - AND borrowernumber = ? - /; + WHERE reserve_id = ? + "; my $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borrower ); + $sth->execute( $reserve_id ); $sth->finish; - $query = qq/ + $query = " INSERT INTO old_reserves SELECT * FROM reserves - WHERE biblionumber = ? - AND borrowernumber = ? - /; + WHERE reserve_id = ? + "; $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borrower ); - $query = qq/ + $sth->execute( $reserve_id ); + $query = " DELETE FROM reserves - WHERE biblionumber = ? - AND borrowernumber = ? - /; + WHERE reserve_id = ? + "; $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borrower ); + $sth->execute( $reserve_id ); } elsif ($rank =~ /^\d+/ and $rank > 0) { my $query = " UPDATE reserves SET priority = ? ,branchcode = ?, itemnumber = ?, found = NULL, waitingdate = NULL - WHERE biblionumber = ? - AND borrowernumber = ? + WHERE reserve_id = ? "; my $sth = $dbh->prepare($query); - $sth->execute( $rank, $branch,$itemnumber, $biblio, $borrower); + $sth->execute( $rank, $branch, $itemnumber, $reserve_id ); $sth->finish; if ( defined( $suspend_until ) ) { if ( $suspend_until ) { $suspend_until = C4::Dates->new( $suspend_until )->output("iso"); - $dbh->do("UPDATE reserves SET suspend = 1, suspend_until = ? WHERE biblionumber = ? AND borrowernumber = ?", undef, ( $suspend_until, $biblio, $borrower ) ); + $dbh->do("UPDATE reserves SET suspend = 1, suspend_until = ? WHERE reserve_id = ?", undef, ( $suspend_until, $reserve_id ) ); } else { - $dbh->do("UPDATE reserves SET suspend_until = NULL WHERE biblionumber = ? AND borrowernumber = ?", undef, ( $biblio, $borrower ) ); + $dbh->do("UPDATE reserves SET suspend_until = NULL WHERE reserve_id = ?", undef, ( $reserve_id ) ); } } - _FixPriority( $biblio, $borrower, $rank); + _FixPriority( $reserve_id, $rank ); } } @@ -1127,6 +1079,7 @@ sub ModReserveFill { my ($res) = @_; my $dbh = C4::Context->dbh; # fill in a reserve record.... + my $reserve_id = $res->{'reserve_id'}; my $biblionumber = $res->{'biblionumber'}; my $borrowernumber = $res->{'borrowernumber'}; my $resdate = $res->{'reservedate'}; @@ -1175,7 +1128,7 @@ sub ModReserveFill { # now fix the priority on the others (if the priority wasn't # already sorted!).... unless ( $priority == 0 ) { - _FixPriority( $biblionumber, $borrowernumber ); + _FixPriority( $reserve_id ); } } @@ -1302,20 +1255,19 @@ Reduce the values of queuded list =cut sub ModReserveMinusPriority { - my ( $itemnumber, $borrowernumber, $biblionumber ) = @_; + my ( $itemnumber, $reserve_id ) = @_; #first step update the value of the first person on reserv my $dbh = C4::Context->dbh; my $query = " UPDATE reserves SET priority = 0 , itemnumber = ? - WHERE borrowernumber=? - AND biblionumber=? + WHERE reserve_id = ? "; my $sth_upd = $dbh->prepare($query); - $sth_upd->execute( $itemnumber, $borrowernumber, $biblionumber ); + $sth_upd->execute( $itemnumber, $reserve_id ); # second step update all others reservs - _FixPriority($biblionumber, $borrowernumber, '0'); + _FixPriority( $reserve_id, '0'); } =head2 GetReserveInfo @@ -1328,7 +1280,7 @@ Current implementation this query should have a single result. =cut sub GetReserveInfo { - my ( $borrowernumber, $biblionumber ) = @_; + my ( $reserve_id ) = @_; my $dbh = C4::Context->dbh; my $strsth="SELECT reservedate, @@ -1362,11 +1314,9 @@ sub GetReserveInfo { LEFT JOIN items USING(itemnumber) LEFT JOIN borrowers USING(borrowernumber) LEFT JOIN biblio ON (reserves.biblionumber=biblio.biblionumber) - WHERE - reserves.borrowernumber=? - AND reserves.biblionumber=?"; + WHERE reserves.reserve_id = ?"; my $sth = $dbh->prepare($strsth); - $sth->execute($borrowernumber,$biblionumber); + $sth->execute($reserve_id); my $data = $sth->fetchrow_hashref; return $data; @@ -1455,29 +1405,25 @@ Input: $where is 'up', 'down', 'top' or 'bottom'. Biblionumber, Date reserve was =cut sub AlterPriority { - my ( $where, $borrowernumber, $biblionumber ) = @_; - + my ( $where, $reserve_id ) = @_; + #warn "C4::Reserves::AlterPriority( $where, $reserve_id )"; my $dbh = C4::Context->dbh; - ## Find this reserve - my $sth = $dbh->prepare('SELECT * FROM reserves WHERE biblionumber = ? AND borrowernumber = ? AND cancellationdate IS NULL'); - $sth->execute( $biblionumber, $borrowernumber ); - my $reserve = $sth->fetchrow_hashref(); - $sth->finish(); + my $reserve = GetReserve( $reserve_id ); if ( $where eq 'up' || $where eq 'down' ) { my $priority = $reserve->{'priority'}; $priority = $where eq 'up' ? $priority - 1 : $priority + 1; - _FixPriority( $biblionumber, $borrowernumber, $priority ) + _FixPriority( $reserve_id, $priority ) } elsif ( $where eq 'top' ) { - _FixPriority( $biblionumber, $borrowernumber, '1' ) + _FixPriority( $reserve_id, '1' ) } elsif ( $where eq 'bottom' ) { - _FixPriority( $biblionumber, $borrowernumber, '999999' ) + _FixPriority( $reserve_id, '999999' ) } } @@ -1491,27 +1437,20 @@ This function sets the lowestPriority field to true if is false, and false if it =cut sub ToggleLowestPriority { - my ( $borrowernumber, $biblionumber ) = @_; + my ( $reserve_id ) = @_; my $dbh = C4::Context->dbh; - my $sth = $dbh->prepare( - "UPDATE reserves SET lowestPriority = NOT lowestPriority - WHERE biblionumber = ? - AND borrowernumber = ?" - ); - $sth->execute( - $biblionumber, - $borrowernumber, - ); + my $sth = $dbh->prepare( "UPDATE reserves SET lowestPriority = NOT lowestPriority WHERE reserve_id = ?"); + $sth->execute( $reserve_id ); $sth->finish; - _FixPriority( $biblionumber, $borrowernumber, '999999' ); + _FixPriority( $reserve_id, '999999' ); } =head2 ToggleSuspend - ToggleSuspend( $borrowernumber, $biblionumber ); + ToggleSuspend( $reserve_id ); This function sets the suspend field to true if is false, and false if it is true. If the reserve is currently suspended with a suspend_until date, that date will @@ -1520,7 +1459,7 @@ be cleared when it is unsuspended. =cut sub ToggleSuspend { - my ( $borrowernumber, $biblionumber, $suspend_until ) = @_; + my ( $reserve_id, $suspend_until ) = @_; $suspend_until = output_pref( dt_from_string( $suspend_until ), 'iso' ) if ( $suspend_until ); @@ -1531,14 +1470,12 @@ sub ToggleSuspend { my $sth = $dbh->prepare( "UPDATE reserves SET suspend = NOT suspend, suspend_until = CASE WHEN suspend = 0 THEN NULL ELSE $do_until END - WHERE biblionumber = ? - AND borrowernumber = ? + WHERE reserve_id = ? "); my @params; push( @params, $suspend_until ) if ( $suspend_until ); - push( @params, $biblionumber ); - push( @params, $borrowernumber ); + push( @params, $reserve_id ); $sth->execute( @params ); $sth->finish; @@ -1603,7 +1540,7 @@ sub SuspendAll { =head2 _FixPriority - &_FixPriority($biblio,$borrowernumber,$rank,$ignoreSetLowestRank); + &_FixPriority( $reserve_id, $rank, $ignoreSetLowestRank); Only used internally (so don't export it) Changed how this functions works # @@ -1615,41 +1552,39 @@ in new priority rank =cut sub _FixPriority { - my ( $biblio, $borrowernumber, $rank, $ignoreSetLowestRank ) = @_; + my ( $reserve_id, $rank, $ignoreSetLowestRank ) = @_; my $dbh = C4::Context->dbh; - if ( $rank eq "del" ) { - CancelReserve( $biblio, undef, $borrowernumber ); - } - if ( $rank eq "W" || $rank eq "0" ) { + + my $res = GetReserve( $reserve_id ); + + if ( $rank eq "del" ) { + CancelReserve( $reserve_id ); + } + elsif ( $rank eq "W" || $rank eq "0" ) { # make sure priority for waiting or in-transit items is 0 - my $query = qq/ + my $query = " UPDATE reserves SET priority = 0 - WHERE biblionumber = ? - AND borrowernumber = ? - AND found IN ('W', 'T') - /; + WHERE reserve_id = ? + AND found IN ('W', 'T') + "; my $sth = $dbh->prepare($query); - $sth->execute( $biblio, $borrowernumber ); + $sth->execute( $reserve_id ); } my @priority; my @reservedates; # get whats left -# FIXME adding a new security in returned elements for changing priority, -# now, we don't care anymore any reservations with itemnumber linked (suppose a waiting reserve) - # This is wrong a waiting reserve has W set - # The assumption that having an itemnumber set means waiting is wrong and should be corrected any place it occurs - my $query = qq/ - SELECT borrowernumber, reservedate, constrainttype + my $query = " + SELECT reserve_id, borrowernumber, reservedate, constrainttype FROM reserves WHERE biblionumber = ? - AND ((found <> 'W' AND found <> 'T') or found is NULL) + AND ((found <> 'W' AND found <> 'T') OR found IS NULL) ORDER BY priority ASC - /; + "; my $sth = $dbh->prepare($query); - $sth->execute($biblio); + $sth->execute( $res->{'biblionumber'} ); while ( my $line = $sth->fetchrow_hashref ) { push( @reservedates, $line ); push( @priority, $line ); @@ -1659,7 +1594,7 @@ sub _FixPriority { my $i; my $key = -1; # to allow for 0 to be a valid result for ( $i = 0 ; $i < @priority ; $i++ ) { - if ( $borrowernumber == $priority[$i]->{'borrowernumber'} ) { + if ( $reserve_id == $priority[$i]->{'reserve_id'} ) { $key = $i; # save the index last; } @@ -1675,29 +1610,25 @@ sub _FixPriority { # now fix the priority on those that are left.... $query = " - UPDATE reserves - SET priority = ? - WHERE biblionumber = ? - AND borrowernumber = ? - AND reservedate = ? - AND found IS NULL + UPDATE reserves + SET priority = ? + WHERE reserve_id = ? "; $sth = $dbh->prepare($query); for ( my $j = 0 ; $j < @priority ; $j++ ) { $sth->execute( - $j + 1, $biblio, - $priority[$j]->{'borrowernumber'}, - $priority[$j]->{'reservedate'} + $j + 1, + $priority[$j]->{'reserve_id'} ); $sth->finish; } - $sth = $dbh->prepare( "SELECT borrowernumber FROM reserves WHERE lowestPriority = 1 ORDER BY priority" ); + $sth = $dbh->prepare( "SELECT reserve_id FROM reserves WHERE lowestPriority = 1 ORDER BY priority" ); $sth->execute(); unless ( $ignoreSetLowestRank ) { while ( my $res = $sth->fetchrow_hashref() ) { - _FixPriority( $biblio, $res->{'borrowernumber'}, '999999', 1 ); + _FixPriority( $res->{'reserve_id'}, '999999', 1 ); } } } @@ -1969,7 +1900,7 @@ sub _ShiftPriorityByDateAndPriority { =head2 MoveReserve - MoveReserve( $itemnumber, $borrowernumber, $cancelreserve ) + MoveReserve( $reserve_id, $cancelreserve ) Use when checking out an item to handle reserves If $cancelreserve boolean is set to true, it will remove existing reserve @@ -2025,7 +1956,7 @@ This shifts the holds from C<$from_biblio> to C<$to_biblio> and reorders them by sub MergeHolds { my ( $dbh, $to_biblio, $from_biblio ) = @_; my $sth = $dbh->prepare( - "SELECT count(*) as reservenumber FROM reserves WHERE biblionumber = ?" + "SELECT count(*) as reserve_id FROM reserves WHERE biblionumber = ?" ); $sth->execute($from_biblio); if ( my $data = $sth->fetchrow_hashref() ) { @@ -2088,6 +2019,33 @@ sub ReserveSlip { ); } +=head2 GetMultiHoldsCountSameEnum + + my $count = GetMultiHoldsCountSameEnum( + borrowernumber => $borrowernumber, + itemnumber => $itemnumber, + ) + + This function returns the number of holds a borrower has placed + on specific items with the same enumchron field as the given + item, on the same bibliographic record. + +=cut + +sub GetMultiHoldsCountSameEnum { + my %params = @_; + my $borrowernumber = $params{'borrowernumber'}; + my $itemnumber = $params{'itemnumber'}; + + return unless ( $borrowernumber && $itemnumber ); + + my $dbh = C4::Context->dbh; + my $query = " + SELECT COUNT(*) AS count FROM reserves + LEFT JOIN items ON ( reserves.itemnumber = items.itemnumber ) + "; +} + =head1 AUTHOR Koha Development Team --- a/circ/circulation.pl +++ a/circ/circulation.pl @@ -352,7 +352,7 @@ if ($borrowernumber) { $getreserv{nottransfered} = 0; $getreserv{reservedate} = format_date( $num_res->{'reservedate'} ); - $getreserv{reservenumber} = $num_res->{'reservenumber'}; + $getreserv{reserve_id} = $num_res->{'reserve_id'}; $getreserv{title} = $getiteminfo->{'title'}; $getreserv{itemtype} = $itemtypeinfo->{'description'}; $getreserv{author} = $getiteminfo->{'author'}; --- a/installer/data/mysql/sysprefs.sql +++ a/installer/data/mysql/sysprefs.sql @@ -375,3 +375,4 @@ INSERT INTO systempreferences (variable,value,options,explanation,type) VALUES ( INSERT INTO systempreferences (variable,value,explanation,type) VALUES('EnableBorrowerFiles','0','If enabled, allows librarians to upload and attach arbitrary files to a borrower record.','YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('UpdateTotalIssuesOnCirc','0','Whether to update the totalissues field in the biblio on each circ.',NULL,'YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('IntranetSlipPrinterJS','','Use this JavaScript for printing slips. Define at least function printThenClose(). For use e.g. with Firefox PlugIn jsPrintSetup, see http://jsprintsetup.mozdev.org/','','Free'); +INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('MaxHoldsPerRecord', '1', 'The maximum number of holds allowed per bibliographic record per borrower', NULL, NULL); --- a/installer/data/mysql/updatedatabase.pl +++ a/installer/data/mysql/updatedatabase.pl @@ -5536,6 +5536,13 @@ if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) { SetVersion($DBversion); } +$DBversion = "3.09.00.XXX"; +if (C4::Context->preference("Version") < TransformToNum($DBversion)) { + $dbh->do("INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('MaxHoldsPerRecord', '1', 'The maximum number of holds allowed per bibliographic record per borrower', NULL, NULL)"); + print "Upgrade to $DBversion done (Add syspref MaxHoldsPerRecord)\n"; + SetVersion ($DBversion); +} + =head1 FUNCTIONS =head2 TableExists($table) --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref @@ -339,7 +339,12 @@ Circulation: - Patrons can only have - pref: maxreserves class: integer - - holds at once. + - total holds at a time. + - + - Patrons can only place + - pref: MaxHoldsPerRecord + class: integer + - holds on a single record at a time. - - pref: emailLibrarianWhenHoldIsPlaced choices: --- a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt @@ -55,7 +55,7 @@ function check() { } if (alreadyreserved > "0"){ - msg += (_("- This patron had already placed a hold on this item") + "\n" + _("Please cancel the previous hold first") + "\n"); + msg += (_("This patron has already placed the maximum number of holds for this record") + "\n" + _("Please cancel a previous hold first") + "\n"); } if (msg == "") return(true); @@ -208,7 +208,7 @@ function checkMultiHold() {
  • Too Many Holds: [% borrowerfirstname %] [% borrowersurname %] has too many holds.
  • [% END %] [% IF ( alreadyreserved ) %] -
  • [% borrowerfirstname %] [% borrowersurname %] already has a hold on this item
  • +
  • [% borrowerfirstname %] [% borrowersurname %] has already placed the maximum allowed number of holds on this record
  • [% END %] [% IF ( none_available ) %]
  • No copies are available to be placed on hold
  • @@ -553,6 +553,7 @@ function checkMultiHold() { [% FOREACH reserveloo IN biblioloo.reserveloop %] [% UNLESS ( loop.odd ) %][% ELSE %][% END %] + + [% IF AutoResumeSuspendedHolds %] - - - Clear Date + + + Clear Date [% END %] [% ELSE %] --- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-user.tt +++ a/koha-tmpl/opac-tmpl/prog/en/modules/opac-user.tt @@ -408,7 +408,7 @@ $.tablesorter.addParser({ [% IF ( RESERVE.cancelable ) %]
    - +
    [% ELSE %] [% END %] --- a/opac/opac-modrequest.pl +++ a/opac/opac-modrequest.pl @@ -41,8 +41,8 @@ my ( $template, $borrowernumber, $cookie ) = get_template_and_user( } ); -my $biblionumber = $query->param('biblionumber'); -if ($biblionumber and $borrowernumber) { - CancelReserve($biblionumber, '', $borrowernumber); +my $reserve_id = $query->param('reserve_id'); +if ($reserve_id && $borrowernumber) { + CancelReserve( $reserve_id ); } print $query->redirect("/cgi-bin/koha/opac-user.pl#opac-user-holds"); --- a/opac/opac-reserve.pl +++ a/opac/opac-reserve.pl @@ -37,7 +37,8 @@ use C4::Debug; use Koha::DateUtils; # use Data::Dumper; -my $MAXIMUM_NUMBER_OF_RESERVES = C4::Context->preference("maxreserves"); +my $maxreserves = C4::Context->preference("maxreserves"); +my $max_holds_per_record = C4::Context->preference('MaxHoldsPerRecord'); my $query = new CGI; my ( $template, $borrowernumber, $cookie ) = get_template_and_user( @@ -298,7 +299,7 @@ if ( CheckBorrowerDebarred($borrowernumber) ) { my @reserves = GetReservesFromBorrowernumber( $borrowernumber ); $template->param( RESERVES => \@reserves ); -if ( $MAXIMUM_NUMBER_OF_RESERVES && (scalar(@reserves) >= $MAXIMUM_NUMBER_OF_RESERVES) ) { +if ( $maxreserves && (scalar(@reserves) >= $maxreserves) ) { $template->param( message => 1 ); $noreserves = 1; $template->param( too_many_reserves => scalar(@reserves)); @@ -306,12 +307,10 @@ if ( $MAXIMUM_NUMBER_OF_RESERVES && (scalar(@reserves) >= $MAXIMUM_NUMBER_OF_RES foreach my $res (@reserves) { foreach my $biblionumber (@biblionumbers) { if ( $res->{'biblionumber'} == $biblionumber && $res->{'borrowernumber'} == $borrowernumber) { -# $template->param( message => 1 ); -# $noreserves = 1; -# $template->param( already_reserved => 1 ); - $biblioDataHash{$biblionumber}->{already_reserved} = 1; + $biblioDataHash{$biblionumber}->{holds_count}++; } } + } unless ($noreserves) { @@ -353,7 +352,7 @@ foreach my $biblioNum (@biblionumbers) { $biblioLoopIter{author} = $biblioData->{author}; $biblioLoopIter{rank} = $biblioData->{rank}; $biblioLoopIter{reservecount} = $biblioData->{reservecount}; - $biblioLoopIter{already_reserved} = $biblioData->{already_reserved}; + $biblioLoopIter{holds_count} = $biblioData->{holds_count}; if (!$itemLevelTypes && $biblioData->{itemtype}) { $biblioLoopIter{description} = $itemTypes->{$biblioData->{itemtype}}{description}; @@ -480,7 +479,7 @@ foreach my $biblioNum (@biblionumbers) { $policy_holdallowed = 0; } - if (IsAvailableForItemLevelRequest($itemNum) and $policy_holdallowed and CanItemBeReserved($borrowernumber,$itemNum) and ($itemLoopIter->{already_reserved} ne 1)) { + if (IsAvailableForItemLevelRequest($itemNum) and $policy_holdallowed and CanItemBeReserved($borrowernumber,$itemNum) and ($itemLoopIter->{holds_count} < $max_holds_per_record )) { $itemLoopIter->{available} = 1; $numCopiesAvailable++; } @@ -513,6 +512,10 @@ foreach my $biblioNum (@biblionumbers) { $biblioLoopIter{holdable} = undef; $anyholdable = undef; } + if ($biblioLoopIter{holds_count} >= $max_holds_per_record) { + $biblioLoopIter{holdable} = undef; + $anyholdable = undef; + } if(not CanBookBeReserved($borrowernumber,$biblioNum)){ $biblioLoopIter{holdable} = undef; $anyholdable = undef; --- a/reserve/modrequest.pl +++ a/reserve/modrequest.pl @@ -41,23 +41,24 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user( } ); -my @rank=$query->param('rank-request'); -my @biblionumber=$query->param('biblionumber'); -my @borrower=$query->param('borrowernumber'); -my @branch=$query->param('pickup'); -my @itemnumber=$query->param('itemnumber'); +my @reserve_id = $query->param('reserve_id'); +my @rank = $query->param('rank-request'); +my @biblionumber = $query->param('biblionumber'); +my @borrower = $query->param('borrowernumber'); +my @branch = $query->param('pickup'); +my @itemnumber = $query->param('itemnumber'); my @suspend_until=$query->param('suspend_until'); my $multi_hold = $query->param('multi_hold'); my $biblionumbers = $query->param('biblionumbers'); my $count=@rank; -my $CancelBiblioNumber=$query->param('CancelBiblioNumber'); -my $CancelBorrowerNumber=$query->param('CancelBorrowerNumber'); -my $CancelItemnumber=$query->param('CancelItemnumber'); +my $CancelBiblioNumber = $query->param('CancelBiblioNumber'); +my $CancelBorrowerNumber = $query->param('CancelBorrowerNumber'); +my $CancelItemnumber = $query->param('CancelItemnumber'); # 2 possibilitys : cancel an item reservation, or modify or cancel the queded list -# 1) cancel an item reservation by fonction ModReserveCancelAll (in reserves.pm) +# 1) cancel an item reservation by function ModReserveCancelAll (in reserves.pm) if ($CancelBorrowerNumber) { ModReserveCancelAll($CancelItemnumber, $CancelBorrowerNumber); $biblionumber[0] = $CancelBiblioNumber, @@ -67,7 +68,7 @@ if ($CancelBorrowerNumber) { else { for (my $i=0;$i<$count;$i++){ undef $itemnumber[$i] unless $itemnumber[$i] ne ''; - ModReserve($rank[$i],$biblionumber[$i],$borrower[$i],$branch[$i],$itemnumber[$i],$suspend_until[$i]); #from C4::Reserves + ModReserve($rank[$i],$reserve_id[$i],$branch[$i],$itemnumber[$i],$suspend_until[$i]); #from C4::Reserves } } my $from=$query->param('from'); --- a/reserve/request.pl +++ a/reserve/request.pl @@ -99,22 +99,18 @@ $action ||= q{}; if ( $action eq 'move' ) { my $where = $input->param('where'); - my $borrowernumber = $input->param('borrowernumber'); - my $biblionumber = $input->param('biblionumber'); - AlterPriority( $where, $borrowernumber, $biblionumber ); + my $reserve_id = $input->param('reserve_id'); + AlterPriority( $where, $reserve_id ); } elsif ( $action eq 'cancel' ) { - my $borrowernumber = $input->param('borrowernumber'); - my $biblionumber = $input->param('biblionumber'); - CancelReserve( $biblionumber, '', $borrowernumber ); + my $reserve_id = $input->param('reserve_id'); + CancelReserve( $reserve_id ); } elsif ( $action eq 'setLowestPriority' ) { - my $borrowernumber = $input->param('borrowernumber'); - my $biblionumber = $input->param('biblionumber'); - ToggleLowestPriority( $borrowernumber, $biblionumber ); + my $reserve_id = $input->param('reserve_id'); + ToggleLowestPriority( $reserve_id ); } elsif ( $action eq 'toggleSuspend' ) { - my $borrowernumber = $input->param('borrowernumber'); - my $biblionumber = $input->param('biblionumber'); + my $reserve_id = $input->param('reserve_id'); my $suspend_until = $input->param('suspend_until'); - ToggleSuspend( $borrowernumber, $biblionumber, $suspend_until ); + ToggleSuspend( $reserve_id, $suspend_until ); } if ($findborrower) { @@ -148,7 +144,7 @@ if ($borrowernumber_hold && !$action) { GetReserveCount( $borrowerinfo->{'borrowernumber'} ); if ( C4::Context->preference('maxreserves') && ($number_reserves >= C4::Context->preference('maxreserves')) ) { - $warnings = 1; + $warnings = 1; $maxreserves = 1; } @@ -162,7 +158,7 @@ if ($borrowernumber_hold && !$action) { # check if the borrower make the reserv in a different branch if ( $borrowerinfo->{'branchcode'} ne C4::Context->userenv->{'branch'} ) { - $messages = 1; + $messages = 1; $diffbranch = 1; } @@ -184,8 +180,8 @@ if ($borrowernumber_hold && !$action) { maxreserves => $maxreserves, expiry => $expiry, diffbranch => $diffbranch, - messages => $messages, - warnings => $warnings + messages => $messages, + warnings => $warnings ); } @@ -225,6 +221,7 @@ if ($borrowerslist) { # FIXME launch another time GetMember perhaps until my $borrowerinfo = GetMember( borrowernumber => $borrowernumber_hold ); +my $max_holds_per_record = C4::Context->preference('MaxHoldsPerRecord'); my @biblionumbers = (); my $biblionumbers = $input->param('biblionumbers'); @@ -239,18 +236,19 @@ my @biblioloop = (); foreach my $biblionumber (@biblionumbers) { my %biblioloopiter = (); - my $maxreserves; + my $maxreserves; - my $dat = GetBiblioData($biblionumber); + my $dat = GetBiblioData($biblionumber); unless ( CanBookBeReserved($borrowerinfo->{borrowernumber}, $biblionumber) ) { - $warnings = 1; + $warnings = 1; $maxreserves = 1; } # get existing reserves ..... my ( $count, $reserves ) = GetReservesFromBiblionumber($biblionumber,1); my $totalcount = $count; - my $alreadyreserved; + my $holds_count = 0; + my $alreadyreserved = 0; foreach my $res (@$reserves) { if ( defined $res->{found} && $res->{found} eq 'W' ) { @@ -258,18 +256,22 @@ foreach my $biblionumber (@biblionumbers) { } if ( defined $borrowerinfo && ($borrowerinfo->{borrowernumber} eq $res->{borrowernumber}) ) { - $warnings = 1; + $holds_count++; + } + } + + if ( $holds_count >= $max_holds_per_record ) { $alreadyreserved = 1; + $warnings = 1; $biblioloopiter{warn} = 1; $biblioloopiter{alreadyres} = 1; - } } $template->param( alreadyreserved => $alreadyreserved, - messages => $messages, - warnings => $warnings, - maxreserves=>$maxreserves - ); + messages => $messages, + warnings => $warnings, + maxreserves=> $maxreserves + ); # FIXME think @optionloop, is maybe obsolete, or must be switchable by a systeme preference fixed rank or not @@ -296,11 +298,11 @@ foreach my $biblionumber (@biblionumbers) { if (my $items = get_itemnumbers_of($biblionumber)->{$biblionumber}){ @itemnumbers = @$items; } - my @hostitems = get_hostitemnumbers_of($biblionumber); - if (@hostitems){ - $template->param('hostitemsflag' => 1); - push(@itemnumbers, @hostitems); - } + my @hostitems = get_hostitemnumbers_of($biblionumber); + if (@hostitems){ + $template->param('hostitemsflag' => 1); + push(@itemnumbers, @hostitems); + } if (!@itemnumbers) { $template->param('noitems' => 1); @@ -378,7 +380,7 @@ foreach my $biblionumber (@biblionumbers) { } # checking reserve - my ($reservedate,$reservedfor,$expectedAt) = GetReservesFromItemnumber($itemnumber); + my ($reservedate,$reservedfor,$expectedAt,$reserve_id) = GetReservesFromItemnumber($itemnumber); my $ItemBorrowerReserveInfo = GetMember( borrowernumber => $reservedfor ); if ( defined $reservedate ) { @@ -575,6 +577,8 @@ foreach my $biblionumber (@biblionumbers) { $reserve{'optionloop'} = \@optionloop; $reserve{'suspend'} = $res->{'suspend'}; $reserve{'suspend_until'} = $res->{'suspend_until'}; + $reserve{'reserve_id'} = $res->{'reserve_id'}; + push( @reserveloop, \%reserve ); } --