[open-ils-commits] [GIT] Evergreen ILS branch master updated. c820aa492739c544e09a7b646a09b8507ae3993b

Evergreen Git git at git.evergreen-ils.org
Wed Aug 24 18:03:42 EDT 2016


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Evergreen ILS".

The branch, master has been updated
       via  c820aa492739c544e09a7b646a09b8507ae3993b (commit)
       via  33746b32ffab1c7d5e530df8acc9336c2b15859f (commit)
       via  92f1bdb26d0027b52b3b3db84ff3d52104d6b1fe (commit)
       via  4d512bfac53dfdc94696f1731bbae6e50542bccc (commit)
       via  f8422d9abd41f9b10b1b1cc4cc824e0eaef5b84e (commit)
       via  9d1d36d3d56dcbe46d462ce8e431911df7b7166b (commit)
       via  aa47fc5fdc1a5a9f3889fc3f67588739ba9e1700 (commit)
       via  f3d2d070e8ca3a8b12a820489ee6caff8dc1c7e9 (commit)
       via  7e3a8e2c61f4ee24317d11d478930851671b1c1d (commit)
       via  53fc56475feedb6433a28f5c49096de7a46d87a9 (commit)
       via  b09ad8fd9ccf37b374ddf9840cf20fc63f45a624 (commit)
      from  be0ed357743e8b36f13fe38a9127dc8bd0f4172e (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit c820aa492739c544e09a7b646a09b8507ae3993b
Author: Mike Rylander <mrylander at gmail.com>
Date:   Wed Aug 24 18:01:33 2016 -0400

    Stamping upgrade for email receipts
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql
index d0fc23a..7e098c0 100644
--- a/Open-ILS/src/sql/Pg/002.schema.config.sql
+++ b/Open-ILS/src/sql/Pg/002.schema.config.sql
@@ -91,7 +91,7 @@ CREATE TRIGGER no_overlapping_deps
     BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
     FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
 
-INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0995', :eg_version); -- kmlussier/miker
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0996', :eg_version); -- gmcharlt/miker
 
 CREATE TABLE config.bib_source (
 	id		SERIAL	PRIMARY KEY,
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.data.email-checkout-receipt.sql b/Open-ILS/src/sql/Pg/upgrade/0996.data.email-checkout-receipt.sql
similarity index 97%
rename from Open-ILS/src/sql/Pg/upgrade/XXXX.data.email-checkout-receipt.sql
rename to Open-ILS/src/sql/Pg/upgrade/0996.data.email-checkout-receipt.sql
index eaeaf36..961c87b 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.data.email-checkout-receipt.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/0996.data.email-checkout-receipt.sql
@@ -1,5 +1,7 @@
 BEGIN;
 
+SELECT evergreen.upgrade_deps_block_check('0996', :eg_version);
+
 INSERT INTO config.usr_setting_type (
     name,
     opac_visible,

commit 33746b32ffab1c7d5e530df8acc9336c2b15859f
Author: Galen Charlton <gmc at esilibrary.com>
Date:   Mon Aug 22 18:08:37 2016 -0400

    LP#1356477: release notes for email checkout receipts
    
    A portion of the text was written by Angela Kilsdonk.
    
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/docs/RELEASE_NOTES_NEXT/Circulation/email_cko_receipts.adoc b/docs/RELEASE_NOTES_NEXT/Circulation/email_cko_receipts.adoc
new file mode 100644
index 0000000..5383bfc
--- /dev/null
+++ b/docs/RELEASE_NOTES_NEXT/Circulation/email_cko_receipts.adoc
@@ -0,0 +1,20 @@
+Email Checkout Receipts
+^^^^^^^^^^^^^^^^^^^^^^^
+This feature allows patrons to receive checkout receipts through email
+at the circulation desk and in the Evergreen self-checkout interface.
+Patrons need to opt in to receive email receipts by default and must
+have an email address associated with their account. Opt in can be staff
+mediated at the time of account creation or in existing accounts.
+Patrons can also opt in directly in their OPAC account or through patron
+self-registration. This feature does not affect the behavior of
+checkouts from SIP2 devices.
+
+Patrons can opt in to receipt email checkout receipts by default via
+a new "Email checkout reciepts by default" patron setting.
+
+This feature also enhances the patron staging tables so that patron
+settings can be chosen during self-registration.
+
+The web staff interface's checkout screen now includes a "Quick
+Receipt" button that allows staff members to generate a receipt
+at any time.

commit 92f1bdb26d0027b52b3b3db84ff3d52104d6b1fe
Author: Mike Rylander <mrylander at gmail.com>
Date:   Thu Aug 4 10:38:48 2016 -0400

    LP#1356477: update selfcheck interface
    
    This patch replaces the Logout and Logout with Receipt buttons in
    the selfcheck interface with a single Logout button and a set of radio
    buttons (whose labels are clickable) that allow the user to specify
    whether they want an email receipt, a print receipt, or no receipt upon
    logging out.
    
    If the user has no email address, the option to select email receipts
    will not be presented to them.
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>

diff --git a/Open-ILS/src/templates/circ/selfcheck/summary.tt2 b/Open-ILS/src/templates/circ/selfcheck/summary.tt2
index 237c345..7b1487c 100644
--- a/Open-ILS/src/templates/circ/selfcheck/summary.tt2
+++ b/Open-ILS/src/templates/circ/selfcheck/summary.tt2
@@ -1,8 +1,13 @@
 <div id='oils-selfck-circ-info-div'>
     <div id='oils-selfck-info-nav'>
         <span><a id='oils-selfck-nav-home' href='javascript:void(0);' class='selected'><button type="button" class="self-button">[% l('Home') %]</button></a></span>
-        <span><a id='oils-selfck-nav-logout-print' href='javascript:void(0);'><button type="button" class="self-button">[% l('Logout') %]</button></a></span>
-        <span><a id='oils-selfck-nav-logout' href='javascript:void(0);'><button type="button" class="self-button">[% l('Logout (No Receipt)') %]</button></a></span>
+        <span><a id='oils-selfck-nav-logout' href='javascript:void(0);'><button type="button" class="self-button">[% l('Logout') %]</button></a></span>
+    </div>
+    <div id='oils-selfck-info-nav'>
+        <span for='oils-selfck-nav-receipt'>[% l('Receipt:') %] </span>
+        <span class="hidden"><input type="radio" name="receipt_type" id='oils-selfck-receipt-email' value="email"/><label for="oils-selfck-receipt-email"> [% l('Email') %]</label></span>
+        <span><input type="radio" name="receipt_type" id='oils-selfck-receipt-print' value="email" checked/><label for="oils-selfck-receipt-print"> [% l('Print') %]</label></span>
+        <span><input type="radio" name="receipt_type" id='oils-selfck-receipt-none' value="email"/><label for="oils-selfck-receipt-none"> [% l('None') %]</label></span>
     </div>
     <fieldset>
         <legend>[% l('Items Checked Out') %]</legend>
diff --git a/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js b/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js
index 03f9dfa..3959fbc 100644
--- a/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js
+++ b/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js
@@ -202,8 +202,7 @@ SelfCheckManager.prototype.init = function() {
             );
         },
         'oils-selfck-nav-home' : function() { self.drawCircPage(); },
-        'oils-selfck-nav-logout' : function() { self.logoutPatron(); },
-        'oils-selfck-nav-logout-print' : function() { self.logoutPatron(true); },
+        'oils-selfck-nav-logout' : function() { self.logoutPatron(true); },
         'oils-selfck-items-out-details-link' : function() { self.drawItemsOutPage(); },
         'oils-selfck-print-list-link' : function() { self.printList(); }
     }
@@ -466,10 +465,28 @@ SelfCheckManager.prototype.fetchPatron = function(barcode, usrname) {
         this.handleAlert('', false, 'login-success');
         dojo.byId('oils-selfck-user-banner').innerHTML = 
             dojo.string.substitute(localeStrings.WELCOME_BANNER, [this.patron.first_given_name()]);
+
+        if (this.patron.email() && // they have an email address set and ...
+            this.patron.email().match(/.*@.*/).length > 0 // it sorta looks like an email address
+        ) {
+            openils.Util.removeCSSClass( dojo.byId('oils-selfck-receipt-email').parentNode, 'hidden' );
+            if (user_setting_value(this.patron, 'circ.send_email_checkout_receipts') == 'true') // their selected default
+                dojo.byId('oils-selfck-receipt-email').checked = true;
+        }
+
         this.drawCircPage();
     }
 }
 
+function user_setting_value (user, setting) {
+    if (user) {
+        var list = user.settings().filter(function(s){
+            return s.name() == setting;
+        });
+
+        if (list.length) return list[0].value();
+    }
+}
 
 SelfCheckManager.prototype.handleAlert = function(message, shouldPopup, sound) {
 
@@ -1311,6 +1328,40 @@ SelfCheckManager.prototype.initPrinter = function() {
 }
 
 /**
+ * Email a receipt for this session's checkouts
+ */
+SelfCheckManager.prototype.emailSessionReceipt = function(callback) {
+
+    var circIds = [];
+
+    // collect the circs and failure info
+    dojo.forEach(
+        this.checkouts, 
+        function(blob) {
+            circIds.push(blob.circ);
+        }
+    );
+
+    var params = [
+        this.authtoken, 
+        this.patron.id(),
+        circIds
+    ];
+
+    var self = this;
+    fieldmapper.standardRequest(
+        ['open-ils.circ', 'open-ils.circ.checkout.batch_notify.session.atomic'],
+        {   
+            async : true,
+            params : params,
+            oncomplete : function() {
+                if (callback) callback(); // fire and forget
+            }
+        }
+    );
+}
+
+/**
  * Print a receipt for this session's checkouts
  */
 SelfCheckManager.prototype.printSessionReceipt = function(callback) {
@@ -1388,6 +1439,7 @@ SelfCheckManager.prototype.printData = function(data, numItems, callback) {
 }
 
 
+
 /**
  * Print a receipt for this user's items out
  */
@@ -1585,11 +1637,22 @@ SelfCheckManager.prototype.printFinesReceipt = function(callback) {
 SelfCheckManager.prototype.logoutPatron = function(print) {
     progressDialog.show(true); // prevent patron from clicking logout link twice
     if(print && this.checkouts.length) {
-        this.printSessionReceipt(
-            function() {
-                location.href = location.href;
-            }
-        );
+        if (dojo.byId('oils-selfck-receipt-print').checked) {
+            this.printSessionReceipt(
+                function() {
+                    location.href = location.href;
+                }
+            );
+        } else if (dojo.byId('oils-selfck-receipt-email').checked) {
+            this.emailSessionReceipt(
+                function() {
+                    location.href = location.href;
+                }
+            );
+        } else {
+            // user elected to get no receipt
+            location.href = location.href;
+        }
     } else {
         location.href = location.href;
     }

commit 4d512bfac53dfdc94696f1731bbae6e50542bccc
Author: Galen Charlton <gmc at esilibrary.com>
Date:   Thu Aug 4 12:07:14 2016 -0400

    LP#1356477: move email receipt checkbox on patron editor
    
    This patch updates the patron editor so that the checkbox
    for setting whether the user wishes to get email checkout
    receipts by default is moved next to the email address
    input.
    
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/src/templates/staff/circ/patron/t_edit.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_edit.tt2
index d6345b0..5543e9d 100644
--- a/Open-ILS/src/templates/staff/circ/patron/t_edit.tt2
+++ b/Open-ILS/src/templates/staff/circ/patron/t_edit.tt2
@@ -334,6 +334,17 @@ within the "form" by name for validation.
   </div>
 </div>
 
+<div class="row reg-field-row" ng-show="show_field('au.email') && opt_in_setting_types['circ.send_email_checkout_receipts']">
+  <div class="col-md-3 reg-field-label">
+    <label>{{opt_in_setting_types['circ.send_email_checkout_receipts'].label()}}</label>
+  </div>
+  <div class="col-md-3 reg-field-input">
+    <input
+      ng-change="field_modified()"
+      type='checkbox' ng-model="user_settings['circ.send_email_checkout_receipts']"/>
+  </div>
+</div>
+
 <!-- DAY_PHONE -->
 
 <div class="row reg-field-row" ng-show="show_field('au.day_phone')">
@@ -657,10 +668,10 @@ within the "form" by name for validation.
 </div>
 
 <div class="row reg-field-row" ng-repeat="type in opt_in_setting_types">
-  <div class="col-md-3 reg-field-label">
+  <div class="col-md-3 reg-field-label" ng-if="type.name() != 'circ.send_email_checkout_receipts'">
     <label>{{type.label()}}</label>
   </div>
-  <div class="col-md-3 reg-field-input">
+  <div class="col-md-3 reg-field-input" ng-if="type.name() != 'circ.send_email_checkout_receipts'">
     <input 
       ng-change="field_modified()" 
       type='checkbox' ng-model="user_settings[type.name()]"/>

commit f8422d9abd41f9b10b1b1cc4cc824e0eaef5b84e
Author: Mike Rylander <miker at esilibrary.com>
Date:   Wed Aug 3 16:51:54 2016 -0400

    LP#1356477: add quick receipt button
    
    This patch adds a new button to the webstaff checkout page
    called Quick Receipt. If the button itself is pushed, a
    receipt containing the current checkouts is either printed
    or emailed, depending on the user's preference. If the
    drop-down portion of the button is used, staff members can
    override the user's default preference to print or email
    the receipt.
    
    The Quick Receipt button is enabled only if at least one
    checkout has been made during the current session.
    
    Note that email receipts is an option only when the patron
    has an email address supplied.
    
    An icon next to the Quick Receipt button will be either a
    printer or an envelope depending on the user's preferred
    receipt setting.
    
    This patch also modifies the "Done" button. If pressed, the session
    is ended and the receipt is generated according to the user's
    preferences, but the drop-down portion can be used to end
    the session while letting the staff member choose how the
    receipt is emitted.
    
    If a receipt is emailed, a toast is displayed saying so.
    
    Signed-off-by: Mike Rylander <miker at esilibrary.com>
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
index 309dd3d..0e936cf 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
@@ -534,6 +534,7 @@ sub flesh_user {
         "cards",
         "card",
         "standing_penalties",
+        "settings",
         "addresses",
         "billing_address",
         "mailing_address",
@@ -3008,6 +3009,7 @@ sub user_retrieve_fleshed_by_id {
         "card",
         "groups",
         "standing_penalties",
+        "settings",
         "addresses",
         "billing_address",
         "mailing_address",
diff --git a/Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2
index 29974aa..8618419 100644
--- a/Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2
+++ b/Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2
@@ -131,12 +131,33 @@
     </label>
   </div>
   <div class="pad-horiz">
-    <button class="btn btn-default" 
-      ng-click="print_receipt()">[% l('Print Receipt') %]</button>
+    <span ng-show="may_email_receipt()" class="glyphicon glyphicon-envelope" aria-label="[% l('Send Email Receipt') %]"></span>
+    <span ng-show="!may_email_receipt()" class="glyphicon glyphicon-print" aria-label="[% l('Print Receipt') %]"></span>
+    <div class="btn-group" uib-dropdown>
+      <button ng-click="print_or_email_receipt()" id="quick-button" type="button" ng-disabled="checkouts.length == 0" class="btn btn-default">[% l('Quick Receipt') %]</button>
+      <button type="button" ng-disabled="checkouts.length == 0" class="btn btn-default" uib-dropdown-toggle>
+        <span class="caret"></span>
+        <span class="sr-only">[% l('receipt option') %]</span>
+      </button>
+      <ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="quick-button">
+        <li role="menuitem" ng-class="{disabled : !has_email_address()}"><a ng-click="email_receipt()" a-disabled="!has_email_address()" href="#">[% l('Email Receipt') %]</a></li>
+        <li role="menuitem"><a ng-click="print_receipt()" href="#">[% l('Print Receipt') %]</a></li>
+      </ul>
+    </div>
   </div>
-  <div>
+  <div class="btn-group" uib-dropdown>
     <button class="btn btn-default" 
-      ng-click="done()">[% l('Done') %]</button>
+      id="done-button" type="button"
+      ng-click="done_auto_receipt()">[% l('Done') %]</button>
+      <button type="button" class="btn btn-default" uib-dropdown-toggle>
+        <span class="caret"></span>
+        <span class="sr-only">[% l('receipt option') %]</span>
+      </button>
+      <ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="done-button">
+        <li role="menuitem"><a ng-click="done_no_receipt()" href="#">[% l('No Receipt') %]</a></li>
+        <li role="menuitem" ng-class="{disabled : !has_email_address()}"><a ng-click="done_email_receipt()" a-disabled="!has_email_address()" href="#">[% l('Email Receipt') %]</a></li>
+        <li role="menuitem"><a ng-click="done_print_receipt()" href="#">[% l('Print Receipt') %]</a></li>
+      </ul>
   </div>
 </div>
 
diff --git a/Open-ILS/src/templates/staff/circ/share/circ_strings.tt2 b/Open-ILS/src/templates/staff/circ/share/circ_strings.tt2
index 316e64a..4f9c917 100644
--- a/Open-ILS/src/templates/staff/circ/share/circ_strings.tt2
+++ b/Open-ILS/src/templates/staff/circ/share/circ_strings.tt2
@@ -12,6 +12,8 @@ s.CIRC_CLAIMS_RETURNED =
   '[% l('Item "[_1]" is marked as Claims Returned', '{{barcode}}') %]';
 s.CHECKOUT_FAILED_GENERIC =
   '[% l('Unable to checkout copy "[_1]" : [_2]', '{{barcode}}', '{{textcode}}') %]';
+s.EMAILED_CHECKOUT_RECEIPT =
+  "[% l('Emailed checkout receipt') %]";
 s.COPY_ALERT_MSG_DIALOG_TITLE =
   '[% l('Copy Alert Message for "[_1]"', '{{copy_barcode}}') %]';
 s.UNCAT_ALERT_DIALOG =
diff --git a/Open-ILS/web/js/ui/default/staff/circ/patron/app.js b/Open-ILS/web/js/ui/default/staff/circ/patron/app.js
index fe13939..687dd3c 100644
--- a/Open-ILS/web/js/ui/default/staff/circ/patron/app.js
+++ b/Open-ILS/web/js/ui/default/staff/circ/patron/app.js
@@ -5,7 +5,14 @@
  */
 
 angular.module('egPatronApp', ['ngRoute', 'ui.bootstrap', 
-    'egCoreMod', 'egUiMod', 'egGridMod', 'egUserMod'])
+    'egCoreMod', 'egUiMod', 'egGridMod', 'egUserMod', 'ngToast'])
+
+.config(['ngToastProvider', function(ngToastProvider) {
+    ngToastProvider.configure({
+        verticalPosition: 'bottom',
+        animation: 'fade'
+    });
+}])
 
 .config(function($routeProvider, $locationProvider, $compileProvider) {
     $locationProvider.html5Mode(true);
diff --git a/Open-ILS/web/js/ui/default/staff/circ/patron/checkout.js b/Open-ILS/web/js/ui/default/staff/circ/patron/checkout.js
index 3d6e9c6..3f82de7 100644
--- a/Open-ILS/web/js/ui/default/staff/circ/patron/checkout.js
+++ b/Open-ILS/web/js/ui/default/staff/circ/patron/checkout.js
@@ -5,10 +5,10 @@
 angular.module('egPatronApp').controller('PatronCheckoutCtrl',
 
        ['$scope','$q','$routeParams','egCore','egUser','patronSvc',
-        'egGridDataProvider','$location','$timeout','egCirc',
+        'egGridDataProvider','$location','$timeout','egCirc','ngToast',
 
 function($scope , $q , $routeParams , egCore , egUser , patronSvc , 
-         egGridDataProvider , $location , $timeout , egCirc) {
+         egGridDataProvider , $location , $timeout , egCirc , ngToast) {
 
     $scope.initTab('checkout', $routeParams.id).finally(function(){
         $scope.focusMe = true;
@@ -34,6 +34,34 @@ function($scope , $q , $routeParams , egCore , egUser , patronSvc ,
         );
     }
 
+    function setting_value (user, setting) {
+        if (user) {
+            var list = user.settings().filter(function(s){
+                return s.name() == setting;
+            });
+
+            if (list.length) return list[0].value();
+        }
+    }
+
+    $scope.has_email_address = function() {
+        return (
+            patronSvc.current &&
+            patronSvc.current.email() &&
+            patronSvc.current.email().match(/.*@.*/).length
+        );
+    }
+
+    $scope.may_email_receipt = function() {
+        return (
+            $scope.has_email_address() &&
+            setting_value(
+                patronSvc.current,
+                'circ.send_email_checkout_receipts'
+            ) == 'true'
+        );
+    }
+
     $scope.using_hatch = egCore.hatch.usingHatch();
 
     egCore.hatch.getItem('circ.checkout.strict_barcode')
@@ -191,18 +219,63 @@ function($scope , $q , $routeParams , egCore , egUser , patronSvc ,
         });
     }
 
-    // Redirect the user to the barcode entry page to load a new patron.
-    // If configured to do so, print the receipt first
-    $scope.done = function() {
-        if (printOnComplete) {
-
-            $scope.print_receipt().then(function() {
-                $location.path('/circ/patron/bcsearch');
+    $scope.email_receipt = function() {
+        if ($scope.has_email_address() && $scope.checkouts.length) {
+            return egCore.net.request(
+                'open-ils.circ',
+                'open-ils.circ.checkout.batch_notify.session.atomic',
+                egCore.auth.token(),
+                patronSvc.current.id(),
+                $scope.checkouts.map(function (c) { return c.circ.id() })
+            ).then(function() {
+                ngToast.create(egCore.strings.EMAILED_CHECKOUT_RECEIPT);
+                return $q.when();
             });
+        }
+        return $q.when();
+    }
+
+    $scope.print_or_email_receipt = function() {
+        if ($scope.may_email_receipt()) return $scope.email_receipt();
+        $scope.print_receipt();
+    }
 
+    // set of functions to issue a receipt (if desired), then
+    // redirect
+    $scope.done_auto_receipt = function() {
+        if ($scope.may_email_receipt()) {
+            $scope.email_receipt().then(function() {
+                $scope.done_redirect();
+            });
         } else {
-            $location.path('/circ/patron/bcsearch');
+            if (printOnComplete) {
+
+                $scope.print_receipt().then(function() {
+                    $scope.done_redirect();
+                });
+
+            } else {
+                $scope.done_redirect();
+            }
         }
     }
+    $scope.done_print_receipt = function() {
+        $scope.print_receipt().then( function () {
+            $scope.done_redirect();
+        });
+    }
+    $scope.done_email_receipt = function() {
+        $scope.email_receipt().then( function () {
+            $scope.done_redirect();
+        });
+    }
+    $scope.done_no_receipt = function() {
+        $scope.done_redirect();
+    }
+
+    // Redirect the user to the barcode entry page to load a new patron.
+    $scope.done_redirect = function() {
+        $location.path('/circ/patron/bcsearch');
+    }
 }])
 
diff --git a/Open-ILS/web/js/ui/default/staff/services/user.js b/Open-ILS/web/js/ui/default/staff/services/user.js
index f2a70c1..9fd32a8 100644
--- a/Open-ILS/web/js/ui/default/staff/services/user.js
+++ b/Open-ILS/web/js/ui/default/staff/services/user.js
@@ -11,6 +11,7 @@ function($q,  $timeout,  egNet,  egAuth,  egOrg) {
     var service = {
         defaultFleshFields : [
             'card',                                                                
+            'settings',
             'standing_penalties',                                                  
             'addresses',                                                           
             'billing_address',                                                     
diff --git a/Open-ILS/web/js/ui/default/staff/test/karma.conf.js b/Open-ILS/web/js/ui/default/staff/test/karma.conf.js
index b8c1259..b0cef3f 100644
--- a/Open-ILS/web/js/ui/default/staff/test/karma.conf.js
+++ b/Open-ILS/web/js/ui/default/staff/test/karma.conf.js
@@ -10,6 +10,8 @@ module.exports = function(config){
       'build/js/angular-route.min.js',
       'bower_components/angular-mocks/angular-mocks.js', // testing only
       'bower_components/angular-file-saver/dist/angular-file-saver.bundle.min.js',
+      'bower_components/ngtoast/dist/ngToast.min.js',
+      'bower_components/angular-sanitize/angular-sanitize.min.js',
       'build/js/ui-bootstrap.min.js',
       'build/js/hotkeys.min.js',
       'build/js/angular-cookies.min.js',

commit 9d1d36d3d56dcbe46d462ce8e431911df7b7166b
Author: Galen Charlton <gmc at esilibrary.com>
Date:   Wed Aug 3 15:38:49 2016 -0400

    LP#1356477: teach OPAC patron registration form about staged user (opt-in) settings
    
    This patch causes a checkbox to be added for each user setting
    that controls opt-in for active Action/Trigger event definitions. In
    the specific context of email checkout receipts, this means that
    a patron who is self-registering can elect to get email checkout
    receipts by default.
    
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Register.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Register.pm
index 0b68f1a..4949d10 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Register.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Register.pm
@@ -24,6 +24,8 @@ sub load_patron_reg {
     $ctx->{register}{valid_orgs} = 
         $self->setting_is_true_for_orgs('opac.allow_pending_user');
 
+    $self->collect_opt_in_settings;
+
     # just loading the form
     return Apache2::Const::OK
         unless $cgi->request_method eq 'POST';
@@ -62,6 +64,20 @@ sub load_patron_reg {
     # attempt to create a pending address
     $addr = undef unless $has_addr;
 
+    # opt-in settings
+    my $settings = [];
+    foreach (grep /^stgs\./, $cgi->param) {
+        my $val = $cgi->param($_);
+        next unless $val; # opt-in settings are always Boolean,
+                          # so just skip if not set
+        $self->inspect_register_value($_, $val);
+        s/^stgs.//g;
+        my $setting = Fieldmapper::staging::setting_stage->new;
+        $setting->setting($_);
+        $setting->value('true');
+        push @$settings, $setting;
+    }
+
     # At least one value was invalid. Exit early and re-render.
     return Apache2::Const::OK if $ctx->{register}{invalid};
 
@@ -72,7 +88,7 @@ sub load_patron_reg {
     my $resp = $U->simplereq(
         'open-ils.actor', 
         'open-ils.actor.user.stage.create',
-        $user, $addr
+        $user, $addr, undef, [], $settings
     );
 
     if (!$resp or ref $resp) {
@@ -115,6 +131,25 @@ sub collect_requestor_info {
     }
 }
 
+sub collect_opt_in_settings {
+    my $self = shift;
+    my $e = $self->editor;
+
+    my $types = $e->json_query({
+        select => {cust => ['name']},
+        from => {atevdef => 'cust'},
+        transform => 'distinct',
+        where => {
+            '+atevdef' => {
+                owner => [ map { $_ } @{ $self->ctx->{register}{valid_orgs} } ],
+                active => 't'
+            }
+        }
+    });
+    $self->ctx->{register}{opt_in_settings} =
+        $e->search_config_usr_setting_type({name => [map {$_->{name}} @$types]});
+}
+
 # if the username is in use by an actor.usr OR a 
 # pending user treat it as taken and warn the user.
 sub test_requested_username {
@@ -223,7 +258,22 @@ sub collect_register_validation_settings {
 sub inspect_register_value {
     my ($self, $field_path, $value) = @_;
     my $ctx = $self->ctx;
-    my ($scls, $field) = split(/\./, $field_path);
+    my ($scls, $field) = split(/\./, $field_path, 2);
+
+    if ($scls eq 'stgs') {
+        my $found = 0;
+        foreach my $type (@{ $self->ctx->{register}{opt_in_settings} }) {
+            if ($field eq $type->name) {
+                $found = 1;
+            }
+        }
+        if (!$found) {
+            $ctx->{register}{invalid}{$scls}{$field}{invalid} = 1;
+            $logger->info("patron register: trying to set an opt-in ".
+                          "setting $field that is not allowed.");
+        }
+        return;
+    }
 
     if (!$value) {
 
diff --git a/Open-ILS/src/templates/opac/register.tt2 b/Open-ILS/src/templates/opac/register.tt2
index 3c2d637..962fa9f 100644
--- a/Open-ILS/src/templates/opac/register.tt2
+++ b/Open-ILS/src/templates/opac/register.tt2
@@ -172,6 +172,21 @@ FOR field_def IN register_fields;
 </tr>
 [% END %]
 <!-- ====> shifting the code back to the right for context -->
+                    [% IF ctx.register.opt_in_settings.size > 0 %]
+                        [% FOR optin IN ctx.register.opt_in_settings %]
+                        <tr>
+                            <td><label for="stgs.[% optin.name | uri %]'">[% optin.label | html %]</label></td>
+                            <td>
+                                <input type='checkbox'
+                                    name='stgs.[% optin.name | uri %]'
+                                    id='stgs.[% optin.name | uri %]'
+                                    title="[% optin.label | html %]"
+                                ></input>
+                            </td>
+                            <td><!-- display errors and example text --></td>
+                        </tr>
+                        [% END %]
+                    [% END %]
                     <tr>
                         <td colspan='3'>
                             <a href="[% ctx.opac_root %]/home" 

commit aa47fc5fdc1a5a9f3889fc3f67588739ba9e1700
Author: Galen Charlton <gmc at esilibrary.com>
Date:   Wed Aug 3 15:36:18 2016 -0400

    LP#1356477: teach webstaff patron editor about staged user settings
    
    This patch also fixes a bug that preferred the webstaff patron editor
    from successfully removing a staged patron upon completing the
    registration of the patron.
    
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/web/js/ui/default/staff/circ/patron/regctl.js b/Open-ILS/web/js/ui/default/staff/circ/patron/regctl.js
index d08d699..1005453 100644
--- a/Open-ILS/web/js/ui/default/staff/circ/patron/regctl.js
+++ b/Open-ILS/web/js/ui/default/staff/circ/patron/regctl.js
@@ -791,6 +791,10 @@ angular.module('egCoreMod')
             if (user.usrname == '') 
                 user.usrname = card.barcode;
         }
+
+        angular.forEach(cuser.settings, function(setting) {
+            service.user_settings[setting.setting()] = Boolean(setting.value());
+        });
     }
 
     // copy select values from the cloned user to the new user.
@@ -1008,7 +1012,7 @@ angular.module('egCoreMod')
             'open-ils.actor',
             'open-ils.actor.user.stage.delete',
             egCore.auth.token(),
-            service.stage_user.row_id()
+            service.stage_user.user.row_id()
         );
     }
 

commit f3d2d070e8ca3a8b12a820489ee6caff8dc1c7e9
Author: Galen Charlton <gmc at esilibrary.com>
Date:   Wed Aug 3 15:37:39 2016 -0400

    LP#1356477: create new staging table for user settings
    
    This patch creates a new table called staging.setting_stage
    that allows for storing user settings when a patron uses
    (for example) the self-registration page.
    
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 65dc4db..485e4c6 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -9608,6 +9608,17 @@ SELECT  usr,
         </fields>
     </class>
 
+    <class id="stgs" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="staging::setting_stage" oils_persist:tablename="staging.setting_stage" reporter:label="User Setting Stage">
+        <fields oils_persist:primary="row_id" oils_persist:sequence="staging.setting_stage_row_id_seq">
+            <field reporter:label="Row ID" name="row_id" reporter:datatype="id"/>
+            <field reporter:label="Row Date" name="row_date" reporter:datatype="timestamp"/>
+            <field reporter:label="User Name" name="usrname" reporter:datatype="text"/>
+            <field reporter:label="User Setting Code" name="setting" reporter:datatype="text"/>
+            <field reporter:label="User Setting Value" name="value" reporter:datatype="text"/>
+            <field reporter:label="Complete" name="complete" reporter:datatype="bool"/>
+        </fields>
+    </class>
+
 	<class id="afs" controller="open-ils.cstore" oils_obj:fieldmapper="action::fieldset" oils_persist:tablename="action.fieldset" reporter:label="Fieldset">
 		<fields oils_persist:primary="id" oils_persist:sequence="action.fieldset_id_seq">
 			<field reporter:label="Fieldset ID" name="id" reporter:datatype="id"/>
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor/Stage.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor/Stage.pm
index 8f094b7..c8be321 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor/Stage.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor/Stage.pm
@@ -13,7 +13,8 @@ __PACKAGE__->register_method (
     api_name    => 'open-ils.actor.user.stage.create',
     signature => {
         desc => q/
-            Creates a new pending user account including addresses and statcats.
+            Creates a new pending user account including addresses, statcats, and
+            settings.
             Users are added to staging tables pending staff review.
         /,
         params => [
@@ -21,6 +22,7 @@ __PACKAGE__->register_method (
             {desc => 'Mailing address.  Optional', type => 'object', class => 'stgma'},
             {desc => 'Billing address.  Optional', type => 'object', class => 'stgba'},
             {desc => 'Statcats.  Optional.  This is an array of "stgsc" objects', type => 'array'},
+            {desc => 'Settings.  Optional.  This is an array of "stgs" objects', type => 'array'},
         ],
         return => {
             desc => 'username on success, Event on error',
@@ -31,7 +33,7 @@ __PACKAGE__->register_method (
 );
 
 sub create_user_stage {
-    my($self, $conn, $user, $mail_addr, $bill_addr, $statcats) = @_; # more?
+    my($self, $conn, $user, $mail_addr, $bill_addr, $statcats, $settings) = @_; # more?
 
     return OpenILS::Event->new('BAD_PARAMS') unless $user;
     return 0 unless $U->ou_ancestor_setting_value($user->home_ou, 'opac.allow_pending_user');
@@ -64,6 +66,13 @@ sub create_user_stage {
         }
     }
 
+    if($settings) {
+        foreach (@$settings) {
+            $_->usrname($uname);
+            $e->create_staging_setting_stage($_) or return $e->die_event;
+        }
+    }
+
     $e->commit;
     $conn->respond_complete($uname);
 
@@ -111,7 +120,8 @@ sub flesh_user_stage {
         billing_addresses => $e->search_staging_billing_address_stage({usrname => $user->usrname}),
         mailing_addresses => $e->search_staging_mailing_address_stage({usrname => $user->usrname}),
         cards => $e->search_staging_card_stage({usrname => $user->usrname}),
-        statcats => $e->search_staging_statcat_stage({usrname => $user->usrname})
+        statcats => $e->search_staging_statcat_stage({usrname => $user->usrname}),
+        settings => $e->search_staging_setting_stage({usrname => $user->usrname}),
     };
 }
 
@@ -171,6 +181,10 @@ sub delete_user_stage {
         $e->delete_staging_statcat_stage($statcat) or return $e->die_event;
     }
 
+    for my $setting (@{$data->{settings}}) {
+        $e->delete_staging_setting_stage($setting) or return $e->die_event;
+    }
+
     $e->commit;
     return 1;
 }
diff --git a/Open-ILS/src/sql/Pg/015.schema.staging.sql b/Open-ILS/src/sql/Pg/015.schema.staging.sql
index 3f558e1..4dfc3ee 100644
--- a/Open-ILS/src/sql/Pg/015.schema.staging.sql
+++ b/Open-ILS/src/sql/Pg/015.schema.staging.sql
@@ -60,6 +60,15 @@ CREATE TABLE staging.statcat_stage (
         complete        BOOL DEFAULT FALSE
 );
 
+CREATE TABLE staging.setting_stage (
+        row_id          BIGSERIAL PRIMARY KEY,
+        row_date        TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
+        usrname         TEXT NOT NULL,
+        setting         TEXT NOT NULL,
+        value           TEXT NOT NULL,
+        complete        BOOL DEFAULT FALSE
+);
+
 -- stored procedure for deleting expired pending patrons
 CREATE OR REPLACE FUNCTION staging.purge_pending_users() RETURNS VOID AS $$
 DECLARE
diff --git a/Open-ILS/src/sql/Pg/upgrade/YYYY.schema.staging-user-setting.sql b/Open-ILS/src/sql/Pg/upgrade/YYYY.schema.staging-user-setting.sql
new file mode 100644
index 0000000..55908ad
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/YYYY.schema.staging-user-setting.sql
@@ -0,0 +1,12 @@
+BEGIN;
+
+CREATE TABLE staging.setting_stage (
+        row_id          BIGSERIAL PRIMARY KEY,
+        row_date        TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
+        usrname         TEXT NOT NULL,
+        setting         TEXT NOT NULL,
+        value           TEXT NOT NULL,
+        complete        BOOL DEFAULT FALSE
+);
+
+COMMIT;

commit 7e3a8e2c61f4ee24317d11d478930851671b1c1d
Author: Galen Charlton <gmc at esilibrary.com>
Date:   Mon Aug 8 16:50:08 2016 -0400

    LP#1356477: add ability to unconditionally send email
    
    This patch defines and uses an
    open-ils.trigger.event.autocreate.ignore_opt_in method that does what
    it says on the tin; the caller is reponsible for not abusing this.
    
    This supports a use case in the webstaff checkout interface where staff
    can choose to have the receipt be emailed, provided that the patron
    record has an email address defined, even if the user setting to
    email receipts by default is not set. (It is assumed that the staff
    member would have gotten permission from the patron on the spot.)
    
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircNotify.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircNotify.pm
index 4629e15..48fc6a8 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircNotify.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircNotify.pm
@@ -74,7 +74,7 @@ sub circ_batch_notify {
     );
 
     $multi->request(
-        'open-ils.trigger.event.autocreate',
+        'open-ils.trigger.event.autocreate.ignore_opt_in',
         $hook => $_ => $e->requestor->ws_ou
     ) for ( @$circs );
     $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger.pm
index ed83cba..920175b 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger.pm
@@ -72,7 +72,7 @@ sub create_active_events_for_object {
     for my $def ( @$defs ) {
         next if ($granularity && $def->granularity ne $granularity );
 
-        if ($def->usr_field && $def->opt_in_setting) {
+        if (!$self->{ignore_opt_in} && $def->usr_field && $def->opt_in_setting) {
             my $ufield = $def->usr_field;
             my $uid = $target->$ufield;
             $uid = $uid->id if (ref $uid); # fleshed user object, unflesh it
@@ -124,6 +124,14 @@ __PACKAGE__->register_method(
     stream   => 1,
     argc     => 3
 );
+__PACKAGE__->register_method(
+    api_name      => 'open-ils.trigger.event.autocreate.ignore_opt_in',
+    method        => 'create_active_events_for_object',
+    api_level     => 1,
+    stream        => 1,
+    argc          => 3,
+    ignore_opt_in => 1
+);
 
 sub create_event_for_object_and_def {
     my $self = shift;

commit 53fc56475feedb6433a28f5c49096de7a46d87a9
Author: Mike Rylander <miker at esilibrary.com>
Date:   Wed Aug 3 12:45:10 2016 -0400

    LP#1356477: business logic for email checking receipts
    
    This patch adds two methods, open-ils.circ.checkout.batch_notify
    and open-ils.circ.checkout.batch_notify.session, that when passed
    a set of circulation IDs, creates A/T events for the
    checkout notification hooks. In the specific case of the email
    checkout receipts feature, the seed data contains a stock
    event that would send out emails, but it is conceivable
    that other forms of notification could be done by defining
    A/T events appropriately.
    
    Signed-off-by: Mike Rylander <miker at esilibrary.com>
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>

diff --git a/Open-ILS/src/extras/ils_events.xml b/Open-ILS/src/extras/ils_events.xml
index b6f0603..31bbc97 100644
--- a/Open-ILS/src/extras/ils_events.xml
+++ b/Open-ILS/src/extras/ils_events.xml
@@ -927,6 +927,9 @@
 	<event code='7026' textcode='COPY_STATUS_LOST_AND_PAID'>
 	        <desc xml:lang="en-US">Copy is marked as lost and paid</desc>
 	</event>
+	<event code='7028' textcode='PATRON_CIRC_MISMATCH'>
+	        <desc xml:lang="en-US">Potentially notified patron does not own the circulation.</desc>
+	</event>
 
 
 	<!-- ================================================================ -->
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
index 3e438ce..ba1133d 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
@@ -8,6 +8,7 @@ use OpenILS::Application::Circ::Survey;
 use OpenILS::Application::Circ::StatCat;
 use OpenILS::Application::Circ::Holds;
 use OpenILS::Application::Circ::HoldNotify;
+use OpenILS::Application::Circ::CircNotify;
 use OpenILS::Application::Circ::CreditCard;
 use OpenILS::Application::Circ::Money;
 use OpenILS::Application::Circ::NonCat;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircNotify.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircNotify.pm
new file mode 100644
index 0000000..4629e15
--- /dev/null
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircNotify.pm
@@ -0,0 +1,145 @@
+# ---------------------------------------------------------------
+# Copyright (C) 2016  Equinox Software, Inc.
+# Mike Rylander <mrylander at gmail.com>
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# ---------------------------------------------------------------
+
+
+package OpenILS::Application::Circ::CircNotify;
+use base qw/OpenILS::Application/;
+use strict; use warnings;
+use OpenSRF::EX qw(:try);
+use vars q/$AUTOLOAD/;
+use OpenILS::Event;
+use OpenSRF::Utils::JSON;
+use OpenSRF::Utils::Logger qw(:logger);
+use OpenILS::Utils::CStoreEditor q/:funcs/;
+use OpenSRF::Utils::SettingsClient;
+use OpenILS::Application::AppUtils;
+use OpenILS::Const qw/:const/;
+use OpenILS::Utils::Fieldmapper;
+use OpenSRF::MultiSession;
+use Email::Send;
+use Data::Dumper;
+use OpenSRF::EX qw/:try/;
+my $U = 'OpenILS::Application::AppUtils';
+
+use open ':utf8';
+
+sub circ_batch_notify {
+    my ($self, $client, $auth, $patronid, $circlist) = @_;
+    my $e = new_editor(authtoken => $auth);
+    return $e->event unless $e->checkauth;
+    return $e->event unless $e->allowed('STAFF_LOGIN');
+
+    my $circs = $e->search_action_circulation({ id => $circlist });
+    return $e->event if $e->event;
+
+    my $hook = 'circ.checkout.batch_notify';
+    $hook .= '.session' if $self->api_name =~ /session/;
+
+    for my $circ (@$circs) {
+        # WISHLIST: This may become more sophisticated and check "friend" permissions
+        # in the future, at lease in the non-session variant.
+        return OpenILS::Event->new('PATRON_CIRC_MISMATCH') if $circ->usr != $patronid;
+        return $e->event unless $e->allowed('VIEW_CIRCULATIONS', $circ->circ_lib);
+    }
+
+    my %events;
+    my $multi = OpenSRF::MultiSession->new(
+        app                 => 'open-ils.trigger',
+        cap                 => 3,
+        success_handler     => sub {
+            my $self = shift;
+            my $req = shift;
+
+            return unless $req->{response}->[0];
+            my $event = $req->{response}->[0]->content;
+            return unless $event;
+            $event = $e->retrieve_action_trigger_event($event);
+
+            return unless $event;
+            $events{$event->event_def} ||= [];
+            push @{$events{$event->event_def}}, $event->id;
+        },
+    );
+
+    $multi->request(
+        'open-ils.trigger.event.autocreate',
+        $hook => $_ => $e->requestor->ws_ou
+    ) for ( @$circs );
+    $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
+
+    $multi->session_wait(1);
+    $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
+
+    if (!keys(%events)) {
+        return $client->respond_complete;
+    }
+
+    $multi = OpenSRF::MultiSession->new(
+        app                 => 'open-ils.trigger',
+        cap                 => 3,
+        success_handler     => sub {
+            my $self = shift;
+            my $req = shift;
+
+            return unless $req->{response}->[0];
+            $client->respond( $req->{response}->[0]->content );
+        },
+    );
+
+    $multi->request(
+        'open-ils.trigger.event_group.fire',
+        $events{$_}
+    ) for ( sort keys %events );
+
+    $multi->session_wait(1);
+    return $client->respond_complete;
+}
+__PACKAGE__->register_method(
+    method   => 'circ_batch_notify',
+    api_name => 'open-ils.circ.checkout.batch_notify',
+    stream   => 1,
+    signature => {
+        desc   => 'Creates and fires grouped events for a set of circulation IDs',
+        params => [
+            { name => 'authtoken', desc => 'Staff auth token',   type => 'string' },
+            { name => 'patronid', desc => 'actor.usr.id of patron which must own the circulations', type => 'number' },
+            { name => 'circlist', desc => 'Arrayref of circulation IDs to bundle into the event group', type => 'array' }
+        ],
+        return => {
+            desc => 'Event on error, stream of zero or more event group firing results '.
+                    'otherwise. See: open-ils.trigger.event_group.fire'
+        }
+    }
+);
+__PACKAGE__->register_method(
+    method   => 'circ_batch_notify',
+    api_name => 'open-ils.circ.checkout.batch_notify.session',
+    stream   => 1,
+    signature => {
+        desc   => 'Creates and fires grouped events for a set of circulation IDs.  '.
+                  'For use by session-specific actions such as self-checkout or circ desk checkout.',
+        params => [
+            { name => 'authtoken', desc => 'Staff auth token',   type => 'string' },
+            { name => 'patronid', desc => 'actor.usr.id of patron which must own the circulations', type => 'number' },
+            { name => 'circlist', desc => 'Arrayref of circulation IDs to bundle into the event group', type => 'array' }
+        ],
+        return => {
+            desc => 'Event on error, stream of zero or more event group firing results '.
+                    'otherwise. See: open-ils.trigger.event_group.fire'
+        }
+    }
+);
+
+1;

commit b09ad8fd9ccf37b374ddf9840cf20fc63f45a624
Author: Galen Charlton <gmc at esilibrary.com>
Date:   Wed Aug 3 12:18:19 2016 -0400

    LP#1356477: seed data for email checkout receipts
    
    This patch adds seed data for email checkout receipts,
    including:
    
    - The action/trigger hooks circ.checkout.batch_notify
      and circ.checkout.batch_notify.session.
    - The default event definition
    - The circ.send_email_checkout_receipts (Email checkout receipts by
      default?) user setting.
    
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    
    Conflicts:
    	Open-ILS/src/sql/Pg/950.data.seed-values.sql

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 dbbbdde..72b7029 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -16349,6 +16349,7 @@ INSERT INTO actor.org_unit_setting (
     'true'
 );
 
+<<<<<<< HEAD
 INSERT INTO config.global_flag (name, enabled, label) VALUES (
     'ingest.disable_authority_auto_update_bib_meta',  FALSE, 
     oils_i18n_gettext(
@@ -16360,3 +16361,104 @@ INSERT INTO config.global_flag (name, enabled, label) VALUES (
     )
 );
 
+=======
+-- email checkout receipts
+INSERT INTO config.usr_setting_type (
+    name,
+    opac_visible,
+    label,
+    description,
+    datatype
+) VALUES (
+    'circ.send_email_checkout_receipts',
+    TRUE,
+    oils_i18n_gettext('circ.send_email_checkout_receipts', 'Email checkout receipts by default?', 'cust', 'label'),
+    oils_i18n_gettext('circ.send_email_checkout_receipts', 'Email checkout receipts by default?', 'cust', 'description'),
+    'bool'
+);
+
+INSERT INTO action_trigger.hook (key, core_type, description, passive)
+VALUES (
+    'circ.checkout.batch_notify',
+    'circ',
+    oils_i18n_gettext(
+        'circ.checkout.batch_notify',
+        'Notification of a group of circs',
+        'ath',
+        'description'
+    ),
+    FALSE
+);
+
+INSERT INTO action_trigger.hook (key, core_type, description, passive)
+VALUES (
+    'circ.checkout.batch_notify.session',
+    'circ',
+    oils_i18n_gettext(
+        'circ.checkout.batch_notify.session',
+        'Notification of a group of circs at the end of a checkout session',
+        'ath',
+        'description'
+    ),
+    FALSE
+);
+
+INSERT INTO action_trigger.event_definition (
+    active,
+    owner,
+    name,
+    hook,
+    validator,
+    reactor,
+    usr_field,
+    opt_in_setting,
+    group_field,
+    template
+) VALUES (
+    TRUE,
+    1,
+    'Email Checkout Receipt',
+    'circ.checkout.batch_notify.session',
+    'NOOP_True',
+    'SendEmail',
+    'usr',
+    'circ.send_email_checkout_receipts',
+    'usr',
+    $$[%- USE date -%]
+[%- user = target.0.usr -%]
+To: [%- params.recipient_email || user.email %]
+From: [%- helpers.get_org_setting(target.0.circ_lib.id, 'org.bounced_emails') || params.sender_email || default_sender %]
+Subject: Checkout Receipt
+Auto-Submitted: auto-generated
+
+You checked out the following items:
+
+[% FOR circ IN target %]
+    [%- copy_details = helpers.get_copy_bib_basics(circ.target_copy.id) -%]
+    Title: [% copy_details.title %]
+    Author: [% copy_details.author %]
+    Call Number: [% circ.target_copy.call_number.label %]
+    Barcode: [% circ.target_copy.barcode %]
+    Due: [% date.format(helpers.format_date(circ.due_date), '%Y-%m-%d') %]
+    Library: [% circ.circ_lib.name %]
+
+[% END %]
+$$);
+
+INSERT INTO action_trigger.environment (
+    event_def,
+    path
+) VALUES (
+    currval('action_trigger.event_definition_id_seq'),
+    'target_copy.call_number'
+), (
+    currval('action_trigger.event_definition_id_seq'),
+    'target_copy.location'
+), (
+    currval('action_trigger.event_definition_id_seq'),
+    'usr'
+), (
+    currval('action_trigger.event_definition_id_seq'),
+    'circ_lib'
+);
+>>>>>>> a452d2f... LP#1356477: seed data for email checkout receipts
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.data.email-checkout-receipt.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.data.email-checkout-receipt.sql
new file mode 100644
index 0000000..eaeaf36
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.data.email-checkout-receipt.sql
@@ -0,0 +1,103 @@
+BEGIN;
+
+INSERT INTO config.usr_setting_type (
+    name,
+    opac_visible,
+    label,
+    description,
+    datatype
+) VALUES (
+    'circ.send_email_checkout_receipts',
+    TRUE,
+    oils_i18n_gettext('circ.send_email_checkout_receipts', 'Email checkout receipts by default?', 'cust', 'label'),
+    oils_i18n_gettext('circ.send_email_checkout_receipts', 'Email checkout receipts by default?', 'cust', 'description'),
+    'bool'
+);
+
+INSERT INTO action_trigger.hook (key, core_type, description, passive)
+VALUES (
+    'circ.checkout.batch_notify',
+    'circ',
+    oils_i18n_gettext(
+        'circ.checkout.batch_notify',
+        'Notification of a group of circs',
+        'ath',
+        'description'
+    ),
+    FALSE
+);
+
+INSERT INTO action_trigger.hook (key, core_type, description, passive)
+VALUES (
+    'circ.checkout.batch_notify.session',
+    'circ',
+    oils_i18n_gettext(
+        'circ.checkout.batch_notify.session',
+        'Notification of a group of circs at the end of a checkout session',
+        'ath',
+        'description'
+    ),
+    FALSE
+);
+
+INSERT INTO action_trigger.event_definition (
+    active,
+    owner,
+    name,
+    hook,
+    validator,
+    reactor,
+    usr_field,
+    opt_in_setting,
+    group_field,
+    template
+) VALUES (
+    TRUE,
+    1,
+    'Email Checkout Receipt',
+    'circ.checkout.batch_notify.session',
+    'NOOP_True',
+    'SendEmail',
+    'usr',
+    'circ.send_email_checkout_receipts',
+    'usr',
+    $$[%- USE date -%]
+[%- user = target.0.usr -%]
+To: [%- params.recipient_email || user.email %]
+From: [%- helpers.get_org_setting(target.0.circ_lib.id, 'org.bounced_emails') || params.sender_email || default_sender %]
+Subject: Checkout Receipt
+Auto-Submitted: auto-generated
+
+You checked out the following items:
+
+[% FOR circ IN target %]
+    [%- copy_details = helpers.get_copy_bib_basics(circ.target_copy.id) -%]
+    Title: [% copy_details.title %]
+    Author: [% copy_details.author %]
+    Call Number: [% circ.target_copy.call_number.label %]
+    Barcode: [% circ.target_copy.barcode %]
+    Due: [% date.format(helpers.format_date(circ.due_date), '%Y-%m-%d') %]
+    Library: [% circ.circ_lib.name %]
+
+[% END %]
+$$);
+
+INSERT INTO action_trigger.environment (
+    event_def,
+    path
+) VALUES (
+    currval('action_trigger.event_definition_id_seq'),
+    'target_copy.call_number'
+), (
+    currval('action_trigger.event_definition_id_seq'),
+    'target_copy.location'
+), (
+    currval('action_trigger.event_definition_id_seq'),
+    'usr'
+), (
+    currval('action_trigger.event_definition_id_seq'),
+    'circ_lib'
+);
+
+COMMIT;
+

-----------------------------------------------------------------------

Summary of changes:
 Open-ILS/examples/fm_IDL.xml                       |   11 ++
 Open-ILS/src/extras/ils_events.xml                 |    3 +
 .../src/perlmods/lib/OpenILS/Application/Actor.pm  |    2 +
 .../lib/OpenILS/Application/Actor/Stage.pm         |   20 +++-
 .../src/perlmods/lib/OpenILS/Application/Circ.pm   |    1 +
 .../lib/OpenILS/Application/Circ/CircNotify.pm     |  145 ++++++++++++++++++++
 .../perlmods/lib/OpenILS/Application/Trigger.pm    |   10 ++-
 .../lib/OpenILS/WWW/EGCatLoader/Register.pm        |   54 +++++++-
 Open-ILS/src/sql/Pg/002.schema.config.sql          |    2 +-
 Open-ILS/src/sql/Pg/015.schema.staging.sql         |    9 ++
 Open-ILS/src/sql/Pg/950.data.seed-values.sql       |  102 ++++++++++++++
 .../upgrade/0996.data.email-checkout-receipt.sql   |  105 ++++++++++++++
 .../upgrade/YYYY.schema.staging-user-setting.sql   |   12 ++
 Open-ILS/src/templates/circ/selfcheck/summary.tt2  |    9 +-
 Open-ILS/src/templates/opac/register.tt2           |   15 ++
 .../src/templates/staff/circ/patron/t_checkout.tt2 |   29 ++++-
 .../src/templates/staff/circ/patron/t_edit.tt2     |   15 ++-
 .../templates/staff/circ/share/circ_strings.tt2    |    2 +
 .../web/js/ui/default/circ/selfcheck/selfcheck.js  |   77 ++++++++++-
 .../web/js/ui/default/staff/circ/patron/app.js     |    9 +-
 .../js/ui/default/staff/circ/patron/checkout.js    |   93 +++++++++++--
 .../web/js/ui/default/staff/circ/patron/regctl.js  |    6 +-
 Open-ILS/web/js/ui/default/staff/services/user.js  |    1 +
 .../web/js/ui/default/staff/test/karma.conf.js     |    2 +
 .../Circulation/email_cko_receipts.adoc            |   20 +++
 25 files changed, 720 insertions(+), 34 deletions(-)
 create mode 100644 Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircNotify.pm
 create mode 100644 Open-ILS/src/sql/Pg/upgrade/0996.data.email-checkout-receipt.sql
 create mode 100644 Open-ILS/src/sql/Pg/upgrade/YYYY.schema.staging-user-setting.sql
 create mode 100644 docs/RELEASE_NOTES_NEXT/Circulation/email_cko_receipts.adoc


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list