@@ -, +, @@ you should not be able to ( default is 1 per record ) you should be able to place 3 holds for one patron on a given record. A 4th hold should result in a message stating the patron has placed the max number of holds allowed on this record. --- C4/Reserves.pm | 59 ++++++++++++-------- installer/data/mysql/sysprefs.sql | 2 +- installer/data/mysql/updatedatabase.pl | 7 ++ .../en/modules/admin/preferences/circulation.pref | 7 ++- .../prog/en/modules/reserve/request.tt | 4 +- .../opac-tmpl/prog/en/modules/opac-reserve.tt | 2 + opac/opac-reserve.pl | 30 ++++++---- reserve/request.pl | 19 +++++-- 8 files changed, 86 insertions(+), 44 deletions(-) --- a/C4/Reserves.pm +++ a/C4/Reserves.pm @@ -397,36 +397,47 @@ sub GetReservesFromItemnumber { =head2 GetReservesFromBorrowernumber - $borrowerreserv = GetReservesFromBorrowernumber($borrowernumber,$tatus); - -TODO :: Descritpion + $borrowerreserv = GetReservesFromBorrowernumber($borrowernumber[,$status][,$biblionumber][,$itemnumber]); =cut sub GetReservesFromBorrowernumber { - my ( $borrowernumber, $status ) = @_; - my $dbh = C4::Context->dbh; - my $sth; - if ($status) { - $sth = $dbh->prepare(" - SELECT * - FROM reserves - WHERE borrowernumber=? - AND found =? - ORDER BY reservedate - "); - $sth->execute($borrowernumber,$status); - } else { - $sth = $dbh->prepare(" - SELECT * - FROM reserves - WHERE borrowernumber=? - ORDER BY reservedate - "); - $sth->execute($borrowernumber); + my ( $borrowernumber, $found, $biblionumber, $itemnumber ) = @_; + + my @params; + + my $sql = "SELECT * FROM reserves WHERE borrowernumber = ?"; + push( @params, $borrowernumber ); + + if ( $biblionumber ) { + $sql .= " AND biblionumber = ? "; + push( @params, $biblionumber ); + } + + if ( $itemnumber ) { + $sql .= " AND itemnumber = ? "; + push( @params, $itemnumber ); } + + if ( $found ) { + $sql .= " AND found = ? "; + push( @params, $found ); + } + + $sql .= " ORDER BY reservedate "; + + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( $sql ); + + $sth->execute( @params ); + my $data = $sth->fetchall_arrayref({}); - return @$data; + + if ( scalar @$data ) { + return @$data; + } else { + return (); + } } #------------------------------------------------------------------------------------- =head2 CanBookBeReserved --- a/installer/data/mysql/sysprefs.sql +++ a/installer/data/mysql/sysprefs.sql @@ -179,6 +179,7 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` ('MARCAuthorityControlField008','|| aca||aabn | a|a d',NULL,'Define the contents of MARC21 authority control field 008 position 06-39','Textarea'), ('MARCOrgCode','OSt','','Define MARC Organization Code for MARC21 records - http://www.loc.gov/marc/organizations/orgshome.html','free'), ('MaxFine',NULL,'','Maximum fine a patron can have for all late returns at one moment. Single item caps are specified in the circulation rules matrix.','Integer'), +('MaxHoldsPerRecord', '1', 'The maximum number of holds allowed per bibliographic record per borrower', 'Integer'), ('MaxItemsForBatch','1000',NULL,'Max number of items record to process in a batch (modification or deletion)','Integer'), ('maxItemsInSearchResults','20',NULL,'Specify the maximum number of items to display for each result on a page of results','free'), ('maxoutstanding','5','','maximum amount withstanding to be able make holds','Integer'), @@ -428,7 +429,6 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` ('XISBN','0','','Use with FRBRizeEditions. If ON, Koha will use the OCLC xISBN web service in the Editions tab on the detail pages. See: http://www.worldcat.org/affiliate/webservices/xisbn/app.jsp','YesNo'), ('XISBNDailyLimit','999','','The xISBN Web service is free for non-commercial use when usage does not exceed 1000 requests per day','Integer'), ('XSLTDetailsDisplay','default','','Enable XSL stylesheet control over details page display on intranet','Free'), -('XSLTResultsDisplay','default','','Enable XSL stylesheet control over results page display on intranet','Free'), ('yuipath','local','local|http://yui.yahooapis.com/2.5.1/build','Insert the path to YUI libraries, choose local if you use koha offline','Choice'), ('z3950AuthorAuthFields','701,702,700',NULL,'Define the MARC biblio fields for Personal Name Authorities to fill biblio.author','free'), ('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo') --- a/installer/data/mysql/updatedatabase.pl +++ a/installer/data/mysql/updatedatabase.pl @@ -8202,6 +8202,13 @@ if ( CheckVersion($DBversion) ) { SetVersion($DBversion); } +$DBversion = "3.15.00.XXX"; +if (C4::Context->preference("Version") < TransformToNum($DBversion)) { + $dbh->do("INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type) VALUES ('MaxHoldsPerRecord', '1', 'The maximum number of holds allowed per bibliographic record per borrower', NULL, 'Integer')"); + print "Upgrade to $DBversion done (Bug 7710 - multiple holds per title)\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 @@ -447,7 +447,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 @@ -62,7 +62,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\n" + _("Please cancel a previous hold first.") + "\n"); } if (msg == "") return(true); @@ -247,7 +247,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 items are available to be placed on hold
  • --- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-reserve.tt +++ a/koha-tmpl/opac-tmpl/prog/en/modules/opac-reserve.tt @@ -330,6 +330,8 @@ [% ELSE %] [% IF ( bibitemloo.already_patron_possession ) %]
    This title cannot be requested because it's already in your possession.
    + [% ELSIF bibitemloo.max_holds %] +
    This title cannot be requested. You have already placed the maximum number of requests allowed for this title.
    [% ELSE %]
    This title cannot be requested.
    [% END %] --- 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( @@ -171,7 +172,7 @@ foreach my $biblioNumber (@biblionumbers) { # if ( $query->param('place_reserve') ) { my $reserve_cnt = 0; - if ($MAXIMUM_NUMBER_OF_RESERVES) { + if ($maxreserves) { $reserve_cnt = GetReservesFromBorrowernumber( $borrowernumber ); } @@ -255,8 +256,8 @@ if ( $query->param('place_reserve') ) { } my $notes = $query->param('notes_'.$biblioNum)||''; - if ( $MAXIMUM_NUMBER_OF_RESERVES - && $reserve_cnt >= $MAXIMUM_NUMBER_OF_RESERVES ) + if ( $maxreserves + && $reserve_cnt >= $maxreserves ) { $canreserve = 0; } @@ -317,7 +318,7 @@ if ( $borr->{'debarred'} ) { 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)); @@ -325,12 +326,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) { @@ -374,6 +373,7 @@ foreach my $biblioNum (@biblionumbers) { $biblioLoopIter{reservecount} = $biblioData->{reservecount}; $biblioLoopIter{already_reserved} = $biblioData->{already_reserved}; $biblioLoopIter{mandatorynotes}=0; #FIXME: For future use + $biblioLoopIter{holds_count} = $biblioData->{holds_count}; if (!$itemLevelTypes && $biblioData->{itemtype}) { $biblioLoopIter{description} = $itemTypes->{$biblioData->{itemtype}}{description}; @@ -502,14 +502,17 @@ 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++; } $itemLoopIter->{imageurl} = getitemtypeimagelocation( 'opac', $itemTypes->{ $itemInfo->{itype} }{imageurl} ); - # Show serial enumeration when needed + # Don't allow multiple item level holds on the same item if borrowers can place multiple holds on one bib + $itemLoopIter->{available} = 0 if ( GetReservesFromBorrowernumber( $borrowernumber, undef, undef, $itemNum ) ); + + # Show serial enumeration when needed if ($itemLoopIter->{enumchron}) { $itemdata_enumchron = 1; } @@ -526,6 +529,11 @@ foreach my $biblioNum (@biblionumbers) { if ($biblioLoopIter{already_reserved}) { $biblioLoopIter{holdable} = undef; } + if ($biblioLoopIter{holds_count} >= $max_holds_per_record) { + $biblioLoopIter{holdable} = undef; + $anyholdable = undef; + $biblioLoopIter{max_holds} = 1; + } if(not CanBookBeReserved($borrowernumber,$biblioNum)){ $biblioLoopIter{holdable} = undef; } --- a/reserve/request.pl +++ a/reserve/request.pl @@ -169,6 +169,7 @@ $template->param( messageborrower => $messageborrower ); # 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'); @@ -212,15 +213,17 @@ foreach my $biblionumber (@biblionumbers) { } } - if ( $holds_count ) { - $alreadyreserved = 1; - $biblioloopiter{warn} = 1; - $biblioloopiter{alreadyres} = 1; + if ( $holds_count > $max_holds_per_record ) { + $alreadyreserved = 1; + $biblioloopiter{warn} = 1; + $biblioloopiter{alreadyres} = 1; + $biblioloopiter{maxreserves} = $max_holds_per_record; } $template->param( - alreadyreserved => $alreadyreserved, + alreadyreserved => $alreadyreserved, alreadypossession => $alreadypossession, + maxreserves => $maxreserves, ); # FIXME think @optionloop, is maybe obsolete, or must be switchable by a systeme preference fixed rank or not @@ -430,6 +433,12 @@ foreach my $biblionumber (@biblionumbers) { $itemdata_enumchron = 1; } + # Don't allow multiple item level holds on the same item if borrowers can place multiple holds on one bib + if ( GetReservesFromBorrowernumber( $borrowerinfo->{borrowernumber}, undef, undef, $itemnumber) ) { + $item->{available} = 0; + $item->{override} = 0; + } + push @{ $biblioitem->{itemloop} }, $item; } --