Mark email addresses and phone numbers invalid
authorLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Tue, 27 Dec 2011 23:02:38 +0000 (18:02 -0500)
committerBill Erickson <berick@esilibrary.com>
Wed, 11 Jan 2012 15:04:29 +0000 (10:04 -0500)
This comes from an idea from Jeff Godin.  For staff to indicate an
email address or phone number "invalid," for whatever definition of
"invalid" has meaning at the institution (as in invalid addresses
today), there is now a UI control in the patron editor.

This UI control invokes a middle layer method that will clear the email
(or phone) field from actor.usr, and create a corresponding standing penalty
against the user, for staff to notice next time they bring up said patron in
the staff client.  Such penalties will be archived whenever that patron's
email address or phone number is updated again.

The middle layer method is a wrapper around a method that may take
different parameters (like an email address instead of a patron ID) to
potentially support future uses such as invalidating email addresses
automatically in batch.

Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Signed-off-by: Bill Erickson <berick@esilibrary.com>

14 files changed:
Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
Open-ILS/src/perlmods/lib/OpenILS/Utils/BadContact.pm [new file with mode: 0644]
Open-ILS/src/sql/Pg/002.schema.config.sql
Open-ILS/src/sql/Pg/upgrade/XXXX.data.mark-email-and-phone-invalid.sql [new file with mode: 0644]
Open-ILS/src/templates/actor/user/register.tt2
Open-ILS/web/js/dojo/openils/Util.js
Open-ILS/web/js/dojo/openils/actor/nls/register.js
Open-ILS/web/js/ui/default/actor/user/register.js
Open-ILS/web/opac/locale/en-US/lang.dtd
Open-ILS/xul/staff_client/server/patron/display_horiz_overlay.xul
Open-ILS/xul/staff_client/server/patron/display_overlay.xul
Open-ILS/xul/staff_client/server/patron/standing_penalties.js
Open-ILS/xul/staff_client/server/patron/util.js
Open-ILS/xul/staff_client/server/skin/patron_display.css

index c646769..b1ec6d8 100644 (file)
@@ -34,6 +34,7 @@ use OpenILS::Application::Actor::Stage;
 
 use OpenILS::Utils::CStoreEditor qw/:funcs/;
 use OpenILS::Utils::Penalty;
+use OpenILS::Utils::BadContact;
 use List::Util qw/max reduce/;
 
 use UUID::Tiny qw/:std/;
@@ -365,10 +366,25 @@ sub update_patron {
                if(ref($patron->mailing_address));
 
        # create/update the patron first so we can use his id
+
+    # $patron is the obj from the client (new data) and $new_patron is the
+    # patron object properly built for db insertion, so we need a third variable
+    # if we want to represent the old patron.
+
+    my $old_patron;
+
        if($patron->isnew()) {
                ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
                return $evt if $evt;
-       } else { $new_patron = $patron; }
+       } else {
+        $new_patron = $patron;
+
+        # Did auth checking above already.
+        my $e = new_editor;
+        $old_patron = $e->retrieve_actor_user($patron->id) or
+            return $e->die_event;
+        $e->disconnect;
+    }
 
        ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
        return $evt if $evt;
@@ -385,6 +401,9 @@ sub update_patron {
                return $evt if $evt;
        }
 
+       ( $new_patron, $evt ) = _clear_badcontact_penalties($session, $old_patron, $new_patron, $user_obj);
+       return $evt if $evt;
+
        ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
        return $evt if $evt;
 
@@ -877,6 +896,60 @@ sub _add_survey_responses {
        return ( $new_patron, undef );
 }
 
+sub _clear_badcontact_penalties {
+    my ($session, $old_patron, $new_patron, $user_obj) = @_;
+
+    return ($new_patron, undef) unless $old_patron;
+
+    my $PNM = $OpenILS::Utils::BadContact::PENALTY_NAME_MAP;
+    my $e = new_editor(xact => 1);
+
+    # This ignores whether the caller of update_patron has any permission
+    # to remove penalties, but these penalties no longer make sense
+    # if an email address field (for example) is changed (and the caller must
+    # have perms to do *that*) so there's no reason not to clear the penalties.
+
+    my $bad_contact_penalties = $e->search_actor_user_standing_penalty([
+        {
+            "+csp" => {"name" => [values(%$PNM)]},
+            "+ausp" => {"stop_date" => undef, "usr" => $new_patron->id}
+        }, {
+            "join" => {"csp" => {}},
+            "flesh" => 1,
+            "flesh_fields" => {"ausp" => ["standing_penalty"]}
+        }
+    ]) or return (undef, $e->die_event);
+
+    return ($new_patron, undef) unless @$bad_contact_penalties;
+
+    my @penalties_to_clear;
+    my ($field, $penalty_name);
+
+    # For each field that might have an associated bad contact penalty, 
+    # check for such penalties and add them to the to-clear list if that
+    # field has changed.
+    while (($field, $penalty_name) = each(%$PNM)) {
+        if ($old_patron->$field ne $new_patron->$field) {
+            push @penalties_to_clear, grep {
+                $_->standing_penalty->name eq $penalty_name
+            } @$bad_contact_penalties;
+        }
+    }
+
+    foreach (@penalties_to_clear) {
+        # Note that this "archives" penalties, in the terminology of the staff
+        # client, instead of just deleting them.  This may assist reporting,
+        # or preserving old contact information when it is still potentially
+        # of interest.
+        $_->standing_penalty($_->standing_penalty->id); # deflesh
+        $_->stop_date('now');
+        $e->update_actor_user_standing_penalty($_) or return (undef, $e->die_event);
+    }
+
+    $e->commit;
+    return ($new_patron, undef);
+}
+
 
 sub _create_stat_maps {
 
@@ -4415,4 +4488,86 @@ sub address_alert_test {
     ];
 }
 
+__PACKAGE__->register_method(
+    method   => "mark_users_contact_invalid",
+    api_name => "open-ils.actor.invalidate.email",
+    signature => {
+        desc => "Given a patron, clear the email field and put the old email address into a note and/or create a standing penalty, depending on OU settings",
+        params => [
+            {desc => "Authentication token", type => "string"},
+            {desc => "Patron ID", type => "number"},
+            {desc => "Additional note text (optional)", type => "string"},
+            {desc => "penalty org unit ID (optional)", type => "number"}
+        ],
+        return => {desc => "Event describing success or failure", type => "object"}
+    }
+);
+
+__PACKAGE__->register_method(
+    method   => "mark_users_contact_invalid",
+    api_name => "open-ils.actor.invalidate.day_phone",
+    signature => {
+        desc => "Given a patron, clear the day_phone field and put the old day_phone into a note and/or create a standing penalty, depending on OU settings",
+        params => [
+            {desc => "Authentication token", type => "string"},
+            {desc => "Patron ID", type => "number"},
+            {desc => "Additional note text (optional)", type => "string"},
+            {desc => "penalty org unit ID (optional)", type => "number"}
+        ],
+        return => {desc => "Event describing success or failure", type => "object"}
+    }
+);
+
+__PACKAGE__->register_method(
+    method   => "mark_users_contact_invalid",
+    api_name => "open-ils.actor.invalidate.evening_phone",
+    signature => {
+        desc => "Given a patron, clear the evening_phone field and put the old evening_phone into a note and/or create a standing penalty, depending on OU settings",
+        params => [
+            {desc => "Authentication token", type => "string"},
+            {desc => "Patron ID", type => "number"},
+            {desc => "Additional note text (optional)", type => "string"},
+            {desc => "penalty org unit ID (optional)", type => "number"}
+        ],
+        return => {desc => "Event describing success or failure", type => "object"}
+    }
+);
+
+__PACKAGE__->register_method(
+    method   => "mark_users_contact_invalid",
+    api_name => "open-ils.actor.invalidate.other_phone",
+    signature => {
+        desc => "Given a patron, clear the other_phone field and put the old other_phone into a note and/or create a standing penalty, depending on OU settings",
+        params => [
+            {desc => "Authentication token", type => "string"},
+            {desc => "Patron ID", type => "number"},
+            {desc => "Additional note text (optional)", type => "string"},
+            {desc => "penalty org unit ID (optional, default to top of org tree)",
+                type => "number"}
+        ],
+        return => {desc => "Event describing success or failure", type => "object"}
+    }
+);
+
+sub mark_users_contact_invalid {
+    my ($self, $conn, $auth, $patron_id, $addl_note, $penalty_ou) = @_;
+
+    # This method invalidates an email address or a phone_number which
+    # removes the bad email address or phone number, copying its contents
+    # to a patron note, and institutes a standing penalty for "bad email"
+    # or "bad phone number" which is cleared when the user is saved or
+    # optionally only when the user is saved with an email address or
+    # phone number (or staff manually delete the penalty).
+
+    my $contact_type = ($self->api_name =~ /invalidate.(\w+)(\.|$)/)[0];
+
+    my $e = new_editor(authtoken => $auth, xact => 1);
+    return $e->die_event unless $e->checkauth;
+
+    return OpenILS::Utils::BadContact->mark_users_contact_invalid(
+        $e, $contact_type, {usr => $patron_id},
+        $addl_note, $penalty_ou, $e->requestor->id
+    );
+}
+
 1;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/BadContact.pm b/Open-ILS/src/perlmods/lib/OpenILS/Utils/BadContact.pm
new file mode 100644 (file)
index 0000000..f1784c1
--- /dev/null
@@ -0,0 +1,115 @@
+package OpenILS::Utils::BadContact;
+
+use warnings;
+use strict;
+
+use OpenILS::Event;
+use OpenILS::Utils::CStoreEditor;
+use OpenILS::Utils::Fieldmapper;
+use OpenILS::Application::AppUtils;
+
+my $U = "OpenILS::Application::AppUtils";
+
+our $PENALTY_NAME_MAP = {
+    email => "INVALID_PATRON_EMAIL_ADDRESS",
+    day_phone => "INVALID_PATRON_DAY_PHONE",
+    evening_phone => "INVALID_PATRON_EVENING_PHONE",
+    other_phone => "INVALID_PATRON_OTHER_PHONE"
+};
+
+sub mark_users_contact_invalid {
+    my (
+        $class, $editor, $contact_type, $howfind,
+        $addl_note, $penalty_ou, $staff_id
+    ) = @_;
+
+    if (not ref $howfind eq "HASH") {
+        return new OpenILS::Event(
+            "BAD_PARAMS", note => "howfind argument must be hash"
+        );
+    }
+
+    if (not exists $PENALTY_NAME_MAP->{$contact_type}) {
+        return new OpenILS::Event(
+            "BAD_PARAMS", note => "contact_type argument invalid"
+        );
+    }
+
+    my $penalty_name = $PENALTY_NAME_MAP->{$contact_type};
+
+    # we can find either user-by-id, or user(s)-by-contact-info
+    my $users;
+
+    if (exists $howfind->{usr}) {
+        # just the specified patron
+
+        $users = $editor->search_actor_user({
+            id => $howfind->{usr}, deleted => "f"
+        }) or return $editor->die_event;
+
+    } elsif (exists $howfind->{$contact_type}) {
+        # all users with matching contact data
+
+        $users = $editor->search_actor_user({
+            $contact_type => $howfind->{$contact_type}, deleted => "f"
+        }) or return $editor->die_event;
+    }
+
+    # waste no more time if no users collected
+    return $editor->die_event(new OpenILS::Event("ACTOR_USER_NOT_FOUND"))
+        unless $users and @$users;
+
+    # we'll need this to apply user penalties
+    my $penalty = $editor->search_config_standing_penalty({
+        name => $penalty_name
+    });
+
+    if (not $penalty_ou) {
+        # Fallback to using top of org tree if no penalty_ou provided. This
+        # possibly makes sense in most cases anyway.
+
+        my $results = $editor->json_query({
+            "select" => {"aou" => ["id"]},
+            "from" => {"aou" => {}},
+            "where" => {"parent_ou" => undef}
+        }) or return $editor->die_event;
+
+        $penalty_ou = $results->[0]->{"id"};
+    }
+
+    return $editor->die_event unless $penalty and @$penalty;
+    $penalty = $penalty->[0];
+
+    my $last_xact_id_map = {};
+    my $clear_meth = "clear_$contact_type";
+
+    foreach (@$users) {
+        if ($editor->requestor) {
+            next unless $editor->allowed("UPDATE_USER", $_->home_ou);
+        }
+
+        my $usr_penalty = new Fieldmapper::actor::user_standing_penalty;
+        $usr_penalty->usr($_->id);
+        $usr_penalty->org_unit($penalty_ou);
+        $usr_penalty->standing_penalty($penalty->id);
+        $usr_penalty->staff($staff_id);
+        $usr_penalty->note($_->$contact_type);
+
+        $editor->create_actor_user_standing_penalty($usr_penalty) or
+            return $editor->die_event;
+
+        $_->$clear_meth;
+        $editor->update_actor_user($_) or return $editor->die_event;
+
+        my $updated = $editor->retrieve_actor_user($editor->data);
+        $last_xact_id_map->{$_->id} = $updated->last_xact_id;
+    }
+
+    $editor->commit or return $editor->die_event;
+
+    return new OpenILS::Event(
+        "SUCCESS", payload => {last_xact_id => $last_xact_id_map}
+    );
+}
+
+1;
index 1c71641..62f1e2f 100644 (file)
@@ -145,6 +145,52 @@ INSERT INTO config.standing_penalty (id,name,label,block_list,staff_alert) VALUE
 INSERT INTO config.standing_penalty (id,name,label,block_list,staff_alert) VALUES (28,'STAFF_R','Alerting block on Renew','RENEW', TRUE);
 INSERT INTO config.standing_penalty (id,name,label) VALUES (29,'INVALID_PATRON_ADDRESS','Patron has an invalid address');
 INSERT INTO config.standing_penalty (id,name,label) VALUES (30,'PATRON_IN_COLLECTIONS','Patron has been referred to a collections agency');
+INSERT INTO config.standing_penalty (id, name, label, staff_alert) VALUES
+    (
+        31,
+        'INVALID_PATRON_EMAIL_ADDRESS',
+        oils_i18n_gettext(
+            31,
+            'Patron had an invalid email address',
+            'csp',
+            'label'
+        ),
+        TRUE
+    ),
+    (
+        32,
+        'INVALID_PATRON_DAY_PHONE',
+        oils_i18n_gettext(
+            32,
+            'Patron had an invalid daytime phone number',
+            'csp',
+            'label'
+        ),
+        TRUE
+    ),
+    (
+        33,
+        'INVALID_PATRON_EVENING_PHONE',
+        oils_i18n_gettext(
+            33,
+            'Patron had an invalid evening phone number',
+            'csp',
+            'label'
+        ),
+        TRUE
+    ),
+    (
+        34,
+        'INVALID_PATRON_OTHER_PHONE',
+        oils_i18n_gettext(
+            34,
+            'Patron had an invalid other phone number',
+            'csp',
+            'label'
+        ),
+        TRUE
+    );
+
 
 SELECT SETVAL('config.standing_penalty_id_seq', 100);
 
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.data.mark-email-and-phone-invalid.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.data.mark-email-and-phone-invalid.sql
new file mode 100644 (file)
index 0000000..0e3fb65
--- /dev/null
@@ -0,0 +1,59 @@
+-- Evergreen DB patch XXXX.data.mark-email-and-phone-invalid.sql
+--
+-- Add org unit settings and standing penalty types to support
+-- the mark email/phone invalid features.
+--
+BEGIN;
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+
+INSERT INTO config.standing_penalty (id, name, label, staff_alert) VALUES
+    (
+        31,
+        'INVALID_PATRON_EMAIL_ADDRESS',
+        oils_i18n_gettext(
+            31,
+            'Patron had an invalid email address',
+            'csp',
+            'label'
+        ),
+        TRUE
+    ),
+    (
+        32,
+        'INVALID_PATRON_DAY_PHONE',
+        oils_i18n_gettext(
+            32,
+            'Patron had an invalid daytime phone number',
+            'csp',
+            'label'
+        ),
+        TRUE
+    ),
+    (
+        33,
+        'INVALID_PATRON_EVENING_PHONE',
+        oils_i18n_gettext(
+            33,
+            'Patron had an invalid evening phone number',
+            'csp',
+            'label'
+        ),
+        TRUE
+    ),
+    (
+        34,
+        'INVALID_PATRON_OTHER_PHONE',
+        oils_i18n_gettext(
+            34,
+            'Patron had an invalid other phone number',
+            'csp',
+            'label'
+        ),
+        TRUE
+    );
+
+
+COMMIT;
index 4e7c7fe..554127d 100644 (file)
@@ -46,6 +46,7 @@
 </div>
 
 <div class='hidden'>
+    <div jsId="progressDialog" dojoType="openils.widget.ProgressDialog"></div>
     <span id='true' style='color:green;'>&#x2713;</span>
     <span id='false' style='color:red;'>&#x2717;</span>
     <div dojoType='dijit.Dialog' jsId='allCardsDialog'>
index 3ea7d74..077ff5a 100644 (file)
@@ -173,7 +173,7 @@ if(!dojo._hasResource["openils.Util"]) {
         if(isList && dojo.isArray(val))
             testval = val[0];
         if(e = openils.Event.parse(testval)) {
-            if(eventOk) return e;
+            if(eventOk || e.textcode == 'SUCCESS') return e;
             console.log(e.toString());
 
             // session timed out.  Stop propagation of requests queued by Util.onload 
index 3df6e1b..f6d6587 100644 (file)
@@ -35,5 +35,6 @@
     "ALL_CARDS_ACTIVE" : "Active",
     "ALL_CARDS_PRIMARY" : "Primary",
     "ALL_CARDS_CLOSE" : "Close",
-    "ALL_CARDS_APPLY" : "Apply Changes"
+    "ALL_CARDS_APPLY" : "Apply Changes",
+    "INVALIDATE": "Invalidate"
 }
index f30a3b2..19106c9 100644 (file)
@@ -8,6 +8,7 @@ dojo.require('fieldmapper.IDL');
 dojo.require('openils.PermaCrud');
 dojo.require('openils.widget.AutoGrid');
 dojo.require('openils.widget.AutoFieldWidget');
+dojo.require('openils.widget.ProgressDialog');
 dojo.require('dijit.form.CheckBox');
 dojo.require('dijit.form.Button');
 dojo.require('dojo.date');
@@ -283,6 +284,7 @@ function load() {
         saveCloneButton.attr('disabled', true);
     }
         
+    uUpdateContactInvalidators();
     lock_ready = true;
 }
 
@@ -830,6 +832,17 @@ function fleshFMRow(row, fmcls, args) {
         else if(fieldIdl.datatype == 'timestamp') {
             ftd.appendChild(document.createTextNode(localeStrings.EXAMPLE + dojo.date.locale.format(new Date(1970,0,31),{selector: "date", fullYear: true, datePattern: (orgSettings['format.date'] ? orgSettings['format.date'] : null)})));
         }
+
+        if (fmcls == "au" && (isphone || fmfield == "email")) {
+            var span = dojo.create(
+                "span", {
+                    "className": "hidden",
+                    "id": "wrap_invalidate_" + fmfield
+                }
+            );
+            uGenerateInvalidatorWidget(span, fmfield);
+            ftd.appendChild(span);
+        }
     }
 
     var span = document.createElement('span');
@@ -1652,6 +1665,11 @@ function _uEditSave(doClone) {
                     xulG.unlock_tab();
                     already_locked = false;
                 }
+                /* There's something that seems to just make the form reload
+                 * on all saves, so this uUpdate... isn't needed here after
+                 * all. */
+                //uUpdateContactInvalidators();
+
                 newPatron = openils.Util.readResponse(r);
                 if(newPatron) {
                     uEditUpdateUserSettings(newPatron.id());
@@ -1663,6 +1681,50 @@ function _uEditSave(doClone) {
     );
 }
 
+function uUpdateContactInvalidators() {
+    /* show invalidator buttons for fields that having anything in them */
+    ["email", "day_phone", "evening_phone", "other_phone"].forEach(
+        function(f) {
+            openils.Util[patron[f]() ? "show" : "hide"]("wrap_invalidate_" + f);
+        }
+    );
+}
+
+function uGenerateInvalidatorWidget(container_node, field) {
+    new dijit.form.Button(
+        {
+            "label": localeStrings.INVALIDATE,
+            "scrollOnFocus": false,
+            "onClick": function() {
+                progressDialog.show(true);
+                fieldmapper.standardRequest(
+                    ["open-ils.actor", "open-ils.actor.invalidate." + field], {
+                        "async": true,
+                        "params": [openils.User.authtoken, patron.id()],
+                        "oncomplete": function(r) {
+                            progressDialog.hide();
+                            // alerts on non-success event
+                            var res = openils.Util.readResponse(r);
+
+                            if (res.payload.last_xact_id) {
+                                for (var id in res.payload.last_xact_id) {
+                                    if (patron.id() == id)
+                                        patron.last_xact_id(
+                                            res.payload.last_xact_id[id]
+                                        );
+                                }
+
+                                findWidget("au",field).widget.attr("value","");
+                                openils.Util.hide(container_node);
+                            }
+                        }
+                    }
+                );
+            }
+        }, dojo.create("span", null, container_node, "only")
+    );
+}
+
 function uEditRemoveStage() {
     var resp = fieldmapper.standardRequest(
         ['open-ils.actor', 'open-ils.actor.user.stage.delete'],
index e2bc1bb..81fa664 100644 (file)
 <!ENTITY staff.patron.display_overlay.has_overdues.value "(Has Overdues)">
 <!ENTITY staff.patron.display_overlay.invalid_dob.value "(Invalid Date of Birth)">
 <!ENTITY staff.patron.display_overlay.invalid_address.value "(Invalid Address)">
+<!ENTITY staff.patron.display_overlay.invalid_email.value "(Invalid Email - See Messages)">
+<!ENTITY staff.patron.display_overlay.invalid_phone.value "(Invalid Phone - See Messages)">
 <!ENTITY staff.patron.display_overlay.exit.label "Exit">
 <!ENTITY staff.patron.display_overlay.exit.accesskey "x">
 <!ENTITY staff.patron.display_overlay.search_form.label "Search Form">
index bbd1b5c..085fd5f 100644 (file)
@@ -30,6 +30,8 @@
         <label class="hideme overdues_indicator" value="&staff.patron.display_overlay.has_overdues.value;"/>
         <label class="hideme invalid_dob_indicator" value="&staff.patron.display_overlay.invalid_dob.value;"/>
         <label class="hideme invalid_address_indicator" value="&staff.patron.display_overlay.invalid_address.value;"/>
+        <label class="hideme invalid_email_indicator" value="&staff.patron.display_overlay.invalid_email.value;"/>
+        <label class="hideme invalid_phone_indicator" value="&staff.patron.display_overlay.invalid_phone.value;"/>
     </hbox>
     <vbox id="PatronNotNavBar" flex="1" class="my_bg">
         <hbox id="left_deck_vbox" flex="1" oils_persist="height"> 
index 03b8d41..876e137 100644 (file)
@@ -30,6 +30,8 @@
         <label class="hideme overdues_indicator" value="&staff.patron.display_overlay.has_overdues.value;"/>
         <label class="hideme invalid_dob_indicator" value="&staff.patron.display_overlay.invalid_dob.value;"/>
         <label class="hideme invalid_address_indicator" value="&staff.patron.display_overlay.invalid_address.value;"/>
+        <label class="hideme invalid_email_indicator" value="&staff.patron.display_overlay.invalid_email.value;"/>
+        <label class="hideme invalid_phone_indicator" value="&staff.patron.display_overlay.invalid_phone.value;"/>
     </hbox>
     <hbox id="PatronNotNavBar" flex="1" class="my_bg">
         <vbox id="left_deck_vbox" flex="1" oils_persist="width"> 
index 79c628d..f72d086 100644 (file)
@@ -230,6 +230,8 @@ function handle_remove_penalty(ev) {
                 }
                 */
                 document.getElementById('progress').hidden = true;
+
+                patron.util.set_penalty_css(xulG.patron, patron.display.w.document.documentElement);
             }
         );
         document.getElementById('progress').hidden = false;
@@ -314,6 +316,8 @@ function handle_edit_penalty(ev) {
                                     row_params.row.my.ausp = p;
                                     row_params.row.my.csp = p.standing_penalty();
                                     list.refresh_row( row_params );
+
+                                    patron.util.set_penalty_css(xulG.patron, patron.display.w.document.documentElement);
                                     document.getElementById('progress').hidden = true;
                                 } catch(E) {
                                     alert(E);
@@ -376,6 +380,8 @@ function handle_archive_penalty(ev) {
                             }
                             if (--outstanding_requests==0) {
                                 document.getElementById('progress').hidden = true;
+
+                                patron.util.set_penalty_css(xulG.patron, patron.display.w.document.documentElement);
                             }
                         }
                     }(ids[i])
index 78c2707..19aab0f 100644 (file)
@@ -647,6 +647,10 @@ patron.util.set_penalty_css = function(patron) {
         removeCSSClass(document.documentElement,'NO_PENALTIES');
         removeCSSClass(document.documentElement,'ONE_PENALTY');
         removeCSSClass(document.documentElement,'MULTIPLE_PENALTIES');
+        removeCSSClass(document.documentElement,'INVALID_PATRON_EMAIL_ADDRESS');
+        removeCSSClass(document.documentElement,'INVALID_PATRON_DAY_PHONE');
+        removeCSSClass(document.documentElement,'INVALID_PATRON_EVENING_PHONE');
+        removeCSSClass(document.documentElement,'INVALID_PATRON_OTHER_PHONE');
         removeCSSClass(document.documentElement,'PATRON_HAS_ALERT');
         removeCSSClass(document.documentElement,'PATRON_BARRED');
         removeCSSClass(document.documentElement,'PATRON_INACTIVE');
@@ -690,8 +694,12 @@ patron.util.set_penalty_css = function(patron) {
         data.last_patron = patron.id(); data.stash('last_patron');
         */
 
-        var penalties = patron.standing_penalties();
-        if (!penalties) { penalties = []; }
+        var penalties = patron.standing_penalties() || [];
+        penalties = penalties.filter(
+            function(p) {
+                return (!(p.isdeleted() || p.stop_date()));
+            }
+        );
         for (var i = 0; i < penalties.length; i++) {
             /* this comes from /opac/common/js/utils.js */
             addCSSClass(document.documentElement,penalties[i].standing_penalty().name());
index 8298d4b..3356c18 100644 (file)
@@ -55,6 +55,11 @@ row#row_billing_zip { padding-bottom: 10px; }
 
 .PATRON_HAS_INVALID_ADDRESS label.invalid_address_indicator { display: inline; color: #CC3300 }
 
+.INVALID_PATRON_EMAIL_ADDRESS label.invalid_email_indicator { display: inline; color: #003399 }
+.INVALID_PATRON_DAY_PHONE label.invalid_phone_indicator { display: inline; color: #003399 }
+.INVALID_PATRON_EVENING_PHONE label.invalid_phone_indicator { display: inline; color: #003399 }
+.INVALID_PATRON_OTHER_PHONE label.invalid_phone_indicator { display: inline; color: #003399 }
+
 .PATRON_HAS_ALERT .patronNameLarge { border: solid thick yellow; padding: 10px }
 .PATRON_HAS_ALERT groupbox.alert { background-color: yellow; color: black; }
 .PATRON_HAS_ALERT [id^=patron_alert] { background: yellow; color: black; }