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

Evergreen Git git at git.evergreen-ils.org
Wed Apr 20 10:53:38 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  7212b5074df1cda1af1aef2ec6a7aa600c3c6c04 (commit)
       via  5435c0ec37d218ffeb59a5bc84b31d9a2f644af5 (commit)
       via  15fa6be7eba1ed9f8968f99f890e8db0a517fd6c (commit)
       via  66d120bdd8f10ac907a5654f4bf5f18a1d325d82 (commit)
       via  05a95958d49e6b9fdbd2d817e8261d9e1a57bb58 (commit)
       via  c309391a11a21db0b55444ddc35c4bdefbc5138f (commit)
       via  b7e42d60141bdf73d104017e6ead3509d73088a9 (commit)
       via  5001213edf127746fdcc9307c5bde5ea18c82da8 (commit)
       via  3a1efa015770a67b5726d922611c5d8f91560414 (commit)
       via  fc007e72a960d93bac7ed514a9bc4f1b8d525240 (commit)
       via  3b311dfb1d6742880d17b479cf28552165e35d06 (commit)
       via  84e988e9684f4a445a820afcb5f8933bb7796f5f (commit)
       via  1f0af17da634454c59ca62af07db95db212fcbe5 (commit)
       via  4d4807e49c3f2f18f7ec1340182193a5c50deab3 (commit)
       via  275d24ece6f3d722c9c8f682ab0f79566590e603 (commit)
      from  ba9af191a2eaa7d5bd640b93c6a5d1481ce85ab7 (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 7212b5074df1cda1af1aef2ec6a7aa600c3c6c04
Author: Galen Charlton <gmc at esilibrary.com>
Date:   Thu Mar 31 16:50:51 2016 -0400

    LP#1570091: webstaff: fix typo
    
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2 b/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2
index bde7ca2..8bbfb45 100644
--- a/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2
+++ b/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2
@@ -83,7 +83,7 @@
 
     <eg-grid-action handler="transferVolumesToRecord" group="[% l('Transfer') %]"
       label="[% l('Volumes to Previously Marked Record') %]"></eg-grid-action>
-    <eg-grid-action handler="transferVolumesLibrary" group="[% l('Transfer') %]"
+    <eg-grid-action handler="transferVolumesToLibrary" group="[% l('Transfer') %]"
       label="[% l('Volumes to Previously Marked Library') %]"></eg-grid-action>
     <eg-grid-action handler="transferVolumesToRecordAndLibrary" group="[% l('Transfer') %]"
       label="[% l('Volumes to Previously Marked Record and Library') %]"></eg-grid-action>

commit 5435c0ec37d218ffeb59a5bc84b31d9a2f644af5
Author: Galen Charlton <gmc at esilibrary.com>
Date:   Thu Mar 31 16:47:02 2016 -0400

    LP#1570091: webstaff: fix transfer Volumes to Previously Marked Library
    
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2 b/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2
index 28bb55e..bde7ca2 100644
--- a/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2
+++ b/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2
@@ -83,7 +83,7 @@
 
     <eg-grid-action handler="transferVolumesToRecord" group="[% l('Transfer') %]"
       label="[% l('Volumes to Previously Marked Record') %]"></eg-grid-action>
-    <eg-grid-action handler="transferVolumes" group="[% l('Transfer') %]"
+    <eg-grid-action handler="transferVolumesLibrary" group="[% l('Transfer') %]"
       label="[% l('Volumes to Previously Marked Library') %]"></eg-grid-action>
     <eg-grid-action handler="transferVolumesToRecordAndLibrary" group="[% l('Transfer') %]"
       label="[% l('Volumes to Previously Marked Record and Library') %]"></eg-grid-action>
diff --git a/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
index 098cabf..a635b9c 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
@@ -1090,7 +1090,7 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
         });
     }
 
-    $scope.transferVolumes = function (new_record){
+    function transferVolumes(new_record){
         var xfer_target = egCore.hatch.getLocalItem('eg.cat.volume_transfer_target');
 
         if (xfer_target) {
@@ -1116,10 +1116,14 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
         
     }
 
+    $scope.transferVolumesToLibrary = function() {
+        transferVolumes();
+    }
+
     $scope.transferVolumesToRecordAndLibrary = function() {
         var target_record = egCore.hatch.getLocalItem('eg.cat.marked_volume_transfer_record');
         if (!target_record) return;
-        $scope.transferVolumes(target_record);
+        transferVolumes(target_record);
     }
 
     // this "transfers" selected copies to a new owning library,

commit 15fa6be7eba1ed9f8968f99f890e8db0a517fd6c
Author: Galen Charlton <gmc at esilibrary.com>
Date:   Wed Mar 30 22:13:43 2016 -0400

    LP#1570091: webstaff: isolate the scope of eg-status-bar
    
    The messages array was fighting with ngToast's
    messages array... and winning.
    
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/web/js/ui/default/staff/services/statusbar.js b/Open-ILS/web/js/ui/default/staff/services/statusbar.js
index 29968f9..8c7d116 100644
--- a/Open-ILS/web/js/ui/default/staff/services/statusbar.js
+++ b/Open-ILS/web/js/ui/default/staff/services/statusbar.js
@@ -15,6 +15,7 @@ angular.module('egCoreMod')
         restrict : 'AE',
         replace : true,
         templateUrl : 'eg-status-bar-template',
+        scope : { },
         controller : [
                     '$scope','$rootScope','egHatch',
             function($scope , $rootScope , egHatch) {
@@ -49,7 +50,7 @@ angular.module('egCoreMod')
             }
 
             $rootScope.$on('egStatusBarMessage', function(evt, args) {
-                $scope.messages.unshift(args.message);
+                $scope.messages.unshift(args);
 
                 // ensure the list does not exceed 10 messages
                 // TODO: configurable?

commit 66d120bdd8f10ac907a5654f4bf5f18a1d325d82
Author: Mike Rylander <mrylander at gmail.com>
Date:   Wed Mar 30 17:25:06 2016 -0400

    LP#1570091: webstaff: make use of ngToast for holdings action notifications
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/templates/staff/cat/catalog/index.tt2 b/Open-ILS/src/templates/staff/cat/catalog/index.tt2
index d8a1a53..c2df10e 100644
--- a/Open-ILS/src/templates/staff/cat/catalog/index.tt2
+++ b/Open-ILS/src/templates/staff/cat/catalog/index.tt2
@@ -34,6 +34,20 @@
       "[% l('One or more items could not be transferred. Override?') %]";                                                                                                    
     s.OVERRIDE_TRANSFER_COPIES_TO_MARKED_VOLUME_BODY =                                                                                                            
       "[% l('Reason(s) include: [_1]', '{{evt_desc}}') %]";                
+    s.VOLS_TRANSFERED =                                                                                                            
+      "[% l('Volume(s) transfered') %]";                
+    s.ITEMS_TRANSFERED =                                                                                                            
+      "[% l('Item(s) transfered') %]";                
+    s.HOLD_TRANSFER_DEST_MARKED =                                                                                                            
+      "[% l('Hold Transfer Destination set') %]";                
+    s.MARK_CONJ_TARGET =                                                                                                            
+      "[% l('Conjoined Item Target set') %]";                
+    s.MARK_VOL_TARGET =                                                                                                            
+      "[% l('Volume Transfer Target set') %]";                
+    s.MARK_ITEM_TARGET =                                                                                                            
+      "[% l('Item Transfer Target set') %]";                
+    s.MARK_OVERLAY_TARGET =                                                                                                            
+      "[% l('Record Overlay Target set') %]";                
   }])
 </script>
 
diff --git a/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
index e2540b6..098cabf 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
@@ -7,7 +7,14 @@
  *
  */
 
-angular.module('egCatalogApp', ['ui.bootstrap','ngRoute','ngLocationUpdate','egCoreMod','egGridMod', 'egMarcMod', 'egUserMod', 'egHoldingsMod'])
+angular.module('egCatalogApp', ['ui.bootstrap','ngRoute','ngLocationUpdate','egCoreMod','egGridMod', 'egMarcMod', 'egUserMod', 'egHoldingsMod', 'ngToast'])
+
+.config(['ngToastProvider', function(ngToastProvider) {
+  ngToastProvider.configure({
+    verticalPosition: 'bottom',
+    animation: 'fade'
+  });
+}])
 
 .config(function($routeProvider, $locationProvider, $compileProvider) {
     $locationProvider.html5Mode(true);
@@ -230,9 +237,9 @@ function($scope , $routeParams , $location , $window , $q , egCore) {
 }])
 
 .controller('CatalogCtrl',
-       ['$scope','$routeParams','$location','$window','$q','egCore','egHolds','egCirc','egConfirmDialog',
+       ['$scope','$routeParams','$location','$window','$q','egCore','egHolds','egCirc','egConfirmDialog','ngToast',
         'egGridDataProvider','egHoldGridActions','$timeout','$modal','holdingsSvc','egUser','conjoinedSvc',
-function($scope , $routeParams , $location , $window , $q , egCore , egHolds , egCirc,  egConfirmDialog,
+function($scope , $routeParams , $location , $window , $q , egCore , egHolds , egCirc , egConfirmDialog , ngToast ,
          egGridDataProvider , egHoldGridActions , $timeout , $modal , holdingsSvc , egUser , conjoinedSvc) {
 
     var holdingsSvcInst = new holdingsSvc();
@@ -343,9 +350,11 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
     $scope.markConjoined = function () {
         $scope.current_conjoined_target = $scope.record_id;
         egCore.hatch.setLocalItem('eg.cat.marked_conjoined_record',$scope.record_id);
+        ngToast.create(egCore.strings.MARK_CONJ_TARGET);
     };
 
     $scope.markVolTransfer = function () {
+        ngToast.create(egCore.strings.MARK_VOL_TARGET);
         $scope.current_voltransfer_target = $scope.record_id;
         egCore.hatch.setLocalItem('eg.cat.marked_volume_transfer_record',$scope.record_id);
     };
@@ -353,6 +362,7 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
     $scope.markOverlay = function () {
         $scope.current_overlay_target = $scope.record_id;
         egCore.hatch.setLocalItem('eg.cat.marked_overlay_record',$scope.record_id);
+        ngToast.create(egCore.strings.MARK_OVERLAY_TARGET);
     };
 
     $scope.clearRecordMarks = function () {
@@ -993,6 +1003,7 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
                 'eg.cat.item_transfer_target',
                 $scope.holdingsGridControls.selectedItems()[0].call_number.id
             );
+            ngToast.create(egCore.strings.MARK_ITEM_TARGET);
         }
     }
 
@@ -1026,6 +1037,7 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
             'eg.cat.volume_transfer_target',
             $scope.holdingsGridControls.selectedItems()[0].owner_id
         );
+        ngToast.create(egCore.strings.MARK_VOL_TARGET);
     }
 
     $scope.selectedHoldingsItemStatusDetail = function (){
@@ -1068,6 +1080,7 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
         });
         $q.all(promises).then(function(success) {
             if (success) {
+                ngToast.create(egCore.strings.VOLS_TRANSFERED);
                 holdingsSvcInst.fetchAgain().then(function() {
                     $scope.holdingsGridDataProvider.refresh();
                 });
@@ -1091,6 +1104,7 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
                 }
             ).then(function(success) {
                 if (success) {
+                    ngToast.create(egCore.strings.VOLS_TRANSFERED);
                     holdingsSvcInst.fetchAgain().then(function() {
                         $scope.holdingsGridDataProvider.refresh();
                     });
@@ -1155,6 +1169,7 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
             }));
         });
         $q.all(promises).then(function() {
+            ngToast.create(egCore.strings.ITEMS_TRANSFERED);
             holdingsSvcInst.fetchAgain().then(function() {
                 $scope.holdingsGridDataProvider.refresh();
             });
@@ -1194,6 +1209,7 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
                             });
                         });
                     } else {
+                        ngToast.create(egCore.strings.ITEMS_TRANSFERED);
                         holdingsSvcInst.fetchAgain().then(function() {
                             $scope.holdingsGridDataProvider.refresh();
                         });
@@ -1373,6 +1389,7 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
     $scope.mark_hold_transfer_dest = function() {
         egCore.hatch.setLocalItem(
             'eg.circ.hold.title_transfer_target', $scope.record_id);
+        ngToast.create(egCore.strings.HOLD_TRANSFER_DEST_MARKED);
     }
 
     // UI presents this option as "all holds"

commit 05a95958d49e6b9fdbd2d817e8261d9e1a57bb58
Author: Mike Rylander <mrylander at gmail.com>
Date:   Wed Mar 30 17:24:32 2016 -0400

    LP#1570091: webstaff: fix deps for ngToast, and put it in place gloablly
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/templates/staff/base.tt2 b/Open-ILS/src/templates/staff/base.tt2
index 1e2c740..7f4c64a 100644
--- a/Open-ILS/src/templates/staff/base.tt2
+++ b/Open-ILS/src/templates/staff/base.tt2
@@ -23,6 +23,7 @@
     <link rel="stylesheet" href="[% ctx.base_path %]/staff/css/print.css" type="text/css" media="print" />
   </head>
   <body>
+    <toast></toast>
 
     <!-- load the navbar template inline since it's used on every page -->
     <script type="text/ng-template" id="eg-navbar-template">
diff --git a/Open-ILS/src/templates/staff/base_js.tt2 b/Open-ILS/src/templates/staff/base_js.tt2
index 4b2861a..4e5ab8f 100644
--- a/Open-ILS/src/templates/staff/base_js.tt2
+++ b/Open-ILS/src/templates/staff/base_js.tt2
@@ -10,6 +10,8 @@
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/build/js/hotkeys.min.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/build/js/angular-file-saver.bundle.min.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/build/js/angular-location-update.min.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/build/js/angular-animate.min.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/build/js/angular-sanitize.min.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/build/js/ngToast.min.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/build/js/angular-tree-control.js"></script>
 
diff --git a/Open-ILS/web/js/ui/default/staff/Gruntfile.js b/Open-ILS/web/js/ui/default/staff/Gruntfile.js
index 796c616..0ba1afe 100644
--- a/Open-ILS/web/js/ui/default/staff/Gruntfile.js
+++ b/Open-ILS/web/js/ui/default/staff/Gruntfile.js
@@ -17,6 +17,10 @@ module.exports = function(grunt) {
           src: [
             'bower_components/angular/angular.min.js',
             'bower_components/angular/angular.min.js.map',
+            'bower_components/angular-animate/angular-animate.min.js',
+            'bower_components/angular-animate/angular-animate.min.js.map',
+            'bower_components/angular-sanitize/angular-sanitize.min.js',
+            'bower_components/angular-sanitize/angular-sanitize.min.js.map',
             'bower_components/angular-route/angular-route.min.js',
             'bower_components/angular-route/angular-route.min.js.map',
             'bower_components/angular-bootstrap/ui-bootstrap.min.js',
@@ -92,6 +96,8 @@ module.exports = function(grunt) {
             // The order is important.
             'build/js/jquery.min.js',
             'build/js/angular.min.js',
+            'build/js/angular-animate.min.js',
+            'build/js/angular-sanitize.min.js',
             'build/js/angular-route.min.js',
             'build/js/ui-bootstrap.min.js',
             'build/js/ui-bootstrap-tpls.min.js',
diff --git a/Open-ILS/web/js/ui/default/staff/bower.json b/Open-ILS/web/js/ui/default/staff/bower.json
index 0e9414e..052978c 100644
--- a/Open-ILS/web/js/ui/default/staff/bower.json
+++ b/Open-ILS/web/js/ui/default/staff/bower.json
@@ -30,6 +30,7 @@
     "angular-location-update": "./extern/angular-location-update/",
     "angular-file-saver": "~1.0.2",
     "ngtoast": "~2.0.0",
-    "angular-tree-control": "~0.2.23"
+    "angular-tree-control": "~0.2.23",
+    "angular-animate": "~1.5.3"
   }
 }

commit c309391a11a21db0b55444ddc35c4bdefbc5138f
Author: Mike Rylander <mrylander at gmail.com>
Date:   Wed Mar 30 13:32:23 2016 -0400

    LP#1570091: webstaff: add tree control and a simple toaster for notifications
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/templates/staff/base.tt2 b/Open-ILS/src/templates/staff/base.tt2
index 9f92d3d..1e2c740 100644
--- a/Open-ILS/src/templates/staff/base.tt2
+++ b/Open-ILS/src/templates/staff/base.tt2
@@ -11,6 +11,10 @@
     [% IF EXPAND_WEB_IMPORTS %]
     <link rel="stylesheet" href="[% WEB_BUILD_PATH %]/css/bootstrap.min.css" />
     <link rel="stylesheet" href="[% WEB_BUILD_PATH %]/css/hotkeys.min.css" />
+    <link rel="stylesheet" href="[% WEB_BUILD_PATH %]/css/ngToast.min.css" />
+    <link rel="stylesheet" href="[% WEB_BUILD_PATH %]/css/ngToast-animations.min.css" />
+    <link rel="stylesheet" href="[% WEB_BUILD_PATH %]/css/tree-control.css" />
+    <link rel="stylesheet" href="[% WEB_BUILD_PATH %]/css/tree-control-attribute.css" />
     [% ELSE %]
     <link rel="stylesheet" href="[% WEB_BUILD_PATH %]/css/evergreen-staff-client-deps.[% EVERGREEN_VERSION %].min.css" />
     [% END %]
diff --git a/Open-ILS/src/templates/staff/base_js.tt2 b/Open-ILS/src/templates/staff/base_js.tt2
index c2b1abb..4b2861a 100644
--- a/Open-ILS/src/templates/staff/base_js.tt2
+++ b/Open-ILS/src/templates/staff/base_js.tt2
@@ -10,6 +10,8 @@
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/build/js/hotkeys.min.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/build/js/angular-file-saver.bundle.min.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/build/js/angular-location-update.min.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/build/js/ngToast.min.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/build/js/angular-tree-control.js"></script>
 
 <!-- IDL / opensrf (network) -->
 <script src="[% ctx.media_prefix %]/js/dojo/opensrf/JSON_v1.js"></script>
diff --git a/Open-ILS/web/js/ui/default/staff/Gruntfile.js b/Open-ILS/web/js/ui/default/staff/Gruntfile.js
index 2c0d289..796c616 100644
--- a/Open-ILS/web/js/ui/default/staff/Gruntfile.js
+++ b/Open-ILS/web/js/ui/default/staff/Gruntfile.js
@@ -24,6 +24,8 @@ module.exports = function(grunt) {
             'bower_components/angular-hotkeys/build/hotkeys.min.js',
             'bower_components/angular-file-saver/dist/angular-file-saver.bundle.min.js',
             'bower_components/angular-location-update/angular-location-update.min.js',
+            'bower_components/angular-tree-control/angular-tree-control.js',
+            'bower_components/ngtoast/dist/ngToast.min.js',
             'bower_components/jquery/dist/jquery.min.js',
           ]
         }]
@@ -37,7 +39,11 @@ module.exports = function(grunt) {
           expand : true,
           src : [
             'bower_components/angular-hotkeys/build/hotkeys.min.css',
-            'bower_components/bootstrap/dist/css/bootstrap.min.css' 
+            'bower_components/bootstrap/dist/css/bootstrap.min.css', 
+            'bower_components/ngtoast/dist/ngToast.min.css',
+            'bower_components/ngtoast/dist/ngToast-animations.min.css',
+            'bower_components/angular-tree-control/css/tree-control.css',
+            'bower_components/angular-tree-control/css/tree-control-attribute.css',
           ]
         }]
       },
@@ -65,7 +71,11 @@ module.exports = function(grunt) {
         files: {
           'build/css/evergreen-staff-client-deps.<%= pkg.version %>.min.css' : [
             'build/css/hotkeys.min.css',
-            'build/css/bootstrap.min.css'
+            'build/css/bootstrap.min.css',
+            'build/css/ngToast.min.css',
+            'build/css/ngToast-animations.min.css',
+            'build/css/tree-control.css',
+            'build/css/tree-control-attribute.css',
           ]
         }
       }
@@ -86,6 +96,8 @@ module.exports = function(grunt) {
             'build/js/ui-bootstrap.min.js',
             'build/js/ui-bootstrap-tpls.min.js',
             'build/js/hotkeys.min.js',
+            'build/js/angular-tree-control.js',
+            'build/js/ngToast.min.js',
             // NOTE: OpenSRF must be installed
             '/openils/lib/javascript/JSON_v1.js',
             '/openils/lib/javascript/opensrf.js',
diff --git a/Open-ILS/web/js/ui/default/staff/bower.json b/Open-ILS/web/js/ui/default/staff/bower.json
index fd1c566..0e9414e 100644
--- a/Open-ILS/web/js/ui/default/staff/bower.json
+++ b/Open-ILS/web/js/ui/default/staff/bower.json
@@ -28,6 +28,8 @@
   "dependencies": {
     "angular-hotkeys": "chieffancypants/angular-hotkeys#~1.3.0",
     "angular-location-update": "./extern/angular-location-update/",
-    "angular-file-saver": "~1.0.2"
+    "angular-file-saver": "~1.0.2",
+    "ngtoast": "~2.0.0",
+    "angular-tree-control": "~0.2.23"
   }
 }

commit b7e42d60141bdf73d104017e6ead3509d73088a9
Author: Mike Rylander <mrylander at gmail.com>
Date:   Fri Mar 25 16:30:02 2016 -0400

    LP#1570091: webstaff: always check for duplciate barcodes
    
    ... and disallow saving if critical data is missing
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/templates/staff/cat/volcopy/t_edit.tt2 b/Open-ILS/src/templates/staff/cat/volcopy/t_edit.tt2
index e110bbf..95e8e50 100644
--- a/Open-ILS/src/templates/staff/cat/volcopy/t_edit.tt2
+++ b/Open-ILS/src/templates/staff/cat/volcopy/t_edit.tt2
@@ -55,7 +55,7 @@
                         </div>
                     </div>
                     <div class="col-xs-2" ng-show="only_vols">
-                        <button class="btn btn-default center-block" ng-click="workingToComplete() && saveAndExit()" type="button">
+                        <button class="btn btn-default center-block" ng-disabled="disableSave()" ng-click="workingToComplete() && saveAndExit()" type="button">
                             <span ng-if="embedded">[% l('Save changes') %]</span>
                             <span ng-if="!embedded">[% l('Save & Exit') %]</span>
                         </button>
@@ -109,7 +109,7 @@
         
                           <eg-grid-menu-item handler="workingToComplete"
                            label="[% l('Store Selected') %]"></eg-grid-menu-item>
-                          <eg-grid-menu-item handler="workingSaveAndExit"
+                          <eg-grid-menu-item disabled="disableSave" handler="workingSaveAndExit"
                            label="[% l('Save & Exit') %]"></eg-grid-menu-item>
         
                         
@@ -143,9 +143,9 @@
                  <eg-grid-menu-item standalone="true" handler="completeToWorking"
                   label="[% l('Edit Selected') %]"></eg-grid-menu-item>
         
-                 <eg-grid-menu-item handler="saveAndContinue"
+                 <eg-grid-menu-item disabled="disableSave" handler="saveAndContinue"
                   label="[% l('Save Completed') %]"></eg-grid-menu-item>
-                 <eg-grid-menu-item handler="saveAndExit"
+                 <eg-grid-menu-item disabled="disableSave" handler="saveAndExit"
                   label="[% l('Save & Exit') %]"></eg-grid-menu-item>
         
                  <eg-grid-field label="[% l('Barcode') %]"     path='barcode' visible></eg-grid-field>
diff --git a/Open-ILS/src/templates/staff/share/t_autogrid.tt2 b/Open-ILS/src/templates/staff/share/t_autogrid.tt2
index 3ffd9f2..e96e2fb 100644
--- a/Open-ILS/src/templates/staff/share/t_autogrid.tt2
+++ b/Open-ILS/src/templates/staff/share/t_autogrid.tt2
@@ -15,7 +15,7 @@
     </button>
     <ul class="dropdown-menu scrollable-menu">
       <li ng-repeat="item in menuItems | filter : { standalone : 'false' }" ng-class="{divider: item.divider}">
-        <a ng-if="!item.divider" href ng-disabled="item.disabled"
+        <a ng-if="!item.divider" href a-disabled="item.disabled()"
           ng-click="item.handler()">{{item.label}}</a>
       </li>
     </ul>
diff --git a/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js b/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js
index ceedfb9..40efacc 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js
@@ -240,9 +240,9 @@ function(egCore , $q) {
         service.copies.push(cp);
     }
 
-    service.checkDuplicateBarcode = function(bc) {
+    service.checkDuplicateBarcode = function(bc, id) {
         var final = false;
-        return egCore.pcrud.search('acp', { deleted : 'f', 'barcode' : bc })
+        return egCore.pcrud.search('acp', { deleted : 'f', 'barcode' : bc, id : { '!=' : id } })
             .then(
                 function () { return final },
                 function () { return final },
@@ -281,6 +281,7 @@ function(egCore , $q) {
         cp.opac_visible('t');
         cp.ref('f');
         cp.mint_condition('t');
+        cp.empty_barcode = true;
 
         var status_setting = isFastAdd ?
             'cat.default_copy_status_fast' :
@@ -328,20 +329,20 @@ function(egCore , $q) {
                 $scope.duplicate_barcode_string = window.duplicate_barcode_string;
                 $scope.empty_barcode_string = window.empty_barcode_string;
 
+                if (!$scope.copy.barcode()) $scope.copy.empty_barcode = true;
+
                 $scope.nextBarcode = function (i) {
                     $scope.focusNext(i, $scope.barcode);
                 }
 
                 $scope.updateBarcode = function () {
                     if ($scope.barcode != '') {
-                        $scope.empty_barcode = false;
+                        $scope.copy.empty_barcode = $scope.empty_barcode = false;
                         $scope.barcode_has_error = !Boolean(itemSvc.checkBarcode($scope.barcode));
-                        if ($scope.copy.isnew()) {
-                            itemSvc.checkDuplicateBarcode($scope.barcode)
-                            .then(function (state) { $scope.duplicate_barcode = state });
-                        }
+                        itemSvc.checkDuplicateBarcode($scope.barcode, $scope.copy.id())
+                            .then(function (state) { $scope.copy.duplicate_barcode = $scope.duplicate_barcode = state });
                     } else {
-                        $scope.empty_barcode = true;
+                        $scope.copy.empty_barcode = $scope.empty_barcode = true;
                     }
                         
                     $scope.copy.barcode($scope.barcode);
@@ -427,6 +428,7 @@ function(egCore , $q) {
         controller : ['$scope','itemSvc','egCore',
             function ( $scope , itemSvc , egCore ) {
                 $scope.callNumber =  $scope.copies[0].call_number();
+                if (!$scope.callNumber.label()) $scope.callNumber.emtpy_label = true;
 
                 $scope.empty_label = false;
                 $scope.empty_label_string = window.empty_label_string;
@@ -557,9 +559,9 @@ function(egCore , $q) {
 
                 $scope.updateLabel = function () {
                     if ($scope.label == '') {
-                        $scope.empty_label = true;
+                        $scope.callNumber.empty_label = $scope.empty_label = true;
                     } else {
-                        $scope.empty_label = false;
+                        $scope.callNumber.empty_label = $scope.empty_label = false;
                     }
                     angular.forEach($scope.copies, function(cp) {
                         cp.call_number().label($scope.label);
@@ -1209,6 +1211,25 @@ function($scope , $q , $window , $routeParams , $location , $timeout , egCore ,
             $scope.workingGridDataProvider.refresh();
         });
 
+        $scope.can_save = false;
+        function check_saveable () {
+            var can_save = true;
+            angular.forEach(
+                itemSvc.copies,
+                function (i) {
+                    if (i.duplicate_barcode || i.empty_barcode || i.call_number().empty_label)
+                        can_save = false;
+                }
+            );
+
+            $scope.can_save = can_save;
+        }
+
+        $scope.disableSave = function () {
+            check_saveable();
+            return !$scope.can_save;
+        }
+
         $scope.focusNextFirst = function(prev_lib,prev_bc) {
             var n;
             var yep = false;

commit 5001213edf127746fdcc9307c5bde5ea18c82da8
Author: Mike Rylander <mrylander at gmail.com>
Date:   Fri Mar 25 13:52:54 2016 -0400

    LP#1539084: webstaff: sort column picker entries by class path and column labels
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    
    Conflicts:
    	Open-ILS/web/js/ui/default/staff/services/grid.js
    
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/web/js/ui/default/staff/services/grid.js b/Open-ILS/web/js/ui/default/staff/services/grid.js
index 4a5ab70..96c7a83 100644
--- a/Open-ILS/web/js/ui/default/staff/services/grid.js
+++ b/Open-ILS/web/js/ui/default/staff/services/grid.js
@@ -1178,8 +1178,7 @@ angular.module('egGridMod',
             var idl_class = egCore.idl.classes[cols.idlClass];
 
             angular.forEach(
-                idl_class.fields.sort(
-                    function(a, b) { return a.name < b.name ? -1 : 1 }),
+                idl_class.fields,
                 function(field) {
                     if (field.virtual) return;
                     if (field.datatype == 'link' || field.datatype == 'org_unit') {
@@ -1218,6 +1217,8 @@ angular.module('egGridMod',
             } else {
                 class_obj = egCore.idl.classes[cols.idlClass];
             }
+            var idl_parent = class_obj;
+            var old_field_label = '';
 
             if (!class_obj) return;
 
@@ -1228,7 +1229,10 @@ angular.module('egGridMod',
             // path before the .*
             // an empty path_parts means expand the root class
             if (path_parts) {
+                var old_field;
                 for (var path_idx in path_parts) {
+                    old_field = idl_field;
+
                     var part = path_parts[path_idx];
                     idl_field = class_obj.field_map[part];
 
@@ -1237,7 +1241,10 @@ angular.module('egGridMod',
                     if (idl_field && idl_field['class'] && (
                         idl_field.datatype == 'link' || 
                         idl_field.datatype == 'org_unit')) {
+                        if (old_field_label) old_field_label += ' : ';
+                        old_field_label += idl_field.label;
                         class_obj = egCore.idl.classes[idl_field['class']];
+                        if (old_field) idl_parent = old_field;
                     } else {
                         if (path_idx < (path_parts.length - 1)) {
                             // we ran out of classes to hop through before
@@ -1263,25 +1270,32 @@ angular.module('egGridMod',
                     col.path = (dotpath ? dotpath + '.' + field.name : field.name);
 
                     // log line below is very chatty.  disable until needed.
-                    // console.debug('egGrid: field: ' +field.name + '; parent field: ' + js2JSON(idl_field));
+                    // console.debug('egGrid: field: ' +field.name + '; parent field: ' + js2JSON(idl_parent));
                     cols.add(col, false, true, 
-                        {idl_parent : idl_field, idl_field : field, idl_class : class_obj});
+                        {idl_parent : idl_parent, idl_field : field, idl_class : class_obj, field_parent_label : old_field_label });
                 });
 
                 cols.columns = cols.columns.sort(
                     function(a, b) {
                         if (a.explicit) return -1;
                         if (b.explicit) return 1;
+
                         if (a.idlclass && b.idlclass) {
-                            return a.idlclass < b.idlclass ? -1 : 1;
-                            return a.idlclass > b.idlclass ? 1 : -1;
+                            if (a.idlclass < b.idlclass) return -1;
+                            if (b.idlclass < a.idlclass) return 1;
                         }
-                        if (a.path && b.path) {
-                            return a.path < b.path ? -1 : 1;
-                            return a.path > b.path ? 1 : -1;
+
+                        if (a.path && b.path && a.path.lastIndexOf('.') && b.path.lastIndexOf('.')) {
+                            if (a.path.substring(0, a.path.lastIndexOf('.')) < b.path.substring(0, b.path.lastIndexOf('.'))) return -1;
+                            if (b.path.substring(0, b.path.lastIndexOf('.')) < a.path.substring(0, a.path.lastIndexOf('.'))) return 1;
+                        }
+
+                        if (a.label && b.label) {
+                            if (a.label < b.label) return -1;
+                            if (b.label < a.label) return 1;
                         }
 
-                        return a.label < b.label ? -1 : 1;
+                        return a.name < b.name ? -1 : 1;
                     }
                 );
 
@@ -1381,8 +1395,8 @@ angular.module('egGridMod',
 
             if (fromExpand && idl_info.idl_class) {
                 column.idlclass = '';
-                if (idl_info.idl_parent) {
-                    column.idlclass = idl_info.idl_parent.label || idl_info.idl_parent.name;
+                if (idl_info.field_parent_label && idl_info.idl_parent.label != idl_info.idl_class.label) {
+                    column.idlclass = (idl_info.field_parent_label || idl_info.idl_parent.label || idl_info.idl_parent.name);
                 } else {
                     column.idlclass += idl_info.idl_class.label || idl_info.idl_class.name;
                 }

commit 3a1efa015770a67b5726d922611c5d8f91560414
Author: Mike Rylander <mrylander at gmail.com>
Date:   Fri Mar 4 12:34:27 2016 -0500

    LP#1570091: webstaff: make the vandelay page taller
    
    ... to support extra-large queue inspection interfaces
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/web/js/ui/default/staff/services/eframe.js b/Open-ILS/web/js/ui/default/staff/services/eframe.js
index 6aa377c..ac54bd5 100644
--- a/Open-ILS/web/js/ui/default/staff/services/eframe.js
+++ b/Open-ILS/web/js/ui/default/staff/services/eframe.js
@@ -115,7 +115,7 @@ angular.module('egCoreMod')
                         // HACK! for vandelay
                         if (!e) {
                             e = $scope.iframe.contentWindow.document.getElementById('vl-body-wrapper');
-                            extra = 50;
+                            extra = 10000;
                         }
 
                         if (!e) {

commit fc007e72a960d93bac7ed514a9bc4f1b8d525240
Author: Mike Rylander <mrylander at gmail.com>
Date:   Fri Mar 4 12:14:07 2016 -0500

    LP#1570091: webstaff: Add Cancel Transit action to item status
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/templates/staff/cat/item/t_list.tt2 b/Open-ILS/src/templates/staff/cat/item/t_list.tt2
index 6dddad3..596a24b 100644
--- a/Open-ILS/src/templates/staff/cat/item/t_list.tt2
+++ b/Open-ILS/src/templates/staff/cat/item/t_list.tt2
@@ -14,11 +14,13 @@
   <eg-grid-action handler="attach_to_peer_bib"
     label="[% l('Link as Conjoined to Previously Marked Bib Record') %]"></eg-grid-action>
   <eg-grid-action handler="selectedHoldingsCopyDelete"
-    label="[% l('Delete Copies') %]"></eg-grid-action>
+    label="[% l('Delete Items') %]"></eg-grid-action>
   <eg-grid-action handler="checkin"
-    label="[% l('Check In Copies') %]"></eg-grid-action>
+    label="[% l('Check In Items') %]"></eg-grid-action>
   <eg-grid-action handler="renew"
-    label="[% l('Renew Copies') %]"></eg-grid-action>
+    label="[% l('Renew Items') %]"></eg-grid-action>
+  <eg-grid-action handler="cancel_transit"
+    label="[% l('Cancel Transit') %]"></eg-grid-action>
     
   <eg-grid-action handler="selectedHoldingsItemStatusTgrEvt" group="[% l('Show') %]"
     label="[% l('Triggered Events') %]"></eg-grid-action>
@@ -33,16 +35,16 @@
     label="[% l('Item as Missing') %]"></eg-grid-action>
     
   <eg-grid-action handler="selectedHoldingsCopyAdd" group="[% l('Add') %]"
-    label="[% l('Copies') %]"></eg-grid-action>
+    label="[% l('Items') %]"></eg-grid-action>
   <eg-grid-action handler="selectedHoldingsVolCopyAdd" group="[% l('Add') %]"
-    label="[% l('Volumes and Copies') %]"></eg-grid-action>
+    label="[% l('Volumes and Items') %]"></eg-grid-action>
 
   <eg-grid-action handler="selectedHoldingsVolEdit" group="[% l('Edit') %]"
     label="[% l('Volumes') %]"></eg-grid-action>
   <eg-grid-action handler="selectedHoldingsCopyEdit" group="[% l('Edit') %]"
-    label="[% l('Copies') %]"></eg-grid-action>
+    label="[% l('Items') %]"></eg-grid-action>
   <eg-grid-action handler="selectedHoldingsVolCopyEdit" group="[% l('Edit') %]"
-    label="[% l('Volumes and Copies') %]"></eg-grid-action>
+    label="[% l('Volumes and Items') %]"></eg-grid-action>
   <eg-grid-action handler="replaceBarcodes" group="[% l('Edit') %]"
     label="[% l('Replace Barcodes') %]"></eg-grid-action>
 
diff --git a/Open-ILS/web/js/ui/default/staff/cat/item/app.js b/Open-ILS/web/js/ui/default/staff/cat/item/app.js
index a2750ce..e42064b 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/item/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/item/app.js
@@ -581,6 +581,15 @@ function($scope , $q , $routeParams , $location , $timeout , $window , egCore ,
             $location.path('/cat/item/' + item.id + '/holds');
     }
 
+    $scope.cancel_transit = function () {
+        var initial_list = copyGrid.selectedItems();
+        angular.forEach(copyGrid.selectedItems(), function(cp) {
+            egCirc.find_copy_transit(null, {copy_barcode:cp.barcode})
+                .then(function(t) { return egCirc.abort_transit(t.id())    })
+                .then(function()  { return add_barcode_to_list(cp.barcode) });
+        });
+    }
+
     $scope.selectedHoldingsDamaged = function () {
         var initial_list = copyGrid.selectedItems();
         egCirc.mark_damaged(gatherSelectedHoldingsIds()).then(function(){

commit 3b311dfb1d6742880d17b479cf28552165e35d06
Author: Mike Rylander <mrylander at gmail.com>
Date:   Thu Mar 3 14:12:37 2016 -0500

    LP#1570091: webstaff: indicate warning/error conditions in volcopy editor
    
    Indicate when the user has an empty barcode, duplicate barcode,
    or empty call number label.
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/templates/staff/cat/volcopy/t_edit.tt2 b/Open-ILS/src/templates/staff/cat/volcopy/t_edit.tt2
index 9511963..e110bbf 100644
--- a/Open-ILS/src/templates/staff/cat/volcopy/t_edit.tt2
+++ b/Open-ILS/src/templates/staff/cat/volcopy/t_edit.tt2
@@ -1,4 +1,7 @@
 <div>
+    <script>window.duplicate_barcode_string = "[% l('Duplicate Barcode') %]";</script>
+    <script>window.empty_barcode_string = "[% l('Missing Barcode') %]";</script>
+    <script>window.empty_label_string = "[% l('Missing Call Number') %]";</script>
     <div ng-show="!only_vols" class="btn-group">
         <label class="btn btn-default" ng-click="show_vols = !show_vols">
             <span ng-show="show_vols" style="padding-right: 5px;">[% l('Hide Volume/Copy Details') %]</span>
diff --git a/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js b/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js
index 51c8dcb..ceedfb9 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js
@@ -240,6 +240,16 @@ function(egCore , $q) {
         service.copies.push(cp);
     }
 
+    service.checkDuplicateBarcode = function(bc) {
+        var final = false;
+        return egCore.pcrud.search('acp', { deleted : 'f', 'barcode' : bc })
+            .then(
+                function () { return final },
+                function () { return final },
+                function () { final = true; }
+            );
+    }
+
     service.fetchIds = function(idList) {
         service.tree = {}; // clear the tree on fetch
         service.copies = []; // clear the copy list on fetch
@@ -301,6 +311,8 @@ function(egCore , $q) {
                     '<input id="{{callNumber.id()}}_{{copy.id()}}"'+
                     ' eg-enter="nextBarcode(copy.id())" class="form-control"'+
                     ' type="text" ng-model="barcode" ng-change="updateBarcode()"/>'+
+                    '<div class="label label-danger" ng-if="duplicate_barcode">{{duplicate_barcode_string}}</div>'+
+                    '<div class="label label-danger" ng-if="empty_barcode">{{empty_barcode_string}}</div>'+
                 '</div>'+
                 '<div class="col-xs-3"><input class="form-control" type="number" ng-model="copy_number" ng-change="updateCopyNo()"/></div>'+
                 '<div class="col-xs-4"><eg-basic-combo-box eg-disabled="record == 0" list="parts" selected="part"></eg-basic-combo-box></div>'+
@@ -311,14 +323,27 @@ function(egCore , $q) {
             function ( $scope , itemSvc , egCore ) {
                 $scope.new_part_id = 0;
                 $scope.barcode_has_error = false;
+                $scope.duplicate_barcode = false;
+                $scope.empty_barcode = false;
+                $scope.duplicate_barcode_string = window.duplicate_barcode_string;
+                $scope.empty_barcode_string = window.empty_barcode_string;
 
                 $scope.nextBarcode = function (i) {
                     $scope.focusNext(i, $scope.barcode);
                 }
 
                 $scope.updateBarcode = function () {
-                    if ($scope.barcode != '')
+                    if ($scope.barcode != '') {
+                        $scope.empty_barcode = false;
                         $scope.barcode_has_error = !Boolean(itemSvc.checkBarcode($scope.barcode));
+                        if ($scope.copy.isnew()) {
+                            itemSvc.checkDuplicateBarcode($scope.barcode)
+                            .then(function (state) { $scope.duplicate_barcode = state });
+                        }
+                    } else {
+                        $scope.empty_barcode = true;
+                    }
+                        
                     $scope.copy.barcode($scope.barcode);
                     $scope.copy.ischanged(1);
                     if (itemSvc.currently_generating)
@@ -385,7 +410,10 @@ function(egCore , $q) {
                 '<div class="col-xs-1">'+
                     '<select ng-disabled="record == 0" class="form-control" ng-model="prefix" ng-change="updatePrefix()" ng-options="p.label() for p in prefix_list"/>'+
                 '</div>'+
-                '<div class="col-xs-2"><input ng-disabled="record == 0" class="form-control" type="text" ng-change="updateLabel()" ng-model="label"/></div>'+
+                '<div class="col-xs-2">'+
+                    '<input ng-disabled="record == 0" class="form-control" type="text" ng-change="updateLabel()" ng-model="label"/>'+
+                    '<div class="label label-danger" ng-if="empty_label">{{empty_label_string}}</div>'+
+                '</div>'+
                 '<div class="col-xs-1">'+
                     '<select ng-disabled="record == 0" class="form-control" ng-model="suffix" ng-change="updateSuffix()" ng-options="s.label() for s in suffix_list"/>'+
                 '</div>'+
@@ -400,6 +428,9 @@ function(egCore , $q) {
             function ( $scope , itemSvc , egCore ) {
                 $scope.callNumber =  $scope.copies[0].call_number();
 
+                $scope.empty_label = false;
+                $scope.empty_label_string = window.empty_label_string;
+
                 $scope.idTracker = function (x) { if (x && x.id) return x.id() };
 
                 // XXX $() is not working! arg
@@ -525,6 +556,11 @@ function(egCore , $q) {
                 }
 
                 $scope.updateLabel = function () {
+                    if ($scope.label == '') {
+                        $scope.empty_label = true;
+                    } else {
+                        $scope.empty_label = false;
+                    }
                     angular.forEach($scope.copies, function(cp) {
                         cp.call_number().label($scope.label);
                         cp.call_number().ischanged(1);

commit 84e988e9684f4a445a820afcb5f8933bb7796f5f
Author: Mike Rylander <mrylander at gmail.com>
Date:   Thu Mar 3 12:13:08 2016 -0500

    LP#1570091: webstaff: adding item status actions
    
      - add items to bucket
      - request items
      - Link as Conjoined to Previously Marked Bib Record
      - Delete Copies
      - Check In Copies
      - Renew Copies
      - Show Triggered Events
      - Show Item Holds
      - Show Record Holds
      - Mark Item As Damaged
      - Mark Item as Missing
      - Add Copies
      - Add Volumes and Copies
      - Edit Volumes
      - Edit Copies
      - Edit Volumes and Copies
      - Replace Barcodes
      - Transfer Items to Previously Marked Library
      - Transfer Items to Previously Marked Volume
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/templates/staff/cat/item/index.tt2 b/Open-ILS/src/templates/staff/cat/item/index.tt2
index 2232a7d..a4f297c 100644
--- a/Open-ILS/src/templates/staff/cat/item/index.tt2
+++ b/Open-ILS/src/templates/staff/cat/item/index.tt2
@@ -13,6 +13,26 @@
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/cat/item/app.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/cat/services/record.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/services/billing.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/services/circ.js"></script>
+[% INCLUDE 'staff/circ/share/circ_strings.tt2' %]
+<script>
+  angular.module('egCoreMod').run(['egStrings', function(s) {
+    s.CONFIRM_DELETE_COPIES_VOLUMES =
+      "[% l('Permanently delete selected copies and/or volumes from catalog?') %]";
+    s.CONFIRM_DELETE_COPIES_VOLUMES_MESSAGE =
+      "[% l('Will delete {{copies}} copies and {{volumes}} volumes') %]";
+    s.CONFIRM_DELETE_PEERS =
+      "[% l('Unlink selected conjoined copies?') %]";
+    s.CONFIRM_DELETE_PEERS_MESSAGE =
+      "[% l('Will unlink {{peers}} copies') %]";
+    s.CONFIRM_TRANSFER_COPIES_TO_MARKED_VOLUME =
+      "[% l('Are you sure you want to transfer selected items to the marked volume?') %]";
+    s.OVERRIDE_TRANSFER_COPIES_TO_MARKED_VOLUME_TITLE =
+      "[% l('One or more items could not be transferred. Override?') %]";
+    s.OVERRIDE_TRANSFER_COPIES_TO_MARKED_VOLUME_BODY =
+      "[% l('Reason(s) include: [_1]', '{{evt_desc}}') %]";
+  }])
+</script>
 [% END %]
 
 <style>
diff --git a/Open-ILS/src/templates/staff/cat/item/t_list.tt2 b/Open-ILS/src/templates/staff/cat/item/t_list.tt2
index f58ea81..6dddad3 100644
--- a/Open-ILS/src/templates/staff/cat/item/t_list.tt2
+++ b/Open-ILS/src/templates/staff/cat/item/t_list.tt2
@@ -7,6 +7,50 @@
   grid-controls="gridControls"
   persist-key="cat.items">
 
+  <eg-grid-action handler="add_copies_to_bucket"
+    label="[% l('Add Items to Bucket') %]"></eg-grid-action>
+  <eg-grid-action handler="requestItems"
+    label="[% l('Request Items') %]"></eg-grid-action>
+  <eg-grid-action handler="attach_to_peer_bib"
+    label="[% l('Link as Conjoined to Previously Marked Bib Record') %]"></eg-grid-action>
+  <eg-grid-action handler="selectedHoldingsCopyDelete"
+    label="[% l('Delete Copies') %]"></eg-grid-action>
+  <eg-grid-action handler="checkin"
+    label="[% l('Check In Copies') %]"></eg-grid-action>
+  <eg-grid-action handler="renew"
+    label="[% l('Renew Copies') %]"></eg-grid-action>
+    
+  <eg-grid-action handler="selectedHoldingsItemStatusTgrEvt" group="[% l('Show') %]"
+    label="[% l('Triggered Events') %]"></eg-grid-action>
+  <eg-grid-action handler="selectedHoldingsItemStatusHolds" group="[% l('Show') %]"
+    label="[% l('Item Holds') %]"></eg-grid-action>
+  <eg-grid-action handler="showBibHolds" group="[% l('Show') %]"
+    label="[% l('Record Holds') %]"></eg-grid-action>
+    
+  <eg-grid-action handler="selectedHoldingsDamaged" group="[% l('Mark') %]"
+    label="[% l('Item as Damaged') %]"></eg-grid-action>
+  <eg-grid-action handler="selectedHoldingsMissing" group="[% l('Mark') %]"
+    label="[% l('Item as Missing') %]"></eg-grid-action>
+    
+  <eg-grid-action handler="selectedHoldingsCopyAdd" group="[% l('Add') %]"
+    label="[% l('Copies') %]"></eg-grid-action>
+  <eg-grid-action handler="selectedHoldingsVolCopyAdd" group="[% l('Add') %]"
+    label="[% l('Volumes and Copies') %]"></eg-grid-action>
+
+  <eg-grid-action handler="selectedHoldingsVolEdit" group="[% l('Edit') %]"
+    label="[% l('Volumes') %]"></eg-grid-action>
+  <eg-grid-action handler="selectedHoldingsCopyEdit" group="[% l('Edit') %]"
+    label="[% l('Copies') %]"></eg-grid-action>
+  <eg-grid-action handler="selectedHoldingsVolCopyEdit" group="[% l('Edit') %]"
+    label="[% l('Volumes and Copies') %]"></eg-grid-action>
+  <eg-grid-action handler="replaceBarcodes" group="[% l('Edit') %]"
+    label="[% l('Replace Barcodes') %]"></eg-grid-action>
+
+  <eg-grid-action handler="changeItemOwningLib" group="[% l('Transfer') %]"
+    label="[% l('Items to Previously Marked Library') %]"></eg-grid-action>
+  <eg-grid-action handler="transferItems" group="[% l('Transfer') %]"
+    label="[% l('Items to Previously Marked Volume') %]"></eg-grid-action>
+
   <eg-grid-field label="[% l('Barcode') %]"     path='barcode' visible></eg-grid-field>
   <eg-grid-field label="[% l('Call Number') %]" path="call_number.label" visible></eg-grid-field>
   <eg-grid-field label="[% l('Location') %]"    path="location.name" visible></eg-grid-field>
diff --git a/Open-ILS/web/js/ui/default/staff/cat/item/app.js b/Open-ILS/web/js/ui/default/staff/cat/item/app.js
index 837b5b3..a2750ce 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/item/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/item/app.js
@@ -3,7 +3,7 @@
  */
 
 angular.module('egItemStatus', 
-    ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'egGridMod'])
+    ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'egGridMod', 'egUserMod'])
 
 .filter('boolText', function(){
     return function (v) {
@@ -139,8 +139,8 @@ function($scope , $location , egCore , egGridDataProvider , itemSvc) {
  * List view - grid stuff
  */
 .controller('ListCtrl', 
-       ['$scope','$q','$routeParams','$location','$timeout','egCore','egGridDataProvider','itemSvc',
-function($scope , $q , $routeParams , $location , $timeout , egCore , egGridDataProvider , itemSvc) {
+       ['$scope','$q','$routeParams','$location','$timeout','$window','egCore','egGridDataProvider','itemSvc','egUser','$modal','egCirc','egConfirmDialog',
+function($scope , $q , $routeParams , $location , $timeout , $window , egCore , egGridDataProvider , itemSvc , egUser , $modal , egCirc , egConfirmDialog) {
     var copyId = [];
     var cp_list = $routeParams.idList;
     if (cp_list) {
@@ -211,6 +211,10 @@ function($scope , $q , $routeParams , $location , $timeout , egCore , egGridData
         })
     }
 
+    var add_barcode_to_list = function (b) {
+        $scope.context.search({barcode:b});
+    }
+
     $scope.context.toggleDisplay = function() {
         var item = copyGrid.selectedItems()[0];
         if (item) 
@@ -223,6 +227,566 @@ function($scope , $q , $routeParams , $location , $timeout , egCore , egGridData
             $location.path('/cat/item/' + item.id + '/triggered_events');
     }
 
+    function gatherSelectedRecordIds () {
+        var rid_list = [];
+        angular.forEach(
+            copyGrid.selectedItems(),
+            function (item) {
+                if (rid_list.indexOf(item['call_number.record.id']) == -1)
+                    rid_list.push(item['call_number.record.id'])
+            }
+        );
+        return rid_list;
+    }
+
+    function gatherSelectedVolumeIds (rid) {
+        var cn_id_list = [];
+        angular.forEach(
+            copyGrid.selectedItems(),
+            function (item) {
+                if (rid && item['call_number.record.id'] != rid) return;
+                if (cn_id_list.indexOf(item['call_number.id']) == -1)
+                    cn_id_list.push(item['call_number.id'])
+            }
+        );
+        return cn_id_list;
+    }
+
+    function gatherSelectedHoldingsIds (rid) {
+        var cp_id_list = [];
+        angular.forEach(
+            copyGrid.selectedItems(),
+            function (item) {
+                if (rid && item['call_number.record.id'] != rid) return;
+                cp_id_list.push(item.id)
+            }
+        );
+        return cp_id_list;
+    }
+
+    $scope.add_copies_to_bucket = function() {
+        var copy_list = gatherSelectedHoldingsIds();
+        if (copy_list.length == 0) return;
+
+        return $modal.open({
+            templateUrl: './cat/catalog/t_add_to_bucket',
+            animation: true,
+            size: 'md',
+            controller:
+                   ['$scope','$modalInstance',
+            function($scope , $modalInstance) {
+
+                $scope.bucket_id = 0;
+                $scope.newBucketName = '';
+                $scope.allBuckets = [];
+
+                egCore.net.request(
+                    'open-ils.actor',
+                    'open-ils.actor.container.retrieve_by_class.authoritative',
+                    egCore.auth.token(), egCore.auth.user().id(),
+                    'copy', 'staff_client'
+                ).then(function(buckets) { $scope.allBuckets = buckets; });
+
+                $scope.add_to_bucket = function() {
+                    var promises = [];
+                    angular.forEach(copy_list, function (cp) {
+                        var item = new egCore.idl.ccbi()
+                        item.bucket($scope.bucket_id);
+                        item.target_copy(cp);
+                        promises.push(
+                            egCore.net.request(
+                                'open-ils.actor',
+                                'open-ils.actor.container.item.create',
+                                egCore.auth.token(), 'copy', item
+                            )
+                        );
+
+                        return $q.all(promises).then(function() {
+                            $modalInstance.close();
+                        });
+                    });
+                }
+
+                $scope.add_to_new_bucket = function() {
+                    var bucket = new egCore.idl.ccb();
+                    bucket.owner(egCore.auth.user().id());
+                    bucket.name($scope.newBucketName);
+                    bucket.description('');
+                    bucket.btype('staff_client');
+
+                    return egCore.net.request(
+                        'open-ils.actor',
+                        'open-ils.actor.container.create',
+                        egCore.auth.token(), 'copy', bucket
+                    ).then(function(bucket) {
+                        $scope.bucket_id = bucket;
+                        $scope.add_to_bucket();
+                    });
+                }
+
+                $scope.cancel = function() {
+                    $modalInstance.dismiss();
+                }
+            }]
+        });
+    }
+
+    $scope.requestItems = function() {
+        var copy_list = gatherSelectedHoldingsIds();
+        if (copy_list.length == 0) return;
+
+        return $modal.open({
+            templateUrl: './cat/catalog/t_request_items',
+            animation: true,
+            controller:
+                   ['$scope','$modalInstance','egUser',
+            function($scope , $modalInstance , egUser) {
+                $scope.user = null;
+                $scope.first_user_fetch = true;
+
+                $scope.hold_data = {
+                    hold_type : 'C',
+                    copy_list : copy_list,
+                    pickup_lib: egCore.org.get(egCore.auth.user().ws_ou()),
+                    user      : egCore.auth.user().id()
+                };
+
+                egUser.get( $scope.hold_data.user ).then(function(u) {
+                    $scope.user = u;
+                    $scope.barcode = u.card().barcode();
+                    $scope.user_name = egUser.format_name(u);
+                    $scope.hold_data.user = u.id();
+                });
+
+                $scope.user_name = '';
+                $scope.barcode = '';
+                $scope.$watch('barcode', function (n) {
+                    if (!$scope.first_user_fetch) {
+                        egUser.getByBarcode(n).then(function(u) {
+                            $scope.user = u;
+                            $scope.user_name = egUser.format_name(u);
+                            $scope.hold_data.user = u.id();
+                        }, function() {
+                            $scope.user = null;
+                            $scope.user_name = '';
+                            delete $scope.hold_data.user;
+                        });
+                    }
+                    $scope.first_user_fetch = false;
+                });
+
+                $scope.ok = function(h) {
+                    var args = {
+                        patronid  : h.user,
+                        hold_type : h.hold_type,
+                        pickup_lib: h.pickup_lib.id(),
+                        depth     : 0
+                    };
+
+                    egCore.net.request(
+                        'open-ils.circ',
+                        'open-ils.circ.holds.test_and_create.batch.override',
+                        egCore.auth.token(), args, h.copy_list
+                    );
+
+                    $modalInstance.close();
+                }
+
+                $scope.cancel = function($event) {
+                    $modalInstance.dismiss();
+                    $event.preventDefault();
+                }
+            }]
+        });
+    }
+
+    $scope.replaceBarcodes = function() {
+        angular.forEach(copyGrid.selectedItems(), function (cp) {
+            $modal.open({
+                templateUrl: './cat/share/t_replace_barcode',
+                animation: true,
+                controller:
+                           ['$scope','$modalInstance',
+                    function($scope , $modalInstance) {
+                        $scope.isModal = true;
+                        $scope.focusBarcode = false;
+                        $scope.focusBarcode2 = true;
+                        $scope.barcode1 = cp.barcode;
+
+                        $scope.updateBarcode = function() {
+                            $scope.copyNotFound = false;
+                            $scope.updateOK = false;
+
+                            egCore.pcrud.search('acp',
+                                {deleted : 'f', barcode : $scope.barcode1})
+                            .then(function(copy) {
+
+                                if (!copy) {
+                                    $scope.focusBarcode = true;
+                                    $scope.copyNotFound = true;
+                                    return;
+                                }
+
+                                $scope.copyId = copy.id();
+                                copy.barcode($scope.barcode2);
+
+                                egCore.pcrud.update(copy).then(function(stat) {
+                                    $scope.updateOK = stat;
+                                    $scope.focusBarcode = true;
+                                    if (stat) add_barcode_to_list(copy.barcode());
+                                });
+
+                            });
+                            $modalInstance.close();
+                        }
+
+                        $scope.cancel = function($event) {
+                            $modalInstance.dismiss();
+                            $event.preventDefault();
+                        }
+                    }
+                ]
+            });
+        });
+    }
+
+    $scope.attach_to_peer_bib = function() {
+        if (copyGrid.selectedItems().length == 0) return;
+
+        egCore.hatch.getItem('eg.cat.marked_conjoined_record').then(function(target_record) {
+            if (!target_record) return;
+
+            return $modal.open({
+                templateUrl: './cat/catalog/t_conjoined_selector',
+                animation: true,
+                controller:
+                       ['$scope','$modalInstance',
+                function($scope , $modalInstance) {
+                    $scope.update = false;
+
+                    $scope.peer_type = null;
+                    $scope.peer_type_list = [];
+
+                    get_peer_types = function() {
+                        if (egCore.env.bpt)
+                            return $q.when(egCore.env.bpt.list);
+
+                        return egCore.pcrud.retrieveAll('bpt', null, {atomic : true})
+                        .then(function(list) {
+                            egCore.env.absorbList(list, 'bpt');
+                            return list;
+                        });
+                    }
+
+                    get_peer_types().then(function(list){
+                        $scope.peer_type_list = list;
+                    });
+
+                    $scope.ok = function(type) {
+                        var promises = [];
+
+                        angular.forEach(copyGrid.selectedItems(), function (cp) {
+                            var n = new egCore.idl.bpbcm();
+                            n.isnew(true);
+                            n.peer_record(target_record);
+                            n.target_copy(cp.id);
+                            n.peer_type(type);
+                            promises.push(egCore.pcrud.create(n).then(function(){add_barcode_to_list(cp.barcode)}));
+                        });
+
+                        return $q.all(promises).then(function(){$modalInstance.close()});
+                    }
+
+                    $scope.cancel = function($event) {
+                        $modalInstance.dismiss();
+                        $event.preventDefault();
+                    }
+                }]
+            });
+        });
+    }
+
+    $scope.selectedHoldingsCopyDelete = function () {
+        var copy_list = gatherSelectedHoldingsIds();
+        if (copy_list.length == 0) return;
+
+        var copy_objects = [];
+        egCore.pcrud.search('acp',
+            {deleted : 'f', id : copy_list},
+            { flesh : 1, flesh_fields : { acp : ['call_number'] } }
+        ).then(function(copy) {
+            copy_objects.push(copy);
+        }).then(function() {
+
+            var cnHash = {};
+            var perCnCopies = {};
+
+            var cn_count = 0;
+            var cp_count = 0;
+
+            angular.forEach(
+                copy_objects,
+                function (cp) {
+                    cp.isdeleted(1);
+                    cp_count++;
+                    var cn_id = cp.call_number().id();
+                    if (!cnHash[cn_id]) {
+                        cnHash[cn_id] = cp.call_number();
+                        perCnCopies[cn_id] = [cp];
+                    } else {
+                        perCnCopies[cn_id].push(cp);
+                    }
+                    cp.call_number(cn_id); // prevent loops in JSON-ification
+                }
+            );
+
+            angular.forEach(perCnCopies, function (v, k) {
+                cnHash[k].copies(v);
+            });
+
+            cnList = [];
+            angular.forEach(cnHash, function (v, k) {
+                cnList.push(v);
+            });
+
+            if (cnList.length == 0) return;
+
+            var flags = {};
+
+            egConfirmDialog.open(
+                egCore.strings.CONFIRM_DELETE_COPIES_VOLUMES,
+                egCore.strings.CONFIRM_DELETE_COPIES_VOLUMES_MESSAGE,
+                {copies : cp_count, volumes : cn_count}
+            ).result.then(function() {
+                egCore.net.request(
+                    'open-ils.cat',
+                    'open-ils.cat.asset.volume.fleshed.batch.update.override',
+                    egCore.auth.token(), cnList, 1, flags
+                ).then(function(){
+                    angular.forEach(copyGrid.selectedItems(), function(cp){add_barcode_to_list(cp.barcode)});
+                });
+            });
+        });
+    }
+
+    $scope.selectedHoldingsItemStatusTgrEvt= function() {
+        var item = copyGrid.selectedItems()[0];
+        if (item)
+            $location.path('/cat/item/' + item.id + '/triggered_events');
+    }
+
+    $scope.selectedHoldingsItemStatusHolds= function() {
+        var item = copyGrid.selectedItems()[0];
+        if (item)
+            $location.path('/cat/item/' + item.id + '/holds');
+    }
+
+    $scope.selectedHoldingsDamaged = function () {
+        var initial_list = copyGrid.selectedItems();
+        egCirc.mark_damaged(gatherSelectedHoldingsIds()).then(function(){
+            angular.forEach(initial_list, function(cp){add_barcode_to_list(cp.barcode)});
+        });
+    }
+
+    $scope.selectedHoldingsMissing = function () {
+        var initial_list = copyGrid.selectedItems();
+        egCirc.mark_missing(gatherSelectedHoldingsIds()).then(function(){
+            angular.forEach(initial_list, function(cp){add_barcode_to_list(cp.barcode)});
+        });
+    }
+
+    $scope.checkin = function () {
+        angular.forEach(copyGrid.selectedItems(), function (cp) {
+            egCirc.checkin({copy_barcode:cp.barcode}).then(
+                function() { add_barcode_to_list(cp.barcode) }
+            );
+        });
+    }
+
+    $scope.renew = function () {
+        angular.forEach(copyGrid.selectedItems(), function (cp) {
+            egCirc.renew({copy_barcode:cp.barcode}).then(
+                function() { add_barcode_to_list(cp.barcode) }
+            );
+        });
+    }
+
+
+    var spawnHoldingsAdd = function (vols,copies){
+        angular.forEach(gatherSelectedRecordIds(), function (r) {
+            var raw = [];
+            if (copies) { // just a copy on existing volumes
+                angular.forEach(gatherSelectedVolumeIds(r), function (v) {
+                    raw.push( {callnumber : v} );
+                });
+            } else if (vols) {
+                angular.forEach(
+                    gatherSelectedHoldingsIds(r),
+                    function (i) {
+                        angular.forEach(copyGrid.selectedItems(), function(item) {
+                            if (i == item.id) raw.push({owner : item['call_number.owning_lib']});
+                        });
+                    }
+                );
+            }
+
+            if (raw.length == 0) raw.push({});
+
+            egCore.net.request(
+                'open-ils.actor',
+                'open-ils.actor.anon_cache.set_value',
+                null, 'edit-these-copies', {
+                    record_id: r,
+                    raw: raw,
+                    hide_vols : false,
+                    hide_copies : false
+                }
+            ).then(function(key) {
+                if (key) {
+                    var url = egCore.env.basePath + 'cat/volcopy/' + key;
+                    $timeout(function() { $window.open(url, '_blank') });
+                } else {
+                    alert('Could not create anonymous cache key!');
+                }
+            });
+        });
+    }
+    $scope.selectedHoldingsVolCopyAdd = function () { spawnHoldingsAdd(true,false) }
+    $scope.selectedHoldingsCopyAdd = function () { spawnHoldingsAdd(false,true) }
+
+    $scope.showBibHolds = function () {
+        angular.forEach(gatherSelectedRecordIds(), function (r) {
+            var url = egCore.env.basePath + 'cat/catalog/record/' + r + '/holds';
+            $timeout(function() { $window.open(url, '_blank') });
+        });
+    }
+
+    var spawnHoldingsEdit = function (hide_vols,hide_copies){
+        angular.forEach(gatherSelectedRecordIds(), function (r) {
+            egCore.net.request(
+                'open-ils.actor',
+                'open-ils.actor.anon_cache.set_value',
+                null, 'edit-these-copies', {
+                    record_id: r,
+                    copies: gatherSelectedHoldingsIds(r),
+                    raw: {},
+                    hide_vols : hide_vols,
+                    hide_copies : hide_copies
+                }
+            ).then(function(key) {
+                if (key) {
+                    var url = egCore.env.basePath + 'cat/volcopy/' + key;
+                    $timeout(function() { $window.open(url, '_blank') });
+                } else {
+                    alert('Could not create anonymous cache key!');
+                }
+            });
+        });
+    }
+    $scope.selectedHoldingsVolCopyEdit = function () { spawnHoldingsEdit(false,false) }
+    $scope.selectedHoldingsVolEdit = function () { spawnHoldingsEdit(false,true) }
+    $scope.selectedHoldingsCopyEdit = function () { spawnHoldingsEdit(true,false) }
+
+    // this "transfers" selected copies to a new owning library,
+    // auto-creating volumes and deleting unused volumes as required.
+    $scope.changeItemOwningLib = function() {
+        var xfer_target = egCore.hatch.getLocalItem('eg.cat.volume_transfer_target');
+        var items = copyGrid.selectedItems();
+        if (!xfer_target || !items.length) {
+            return;
+        }
+        var vols_to_move   = {};
+        var copies_to_move = {};
+        angular.forEach(items, function(item) {
+            if (item['call_number.owning_lib'] != xfer_target) {
+                if (item['call_number.id'] in vols_to_move) {
+                    copies_to_move[item['call_number.id']].push(item.id);
+                } else {
+                    vols_to_move[item['call_number.id']] = {
+                        label       : item['call_number.label'],
+                        label_class : item['call_number.label_class'],
+                        record      : item['call_number.record.id'],
+                        prefix      : item['call_number.prefix.id'],
+                        suffix      : item['call_number.suffix.id']
+                    };
+                    copies_to_move[item['call_number.id']] = new Array;
+                    copies_to_move[item['call_number.id']].push(item.id);
+                }
+            }
+        });
+
+        var promises = [];
+        angular.forEach(vols_to_move, function(vol) {
+            promises.push(egCore.net.request(
+                'open-ils.cat',
+                'open-ils.cat.call_number.find_or_create',
+                egCore.auth.token(),
+                vol.label,
+                vol.record,
+                xfer_target,
+                vol.prefix,
+                vol.suffix,
+                vol.label_class
+            ).then(function(resp) {
+                var evt = egCore.evt.parse(resp);
+                if (evt) return;
+                return egCore.net.request(
+                    'open-ils.cat',
+                    'open-ils.cat.transfer_copies_to_volume',
+                    egCore.auth.token(),
+                    resp.acn_id,
+                    copies_to_move[vol.id]
+                );
+            }));
+        });
+
+        angular.forEach(
+            copyGrid.selectedItems(),
+            function(cp){
+                promises.push(
+                    function(){ add_barcode_to_list(cp.barcode) }
+                )
+            }
+        );
+        $q.all(promises);
+    }
+
+    $scope.transferItems = function (){
+        var xfer_target = egCore.hatch.getLocalItem('eg.cat.item_transfer_target');
+        var copy_ids = gatherSelectedHoldingsIds();
+        if (xfer_target && copy_ids.length > 0) {
+            egCore.net.request(
+                'open-ils.cat',
+                'open-ils.cat.transfer_copies_to_volume',
+                egCore.auth.token(),
+                xfer_target,
+                copy_ids
+            ).then(
+                function(resp) { // oncomplete
+                    var evt = egCore.evt.parse(resp);
+                    egConfirmDialog.open(
+                        egCore.strings.OVERRIDE_TRANSFER_COPIES_TO_MARKED_VOLUME_TITLE,
+                        egCore.strings.OVERRIDE_TRANSFER_COPIES_TO_MARKED_VOLUME_BODY,
+                        {'evt_desc': evt.desc}
+                    ).result.then(function() {
+                        egCore.net.request(
+                            'open-ils.cat',
+                            'open-ils.cat.transfer_copies_to_volume.override',
+                            egCore.auth.token(),
+                            xfer_target,
+                            copy_ids,
+                            { events: ['TITLE_LAST_COPY', 'COPY_DELETE_WARNING'] }
+                        );
+                    });
+                },
+                null, // onerror
+                null // onprogress
+            ).then(function() {
+                    angular.forEach(copyGrid.selectedItems(), function(cp){add_barcode_to_list(cp.barcode)});
+            });
+        }
+    }
+
     if (copyId.length > 0) {
         itemSvc.fetch(null,copyId).then(
             function() {

commit 1f0af17da634454c59ca62af07db95db212fcbe5
Author: Mike Rylander <mrylander at gmail.com>
Date:   Wed Feb 24 11:32:50 2016 -0500

    LP#1570091: webstaff: fix handling of default copy status
    
    The status must be an int, so we cast it when coming from an YAOUS
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js b/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js
index 78c14b0..51c8dcb 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js
@@ -279,8 +279,9 @@ function(egCore , $q) {
             [status_setting],
             owningLib
         ).then(function(set) {
-            var default_ccs = set[status_setting] || 
-                (isFastAdd ? 0 : 5); // 0 is Available, 5 is In Process
+            var default_ccs = parseInt(set[status_setting]);
+            if (isNaN(default_ccs))
+                default_ccs = (isFastAdd ? 0 : 5); // 0 is Available, 5 is In Process
             cp.status(default_ccs);
         });
 

commit 4d4807e49c3f2f18f7ec1340182193a5c50deab3
Author: Mike Rylander <mrylander at gmail.com>
Date:   Wed Feb 24 11:02:42 2016 -0500

    LP#1570091: Show (index / hit count) in the Back To Results button
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    
    Conflicts:
    	Open-ILS/src/templates/opac/parts/record/navigation.tt2
    
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/templates/opac/parts/record/navigation.tt2 b/Open-ILS/src/templates/opac/parts/record/navigation.tt2
index 003f6bd..e44e8da 100644
--- a/Open-ILS/src/templates/opac/parts/record/navigation.tt2
+++ b/Open-ILS/src/templates/opac/parts/record/navigation.tt2
@@ -2,6 +2,11 @@
 <div class="rdetail_header">
     <span class="rdetail_results">
         <a href='[% mkurl(ctx.opac_root _ '/results', {}, stop_parms); %]'><span class="nav_arrow_fix">◄</span> [% l('Search Results') %]</a>
+        <!-- stash these in JS for the web staff client to find -->
+        <script>
+            window.search_result_hit_count = [% ctx.hit_count %];
+            window.search_result_index = [% ctx.search_result_index %];
+        </script>
         <span class="rdetail_result_count">
             [% l('Showing Item [_1] of [_2]', ctx.search_result_index + 1, ctx.hit_count) %]
         </span>
diff --git a/Open-ILS/src/templates/staff/cat/catalog/t_catalog.tt2 b/Open-ILS/src/templates/staff/cat/catalog/t_catalog.tt2
index 39ec701..3dd6638 100644
--- a/Open-ILS/src/templates/staff/cat/catalog/t_catalog.tt2
+++ b/Open-ILS/src/templates/staff/cat/catalog/t_catalog.tt2
@@ -18,7 +18,7 @@
        [% l('End') %]
     </button>
     <button type="button" ng-disabled="from_route" class="btn btn-default" ng-click="opac_call('rdetailBackToResults', true)">
-       [% l('Back To Results') %]
+       [% l('Back To Results') %] ( {{ search_result_index }} / {{ search_result_hit_count }} )
     </button>
   </div>
   <div class="col-md-3">
diff --git a/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
index bfa8ee6..e2540b6 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
@@ -248,6 +248,25 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
     $scope.opac_iframe = null;
     $scope.parts_iframe = null;
 
+    $scope.search_result_index = 1;
+    $scope.search_result_hit_count = 1;
+
+    $scope.$watch(
+        'opac_iframe.dom.contentWindow.search_result_index',
+        function (n,o) {
+            if (!isNaN(parseInt(n)))
+                $scope.search_result_index = n + 1;
+        }
+    );
+
+    $scope.$watch(
+        'opac_iframe.dom.contentWindow.search_result_hit_count',
+        function (n,o) {
+            if (!isNaN(parseInt(n)))
+                $scope.search_result_hit_count = n;
+        }
+    );
+
     $scope.in_opac_call = false;
     $scope.opac_call = function (opac_frame_function, force_opac_tab) {
         if ($scope.opac_iframe) {

commit 275d24ece6f3d722c9c8f682ab0f79566590e603
Author: Mike Rylander <mrylander at gmail.com>
Date:   Wed Feb 24 09:54:29 2016 -0500

    LP#1570091: webstaff: tweak display of fixed fields in MARC editor
    
    - Add border to FF boxes
    - align Source mnewmonic for the day that the seed data exists
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>
    Signed-off-by: Galen Charlton <gmc at esilibrary.com>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/templates/staff/cat/share/t_marcedit.tt2 b/Open-ILS/src/templates/staff/cat/share/t_marcedit.tt2
index 5b61974..352da20 100644
--- a/Open-ILS/src/templates/staff/cat/share/t_marcedit.tt2
+++ b/Open-ILS/src/templates/staff/cat/share/t_marcedit.tt2
@@ -61,7 +61,7 @@
         <div class="row col-md-12">
             <eg-marc-edit-fixed-field fixed-field="Type" record="record"></eg-marc-edit-fixed-field>
             <eg-marc-edit-fixed-field fixed-field="ELvl" record="record"></eg-marc-edit-fixed-field>
-            <eg-marc-edit-fixed-field fixed-field="Srce" record="record"></eg-marc-edit-fixed-field>
+            <eg-marc-edit-fixed-field fixed-field="Source" record="record"></eg-marc-edit-fixed-field>
             <eg-marc-edit-fixed-field fixed-field="Audn" record="record"></eg-marc-edit-fixed-field>
             <eg-marc-edit-fixed-field fixed-field="Ctrl" record="record"></eg-marc-edit-fixed-field>
             <eg-marc-edit-fixed-field fixed-field="Lang" record="record"></eg-marc-edit-fixed-field>
@@ -145,7 +145,6 @@
             <eg-marc-edit-fixed-field fixed-field="Name" record="record"></eg-marc-edit-fixed-field>
             <eg-marc-edit-fixed-field fixed-field="Status" record="record"></eg-marc-edit-fixed-field>
             <eg-marc-edit-fixed-field fixed-field="ModRec" record="record"></eg-marc-edit-fixed-field>
-            <eg-marc-edit-fixed-field fixed-field="Source" record="record"></eg-marc-edit-fixed-field>
         </div>
       </div>
     </div>
diff --git a/Open-ILS/src/templates/staff/css/cat.css.tt2 b/Open-ILS/src/templates/staff/css/cat.css.tt2
index 6502673..6f51d0f 100644
--- a/Open-ILS/src/templates/staff/css/cat.css.tt2
+++ b/Open-ILS/src/templates/staff/css/cat.css.tt2
@@ -41,6 +41,10 @@ input.marcedit:focus {
     max-width: 800px;
 }
 
+.fixed-field-box {
+    border: 1px solid gray;
+}
+
 .marcsfvalue {
     border-left: 0px !important;
 }
diff --git a/Open-ILS/web/js/ui/default/staff/cat/services/marcedit.js b/Open-ILS/web/js/ui/default/staff/cat/services/marcedit.js
index 0a42054..c4862f9 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/services/marcedit.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/services/marcedit.js
@@ -197,6 +197,7 @@ angular.module('egMarcMod', ['egCoreMod', 'ui.bootstrap'])
         replace: true,
         controller : ['$scope', '$element', 'egTagTable',
             function ( $scope ,  $element ,  egTagTable) {
+                $($element).removeClass('fixed-field-box');
                 $($element).children().css({ display : 'none' });
                 $scope.me = null;
                 $scope.content = null; // this is where context menus dump their values
@@ -225,6 +226,7 @@ angular.module('egMarcMod', ['egCoreMod', 'ui.bootstrap'])
                                     if (ff.fixed_field == $scope.fixedField && ff.rec_type == $scope.rtype) {
                                         $scope.me = ff;
                                         $scope.ready = true;
+                                        $($element).addClass('fixed-field-box');
                                         $($element).children().css({ display : 'inline' });
 
                                         var input = $($element).find('input');

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

Summary of changes:
 .../src/templates/opac/parts/record/navigation.tt2 |    5 +
 Open-ILS/src/templates/staff/base.tt2              |    5 +
 Open-ILS/src/templates/staff/base_js.tt2           |    4 +
 Open-ILS/src/templates/staff/cat/catalog/index.tt2 |   14 +
 .../src/templates/staff/cat/catalog/t_catalog.tt2  |    2 +-
 .../src/templates/staff/cat/catalog/t_holdings.tt2 |    2 +-
 Open-ILS/src/templates/staff/cat/item/index.tt2    |   20 +
 Open-ILS/src/templates/staff/cat/item/t_list.tt2   |   46 ++
 .../src/templates/staff/cat/share/t_marcedit.tt2   |    3 +-
 .../src/templates/staff/cat/volcopy/t_edit.tt2     |   11 +-
 Open-ILS/src/templates/staff/css/cat.css.tt2       |    4 +
 Open-ILS/src/templates/staff/share/t_autogrid.tt2  |    2 +-
 Open-ILS/web/js/ui/default/staff/Gruntfile.js      |   22 +-
 Open-ILS/web/js/ui/default/staff/bower.json        |    5 +-
 .../web/js/ui/default/staff/cat/catalog/app.js     |   50 ++-
 Open-ILS/web/js/ui/default/staff/cat/item/app.js   |  579 +++++++++++++++++++-
 .../js/ui/default/staff/cat/services/marcedit.js   |    2 +
 .../web/js/ui/default/staff/cat/volcopy/app.js     |   66 +++-
 .../web/js/ui/default/staff/services/eframe.js     |    2 +-
 Open-ILS/web/js/ui/default/staff/services/grid.js  |   38 +-
 .../web/js/ui/default/staff/services/statusbar.js  |    3 +-
 21 files changed, 847 insertions(+), 38 deletions(-)


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list