LP#1706124: Make include inactive patrons checkbox sticky
[working/Evergreen.git] / Open-ILS / web / js / ui / default / staff / services / patron_search.js
index 48859d8..7897671 100644 (file)
@@ -9,8 +9,8 @@ angular.module('egPatronSearchMod', ['ngRoute', 'ui.bootstrap',
  * Patron service
  */
 .factory('patronSvc',
-       ['$q','$timeout','$location','egCore','egUser','$locale',
-function($q , $timeout , $location , egCore,  egUser , $locale) {
+       ['$q','$timeout','$location','egCore','egUser','egConfirmDialog','$locale',
+function($q , $timeout , $location , egCore,  egUser , egConfirmDialog , $locale) {
 
     var service = {
         // cached patron search results
@@ -48,6 +48,18 @@ function($q , $timeout , $location , egCore,  egUser , $locale) {
     }
     service.resetPatronLists();  // initialize
 
+    // Max recents setting is loaded and scrubbed during egStartup.
+    // Copy it to a local variable here for ease of local access
+    // after startup has run.
+    egCore.startup.go().then(
+        function() {
+            egCore.org.settings('ui.staff.max_recent_patrons')
+            .then(function(s) {
+                service.maxRecentPatrons = s['ui.staff.max_recent_patrons'];
+            });
+        }
+    );
+
     // Returns true if the last alerted patron matches the current
     // patron.  Otherwise, the last alerted patron is set to the 
     // current patron and false is returned.
@@ -74,6 +86,60 @@ function($q , $timeout , $location , egCore,  egUser , $locale) {
         return $q.when();
     }
 
+    service.getRecentPatrons = function() {
+        // avoid getting stuck in a show-recent loop
+        service.showRecent = false;
+
+        if (service.maxRecentPatrons < 1) return $q.when();
+        var patrons = 
+            egCore.hatch.getLoginSessionItem('eg.circ.recent_patrons') || [];
+
+        // Ensure the cached list is no bigger than the current config.
+        // This can happen if the setting changes while logged in.
+        patrons = patrons.slice(0, service.maxRecentPatrons);
+
+        // add home_ou to the list of fleshed fields for recent patrons
+        var fleshFields = egUser.defaultFleshFields.slice(0);
+        fleshFields.push('home_ou');
+
+        var deferred = $q.defer();
+        function getNext() {
+            if (patrons.length == 0) {
+                deferred.resolve();
+                return;
+            }
+            egUser.get(patrons[0], {useFields : fleshFields}).then(
+                function(usr) { // fetch first user
+                    deferred.notify(usr);
+                    patrons.splice(0, 1); // remove first user from list
+                    getNext();
+                }
+            );
+        }
+
+        getNext();
+        return deferred.promise;
+    }
+
+    service.addRecentPatron = function(user_id) {
+        if (service.maxRecentPatrons < 1) return;
+
+        // no need to re-track same user
+        if (service.current && service.current.id() == user_id) return;
+
+        var patrons = 
+            egCore.hatch.getLoginSessionItem('eg.circ.recent_patrons') || [];
+        patrons.splice(0, 0, user_id);  // put this user at front
+        patrons.splice(service.maxRecentPatrons, 1); // remove excess
+
+        // remove any other occurrences of this user, which may have been
+        // added before the most recent user.
+        var idx = patrons.indexOf(user_id, 1);
+        if (idx > 0) patrons.splice(idx, 1);
+
+        egCore.hatch.setLoginSessionItem('eg.circ.recent_patrons', patrons);
+    }
+
     // sets the primary display user, fetching data as necessary.
     service.setPrimary = function(id, user, force) {
         var user_id = id ? id : (user ? user.id() : null);
@@ -82,9 +148,7 @@ function($q , $timeout , $location , egCore,  egUser , $locale) {
 
         if (!user_id) return $q.reject();
 
-        // when loading a new patron, update the last patron setting
-        if (!service.current || service.current.id() != user_id)
-            egCore.hatch.setLoginSessionItem('eg.circ.last_patron', user_id);
+        service.addRecentPatron(user_id);
 
         // avoid running multiple retrievals for the same patron, which
         // can happen during dbl-click by maintaining a single running
@@ -126,9 +190,17 @@ function($q , $timeout , $location , egCore,  egUser , $locale) {
             }
 
             service.resetPatronLists();
-            service.current = user;
-            service.localFlesh(user);
-            return service.fetchUserStats();
+
+            return service.checkOptIn(user).then(
+                function() {
+                    service.current = user;
+                    service.localFlesh(user);
+                    return service.fetchUserStats();
+                },
+                function() {
+                    return $q.reject();
+                }
+            );
 
         } else if (id) {
             if (!force && service.current && service.current.id() == id) {
@@ -143,9 +215,16 @@ function($q , $timeout , $location , egCore,  egUser , $locale) {
 
             return egUser.get(id).then(
                 function(user) {
-                    service.current = user;
-                    service.localFlesh(user);
-                    return service.fetchUserStats();
+                    return service.checkOptIn(user).then(
+                        function() {
+                            service.current = user;
+                            service.localFlesh(user);
+                            return service.fetchUserStats();
+                        },
+                        function() {
+                            return $q.reject();
+                        }
+                    );
                 },
                 function(err) {
                     console.error(
@@ -354,6 +433,59 @@ function($q , $timeout , $location , egCore,  egUser , $locale) {
         });
     }
 
+    service.createOptIn = function(user_id) {
+        return egCore.net.request(
+            'open-ils.actor',
+            'open-ils.actor.user.org_unit_opt_in.create',
+            egCore.auth.token(), user_id);
+    }
+
+    service.checkOptIn = function(user) {
+        var deferred = $q.defer();
+        egCore.net.request(
+            'open-ils.actor',
+            'open-ils.actor.user.org_unit_opt_in.check',
+            egCore.auth.token(), user.id())
+        .then(function(optInResp) {
+            if (eg_evt = egCore.evt.parse(optInResp)) {
+                deferred.reject();
+                console.log('error on opt-in check: ' + eg_evt);
+            } else if (optInResp == 2) {
+                // opt-in disallowed at this location by patron's home library
+                deferred.reject();
+                alert(egCore.strings.OPT_IN_RESTRICTED);
+            } else if (optInResp == 1) {
+                // opt-in handled or not needed, do nothing
+                deferred.resolve();
+            } else {
+                // opt-in needed, show the opt-in dialog
+                var org = egCore.org.get(user.home_ou());
+                egConfirmDialog.open(
+                    egCore.strings.OPT_IN_DIALOG_TITLE,
+                    egCore.strings.OPT_IN_DIALOG,
+                    {   family_name : user.family_name(),
+                        first_given_name : user.first_given_name(),
+                        org_name : org.name(),
+                        org_shortname : org.shortname(),
+                        ok : function() {
+                            service.createOptIn(user.id())
+                            .then(function(resp) {
+                                if (evt = egCore.evt.parse(resp)) {
+                                    deferred.reject();
+                                    alert(evt);
+                                } else {
+                                    deferred.resolve();
+                                }
+                            });
+                        },
+                        cancel : function() { deferred.reject(); }
+                    }
+                );
+            }
+        });
+        return deferred.promise;
+    }
+
     // Avoid using parens [e.g. (1.23)] to indicate negative numbers, 
     // which is the Angular default.
     // http://stackoverflow.com/questions/17441254/why-angularjs-currency-filter-formats-negative-numbers-with-parenthesis
@@ -389,6 +521,16 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egCore,
         selectedItems : function() {return []}
     }
 
+    // The first time we encounter the show-recent CGI param, put the
+    // service into show-recent mode.  The first time recents are shown,
+    // the service is taken out of show-recent mode so the page does not
+    // get stuck in a show-recent loop.
+    if (patronSvc.showRecent === undefined 
+        && Boolean($location.path().match(/search/))
+        && Boolean($location.search().show_recent)) {
+        patronSvc.showRecent = true;
+    }
+
     // Handle URL-encoded searches
     if ($location.search().search) {
         console.log('URL search = ' + $location.search().search);
@@ -411,7 +553,7 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egCore,
 
     var propagate;
     var propagate_inactive;
-    if (patronSvc.lastSearch) {
+    if (patronSvc.lastSearch && !patronSvc.showRecent) {
         propagate = patronSvc.lastSearch.search;
         // home_ou needs to be treated specially
         propagate.home_ou = {
@@ -457,6 +599,11 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egCore,
     provider.get = function(offset, count) {
         var deferred = $q.defer();
 
+        if (patronSvc.showRecent) {
+            // avoid getting stuck in show-recent mode
+            return patronSvc.getRecentPatrons();
+        }
+
         var fullSearch;
         if (patronSvc.urlSearch) {
             fullSearch = patronSvc.urlSearch;
@@ -598,7 +745,17 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egCore,
     egCore.hatch.getItem('eg.circ.patron.search.show_extras')
     .then(function(val) {$scope.showExtras = val});
 
-    // map form arguments into search params
+// check searchArgs.inactive setting
+    egCore.hatch.getItem('searchArgs.inactive')
+                .then(function(searchInactive){
+                    if (searchInactive) $scope.searchArgs.inactive = searchInactive; 
+                });    
+
+     $scope.onSearchInactiveChanged = function(){
+        egCore.hatch.setItem('searchArgs.inactive', $scope.searchArgs.inactive);
+    }
+
+// map form arguments into search params
     function compileSearch(args) {
         var search = {};
         angular.forEach(args, function(val, key) {
@@ -619,6 +776,18 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egCore,
                     search[key].group = 1;
                 } else if (key == 'card') {
                     search[key].group = 3
+                } else if (key.match(/dob_/)) {
+                    // DOB should always be numeric
+                    search[key].value = search[key].value.replace(/\D/g,'');
+                    if (search[key].value.length == 0) {
+                        delete search[key];
+                    }
+                    else {
+                        if (!key.match(/year/)) {
+                            search[key].value = ('0'+search[key].value).slice(-2);
+                        }
+                        search[key].group = 4;
+                    }
                 }
             }
         });