@@ -, +, @@ 1) Apply patch 2) Run updatedatabase.pl 3) Pick a patron, note the patron's category 5) Issue an item to this patron 4) Edit that category, set "Block expired patrons" to "Block" 5) Verify the patron cannot renew or place holds in the opac 6) Edit the category again, set "Block expired patrons" to "Don't block" 7) Verify the patron *can* renew and place holds in the opac 8) Edit the category again, set "Block expired patrons" to "Follow system preference BlockExpiredPatronOpacActions" 9) Set the system preference BlockExpiredPatronOpacActions to "Block" --- C4/Members.pm | 24 +++++- admin/categorie.pl | 81 +++++++++++++++++--- installer/data/mysql/kohastructure.sql | 1 + installer/data/mysql/sysprefs.sql | 1 + installer/data/mysql/updatedatabase.pl | 17 ++++ .../prog/en/modules/admin/categorie.tt | 8 ++ .../prog/en/modules/admin/preferences/opac.pref | 6 ++ .../prog/en/modules/admin/preferences/patrons.pref | 2 +- .../opac-tmpl/prog/en/modules/opac-reserve.tt | 3 + koha-tmpl/opac-tmpl/prog/en/modules/opac-user.tt | 10 +++ opac/opac-renew.pl | 62 ++++++++------- opac/opac-reserve.pl | 11 +++ opac/opac-user.pl | 32 ++++---- 13 files changed, 202 insertions(+), 56 deletions(-) --- a/C4/Members.pm +++ a/C4/Members.pm @@ -324,11 +324,31 @@ sub GetMemberDetails { my $query; my $sth; if ($borrowernumber) { - $sth = $dbh->prepare("SELECT borrowers.*,category_type,categories.description,reservefee,enrolmentperiod FROM borrowers LEFT JOIN categories ON borrowers.categorycode=categories.categorycode WHERE borrowernumber=?"); + $sth = $dbh->prepare(" + SELECT borrowers.*, + category_type, + categories.description, + categories.BlockExpiredPatronOpacActions, + reservefee, + enrolmentperiod + FROM borrowers + LEFT JOIN categories ON borrowers.categorycode=categories.categorycode + WHERE borrowernumber = ? + "); $sth->execute($borrowernumber); } elsif ($cardnumber) { - $sth = $dbh->prepare("SELECT borrowers.*,category_type,categories.description,reservefee,enrolmentperiod FROM borrowers LEFT JOIN categories ON borrowers.categorycode=categories.categorycode WHERE cardnumber=?"); + $sth = $dbh->prepare(" + SELECT borrowers.*, + category_type, + categories.description, + categories.BlockExpiredPatronOpacActions, + reservefee, + enrolmentperiod + FROM borrowers + LEFT JOIN categories ON borrowers.categorycode = categories.categorycode + WHERE cardnumber = ? + "); $sth->execute($cardnumber); } else { --- a/admin/categorie.pl +++ a/admin/categorie.pl @@ -68,6 +68,7 @@ my $searchfield=$input->param('description'); my $script_name="/cgi-bin/koha/admin/categorie.pl"; my $categorycode=$input->param('categorycode'); my $op = $input->param('op'); +my $block_expired = $input->param("block_expired"); my ($template, $loggedinuser, $cookie) = get_template_and_user({template_name => "admin/categorie.tmpl", @@ -94,7 +95,7 @@ if ($op eq 'add_form') { my @selected_branches; if ($categorycode) { my $dbh = C4::Context->dbh; - my $sth=$dbh->prepare("select categorycode,description,enrolmentperiod,enrolmentperioddate,upperagelimit,dateofbirthrequired,enrolmentfee,issuelimit,reservefee,hidelostitems,overduenoticerequired,category_type from categories where categorycode=?"); + my $sth=$dbh->prepare("SELECT * FROM categories WHERE categorycode=?"); $sth->execute($categorycode); $data=$sth->fetchrow_hashref; @@ -134,6 +135,7 @@ if ($op eq 'add_form') { TalkingTechItivaPhone => C4::Context->preference("TalkingTechItivaPhoneNotification"), "type_".$data->{'category_type'} => 1, branches_loop => \@branches_loop, + BlockExpiredPatronOpacActions => $data->{'BlockExpiredPatronOpacActions'}, ); if (C4::Context->preference('EnhancedMessagingPreferences')) { C4::Form::MessagingPreferences::set_form_values({ categorycode => $categorycode } , $template); @@ -150,8 +152,37 @@ if ($op eq 'add_form') { } if ($is_a_modif) { - my $sth=$dbh->prepare("UPDATE categories SET description=?,enrolmentperiod=?, enrolmentperioddate=?,upperagelimit=?,dateofbirthrequired=?,enrolmentfee=?,reservefee=?,hidelostitems=?,overduenoticerequired=?,category_type=? WHERE categorycode=?"); - $sth->execute(map { $input->param($_) } ('description','enrolmentperiod','enrolmentperioddate','upperagelimit','dateofbirthrequired','enrolmentfee','reservefee','hidelostitems','overduenoticerequired','category_type','categorycode')); + my $sth=$dbh->prepare(" + UPDATE categories + SET description=?, + enrolmentperiod=?, + enrolmentperioddate=?, + upperagelimit=?, + dateofbirthrequired=?, + enrolmentfee=?, + reservefee=?, + hidelostitems=?, + overduenoticerequired=?, + category_type=?, + BlockExpiredPatronOpacActions=? + WHERE categorycode=?" + ); + $sth->execute( + map { $input->param($_) } ( + 'description', + 'enrolmentperiod', + 'enrolmentperioddate', + 'upperagelimit', + 'dateofbirthrequired', + 'enrolmentfee', + 'reservefee', + 'hidelostitems', + 'overduenoticerequired', + 'category_type', + 'block_expired', + 'categorycode' + ) + ); my @branches = $input->param("branches"); if ( @branches ) { $sth = $dbh->prepare("DELETE FROM categories_branches WHERE categorycode = ?"); @@ -169,11 +200,42 @@ if ($op eq 'add_form') { } } $sth->finish; - } else { - my $sth=$dbh->prepare("INSERT INTO categories (categorycode,description,enrolmentperiod,enrolmentperioddate,upperagelimit,dateofbirthrequired,enrolmentfee,reservefee,hidelostitems,overduenoticerequired,category_type) values (?,?,?,?,?,?,?,?,?,?,?)"); - $sth->execute(map { $input->param($_) } ('categorycode','description','enrolmentperiod','enrolmentperioddate','upperagelimit','dateofbirthrequired','enrolmentfee','reservefee','hidelostitems','overduenoticerequired','category_type')); - $sth->finish; - } + } else { + my $sth=$dbh->prepare(" + INSERT INTO categories ( + categorycode, + description, + enrolmentperiod, + enrolmentperioddate, + upperagelimit, + dateofbirthrequired, + enrolmentfee, + reservefee, + hidelostitems, + overduenoticerequired, + category_type, + BlockExpiredPatronOpacActions + ) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"); + $sth->execute( + map { $input->param($_) } ( + 'categorycode', + 'description', + 'enrolmentperiod', + 'enrolmentperioddate', + 'upperagelimit', + 'dateofbirthrequired', + 'enrolmentfee', + 'reservefee', + 'hidelostitems', + 'overduenoticerequired', + 'category_type', + 'block_expired' + ) + ); + $sth->finish; + } + if (C4::Context->preference('EnhancedMessagingPreferences')) { C4::Form::MessagingPreferences::handle_form_action($input, { categorycode => $input->param('categorycode') }, $template); @@ -194,7 +256,7 @@ if ($op eq 'add_form') { $sth->finish; $template->param(total => $total->{'total'}); - my $sth2=$dbh->prepare("select categorycode,description,enrolmentperiod,enrolmentperioddate,upperagelimit,dateofbirthrequired,enrolmentfee,issuelimit,reservefee,hidelostitems,overduenoticerequired,category_type from categories where categorycode=?"); + my $sth2=$dbh->prepare("SELECT * FROM categories WHERE categorycode=?"); $sth2->execute($categorycode); my $data=$sth2->fetchrow_hashref; $sth2->finish; @@ -213,6 +275,7 @@ if ($op eq 'add_form') { reservefee => sprintf("%.2f",$data->{'reservefee'}), hidelostitems => $data->{'hidelostitems'}, category_type => $data->{'category_type'}, + BlockExpiredPatronOpacActions => $data->{'BlockExpiredPatronOpacActions'}, ); # END $OP eq DELETE_CONFIRM ################## DELETE_CONFIRMED ################################## --- a/installer/data/mysql/kohastructure.sql +++ a/installer/data/mysql/kohastructure.sql @@ -441,6 +441,7 @@ CREATE TABLE `categories` ( -- this table shows information related to Koha patr `reservefee` decimal(28,6) default NULL, -- cost to place holds `hidelostitems` tinyint(1) NOT NULL default '0', -- are lost items shown to this category (1 for yes, 0 for no) `category_type` varchar(1) NOT NULL default 'A', -- type of Koha patron (Adult, Child, Professional, Organizational, Statistical, Staff) + `BlockExpiredPatronOpacActions` tinyint(1) NOT NULL default '-1', -- wheither or not a patron of this categori can renew books or place holds once their card has expired. 0 means they can, 1 means they cannot, -1 means use syspref BLockExpiredPAtronOpacACtions PRIMARY KEY (`categorycode`), UNIQUE KEY `categorycode` (`categorycode`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; --- a/installer/data/mysql/sysprefs.sql +++ a/installer/data/mysql/sysprefs.sql @@ -56,6 +56,7 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` ('BasketConfirmations','1','always ask for confirmation.|do not ask for confirmation.','When closing or reopening a basket,','Choice'), ('BiblioAddsAuthorities','0',NULL,'If ON, adding a new biblio will check for an existing authority record and create one on the fly if one doesn\'t exist','YesNo'), ('BiblioDefaultView','normal','normal|marc|isbd','Choose the default detail view in the catalog; choose between normal, marc or isbd','Choice'), +('BlockExpiredPatronOpacActions','yes',NULL,'Set whether an expired patron can perform opac actions such as placing a hold or reserve, can be overridden on a per patron-type basis','YesNo'), ('BlockReturnOfWithdrawnItems','1','0','If enabled, items that are marked as withdrawn cannot be returned.','YesNo'), ('BorrowerMandatoryField','surname|cardnumber',NULL,'Choose the mandatory fields for a patron\'s account','free'), ('borrowerRelationship','father|mother','','Define valid relationships between a guarantor & a guarantee (separated by | or ,)','free'), --- a/installer/data/mysql/updatedatabase.pl +++ a/installer/data/mysql/updatedatabase.pl @@ -7067,6 +7067,23 @@ if ( CheckVersion($DBversion) ) { SetVersion($DBversion); } +$DBversion = "3.13.00.XXX"; +if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) { + $dbh->do(" + INSERT INTO systempreferences (variable,value,explanation,options,type) + VALUES ( + 'BlockExpiredPatronOpacActions', + 'yes', + 'Set whether an expired patron can perform opac actions such as placing a hold or reserve, can be overridden on a per patron-type basis', + NULL, + 'YesNo' + ) + "); + $dbh->do("ALTER TABLE `categories` ADD COLUMN `BlockExpiredPatronOpacActions` TINYINT(1) DEFAULT 0 NOT NULL"); + print "Upgraded to $DBversion done (Bug 6739 - expired patrons not blocked from opac actions)\n"; + SetVersion ($DBversion); +} + =head1 FUNCTIONS =head2 TableExists($table) --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/categorie.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/categorie.tt @@ -205,6 +205,14 @@ Select All if this category type must to be displayed all the time. Otherwise select librairies you want to associate with this value. +
  • + + Should patrons of this category be blocked from opac actions such as renew and reserve when their cards have expired. +
  • --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref @@ -510,6 +510,12 @@ OPAC: no: "Don't allow" yes: Allow - opac users to share private lists with other patrons. This feature is not active yet but will be released soon + - + - pref: BlockExpiredPatronOpacActions + choices: + yes: "Block" + no: "Don't Block" + - expired patrons from opac actions such as placing a hold or renew, the setting for a patron category takes priority over this Privacy: - --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/patrons.pref +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/patrons.pref @@ -91,7 +91,7 @@ Patrons: class: integer - characters long. - - - Show a notice that a patron is about to expire + - Show a notice if the patron is about to expire or has expired - pref: NotifyBorrowerDeparture class: integer - days beforehand. --- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-reserve.tt +++ a/koha-tmpl/opac-tmpl/prog/en/modules/opac-reserve.tt @@ -230,6 +230,9 @@ [% IF ( bad_data ) %]
    ERROR: Internal error: incomplete hold request.
    [% END %] + [% IF ( expired_patron ) %] +

    Sorry, you cannot place holds because your library card has expired.

    Please contact your librarian if you wish to renew your card

    + [% END %] [% ELSE %] [% IF ( none_available ) %]
    Sorry, none of these items can be placed on hold. --- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-user.tt +++ a/koha-tmpl/opac-tmpl/prog/en/modules/opac-user.tt @@ -94,6 +94,16 @@ var MSG_CONFIRM_RESUME_HOLDS = _("Are you sure you want to resume all suspended
    Please note: Your card has expired. Please contact the library for more information.
    + [% ELSIF ( BORROWER_INF.warnexpired ) %] +
    + Please note: You card has expired as of [% BORROWER_INF.warnexpired %]. Please contact the library if you wish to renew your subscription. +
    + [% END %] + + [% IF ( RENEW_ERROR ) %] +
    + Please note: You're renew failed with the following error: [% RENEW_ERROR %] +
    [% END %] [% IF ( patron_flagged ) %] --- a/opac/opac-renew.pl +++ a/opac/opac-renew.pl @@ -29,6 +29,7 @@ use C4::Circulation; use C4::Auth; use C4::Items; use C4::Members; +use Date::Calc qw( Today Date_to_Days ); my $query = new CGI; my ( $template, $borrowernumber, $cookie ) = get_template_and_user( @@ -46,33 +47,40 @@ my @items = $query->param('item'); my $opacrenew = C4::Context->preference("OpacRenewalAllowed"); my $errorstring=''; -for my $itemnumber ( @items ) { - my ($status,$error) = CanBookBeRenewed( $borrowernumber, $itemnumber ); - if ( $status == 1 && $opacrenew == 1 ) { - my $renewalbranch = C4::Context->preference('OpacRenewalBranch'); - my $branchcode; - if ($renewalbranch eq 'itemhomebranch'){ - my $item = GetItem($itemnumber); - $branchcode=$item->{'homebranch'}; - } - elsif ($renewalbranch eq 'patronhomebranch'){ - my $borrower = GetMemberDetails($borrowernumber); - $branchcode = $borrower->{'branchcode'}; - } - elsif ($renewalbranch eq 'checkoutbranch'){ - my $issue = GetOpenIssue($itemnumber); - $branchcode = $issue->{'branchcode'}; - } - elsif ($renewalbranch eq 'NULL'){ - $branchcode=''; - } - else { - $branchcode='OPACRenew' - } - AddRenewal( $borrowernumber, $itemnumber, $branchcode); - } - else { - $errorstring .= $error ."|"; +my $member_details = GetMemberDetails($borrowernumber); +# BlockExpiredPatronOpacActions syspref 0 is false, 1 is true. BlockExpiredPatronOpacActions for categories (from GetMemberDetails) -1 means use syspref, 0 is false, 1 is true (where false means dont block, true means block) +if( ($member_details->{'BlockExpiredPatronOpacActions'} == -1 ? C4::Conext->preference('BlockExpiredPatronOpacActions') : $member_details->{'BlockExpiredPatronOpacActions'}) + && Date_to_Days( Today() ) > Date_to_Days( split /-/, $member_details->{'dateexpiry'} ) ){ + $errorstring='unable to renew as your card has expired'; +} else { + for my $itemnumber ( @items ) { + my ($status,$error) = CanBookBeRenewed( $borrowernumber, $itemnumber ); + if ( $status == 1 && $opacrenew == 1 ) { + my $renewalbranch = C4::Context->preference('OpacRenewalBranch'); + my $branchcode; + if ($renewalbranch eq 'itemhomebranch'){ + my $item = GetItem($itemnumber); + $branchcode=$item->{'homebranch'}; + } + elsif ($renewalbranch eq 'patronhomebranch'){ + my $borrower = GetMemberDetails($borrowernumber); + $branchcode = $borrower->{'branchcode'}; + } + elsif ($renewalbranch eq 'checkoutbranch'){ + my $issue = GetOpenIssue($itemnumber); + $branchcode = $issue->{'branchcode'}; + } + elsif ($renewalbranch eq 'NULL'){ + $branchcode=''; + } + else { + $branchcode='OPACRenew' + } + AddRenewal( $borrowernumber, $itemnumber, $branchcode); + } + else { + $errorstring .= $error ."|"; + } } } --- a/opac/opac-reserve.pl +++ a/opac/opac-reserve.pl @@ -35,6 +35,7 @@ use C4::Branch; # GetBranches use C4::Overdues; use C4::Debug; use Koha::DateUtils; +use Date::Calc qw/Today Date_to_Days/; # use Data::Dumper; my $MAXIMUM_NUMBER_OF_RESERVES = C4::Context->preference("maxreserves"); @@ -65,6 +66,16 @@ sub get_out { # get borrower information .... my ( $borr ) = GetMemberDetails( $borrowernumber ); +# check if this user can place a reserve, -1 means use sys pref, 0 means dont block, 1 means block +if( $borr->{'BlockExpiredPatronOpacActions'} == -1 ? C4::Context->preference("BlockExpiredPatronOpacActions") : $borr->{'BlockExpiredPatronOpacActions'} ) { + + if( Date_to_Days( Today() ) > Date_to_Days( split /-/, $borr->{'dateexpiry'} ) ){ + # cannot reserve, their card has expired and the rules set mean this is not allowed + $template->param( message=>1, expired_patron=>1 ); + get_out($query, $cookie, $template->output); + } +} + # Pass through any reserve charge if ($borr->{reservefee} > 0){ $template->param( RESERVE_CHARGE => sprintf("%.2f",$borr->{reservefee})); --- a/opac/opac-user.pl +++ a/opac/opac-user.pl @@ -65,10 +65,7 @@ my ( $template, $borrowernumber, $cookie ) = get_template_and_user( } ); -my $show_priority; -for ( C4::Context->preference("OPACShowHoldQueueDetails") ) { - m/priority/ and $show_priority = 1; -} +my $OPACDisplayRequestPriority = (C4::Context->preference("OPACDisplayRequestPriority")) ? 1 : 0; my $patronupdate = $query->param('patronupdate'); my $canrenew = 1; @@ -110,7 +107,6 @@ if ( C4::Context->preference( 'OpacRenewalAllowed' ) && $borr->{amountoutstandi $canrenew = 0; $template->param( renewal_blocked_fines => sprintf( '%.02f', $no_renewal_amt ), - renewal_blocked_fines_amountoutstanding => sprintf( '%.02f', $borr->{amountoutstanding} ), ); } @@ -125,19 +121,22 @@ my @bordat; $bordat[0] = $borr; # Warningdate is the date that the warning starts appearing -if ( $borr->{dateexpiry} && Date_to_Days( $today_year, $today_month, $today_day ) > Date_to_Days( $warning_year, $warning_month, $warning_day ) ) { - $borr->{'warnexpired'} = 1; -} -elsif ( $borr->{dateexpiry} && C4::Context->preference('NotifyBorrowerDeparture') && - Date_to_Days(Add_Delta_Days($warning_year, $warning_month, $warning_day,- C4::Context->preference('NotifyBorrowerDeparture'))) < - Date_to_Days( $today_year, $today_month, $today_day ) ) { +if ( $borr->{'dateexpiry'} && C4::Context->preference('NotifyBorrowerDeparture') ) { + my $days_to_expiry = Date_to_Days( $warning_year, $warning_month, $warning_day ) - Date_to_Days( $today_year, $today_month, $today_day ); + if ( $days_to_expiry < 0 ) { + #borrower card has expired, warn the borrower + $borr->{'warnexpired'} = $borr->{'dateexpiry'}; + } elsif ( $days_to_expiry < C4::Context->preference('NotifyBorrowerDeparture') ) { # borrower card soon to expire, warn the borrower $borr->{'warndeparture'} = $borr->{dateexpiry}; if (C4::Context->preference('ReturnBeforeExpiry')){ $borr->{'returnbeforeexpiry'} = 1; } + } } +# pass on any renew errors to the template for displaying +$template->param( RENEW_ERROR => $query->param('renew_error') ) if $query->param('renew_error'); $template->param( BORROWER_INFO => \@bordat, borrowernumber => $borrowernumber, @@ -158,7 +157,7 @@ my $issues = GetPendingIssues($borrowernumber); if ($issues){ foreach my $issue ( sort { $b->{date_due}->datetime() cmp $a->{date_due}->datetime() } @{$issues} ) { # check for reserves - my $restype = GetReserveStatus( $issue->{'itemnumber'} ); + my ( $restype, $res, undef ) = CheckReserves( $issue->{'itemnumber'} ); if ( $restype ) { $issue->{'reserved'} = 1; } @@ -170,8 +169,6 @@ if ($issues){ $charges += $ac->{'amountoutstanding'} if $ac->{'accounttype'} eq 'F'; $charges += $ac->{'amountoutstanding'} - if $ac->{'accounttype'} eq 'FU'; - $charges += $ac->{'amountoutstanding'} if $ac->{'accounttype'} eq 'L'; } } @@ -261,8 +258,8 @@ foreach my $res (@reserves) { $res->{'branch'} = $branches->{ $res->{'branchcode'} }->{'branchname'}; my $biblioData = GetBiblioData($res->{'biblionumber'}); $res->{'reserves_title'} = $biblioData->{'title'}; - if ($show_priority) { - $res->{'priority'} ||= ''; + if ($OPACDisplayRequestPriority) { + $res->{'priority'} = '' if $res->{'priority'} eq '0'; } $res->{'suspend_until'} = C4::Dates->new( $res->{'suspend_until'}, "iso")->output("syspref") if ( $res->{'suspend_until'} ); } @@ -272,7 +269,7 @@ foreach my $res (@reserves) { $template->param( RESERVES => \@reserves ); $template->param( reserves_count => $#reserves+1 ); -$template->param( showpriority=>$show_priority ); +$template->param( showpriority=>1 ) if $OPACDisplayRequestPriority; my @waiting; my $wcount = 0; @@ -359,6 +356,7 @@ $template->param( patronupdate => $patronupdate, OpacRenewalAllowed => C4::Context->preference("OpacRenewalAllowed"), userview => 1, + dateformat => C4::Context->preference("dateformat"), ); $template->param( --