webstaff: Conjoined items management
authorMike Rylander <mrylander@gmail.com>
Wed, 9 Sep 2015 14:17:05 +0000 (10:17 -0400)
committerJason Stephenson <jstephenson@mvlc.org>
Mon, 14 Sep 2015 19:44:21 +0000 (15:44 -0400)
Signed-off-by: Mike Rylander <mrylander@gmail.com>
Signed-off-by: Galen Charlton <gmc@esilibrary.com>
Signed-off-by: Jason Stephenson <jstephenson@mvlc.org>
Open-ILS/src/templates/staff/cat/catalog/index.tt2
Open-ILS/src/templates/staff/cat/catalog/t_catalog.tt2
Open-ILS/src/templates/staff/cat/catalog/t_conjoined_items.tt2 [new file with mode: 0644]
Open-ILS/src/templates/staff/cat/catalog/t_conjoined_selector.tt2
Open-ILS/web/js/ui/default/staff/cat/catalog/app.js

index 9635c5d..f5db2fe 100644 (file)
       "[% 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') %]";
   }])
 </script>
 
index 9b5eaba..62db3ca 100644 (file)
         [% l('Holdings View') %]
     </a>
   </li>
+  <li ng-class="{disabled : !record_id, active : record_tab == 'conjoined'}">
+    <a ng-click="set_record_tab('conjoined')" >
+        [% l('Conjoined Items') %]
+    </a>
+  </li>
 </ul>
 
 <div class="tab-content">
@@ -86,5 +91,8 @@
     <div ng-if="record_tab == 'holdings'">
       [% INCLUDE 'staff/cat/catalog/t_holdings.tt2' %]
     </div>
+    <div ng-if="record_tab == 'conjoined'">
+      [% INCLUDE 'staff/cat/catalog/t_conjoined_items.tt2' %]
+    </div>
   </div>
 </div>
diff --git a/Open-ILS/src/templates/staff/cat/catalog/t_conjoined_items.tt2 b/Open-ILS/src/templates/staff/cat/catalog/t_conjoined_items.tt2
new file mode 100644 (file)
index 0000000..a4ad8c9
--- /dev/null
@@ -0,0 +1,35 @@
+
+<div>
+  <eg-grid
+    id-field="id"
+    idl-class="bpbcm"
+    features="-display,-sort,-multisort,-pagination,-picker,-actions"
+    items-provider="conjoinedGridDataProvider"
+    grid-controls="conjoinedGridControls"
+    persist-key="cat.peer_bibs">
+
+     <eg-grid-menu-item handler="refreshConjoined"
+      label="[% l('Refresh') %]"/>
+     <eg-grid-menu-item handler="deleteSelectedConjoined"
+      label="[% l('Unlink') %]"/>
+    <eg-grid-menu-item handler="changeConjoinedType"
+      label="[% l('Change Type') %]"/>
+
+    <eg-grid-field label="[% l('Copy') %]"  path="target_copy.barcode" visible>
+      <a target="_self" href="[% ctx.base_path %]/staff/cat/item/{{item.target_copy().id()}}">
+        {{item.target_copy().barcode()}}
+      </a>
+    </eg-grid-field>
+    <eg-grid-field label="[% l('Title') %]"  path="target_copy.call_number.record.simple_record.title" visible>
+      <a target="_self" href="[% ctx.base_path %]/staff/cat/catalog/record/{{item.target_copy().call_number().record().id()}}">
+        {{item.target_copy().call_number().record().simple_record().title()}}
+      </a>
+    </eg-grid-field>
+    <eg-grid-field label="[% l('Type') %]"  path="peer_type.name" visible></eg-grid-field>
+    <eg-grid-field label="[% l('ID') %]"  path="peer_type.id" ></eg-grid-field>
+  
+  </eg-grid>
+</div>
+  
index 377c426..aac05ae 100644 (file)
@@ -2,7 +2,8 @@
     <div class="modal-header">
       <button type="button" class="close" ng-click="cancel()" 
         aria-hidden="true">&times;</button>
-      <h4 class="modal-title">[% l('Attach conjoined items') %]</h4>
+      <h4 ng-if="update" class="modal-title">[% l('Update conjoined items') %]</h4>
+      <h4 ng-if="!update" class="modal-title">[% l('Attach conjoined items') %]</h4>
     </div>
     <div class="modal-body">
       <div class="row">
index a5331d7..20fe682 100644 (file)
@@ -227,9 +227,9 @@ function($scope , $routeParams , $location , $window , $q , egCore) {
 
 .controller('CatalogCtrl',
        ['$scope','$routeParams','$location','$window','$q','egCore','egHolds','egCirc','egConfirmDialog',
-        'egGridDataProvider','egHoldGridActions','$timeout','$modal','holdingsSvc','egUser',
+        'egGridDataProvider','egHoldGridActions','$timeout','$modal','holdingsSvc','egUser','conjoinedSvc',
 function($scope , $routeParams , $location , $window , $q , egCore , egHolds , egCirc,  egConfirmDialog,
-         egGridDataProvider , egHoldGridActions , $timeout , $modal , holdingsSvc , egUser) {
+         egGridDataProvider , egHoldGridActions , $timeout , $modal , holdingsSvc , egUser , conjoinedSvc) {
 
     // set record ID on page load if available...
     $scope.record_id = $routeParams.record_id;
@@ -338,6 +338,9 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
             $scope.record_id = match[1];
             egCore.hatch.setLocalItem("eg.cat.last_record_retrieved", $scope.record_id);
             $scope.holdings_record_id_changed($scope.record_id);
+            conjoinedSvc.fetch($scope.record_id).then(function(){
+                $scope.conjoinedGridDataProvider.refresh();
+            });
             init_parts_url();
         } else {
             delete $scope.record_id;
@@ -364,6 +367,92 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
     $scope.handlers = { }
 
     // ------------------------------------------------------------------
+    // Conjoined items
+
+    $scope.conjoinedGridControls = {};
+    $scope.conjoinedGridDataProvider = egGridDataProvider.instance({
+        get : function(offset, count) {
+            return this.arrayNotifier(conjoinedSvc.items, offset, count);
+        }
+    });
+
+    $scope.changeConjoinedType = function () {
+        var peers = egCore.idl.Clone($scope.conjoinedGridControls.selectedItems());
+        angular.forEach(peers, function (p) {
+            p.target_copy(p.target_copy().id());
+            p.peer_type(p.peer_type().id());
+        });
+
+        var conjoinedGridDataProviderRef = $scope.conjoinedGridDataProvider;
+
+        return $modal.open({
+            templateUrl: './cat/catalog/t_conjoined_selector',
+            animation: true,
+            controller:
+                   ['$scope','$modalInstance',
+            function($scope , $modalInstance) {
+                $scope.update = true;
+
+                $scope.peer_type = null;
+                $scope.peer_type_list = [];
+                conjoinedSvc.get_peer_types().then(function(list){
+                    $scope.peer_type_list = list;
+                });
+    
+                $scope.ok = function(type) {
+                    var promises = [];
+    
+                    angular.forEach(peers, function (p) {
+                        p.ischanged(1);
+                        p.peer_type(type);
+                        promises.push(egCore.pcrud.update(p));
+                    });
+    
+                    return $q.all(promises)
+                        .then(function(){$modalInstance.close()})
+                        .then(function(){return conjoinedSvc.fetch()})
+                        .then(function(){conjoinedGridDataProviderRef.refresh()});
+                }
+    
+                $scope.cancel = function($event) {
+                    $modalInstance.dismiss();
+                    $event.preventDefault();
+                }
+            }]
+        });
+        
+    }
+
+    $scope.refreshConjoined = function () {
+        conjoinedSvc.fetch($scope.record_id)
+        .then(function(){$scope.conjoinedGridDataProvider.refresh();});
+    }
+
+    $scope.deleteSelectedConjoined = function () {
+        var peers = $scope.conjoinedGridControls.selectedItems();
+
+        if (peers.length > 0) {
+            egConfirmDialog.open(
+                egCore.strings.CONFIRM_DELETE_PEERS,
+                egCore.strings.CONFIRM_DELETE_PEERS_MESSAGE,
+                {peers : peers.length}
+            ).result.then(function() {
+                angular.forEach(peers, function (p) {
+                    p.isdeleted(1);
+                });
+
+                egCore.pcrud.remove(peers).then(function() {
+                    return conjoinedSvc.fetch();
+                }).then(function() {
+                    $scope.conjoinedGridDataProvider.refresh();
+                });
+            });
+        }
+    }
+    if ($scope.record_id)
+        conjoinedSvc.fetch($scope.record_id);
+
+    // ------------------------------------------------------------------
     // Holdings
 
     $scope.holdingsGridControls = {};
@@ -827,9 +916,11 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
                 controller:
                        ['$scope','$modalInstance',
                 function($scope , $modalInstance) {
+                    $scope.update = false;
+
                     $scope.peer_type = null;
                     $scope.peer_type_list = [];
-                    holdingsSvc.get_peer_types().then(function(list){
+                    conjoinedSvc.get_peer_types().then(function(list){
                         $scope.peer_type_list = list;
                     });
     
@@ -1328,6 +1419,53 @@ function(egCore , $q) {
         );
     }
 
+    return service;
+}])
+
+.factory('conjoinedSvc', 
+       ['egCore','$q',
+function(egCore , $q) {
+
+    var service = {
+        items : [], // record search results
+        index : 0, // search grid index
+        rid : null
+    };
+
+    service.flesh = {   
+        flesh : 4, 
+        flesh_fields : {
+            bpbcm : ['target_copy','peer_type'],
+            acp : ['call_number'],
+            acn : ['record'],
+            bre : ['simple_record']
+        },
+        // avoid fetching the MARC blob by specifying which
+        // fields on the bre to select.  More may be needed.
+        // note that fleshed fields are explicitly selected.
+        select : { bre : ['id'] },
+        order_by : { bpbcm : ['id'] },
+    }
+
+    // resolved with the last received copy
+    service.fetch = function(rid) {
+        if (!rid && !service.rid) return $q.when();
+
+        if (rid) service.rid = rid;
+        service.items = [];
+        service.index = 0;
+
+        return egCore.pcrud.search(
+            'bpbcm',
+            {peer_record : service.rid},
+            service.flesh,
+            {atomic : true}
+        ).then( function(list) { // finished
+            service.items = list;
+            return service.items;
+        });
+    }
+
     // returns a promise resolved with the list of peer bib types
     service.get_peer_types = function() {
         if (egCore.env.bpt)