From: Terran McCanna Date: Tue, 27 Dec 2022 18:05:33 +0000 (-0500) Subject: LP1902937: Quipu Online Account Renewal X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=a3705d8610538599274716c27f37d20ed741451c;p=working%2FEvergreen.git LP1902937: Quipu Online Account Renewal Online Renewal - Squashed from several working branch commits. - Creates placeholders for new English & Spanish pages - Adds ability for those pages to load - Create div on My Account main page & prefs page to hold renewal message or button - Adds code to check whether or not patron is eligible to renew online or not: * patron has not already had one temporary renewal * patron account is still active * patron account expiration date is no more than 30 days in the future * patron account is not barred * patron account does not have a staff-added blocking alert * patron does not owe any fines * patron is not in collections (even if patron pays fines, staff still need to remove collections note) * patron permission group allow e-renewal * patron has a valid billing address * patron has a valid day phone Add standing penalty The ID of the standing penalty has to be under 100 to prevent staff from manually adding it to patron accounts through the client interface. Add Standing Penalty for temporary renewal. Add temporay renewal flag to Quipu response. Create new API instead of piggybacking on vital_stats: open-ils.actor.user.opac.renewal Add column to permission.grp_tree to allow e-renew. See & update permission group setting through staff client. Look up permission groups by name or by e-renewal eligibility flag. Commit message edited by Jason Stephenson - 20230127 Signed-off-by: Terran McCanna Signed-off-by: Chris Sharp Signed-off-by: Jason Stephenson --- diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml index 63353a3704..fae73e21c4 100644 --- a/Open-ILS/examples/fm_IDL.xml +++ b/Open-ILS/examples/fm_IDL.xml @@ -8399,6 +8399,7 @@ SELECT usr, + diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-tree.component.html b/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-tree.component.html index 02d045f54a..9de97819b1 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-tree.component.html +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-tree.component.html @@ -116,6 +116,14 @@ {{selected.callerData.usergroup() === 't'}} +
+
+ +
+
+ {{selected.callerData.erenew() == 't'}} +
+
  • diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm index da0c9d7fa2..577034f2d0 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm @@ -623,8 +623,12 @@ sub update_patron { ($new_patron, $evt) = _create_stat_maps($e, $patron, $new_patron); return $evt if $evt; - ($new_patron, $evt) = _create_perm_maps($e, $patron, $new_patron); - return $evt if $evt; + # PINES - modified this because it was breaking e-renew, could not verify that it actually + # worked anywhere because the subroutine looks like it's trying to update the wrong table - TM + if($patron->isnew()) { + ($new_patron, $evt) = _create_perm_maps($e, $patron, $new_patron); + return $evt if $evt; + } $evt = apply_invalid_addr_penalty($e, $patron); return $evt if $evt; @@ -2064,6 +2068,69 @@ sub user_opac_vitals { }; } +# PINES ECARD RENEWAL INFO API +__PACKAGE__->register_method( + method => "user_opac_renewal", + api_name => "open-ils.actor.user.opac.renewal", + argc => 1, + authoritative => 1, + signature => { + desc => 'Returns minimal patron info for 3rd party renewal', + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Optional User ID, for use in the staff client', type => 'number'} # number? + ], + return => { + desc => "A user object." + } + } +); + +sub user_opac_renewal { + my( $self, $client, $auth, $user_id ) = @_; + + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + + $user_id ||= $e->requestor->id; + + my $user = $e->retrieve_actor_user([ $user_id, { + flesh => 1, + flesh_fields => { + au => ['card', 'billing_address', 'mailing_address'] + } + }]); + + return { + user => { + first_given_name => $user->first_given_name, + second_given_name => $user->second_given_name, + family_name => $user->family_name, + pref_first_given_name => $user->pref_first_given_name, + pref_second_given_name => $user->pref_second_given_name, + pref_family_name => $user->pref_family_name, + day_phone => $user->day_phone, + email => $user->email, + home_ou => $user->home_ou, + barcode => $user->card->barcode, + physical_street1 => $user->billing_address->street1, + physical_street2 => $user->billing_address->street2, + physical_city => $user->billing_address->city, + physical_post_code => $user->billing_address->post_code, + physical_county => $user->billing_address->county, + physical_state => $user->billing_address->state, + physical_country => $user->billing_address->country, + mailing_street1 => $user->mailing_address->street1, + mailing_street2 => $user->mailing_address->street2, + mailing_city => $user->mailing_address->city, + mailing_post_code => $user->mailing_address->post_code, + mailing_county => $user->mailing_address->county, + mailing_state => $user->mailing_address->state, + mailing_country => $user->mailing_address->country + } + }; +} + ##### a small consolidation of related method registrations my $common_params = [ diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/permission.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/permission.pm index 7c7cab6d85..af9ada555a 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/permission.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/permission.pm @@ -16,7 +16,7 @@ use base qw/permission/; __PACKAGE__->table('permission_grp_tree'); __PACKAGE__->columns(Primary => qw/id/); __PACKAGE__->columns(Essential => qw/name parent description perm_interval - application_perm usergroup hold_priority/); + application_perm usergroup hold_priority erenew/); #------------------------------------------------------------------------------- package permission::usr_grp_map; use base qw/permission/; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm index 11eff6a422..49bcd63de8 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm @@ -187,8 +187,11 @@ sub load { $self->load_simple("myopac") if $path =~ m:opac/myopac:; # A default page for myopac parts return $self->load_ecard_form if $path =~ m|opac/ecard/form|; + # PINES - online account registration return $self->load_ecard_submit if $path =~ m|opac/ecard/submit|; - return $self->load_ecard_verify if $path =~ m|opac/ecard/verify|; + + # PINES - online account renewal + return $self->load_simple("renew-account") if $path =~ m|opac/renew-account|; if($path =~ m|opac/login|) { return $self->load_login unless $self->editor->requestor; # already logged in? diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm index 78bba4acac..720387ca5f 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm @@ -37,7 +37,7 @@ sub prepare_extended_user_info { { flesh => 2, flesh_fields => { - au => [qw/card home_ou addresses ident_type locale billing_address waiver_entries stat_cat_entries/, @extra_flesh], + au => [qw/card home_ou addresses ident_type locale billing_address mailing_address waiver_entries stat_cat_entries/, @extra_flesh], "aou" => ["billing_address"], "actscecm" => ["stat_cat"] } @@ -120,6 +120,20 @@ sub load_myopac_prefs { $self->prepare_extended_user_info; my $user = $self->ctx->{user}; + # PINES - check whether or not to provide account renewal link + if ($user->billing_address and $user->billing_address->valid and $user->billing_address->valid eq 't') { + $self->ctx->{valid_billing_address} = 't'; + } else { + $self->ctx->{valid_billing_address} = 'f'; + } + if ($user->mailing_address and $user->mailing_address->valid and $user->mailing_address->valid eq 't') { + $self->ctx->{valid_mailing_address} = 't'; + } else { + $self->ctx->{valid_mailing_address} = 'f'; + } + + $self->check_account_exp(); + my $lock_usernames = $self->ctx->{get_org_setting}->($e->requestor->home_ou, 'opac.lock_usernames'); if(defined($lock_usernames) and $lock_usernames == 1) { # Policy says no username changes @@ -189,7 +203,6 @@ sub load_myopac_prefs_notify { my $self = shift; my $e = $self->editor; - my $stat = $self->_load_user_with_prefs; return $stat if $stat; @@ -2645,6 +2658,22 @@ sub load_myopac_main { pub => 't' }) ); + + # PINES - need to make sure we're retrieving current info + $self->prepare_extended_user_info; + # PINES - check whether or not to provide account renewal link + if ($self->ctx->{user}->billing_address) { + $self->ctx->{valid_billing_address} = $self->editor->retrieve_actor_user_address($self->ctx->{user}->billing_address)->valid; + } else { + $self->ctx->{valid_billing_address} = 0; + } + if ($self->ctx->{user}->mailing_address) { + $self->ctx->{valid_mailing_address} = $self->editor->retrieve_actor_user_address($self->ctx->{user}->mailing_address)->valid; + } else { + $self->ctx->{valid_mailing_address} = 0; + } + $self->check_account_exp(); + return $self->prepare_fines($limit, $offset) || Apache2::Const::OK; } @@ -3537,4 +3566,144 @@ sub load_password_reset { return Apache2::Const::OK; } +# PINES - check whether patron has standing penalties that should block +# online account renewal +sub has_penalties { + my $self = shift; + my $ctx = $self->ctx; + my $user = $self->ctx->{user}; + my $e = new_editor(xact => 1); + + #I'm sure there is a way to combine the following standing penalty checks, but this is working for now + + #check for INVALID_PATRON_ADDRESS + my $findpenalty_address = $e->search_config_standing_penalty({name => 'INVALID_PATRON_ADDRESS'})->[0]; + my $searchpenalty_address = $e->search_actor_user_standing_penalty({ + usr => $user->id, + standing_penalty => $findpenalty_address->id, + '-or' => [ + {stop_date => undef}, + {stop_date => {'>' => 'now'}} + ] + }); + + #check for INVALID_PATRON_DAY_PHONE + my $findpenalty_phone = $e->search_config_standing_penalty({name => 'INVALID_PATRON_DAY_PHONE'})->[0]; + my $searchpenalty_phone = $e->search_actor_user_standing_penalty({ + usr => $user->id, + standing_penalty => $findpenalty_phone->id, + '-or' => [ + {stop_date => undef}, + {stop_date => {'>' => 'now'}} + ] + }); + + #check for PATRON_IN_COLLECTIONS + my $findpenalty_coll = $e->search_config_standing_penalty({name => 'PATRON_IN_COLLECTIONS'})->[0]; + my $searchpenalty_coll = $e->search_actor_user_standing_penalty({ + usr => $user->id, + standing_penalty => $findpenalty_coll->id, + '-or' => [ + {stop_date => undef}, + {stop_date => {'>' => 'now'}} + ] + }); + + #check for alerting block + my $findpenalty_alertblock = $e->search_config_standing_penalty({name => 'STAFF_CHR'})->[0]; + my $searchpenalty_alertblock = $e->search_actor_user_standing_penalty({ + usr => $user->id, + standing_penalty => $findpenalty_alertblock->id, + '-or' => [ + {stop_date => undef}, + {stop_date => {'>' => 'now'}} + ] + }); + + #check for PATRON_TEMP_RENEWAL + my $findpenalty_temp = $e->search_config_standing_penalty({name => 'PATRON_TEMP_RENEWAL'})->[0]; + my $searchpenalty_temp = $e->search_actor_user_standing_penalty({ + usr => $user->id, + standing_penalty => $findpenalty_temp->id, + '-or' => [ + {stop_date => undef}, + {stop_date => {'>' => 'now'}} + ] + }); + + if (@$searchpenalty_address || @$searchpenalty_coll || @$searchpenalty_phone || @$searchpenalty_alertblock) { + $ctx->{haspenalty} = 1; + } else { + $ctx->{haspenalty} = 0; + } + + if (@$searchpenalty_temp) { + $ctx->{hastemprenew} = 1; + } else { + $ctx->{hastemprenew} = 0; + } + + return; +} + +# PINES - check whether or not to show account renewal link +sub check_account_exp { + my $self = shift; + my $ctx = $self->ctx; + $self->update_dashboard_stats(); + + #make sure patron is in an eligible perm group for renewal + my $grp = new_editor()->retrieve_permission_grp_tree($ctx->{user}->profile); + + if ($grp->erenew eq 't') { + $ctx->{eligible_permgroup} = 1; + } else { + $ctx->{eligible_permgroup} = 0; + } + + #check for various standing penalties that would block an online renewal + $self->has_penalties(); + + #check for other problems that would block an online renewal + if ($ctx->{user}->active ne 't') { #user is no longer active + $ctx->{hasproblem} = 1; + } elsif ($ctx->{haspenalty} eq 1) { #user has a standing penalty block + $ctx->{hasproblem} = 1; + } elsif ($ctx->{user}->barred eq 't') { #user is barred + $ctx->{hasproblem} = 1; + } elsif ($ctx->{valid_billing_address} ne 't') { #user has invalid billing address + $ctx->{hasproblem} = 1; + } elsif ($ctx->{valid_mailing_address} ne 't') { #user has invalid mailing address + $ctx->{hasproblem} = 1; + } else { + $ctx->{hasproblem} = 0; + } + + #determine which message to show (if any) + my $cache = OpenSRF::Utils::Cache->new('global'); + $cache->put_cache('account_renew_ok','false',3600); + + if ($ctx->{hastemprenew} eq 1) { #user already has active temp renewal + $ctx->{account_renew_message} = '
    Your account + could only be temporarily renewed because your address changed. Please visit your nearest PINES + library with your current proof of address to complete your account renewal.
    '; + } elsif (DateTime->today->add(days=>29) lt $ctx->{user}->expire_date) { + #expiration date is too far in future - don't show message + $ctx->{account_renew_message} = ''; + } elsif ($ctx->{hasproblem} eq 1 or $ctx->{eligible_permgroup} eq 0) { #see other problems above + $ctx->{account_renew_message} = '
    Your account is + due for renewal, but it cannot be renewed online. Please visit your nearest PINES + library with your current ID and proof of address to update and renew your account.
    '; + } elsif ($ctx->{user_stats}->{fines}->{balance_owed} gt 0) { #user has fines + $ctx->{account_renew_message} = '
    Your account + is due for renewal. Please pay your outstanding fines in order to renew your account.
    '; + } else { + $ctx->{account_renew_message} = 'Click here to renew your account'; + $cache->put_cache('account_renew_ok','true',3600); + } + + return 1; +} + 1; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Ecard.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Ecard.pm index 1b16c94c2c..21af15c2dc 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Ecard.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Ecard.pm @@ -5,6 +5,7 @@ use OpenSRF::Utils::Logger qw/$logger/; use OpenSRF::Utils::JSON; use OpenSRF::Utils qw/:datetime/; use OpenILS::Utils::Fieldmapper; +use OpenSRF::Utils::Cache; use OpenILS::Application::AppUtils; use OpenILS::Utils::CStoreEditor qw/:funcs/; use OpenILS::Event; @@ -15,7 +16,39 @@ use Digest::MD5 qw(md5_hex); $Data::Dumper::Indent = 0; my $U = 'OpenILS::Application::AppUtils'; -my @api_fields = ( +my $update_type = 'register'; + +my @api_fields_renew = ( + {name => 'vendor_username', required => 1}, + {name => 'vendor_password', required => 1}, + {name => 'email', class => 'au'}, + {name => 'day_phone', class => 'au', required => 1}, + {name => 'home_ou', class => 'au'}, + {name => 'pref_first_given_name', class => 'au'}, + {name => 'pref_second_given_name', class => 'au'}, + {name => 'pref_family_name', class => 'au'}, + {name => 'physical_id', class => 'aua'}, + {name => 'physical_street1', class => 'aua'}, + {name => 'physical_street1_name'}, + {name => 'physical_street2', class => 'aua'}, + {name => 'physical_city', class => 'aua'}, + {name => 'physical_post_code', class => 'aua'}, + {name => 'physical_county', class => 'aua'}, + {name => 'physical_state', class => 'aua'}, + {name => 'physical_country', class => 'aua'}, + {name => 'mailing_id', class => 'aua'}, + {name => 'mailing_street1', class => 'aua'}, + {name => 'mailing_street1_name'}, + {name => 'mailing_street2', class => 'aua'}, + {name => 'mailing_city', class => 'aua'}, + {name => 'mailing_post_code', class => 'aua'}, + {name => 'mailing_county', class => 'aua'}, + {name => 'mailing_state', class => 'aua'}, + {name => 'mailing_country', class => 'aua'}, + {name => 'voter_registration', class => 'asvr'} +); + +my @api_fields_register = ( {name => 'vendor_username', required => 1}, {name => 'vendor_password', required => 1}, {name => 'first_given_name', class => 'au', required => 1}, @@ -189,7 +222,7 @@ sub handle_testmode_api { # Strip data we don't want to publish. my @doc_fields; - for my $field_info (@api_fields) { + for my $field_info (@api_fields_register) { my $doc_info = {}; for my $info_key (keys %$field_info) { $doc_info->{$info_key} = $field_info->{$info_key} @@ -200,6 +233,7 @@ sub handle_testmode_api { $ctx->{response}->{messages} = [fields => \@doc_fields]; $ctx->{response}->{status} = 'API_OK'; + return $self->compile_response; } @@ -219,6 +253,7 @@ sub handle_datamode_api { } $ctx->{response}->{status} = 'DATA_OK'; + return $self->compile_response; } @@ -227,6 +262,23 @@ sub load_ecard_submit { my $ctx = $self->ctx; my $cgi = $self->cgi; + #determine whether this is a new registration or a renewal + if ($cgi->param('patron_id') > 1) { + $update_type = 'renew'; + } else { + $update_type = 'register'; + } + + #If this is a renewal, double-check that they are eligible to renew + my $cache = OpenSRF::Utils::Cache->new('global'); + if ($update_type eq 'renew') { + if ($cache->get_cache("account_renew_ok") && $cache->get_cache("account_renew_ok") eq 'true') { + } else { + $logger->error("ERENEW - User not in correct status to renew account"); + return $self->compile_response; + } + } + $self->log_params; my $testmode = $cgi->param('testmode') || ''; @@ -254,28 +306,54 @@ sub load_ecard_submit { return $self->handle_testmode_api if $testmode eq 'API'; return $self->handle_datamode_api($datamode) if $datamode; - return $self->compile_response unless $self->make_user; - return $self->compile_response unless $self->add_addresses; - return $self->compile_response unless $self->check_dupes; - return $self->compile_response unless $self->add_card; - # Add survey responses commented out because it is not universal. - # We should come up with a way to configure it before uncommenting - # it globally. - #return $self->compile_response unless $self->add_survey_responses; - return $self->compile_response unless $self->save_user; - return $self->compile_response unless $self->add_usr_settings; - return $self->compile_response if $ctx->{response}->{status}; - - # The code below does nothing in a stock Evergreen installation. - # It is included in case a site wishes to set up action trigger - # events to do some additional verification or notification for - # patrons who have signed up for an eCard. - $U->create_events_for_hook( - 'au.create.ecard', $ctx->{user}, $ctx->{user}->home_ou); + # Accommodate reg vs renew + if ($update_type eq 'register') { + return $self->compile_response unless $self->make_user; + return $self->compile_response unless $self->add_addresses; + return $self->compile_response unless $self->check_dupes; + return $self->compile_response unless $self->add_card; + return $self->compile_response unless $self->add_survey_responses; + return $self->compile_response unless $self->save_user; + return $self->compile_response unless $self->add_usr_settings; + return $self->compile_response if $ctx->{response}->{status}; + + $U->create_events_for_hook( + 'au.create.ecard', $ctx->{user}, $ctx->{user}->home_ou); + } else { + return $self->compile_response unless $self->update_user; + return $self->compile_response unless $self->update_addresses; + return $self->compile_response unless $self->add_survey_responses; + return $self->compile_response unless $self->save_user; + return $self->compile_response if $ctx->{response}->{status}; + } + # Add extra info to response message $ctx->{response}->{status} = 'OK'; - $ctx->{response}->{barcode} = $ctx->{user}->card->barcode; - $ctx->{response}->{expiration_date} = substr($ctx->{user}->expire_date, 0, 10); + + if ($update_type eq 'renew') { + #New expiration date + $ctx->{response}->{expire_date} = $ctx->{user}->expire_date; + #Mark whether this is a temporary renewal or not + my $findpenalty_temp = $e->search_config_standing_penalty({name => 'PATRON_TEMP_RENEWAL'})->[0]; + my $searchpenalty_temp = $e->search_actor_user_standing_penalty({ + usr => $cgi->param('patron_id'), + standing_penalty => $findpenalty_temp->id, + '-or' => [ + {stop_date => undef}, + {stop_date => {'>' => 'now'}} + ] + }); + if (@$searchpenalty_temp) { + $ctx->{response}->{temp_renew} = 1; + } else { + $ctx->{response}->{temp_renew} = 0; + } + #set renewal flag in cache to false to prevent user from refreshing the page and submitting again + $cache->put_cache('account_renew_ok','false',3600); + } else { + $ctx->{response}->{barcode} = $ctx->{user}->card->barcode; + $ctx->{response}->{expiration_date} = substr($ctx->{user}->expire_date, 0, 10); + } return $self->compile_response; } @@ -327,28 +405,18 @@ sub verify_vendor_host { return 1; } - sub compile_response { my $self = shift; my $ctx = $self->ctx; + $self->apache->content_type("application/json; charset=utf-8"); $ctx->{response} = OpenSRF::Utils::JSON->perl2JSON($ctx->{response}); $logger->info("ECARD responding with " . $ctx->{response}); - return Apache2::Const::OK; -} -my %keep_case = (usrname => 1, passwd => 1, email => 1); -sub upperclense { - my $self = shift; - my $field = shift; - my $value = shift; - $value = uc($value) unless $keep_case{$field}; - $value = lc($value) if $field eq 'email'; # force it - $value =~ s/(^\s*|\s*$)//g; - return $value; + return Apache2::Const::OK; } -# Create actor.usr perl object and populate column data +# Create actor.usr perl object and populate column data (for new registration) sub make_user { my $self = shift; my $ctx = $self->ctx; @@ -358,6 +426,8 @@ sub make_user { $au->isnew(1); $au->net_access_level(1); # Filtered + $au->name_keywords($in_house ? 'quipu_inhouse' : 'quipu_remote'); + my $home_ou = $cgi->param('home_ou'); my $perm_grp = $U->ou_ancestor_setting_value( @@ -373,7 +443,7 @@ sub make_user { seconds => interval_to_seconds($grp->perm_interval))->iso8601() ); - for my $field_info (@api_fields) { + for my $field_info (@api_fields_register) { my $field = $field_info->{name}; next unless $field_info->{class} eq 'au'; @@ -390,6 +460,7 @@ sub make_user { } $self->verify_dob($val) if $field eq 'dob' && $val; + $au->$field($val); } @@ -397,6 +468,85 @@ sub make_user { return $ctx->{user} = $au; } +# If existing account, update instead of create +sub update_user { + + my $self = shift; + my @extra_flesh = @_; + my $e = $self->editor; + my $ctx = $self->ctx; + my $cgi = $self->cgi; + + # Grab user id, retrieve patron info from db and create patron object + my $patron_id = $cgi->param('patron_id'); + + my $au = $self->editor->retrieve_actor_user([$patron_id, + { + flesh => 1, + flesh_fields => { + au => ['billing_address', 'mailing_address', 'groups', 'permissions', 'standing_penalties'] + } + } + ]); + #indicate that this is an update, not a new record + $au->isnew(0); + + # Replace values in patron object with new data + + # Need to append new keyword for use in reports later + my $orig_kw = $au->name_keywords; + my $dt = DateTime->now; + my $dty = $dt->year; + my $dtm = $dt->month; + if ($orig_kw ne '') { + $au->name_keywords("$orig_kw quipu_renew_$dty$dtm"); + } else { + $au->name_keywords("quipu_renew_$dty$dtm"); + } + + # Temp renewal is only 30 days, otherwise use perm_interval + # If perm group is Homebound or GLS, allow full renewal + my $temp_renewal = $cgi->param('temp_renewal'); + my $grp = new_editor()->retrieve_permission_grp_tree($au->profile); + + if ($temp_renewal eq '1' && $grp->name ne 'GLS' && $grp->name ne 'Homebound') { + $au->expire_date( + DateTime->now(time_zone => 'local')->add( + seconds => interval_to_seconds('30 days'))->iso8601() + ); + # Add temp renewal standing penalty to account + $self->apply_temp_renewal_penalty; + } else { + $au->expire_date( + DateTime->now(time_zone => 'local')->add( + seconds => interval_to_seconds($grp->perm_interval))->iso8601() + ); + } + + # loop through fields submitted by quipu + for my $field_info (@api_fields_renew) { + my $field = $field_info->{name}; + next unless $field_info->{class} eq 'au'; + + my $val = $cgi->param($field); + + if ($field_info->{required} && !$val) { + my $msg = "Value required for field: '$field'"; + $ctx->{response}->{status} = 'INVALID_PARAMS'; + push(@{$ctx->{response}->{messages}}, $msg); + $logger->error("E-RENEW $msg"); + } + + $val = undef if $field eq 'day_phone' && $val eq '--'; + $val = $au->home_ou if $field eq 'home_ou' && $val eq ''; + + $au->$field($val); + } + + return $ctx->{user} = $au; +} + +# Card generation must occur after the user is saved in the DB. sub add_card { my $self = shift; my $ctx = $self->ctx; @@ -494,16 +644,6 @@ sub verify_dob { return 1; } -# returns true if the addresses contain all of the same values. -sub addrs_match { - my ($self, $addr1, $addr2) = @_; - for my $field ($addr1->real_fields) { - return 0 if ($addr1->$field() || '') ne ($addr2->$field() || ''); - } - return 1; -} - - sub add_addresses { my $self = shift; my $cgi = $self->cgi; @@ -531,7 +671,7 @@ sub add_addresses { # Confirm we have values for all of the required fields. # Apply values to our in-progress address object. - for my $field_info (@api_fields) { + for my $field_info (@api_fields_register) { my $field = $field_info->{name}; next unless $field =~ /physical|mailing/; next if $field =~ /street1_/; @@ -565,6 +705,90 @@ sub add_addresses { return 1; } +sub update_addresses { + my $self = shift; + my $cgi = $self->cgi; + my $ctx = $self->ctx; + my $e = $ctx->{editor}; + my $user = $ctx->{user}; + + my $physical_addr = Fieldmapper::actor::user_address->new; + $physical_addr->id($user->billing_address->id); + $physical_addr->usr($user->id); + $physical_addr->address_type('PHYSICAL'); + $physical_addr->within_city_limits($user->billing_address->within_city_limits); + $physical_addr->valid('t'); + $physical_addr->pending('f'); + + my $mailing_addr = Fieldmapper::actor::user_address->new; + $mailing_addr->id($user->mailing_address->id); + $mailing_addr->usr($user->id); + $mailing_addr->address_type('MAILING'); + $mailing_addr->within_city_limits($user->mailing_address->within_city_limits); + $mailing_addr->valid('t'); + $mailing_addr->pending('f'); + + # Confirm we have values for all of the required fields. + # Apply values to our in-progress address object. + for my $field_info (@api_fields_renew) { + my $field = $field_info->{name}; + next unless $field =~ /physical|mailing/; + next if $field =~ /street1_/; + + my $val = $cgi->param($field); + + if ($field_info->{required} && !$val) { + my $msg = "Value required for field: '$field'"; + $ctx->{response}->{status} = 'INVALID_PARAMS'; + push(@{$ctx->{response}->{messages}}, $msg); + $logger->error("E-RENEW $msg"); + } + + if ($field =~ /physical/) { + (my $col_field = $field) =~ s/physical_//g; + $physical_addr->$col_field($val) if $val; + } else { + (my $col_field = $field) =~ s/mailing_//g; + $mailing_addr->$col_field($val) if $val; + } + } + + # Determine what exactly to do with addresses + if ($physical_addr->id eq $mailing_addr->id && $physical_addr->street1 eq $mailing_addr->street1) { + # if one address & stays at one address, just update it (don't need to do both physical & mailing) + $mailing_addr->isnew(0); + $mailing_addr->ischanged(1); + } elsif ($physical_addr->id eq $mailing_addr->id && $physical_addr->street1 ne $mailing_addr->street1) { + # if one address splitting to two addresses, update the first and create a second address entry + $physical_addr->isnew(0); + $physical_addr->ischanged(1); + $mailing_addr->isnew(1); + $mailing_addr->id(-1); + } elsif ($physical_addr->id ne $mailing_addr->id && $physical_addr->street1 eq $mailing_addr->street1) { + # if there were previously 2 addresses, but there is only one address now, use the updated single address entry for both + $physical_addr->isnew(0); + $physical_addr->ischanged(1); + $mailing_addr->isnew(0); + $mailing_addr->ischanged(1); + $mailing_addr->id($physical_addr->id); + } else { + # otherwise, update existing entries + $physical_addr->isnew(0); + $physical_addr->ischanged(1); + $mailing_addr->isnew(0); + $mailing_addr->ischanged(1); + } + + # exit if there were any errors above. + return undef if $ctx->{response}->{status}; + + $user->billing_address($physical_addr); + $user->mailing_address($mailing_addr); + $user->addresses([$physical_addr, $mailing_addr]); + + return 1; +} + # TODO: The code in add_usr_settings is totally arbitrary and should # be modified to look up settings in the database. sub add_usr_settings { @@ -595,9 +819,10 @@ sub add_survey_responses { my $answer = $cgi->param('voter_registration'); my $survey_response = Fieldmapper::action::survey_response->new; + $survey_response->id(-1); $survey_response->isnew(1); - $survey_response->survey(1); # voter registration survey + $survey_response->survey(1); $survey_response->question(1); $survey_response->answer($answer); @@ -605,35 +830,6 @@ sub add_survey_responses { return 1; } -# TODO: this is KCLS-specific, but maybe we can make it something -# generic for adding stat cats to the patron - -#sub add_stat_cats { -# my $self = shift; -# my $cgi = $self->cgi; -# my $user = $self->ctx->{user}; -# -# my $ds_map = Fieldmapper::actor::stat_cat_entry_user_map->new; -# $ds_map->isnew(1); -# $ds_map->stat_cat(12); -# $ds_map->stat_cat_entry('KCLS'); -# -# my $events = $cgi->param('events_mailing'); -# my $em_map = Fieldmapper::actor::stat_cat_entry_user_map->new; -# $em_map->isnew(1); -# $em_map->stat_cat(3); -# $em_map->stat_cat_entry($events ? 'Y' : 'N'); -# -# my $foundation = $cgi->param('foundation_mailing'); -# my $fm_map = Fieldmapper::actor::stat_cat_entry_user_map->new; -# $fm_map->isnew(1); -# $fm_map->stat_cat(4); -# $fm_map->stat_cat_entry($foundation ? 'Y' : 'N'); -# -# $user->stat_cat_entries([$ds_map, $em_map, $fm_map]); -# return 1; -#} - # Returns true if no dupes found, false if dupes are found. sub check_dupes { my $self = shift; @@ -717,6 +913,7 @@ sub save_user { my $ctx = $self->ctx; my $cgi = $self->cgi; my $user = $ctx->{user}; + my $update_type = $user->isnew; my $resp = $U->simplereq( 'open-ils.actor', @@ -727,11 +924,18 @@ sub save_user { $resp = {textcode => 'UNKNOWN_ERROR'} unless $resp; if ($U->is_event($resp)) { + my $msg = ''; - my $msg = "Error creating user account: " . $resp->{textcode}; - $logger->error("ECARD: $msg"); + if ($update_type eq '1') { + $msg = "Error creating user account: " . $resp->{textcode}; + $logger->error("ECARD: $msg"); + $ctx->{response}->{status} = 'CREATE_ERR'; + } else { + $msg = "Error updating user account: " . $resp->{textcode}; + $logger->error("E-RENEW: $msg"); + $ctx->{response}->{status} = 'UPDATE_ERR'; + } - $ctx->{response}->{status} = 'CREATE_ERR'; $ctx->{response}->{messages} = [{msg => $msg, pid => $$}]; return 0; @@ -741,5 +945,48 @@ sub save_user { return 1; } +sub apply_temp_renewal_penalty { + + my $self = shift; + my $ctx = $self->ctx; + my $cgi = $self->cgi; + my $patron_id = $cgi->param('patron_id'); + + my $e = new_editor(xact => 1); + my $ptype = $e->search_config_standing_penalty({name => 'PATRON_TEMP_RENEWAL'})->[0]; + + my $penalty = Fieldmapper::actor::user_standing_penalty->new; + $penalty->usr($patron_id); + $penalty->org_unit(1); + $penalty->standing_penalty($ptype->id); + + my $aum = Fieldmapper::actor::usr_message->new; + $aum->create_date('now'); + $aum->sending_lib(1); + $aum->title('Temporary Account Renewal'); + $aum->usr($penalty->usr); + $aum->message('Patron renewed online with an address change so was given a 30-day + temporary account renewal. Please archive this message after the address is + verified and the renewal date extended.'); + $aum->pub(0); + + $aum = $e->create_actor_usr_message($aum); + unless($aum) { + $e->rollback; + return 0; + } + + $penalty->usr_message($aum->id); + + unless($e->create_actor_user_standing_penalty($penalty)) { + $e->rollback; + return 0; + } + + $e->commit; + return 1; +} + + 1; diff --git a/Open-ILS/src/sql/Pg/006.schema.permissions.sql b/Open-ILS/src/sql/Pg/006.schema.permissions.sql index 05d38a6243..c1f1622194 100644 --- a/Open-ILS/src/sql/Pg/006.schema.permissions.sql +++ b/Open-ILS/src/sql/Pg/006.schema.permissions.sql @@ -39,7 +39,8 @@ CREATE TABLE permission.grp_tree ( perm_interval INTERVAL DEFAULT '3 years'::interval NOT NULL, description TEXT, application_perm TEXT, - hold_priority INT NOT NULL DEFAULT 0 + hold_priority INT NOT NULL DEFAULT 0, + erenew BOOL NOT NULL DEFAULT TRUE ); CREATE INDEX grp_tree_parent_idx ON permission.grp_tree (parent); diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql index 1aba18c765..cc293c211b 100644 --- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql +++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql @@ -91,7 +91,8 @@ INSERT INTO config.standing_penalty (id, name, label, staff_alert, org_depth) VA TRUE, 0 ); - +-- Temp renewal penalty must be under 100 to prevent staff from manually adding it through client interface +INSERT INTO config.standing_penalty (id, name, label, staff_alert, org_depth) VALUES (90, 'PATRON_TEMP_RENEWAL', 'Patron was given a 30-day temporary account renewal. Please archive this message after the account is fully renewed.', TRUE, 0); SELECT SETVAL('config.standing_penalty_id_seq', 100); diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX-quipu-standing_penalty.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX-quipu-standing_penalty.sql new file mode 100644 index 0000000000..e7d82fd51b --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/XXXX-quipu-standing_penalty.sql @@ -0,0 +1,11 @@ +BEGIN; + +-- ID has to be under 100 in order to prevent it from appearing as a dropdown in the patron editor. + +INSERT INTO config.standing_penalty (id, name, label, staff_alert, org_depth) +VALUES (90, 'PATRON_TEMP_RENEWAL', + 'Patron was given a 30-day temporary account renewal. + Please archive this message after the account is fully renewed.', TRUE, 0 + ); + +COMMIT; \ No newline at end of file diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.erenew_column_pgt.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.erenew_column_pgt.sql new file mode 100644 index 0000000000..cf584015e7 --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.erenew_column_pgt.sql @@ -0,0 +1,17 @@ +BEGIN; + +ALTER TABLE permission.grp_tree ADD COLUMN erenew BOOL; + +COMMIT; + +BEGIN; + +UPDATE permission.grp_tree SET erenew = FALSE; + +COMMIT; + +BEGIN; + +ALTER TABLE permission.grp_tree ALTER COLUMN erenew SET NOT NULL; + +COMMIT; diff --git a/Open-ILS/src/templates-bootstrap/opac/myopac/main.tt2 b/Open-ILS/src/templates-bootstrap/opac/myopac/main.tt2 index 87dae3f4d8..841201bb46 100755 --- a/Open-ILS/src/templates-bootstrap/opac/myopac/main.tt2 +++ b/Open-ILS/src/templates-bootstrap/opac/myopac/main.tt2 @@ -20,16 +20,9 @@ [% date.format(ctx.parse_datetime(ctx.user.expire_date), DATE_FORMAT) %] -
    - [% IF ctx.expired_card %] - - - [% l("
    Your library card has expired.
    Please contact a librarian to resolve this issue.", fmt_expire_date) %] -
    -
    - [% END %] +
    + [% ctx.account_renew_message %]
    -
    +[%- END %] diff --git a/Open-ILS/src/templates-bootstrap/opac/renew-account.tt2 b/Open-ILS/src/templates-bootstrap/opac/renew-account.tt2 new file mode 100644 index 0000000000..0059a2865b --- /dev/null +++ b/Open-ILS/src/templates-bootstrap/opac/renew-account.tt2 @@ -0,0 +1,38 @@ +[%- PROCESS "opac/parts/header.tt2"; + PROCESS "opac/parts/org_selector.tt2"; + WRAPPER "opac/parts/base.tt2"; + INCLUDE "opac/parts/topnav.tt2"; + ctx.page_title = l("Renew Your Library Card"); +%] + +[% IF ctx.user %] + + + + +

    [% l('Renew Your Library Card') %]

    + +
    +
    +
    +

    (English | Español)

    + + + +
    + + + + + +
    + +
    +
    +
    +[% END %] +[%- END %]