LP#1715767: Allow others to use my account (privacy waiver)
authorJeff Davis <jdavis@sitka.bclibraries.ca>
Wed, 7 Feb 2018 23:11:18 +0000 (15:11 -0800)
committerChris Sharp <csharp@georgialibraries.org>
Thu, 7 Feb 2019 22:28:16 +0000 (17:28 -0500)
Use case: Jane Doe has a hold ready for pickup but is unable to come
into the library.  Her husband John Doe goes to the library to pick up
the hold on her behalf.  His name is listed on Jane's account, so
library staff know it's okay to check out the book on Jane's account and
give it to John.

This commit adds a new table which lists the names of people who are
allowed to place holds, pick up holds, check out items, or view
borrowing history for a user account.  Staff can add, edit, or remove
entries via the patron editor in the web client; patrons can do so in My
Account.  The entries are not linked to other user accounts and they do
not add any extra functionality.  They are essentially special patron
notes for circulation staff.

Signed-off-by: Jeff Davis <jdavis@sitka.bclibraries.ca>
Signed-off-by: Chris Sharp <csharp@georgialibraries.org>

13 files changed:
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm
Open-ILS/src/sql/Pg/005.schema.actors.sql
Open-ILS/src/sql/Pg/950.data.seed-values.sql
Open-ILS/src/sql/Pg/upgrade/XXXX.data.privacy_waiver.sql [new file with mode: 0644]
Open-ILS/src/sql/Pg/upgrade/XXXX.schema.actor.privacy_waiver.sql [new file with mode: 0644]
Open-ILS/src/templates/opac/myopac/prefs_settings.tt2
Open-ILS/src/templates/staff/circ/patron/t_edit.tt2
Open-ILS/src/templates/staff/circ/patron/t_summary.tt2
Open-ILS/web/js/ui/default/staff/circ/patron/regctl.js
Open-ILS/web/js/ui/default/staff/services/user.js
docs/RELEASE_NOTES_NEXT/Circulation/privacy_waiver.adoc [new file with mode: 0644]

index c66eaad..b8d0eff 100644 (file)
@@ -2320,6 +2320,36 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
             </actions>
         </permacrud>
        </class>
+       <class id="aupw" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="actor::usr_privacy_waiver" oils_persist:tablename="actor.usr_privacy_waiver" reporter:label="Privacy Waiver">
+               <fields oils_persist:primary="id" oils_persist:sequence="actor.usr_privacy_waiver_id_seq">
+                       <field reporter:label="ID" name="id" reporter:datatype="id" />
+                       <field reporter:label="User" name="usr" reporter:datatype="link" />
+                       <field reporter:label="Name" name="name" reporter:datatype="text"/>
+                       <field reporter:label="Place Holds?" name="place_holds" reporter:datatype="bool" />
+                       <field reporter:label="Pick Up Holds?" name="pickup_holds" reporter:datatype="bool" />
+                       <field reporter:label="View Borrowing History?" name="view_history" reporter:datatype="bool" />
+                       <field reporter:label="Check Out Items?" name="checkout_items" reporter:datatype="bool" />
+               </fields>
+               <links>
+                       <link field="usr" reltype="has_a" key="id" map="" class="au"/>
+               </links>
+               <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+                       <actions>
+                               <create permission="UPDATE_USER">
+                                       <context link="usr" field="home_ou"/>
+                               </create>
+                               <retrieve permission="VIEW_USER">
+                                       <context link="usr" field="home_ou"/>
+                               </retrieve>
+                               <update permission="UPDATE_USER">
+                                       <context link="usr" field="home_ou"/>
+                               </update>
+                               <delete permission="UPDATE_USER">
+                                       <context link="usr" field="home_ou"/>
+                               </delete>
+                       </actions>
+               </permacrud>
+       </class>
        <class id="aupr" controller="open-ils.cstore" oils_obj:fieldmapper="actor::usr_password_reset" oils_persist:tablename="actor.usr_password_reset" reporter:label="User password reset requests">
                <fields oils_persist:primary="id" oils_persist:sequence="actor.usr_password_reset_id_seq">
                        <field reporter:label="Request ID" name="id" reporter:datatype="id"/>
@@ -3601,6 +3631,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <field reporter:label="Standing Penalties" name="standing_penalties" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Statistical Category Entries" name="stat_cat_entries" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Survey Responses" name="survey_responses" oils_persist:virtual="true" reporter:datatype="link"/>
+                       <field reporter:label="Privacy Waiver Entries" name = "waiver_entries" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Workstation Org Unit" name="ws_ou" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Workstation ID" name="wsid" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Active" name="active" reporter:datatype="bool"/>
@@ -3676,6 +3707,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <link field="card" reltype="has_a" key="id" map="" class="ac"/>
                        <link field="ident_type2" reltype="has_a" key="id" map="" class="cit"/>
                        <link field="stat_cat_entries" reltype="has_many" key="target_usr" map="" class="actscecm"/>
+                       <link field="waiver_entries" reltype="has_many" key="usr" map="" class="aupw"/>
                        <link field="groups" reltype="has_many" key="usr" map="grp" class="pugm"/>
                        <link field="usrgroup" reltype="has_many" key="usrgroup" map="" class="au"/>
                        <link field="checkouts" reltype="has_many" key="usr" map="" class="circ"/>
index c2b701a..219f611 100644 (file)
@@ -115,6 +115,78 @@ sub update_user_setting {
 
 
 __PACKAGE__->register_method(
+    method    => "update_privacy_waiver",
+    api_name  => "open-ils.actor.patron.privacy_waiver.update",
+    signature => {
+        desc => "Replaces any existing privacy waiver entries for the patron with the supplied values.",
+        params => [
+            {desc => 'Authentication token', type => 'string'},
+            {desc => 'User ID', type => 'number'},
+            {desc => 'Arrayref of privacy waiver entries', type => 'object'}
+        ],
+        return => {desc => '1 on success, Event on error'}
+    }
+);
+sub update_privacy_waiver {
+    my($self, $conn, $auth, $user_id, $waiver) = @_;
+    my $e = new_editor(xact => 1, authtoken => $auth);
+    return $e->die_event unless $e->checkauth;
+
+    $user_id = $e->requestor->id unless defined $user_id;
+
+    unless($e->requestor->id == $user_id) {
+        my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
+        return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
+    }
+
+    foreach my $w (@$waiver) {
+        $w->{usr} = $user_id unless $w->{usr};
+        if ($w->{id} && $w->{id} ne 'new') {
+            my $existing_rows = $e->search_actor_usr_privacy_waiver({usr => $user_id, id => $w->{id}});
+            if ($existing_rows) {
+                my $existing = $existing_rows->[0];
+                # delete existing if name is empty
+                if (!$w->{name} or $w->{name} =~ /^\s*$/) {
+                    $e->delete_actor_usr_privacy_waiver($existing) or return $e->die_event;
+
+                # delete existing if none of the boxes were checked
+                } elsif (!$w->{place_holds} && !$w->{pickup_holds} && !$w->{checkout_items} && !$w->{view_history}) {
+                    $e->delete_actor_usr_privacy_waiver($existing) or return $e->die_event;
+
+                # otherwise, update existing waiver entry
+                } else {
+                    $existing->name($w->{name});
+                    $existing->place_holds($w->{place_holds});
+                    $existing->pickup_holds($w->{pickup_holds});
+                    $existing->checkout_items($w->{checkout_items});
+                    $existing->view_history($w->{view_history});
+                    $e->update_actor_usr_privacy_waiver($existing) or return $e->die_event;
+                }
+            } else {
+                $logger->warn("No privacy waiver entry found for user $user_id with ID " . $w->{id});
+            }
+
+        } else {
+            # ignore new entries with empty name or with no boxes checked
+            next if (!$w->{name} or $w->{name} =~ /^\s*$/);
+            next if (!$w->{place_holds} && !$w->{pickup_holds} && !$w->{checkout_items} && !$w->{view_history});
+            my $new = Fieldmapper::actor::usr_privacy_waiver->new;
+            $new->usr($w->{usr});
+            $new->name($w->{name});
+            $new->place_holds($w->{place_holds});
+            $new->pickup_holds($w->{pickup_holds});
+            $new->checkout_items($w->{checkout_items});
+            $new->view_history($w->{view_history});
+            $e->create_actor_usr_privacy_waiver($new) or return $e->die_event;
+        }
+    }
+
+    $e->commit;
+    return 1;
+}
+
+
+__PACKAGE__->register_method(
     method    => "set_ou_settings",
     api_name  => "open-ils.actor.org_unit.settings.update",
     signature => {
@@ -444,6 +516,9 @@ sub update_patron {
     ( $new_patron, $evt ) = _add_update_cards($e, $patron, $new_patron);
     return $evt if $evt;
 
+    ( $new_patron, $evt ) = _add_update_waiver_entries($e, $patron, $new_patron);
+    return $evt if $evt;
+
     ( $new_patron, $evt ) = _add_survey_responses($e, $patron, $new_patron);
     return $evt if $evt;
 
@@ -540,6 +615,7 @@ sub flesh_user {
         "billing_address",
         "mailing_address",
         "stat_cat_entries",
+        "waiver_entries",
         "settings",
         "usr_activity"
     ];
@@ -568,6 +644,7 @@ sub _clone_patron {
     $new_patron->clear_ischanged();
     $new_patron->clear_isdeleted();
     $new_patron->clear_stat_cat_entries();
+    $new_patron->clear_waiver_entries();
     $new_patron->clear_permissions();
     $new_patron->clear_standing_penalties();
 
@@ -888,6 +965,32 @@ sub _update_card {
 }
 
 
+sub _add_update_waiver_entries {
+    my $e = shift;
+    my $patron = shift;
+    my $new_patron = shift;
+    my $evt;
+
+    my $waiver_entries = $patron->waiver_entries();
+    for my $waiver (@$waiver_entries) {
+        next unless ref $waiver;
+        $waiver->usr($new_patron->id());
+        if ($waiver->isnew()) {
+            next if (!$waiver->name() or $waiver->name() =~ /^\s*$/);
+            next if (!$waiver->place_holds() && !$waiver->pickup_holds() && !$waiver->checkout_items() && !$waiver->view_history());
+            $logger->info("Adding new patron waiver entry");
+            $waiver->clear_id();
+            $e->create_actor_usr_privacy_waiver($waiver) or return (undef, $e->die_event);
+        } elsif ($waiver->ischanged()) {
+            $logger->info("Updating patron waiver entry " . $waiver->id);
+            $e->update_actor_usr_privacy_waiver($waiver) or return (undef, $e->die_event);
+        } elsif ($waiver->isdeleted()) {
+            $logger->info("Deleting patron waiver entry " . $waiver->id);
+            $e->delete_actor_usr_privacy_waiver($waiver) or return (undef, $e->die_event);
+        }
+    }
+    return ($new_patron, undef);
+}
 
 
 # returns event on error.  returns undef otherwise
@@ -3007,6 +3110,7 @@ sub user_retrieve_fleshed_by_id {
         "billing_address",
         "mailing_address",
         "stat_cat_entries",
+        "waiver_entries",
         "usr_activity" ];
     return new_flesh_user($user_id, $fields, $e);
 }
index d78b59b..19e8fbc 100644 (file)
@@ -34,7 +34,7 @@ sub prepare_extended_user_info {
         {
             flesh => 1,
             flesh_fields => {
-                au => [qw/card home_ou addresses ident_type billing_address/, @extra_flesh]
+                au => [qw/card home_ou addresses ident_type billing_address waiver_entries/, @extra_flesh]
                 # ...
             }
         }
@@ -603,6 +603,9 @@ sub load_myopac_prefs_settings {
         }
     }
 
+    my $use_privacy_waiver = $self->ctx->{get_org_setting}->(
+        $e->requestor->home_ou, 'circ.privacy_waiver');
+
     return Apache2::Const::OK
         unless $self->cgi->request_method eq 'POST';
 
@@ -699,8 +702,52 @@ sub load_myopac_prefs_settings {
         'open-ils.actor.patron.settings.update',
         $self->editor->authtoken, undef, \%settings);
 
-    # re-fetch user prefs
     $self->ctx->{updated_user_settings} = \%settings;
+
+    if ($use_privacy_waiver) {
+        my %waiver;
+        my $saved_entries = ();
+        my @waiver_types = qw/place_holds pickup_holds checkout_items view_history/;
+
+        # initialize our waiver hash with waiver IDs from hidden input
+        # (this ensures that we capture entries with no checked boxes)
+        foreach my $waiver_row_id ($self->cgi->param("waiver_id")) {
+            $waiver{$waiver_row_id} = {};
+        }
+
+        # process our waiver checkboxes into a hash, keyed by waiver ID
+        # (a new entry, if any, has id = 'new')
+        foreach my $waiver_type (@waiver_types) {
+            if ($self->cgi->param("waiver_$waiver_type")) {
+                foreach my $waiver_id ($self->cgi->param("waiver_$waiver_type")) {
+                    # ensure this waiver exists in our hash
+                    $waiver{$waiver_id} = {} if !$waiver{$waiver_id};
+                    $waiver{$waiver_id}->{$waiver_type} = 1;
+                }
+            }
+        }
+
+        foreach my $k (keys %waiver) {
+            my $w = $waiver{$k};
+            # get name from textbox
+            $w->{name} = $self->cgi->param("waiver_name_$k");
+            $w->{id} = $k;
+            foreach (@waiver_types) {
+                $w->{$_} = 0 unless ($w->{$_});
+            }
+            push @$saved_entries, $w;
+        }
+
+        # update patron privacy waiver entries
+        $U->simplereq(
+            'open-ils.actor',
+            'open-ils.actor.patron.privacy_waiver.update',
+            $self->editor->authtoken, undef, $saved_entries);
+
+        $self->ctx->{updated_waiver_entries} = $saved_entries;
+    }
+
+    # re-fetch user prefs
     return $self->_load_user_with_prefs || Apache2::Const::OK;
 }
 
index c387967..c97afd7 100644 (file)
@@ -1265,5 +1265,15 @@ BEGIN
 END;
 $FUNC$ LANGUAGE PLPGSQL;
 
+CREATE TABLE actor.usr_privacy_waiver (
+    id BIGSERIAL PRIMARY KEY,
+    usr BIGINT NOT NULL REFERENCES actor.usr(id) DEFERRABLE INITIALLY DEFERRED,
+    name TEXT NOT NULL,
+    place_holds BOOL DEFAULT FALSE,
+    pickup_holds BOOL DEFAULT FALSE,
+    view_history BOOL DEFAULT FALSE,
+    checkout_items BOOL DEFAULT FALSE
+);
+CREATE INDEX actor_usr_privacy_waiver_usr_idx ON actor.usr_privacy_waiver (usr);
 
 COMMIT;
index 649ced5..1fe0bb6 100644 (file)
@@ -19495,3 +19495,18 @@ VALUES (
 
 
 
+INSERT INTO config.org_unit_setting_type
+    (name, label, description, grp, datatype)
+    VALUES (
+        'circ.privacy_waiver',
+        oils_i18n_gettext('circ.privacy_waiver',
+            'Allow others to use patron account (privacy waiver)',
+            'coust', 'label'),
+        oils_i18n_gettext('circ.privacy_waiver',
+            'Add a note to a user account indicating that specified people are allowed to ' ||
+            'place holds, pick up holds, check out items, or view borrowing history for that user account',
+            'coust', 'description'),
+        'circ',
+        'bool'
+    );
+
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.data.privacy_waiver.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.data.privacy_waiver.sql
new file mode 100644 (file)
index 0000000..ab8a6c2
--- /dev/null
@@ -0,0 +1,21 @@
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+INSERT INTO config.org_unit_setting_type
+    (name, label, description, grp, datatype)
+    VALUES (
+        'circ.privacy_waiver',
+        oils_i18n_gettext('circ.privacy_waiver',
+            'Allow others to use patron account (privacy waiver)',
+            'coust', 'label'),
+        oils_i18n_gettext('circ.privacy_waiver',
+            'Add a note to a user account indicating that specified people are allowed to ' ||
+            'place holds, pick up holds, check out items, or view borrowing history for that user account',
+            'coust', 'description'),
+        'circ',
+        'bool'
+    );
+
+COMMIT;
+
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.actor.privacy_waiver.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.actor.privacy_waiver.sql
new file mode 100644 (file)
index 0000000..6b2b7b8
--- /dev/null
@@ -0,0 +1,17 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('XXXX');
+
+CREATE TABLE actor.usr_privacy_waiver (
+    id BIGSERIAL PRIMARY KEY,
+    usr BIGINT NOT NULL REFERENCES actor.usr(id) DEFERRABLE INITIALLY DEFERRED,
+    name TEXT NOT NULL,
+    place_holds BOOL DEFAULT FALSE,
+    pickup_holds BOOL DEFAULT FALSE,
+    view_history BOOL DEFAULT FALSE,
+    checkout_items BOOL DEFAULT FALSE
+);
+CREATE INDEX actor_usr_privacy_waiver_usr_idx ON actor.usr_privacy_waiver (usr);
+
+COMMIT;
+
index 415dcec..70f76a9 100644 (file)
@@ -23,7 +23,7 @@
                   <input type='hidden' name='history_delete_confirmed' value='1'/>
                   <input type="submit" value="[% l('Confirm') %]" class="opac-button"/>
                 </td></tr>
-                [% ELSIF ctx.updated_user_settings %]
+                [% ELSIF ctx.updated_user_settings OR ctx.updated_waiver_entries %]
                 <tr><td colspan='2'>
                     <div class='renew-summary'>
                         [% l('Account Successfully Updated') %]
                 </tr>
                 [% END %]
 
+                [%- IF ctx.get_org_setting(ctx.user.home_ou.id, 'circ.privacy_waiver'); %]
+                <tr>
+                    <td>[% l('Allow others to use my account') %]</td>
+                    <td>
+                        [% FOR waiver IN ctx.user.waiver_entries %]
+                        <div id="waiver_[% waiver.id %]">
+                            <input type="hidden" name="waiver_id" value="[% waiver.id %]"/>
+                            [% l('Name:') %] <input type="textbox" name="waiver_name_[% waiver.id %]" value="[% waiver.name | html %]"/><br/>
+                            <label>
+                                <input type="checkbox" name="waiver_place_holds" 
+                                    value="[% waiver.id %]" [% waiver.place_holds == 't' ? 'checked="checked"' : '' %]/>
+                                [% l('Place Holds') %]
+                            </label>
+                            <label>
+                                <input type="checkbox" name="waiver_pickup_holds"
+                                    value="[% waiver.id %]" [% waiver.pickup_holds == 't' ? 'checked="checked"' : '' %]/>
+                                [% l('Pick Up Holds') %]
+                            </label>
+                            <label>
+                                <input type="checkbox" name="waiver_checkout_items"
+                                    value="[% waiver.id %]" [% waiver.checkout_items == 't' ? 'checked="checked"' : '' %]/>
+                                [% l('Check Out Items') %]
+                            </label>
+                            <label>
+                                <input type="checkbox" name="waiver_view_history"
+                                    value="[% waiver.id %]" [% waiver.view_history == 't' ? 'checked="checked"' : '' %]/>
+                                [% l('View Borrowing History') %]
+                            </label>
+                        </div>
+                        [% END %]
+                        <br/>
+                        <div>
+                            [% l('Name:') %] <input type="textbox" name="waiver_name_new"/><br/>
+                            <label><input type="checkbox" name="waiver_place_holds" value="new"/>[% l('Place Holds') %]</label>
+                            <label><input type="checkbox" name="waiver_pickup_holds" value="new"/>[% l('Pick Up Holds') %]</label>
+                            <label><input type="checkbox" name="waiver_checkout_items" value="new"/>[% l('Check Out Items') %]</label>
+                            <label><input type="checkbox" name="waiver_view_history" value="new"/>[% l('View Borrowing History') %]</label>
+                        </div>
+                    </td>
+                </tr>
+                [% END %]
+
                 <!--
                 <tr>
                     <td><label for='prefs_def_font'>[% l("Default Font Size") %]</label></td>
index 05e0d94..f61f5ee 100644 (file)
@@ -803,6 +803,55 @@ within the "form" by name for validation.
   </div>
 </div>
 
+<div class="row reg-field-row" ng-if="org_settings['circ.privacy_waiver']">
+  <div class="col-md-3 reg-field-label">
+    <label>[% l('Allow others to use my account') %]</label>
+  </div>
+  <div class="col-md-3 reg-field-input">
+    <div class="row" ng-repeat="waiver_entry in patron.waiver_entries" ng-hide="waiver_entry.isdeleted">
+      <div class="row flex-row">
+        <div class="flex-cell">
+          <input ng-change="field_modified()"
+            type='text' ng-model="waiver_entry.name"/>
+        </div>
+        <div class="flex-cell">
+          <button type="button" 
+            ng-click="field_modified();delete_waiver_entry(waiver_entry)" 
+            class="btn btn-danger">[% l('X') %]</button>
+        </div>
+      </div>
+      <div class="row flex-row reg-field-input">
+        <div class="flex-cell">
+          <label><input ng-change="field_modified()"
+            type='checkbox' ng-model="waiver_entry.place_holds"/>
+            [% l('Place Holds?') %]</label>
+        </div>
+        <div class="flex-cell">
+          <label><input ng-change="field_modified()"
+            type='checkbox' ng-model="waiver_entry.pickup_holds"/>
+            [% l('Pick Up Holds?') %]</label>
+        </div>
+        <div class="flex-cell">
+          <label><input ng-change="field_modified()"
+            type='checkbox' ng-model="waiver_entry.view_history"/>
+            [% l('View Borrowing History?') %]</label>
+        </div>
+        <div class="flex-cell">
+          <label><input ng-change="field_modified()"
+            type='checkbox' ng-model="waiver_entry.checkout_items"/>
+            [% l('Check Out Items?') %]</label>
+        </div>
+      </div> <!-- end checkboxes -->
+    </div> <!-- end ng-repeat waiver_entry -->
+    <div class="row">
+      <div class="col-md-3 reg-field-input">
+      <button type="button" ng-click="new_waiver_entry()" 
+        class="btn btn-success">[% l('Add Person') %]</button>
+      </div>
+    </div>
+  </div> <!-- end waiver entries input -->
+</div> <!-- end waiver entries row -->
+
 </div> <!-- end offline test -->
 
 <!-- addresses -->
index 11cf06a..7963ca0 100644 (file)
       <div class="col-md-5">[% l('Name Keywords') %]</div>
       <div class="col-md-7">{{patron().name_keywords()}}</div>
     </div>
+    <div class="row patron-summary-divider" ng-if="patron().waiver_entries().length > 0">
+      [% l('Allow others to use my account') %]
+    </div>
+    <div class="row" ng-repeat="waiver in patron().waiver_entries()">
+      <div class="col-md-5">{{waiver.name()}}</div>
+      <div class="col-md-7">
+        <ul>
+          <li ng-show="waiver.place_holds() == 't'">[% l('Place holds') %]</li>
+          <li ng-show="waiver.pickup_holds() == 't'">[% l('Pick up holds') %]</li>
+          <li ng-show="waiver.view_history() == 't'">[% l('View borrowing history') %]</li>
+          <li ng-show="waiver.checkout_items() == 't'">[% l('Check out items') %]</li>
+        </ul>
+      </div>
+    </div>
   </div>
 
   <div class="row" ng-repeat="addr in patron().addresses()">
index 4e2e9a2..e33933a 100644 (file)
@@ -334,6 +334,7 @@ angular.module('egCoreMod')
             'ui.patron.registration.require_address',
             'circ.holds.behind_desk_pickup_supported',
             'circ.patron_edit.clone.copy_address',
+            'circ.privacy_waiver',
             'ui.patron.edit.au.prefix.require',
             'ui.patron.edit.au.prefix.show',
             'ui.patron.edit.au.prefix.suggest',
@@ -726,6 +727,13 @@ angular.module('egCoreMod')
         addr.pending = addr.pending === 't';
     }
 
+    service.ingest_waiver_entry = function(patron, waiver_entry) {
+        waiver_entry.place_holds = waiver_entry.place_holds == 't';
+        waiver_entry.pickup_holds = waiver_entry.pickup_holds == 't';
+        waiver_entry.view_history = waiver_entry.view_history == 't';
+        waiver_entry.checkout_items = waiver_entry.checkout_items == 't';
+    }
+
     /*
      * Existing patron objects reqire some data munging before insertion
      * into the scope.
@@ -773,6 +781,9 @@ angular.module('egCoreMod')
             }
         });
 
+        angular.forEach(patron.waiver_entries,
+            function(waiver_entry) { service.ingest_waiver_entry(patron, waiver_entry) });
+
         service.get_linked_addr_users(patron.addresses);
 
         // Remove stat cat entries that link to out-of-scope stat
@@ -824,6 +835,7 @@ angular.module('egCoreMod')
             cards : [card],
             home_ou : egCore.org.get(egCore.auth.user().ws_ou()),
             stat_cat_entries : [],
+            waiver_entries : [],
             groups : [],
             addresses : [addr]
         };
@@ -1137,6 +1149,15 @@ angular.module('egCoreMod')
             patron.stat_cat_entries().push(newmap);
         });
 
+        var waiver_hashes = patron.waiver_entries();
+        patron.waiver_entries([]);
+        angular.forEach(waiver_hashes, function(waiver_hash) {
+            if (!waiver_hash.isnew && !waiver_hash.isdeleted)
+                waiver_hash.ischanged = true;
+            var waiver_entry = egCore.idl.fromHash('aupw', waiver_hash);
+            patron.waiver_entries().push(waiver_entry);
+        });
+
         if (!patron.isnew()) patron.ischanged(true);
 
         return egCore.net.request(
@@ -1634,6 +1655,24 @@ function($scope , $routeParams , $q , $uibModal , $window , egCore ,
         });
     }
 
+    $scope.new_waiver_entry = function() {
+        var waiver = egCore.idl.toHash(new egCore.idl.aupw());
+        patronRegSvc.ingest_waiver_entry($scope.patron, waiver);
+        waiver.id = patronRegSvc.virt_id--;
+        waiver.isnew = true;
+        $scope.patron.waiver_entries.push(waiver);
+    }
+
+    deleted_waiver_entries = [];
+    $scope.delete_waiver_entry = function(waiver_entry) {
+        if (waiver_entry.id > 0) {
+            waiver_entry.isdeleted = true;
+            deleted_waiver_entries.push(waiver_entry);
+        }
+        var index = $scope.patron.waiver_entries.indexOf(waiver_entry);
+        $scope.patron.waiver_entries.splice(index, 1);
+    }
+
     $scope.replace_card = function() {
         $scope.patron.card.active = false;
         $scope.patron.card.ischanged = true;
@@ -2059,6 +2098,10 @@ function($scope , $routeParams , $q , $uibModal , $window , egCore ,
         $scope.patron.addresses = 
             $scope.patron.addresses.concat(deleted_addresses);
         
+        // ditto for waiver entries
+        $scope.patron.waiver_entries = 
+            $scope.patron.waiver_entries.concat(deleted_waiver_entries);
+
         compress_hold_notify();
 
         var updated_user;
index ccd1d0a..80b2838 100644 (file)
@@ -17,6 +17,7 @@ function($q,  $timeout,  egNet,  egAuth,  egOrg) {
             'billing_address',                                                     
             'mailing_address',                                                     
             'stat_cat_entries',                                                    
+            'waiver_entries',
             'usr_activity',
             'notes'
         ]
diff --git a/docs/RELEASE_NOTES_NEXT/Circulation/privacy_waiver.adoc b/docs/RELEASE_NOTES_NEXT/Circulation/privacy_waiver.adoc
new file mode 100644 (file)
index 0000000..d59f652
--- /dev/null
@@ -0,0 +1,15 @@
+Allow Others to Use My Account (Privacy Waiver)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Patrons who wish to authorize other people to use their account may
+now do so via My Account.  In the Search and History Preferences tab
+under Account Preferences, a new section labeled "Allow others to use
+my account" allows patrons to enter a name and indicate that the
+specified person is allowed to place holds, pickup holds, view
+borrowing history, or check out items on their account.  This
+information is displayed to circulation staff in the patron account
+summary in the web client.  (Staff may also add, edit, and remove
+entries via the patron editor.)
+
+A new library setting, "Allow others to use patron account (privacy
+waiver)," is used to enable or disable this feature.
+