1 angular.module('egHoldsApp',
2 ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'egGridMod'])
4 .config(function($routeProvider, $locationProvider, $compileProvider) {
5 $locationProvider.html5Mode(true);
6 $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob):/); // grid export
8 var resolver = {delay :
9 ['egStartup', function(egStartup) {return egStartup.go()}]}
11 $routeProvider.when('/circ/holds/shelf', {
12 templateUrl: './circ/holds/t_shelf',
13 controller: 'HoldsShelfCtrl',
17 $routeProvider.when('/circ/holds/shelf/:hold_id', {
18 templateUrl: './circ/holds/t_shelf',
19 controller: 'HoldsShelfCtrl',
23 $routeProvider.when('/circ/holds/pull', {
24 templateUrl: './circ/holds/t_pull',
25 controller: 'HoldsPullListCtrl',
29 $routeProvider.when('/circ/holds/pull/:hold_id', {
30 templateUrl: './circ/holds/t_pull',
31 controller: 'HoldsPullListCtrl',
35 $routeProvider.otherwise({redirectTo : '/circ/holds/shelf'});
39 .controller('HoldsShelfCtrl',
40 ['$scope','$q','$routeParams','$window','$location','egCore','egHolds','egHoldGridActions','egCirc','egGridDataProvider',
41 function($scope , $q , $routeParams , $window , $location , egCore , egHolds , egHoldGridActions , egCirc , egGridDataProvider) {
42 $scope.detail_hold_id = $routeParams.hold_id;
46 var clear_mode = false;
47 $scope.gridControls = {};
48 $scope.grid_actions = egHoldGridActions;
50 function fetch_holds(offset, count) {
51 var ids = hold_ids.slice(offset, offset + count);
52 return egHolds.fetch_holds(ids).then(null, null,
54 holds.push(hold_data);
55 return hold_data; // to the grid
60 var provider = egGridDataProvider.instance({});
61 $scope.gridDataProvider = provider;
63 function refresh_page() {
68 // called after any egHoldGridActions action occurs
69 $scope.grid_actions.refresh = refresh_page;
71 provider.get = function(offset, count) {
73 // see if we have the requested range cached
75 return provider.arrayNotifier(holds, offset, count);
78 // see if we have the holds IDs for this range already loaded
79 if (hold_ids[offset]) {
80 return fetch_holds(offset, count);
83 var deferred = $q.defer();
87 var method = 'open-ils.circ.captured_holds.id_list.on_shelf.retrieve.authoritative.atomic';
89 method = 'open-ils.circ.captured_holds.id_list.expired_on_shelf_or_wrong_shelf.retrieve.atomic';
92 'open-ils.circ', method,
93 egCore.auth.token(), $scope.pickup_ou.id()
95 ).then(function(ids) {
102 fetch_holds(offset, count)
103 .then(deferred.resolve, null, deferred.notify);
106 return deferred.promise;
109 // re-draw the grid when user changes the org selector
110 $scope.pickup_ou = egCore.org.get(egCore.auth.user().ws_ou());
111 $scope.$watch('pickup_ou', function(newVal, oldVal) {
112 if (newVal && newVal != oldVal)
116 $scope.detail_view = function(action, user_data, items) {
118 $location.path('/circ/holds/shelf/' + h.hold.id());
122 $scope.list_view = function(items) {
123 $location.path('/circ/holds/shelf');
126 // when the detail hold is fetched (and updated), update the bib
127 // record summary display record id.
128 $scope.set_hold = function(hold_data) {
129 $scope.detail_hold_record_id = hold_data.mvr.doc_id();
132 // manage active vs. clearable holds display
133 var clearing = false; // true if actively clearing holds (below)
134 $scope.is_clearing = function() { return clearing };
135 $scope.active_mode = function() {return !clear_mode}
136 $scope.clear_mode = function() {return clear_mode}
137 $scope.show_clearable = function() { clear_mode = true; refresh_page() }
138 $scope.show_active = function() { clear_mode = false; refresh_page() }
139 $scope.disable_clear = function() { return clearing || !clear_mode }
141 // udpate the in-grid hold with the clear-shelf cached response info.
142 function handle_clear_cache_resp(resp) {
143 if (!angular.isArray(resp)) resp = [resp];
144 angular.forEach(resp, function(info) {
146 var grid_item = holds.filter(function(item) {
147 return item.hold.id() == info.hold_details.id
150 // there will be no grid item if the hold is off-page
152 grid_item.post_clear =
153 egCore.strings['CLEAR_SHELF_ACTION_' + info.action];
159 $scope.clear_holds = function() {
161 $scope.clear_progress = {max : 0, value : 0};
163 // we want to see all processed holds, so (effectively) remove
165 $scope.gridControls.setLimit(1000, true);
167 // initiate clear shelf and grab cache key
170 'open-ils.circ.hold.clear_shelf.process',
171 egCore.auth.token(), $scope.pickup_ou.id(),
174 // request responses from the clear shelf cache
177 // clear shelf done; fetch the cached results.
182 'open-ils.circ.hold.clear_shelf.get_cache',
183 egCore.auth.token(), resp.cache_key, 1
184 ).then(null, null, handle_clear_cache_resp);
189 // handle streamed clear_shelf progress updates
192 $scope.clear_progress.max = resp.maximum;
194 $scope.clear_progress.value = resp.progress;
200 $scope.print_list_progress = null;
201 $scope.print_shelf_list = function() {
202 var print_holds = [];
203 $scope.print_list_loading = true;
204 $scope.print_list_progress = 0;
206 // collect the full list of holds
207 var method = 'open-ils.circ.captured_holds.id_list.on_shelf.retrieve.authoritative.atomic';
209 method = 'open-ils.circ.captured_holds.id_list.expired_on_shelf_or_wrong_shelf.retrieve.atomic';
213 egCore.auth.token(), $scope.pickup_ou.id()
214 ).then( function(idlist) {
216 egHolds.fetch_holds(idlist).then(
218 console.debug('printing ' + print_holds.length + ' holds');
219 // holds fetched, send to print
222 template : 'hold_shelf_list',
223 scope : {holds : print_holds}
227 function(hold_data) {
228 $scope.print_list_progress++;
229 egHolds.local_flesh(hold_data);
230 print_holds.push(hold_data);
231 hold_data.title = hold_data.mvr.title();
232 hold_data.author = hold_data.mvr.author();
233 hold_data.hold = egCore.idl.toHash(hold_data.hold);
234 hold_data.copy = egCore.idl.toHash(hold_data.copy);
235 hold_data.volume = egCore.idl.toHash(hold_data.volume);
236 hold_data.part = egCore.idl.toHash(hold_data.part);
239 }).finally(function() {
240 $scope.print_list_loading = false;
241 $scope.print_list_progress = null;
249 .controller('HoldsPullListCtrl',
250 ['$scope','$q','$routeParams','$window','$location','egCore',
251 'egHolds','egCirc','egHoldGridActions','egProgressDialog',
252 function($scope , $q , $routeParams , $window , $location , egCore ,
253 egHolds , egCirc , egHoldGridActions , egProgressDialog) {
255 $scope.detail_hold_id = $routeParams.hold_id;
257 var cached_details = {};
258 var details_needed = {};
260 $scope.gridControls = {
261 setQuery : function() {
262 return {'copy_circ_lib_id' : egCore.auth.user().ws_ou()}
264 setSort : function() {
265 return ['copy_location_order_position','call_number_sort_key']
267 collectStarted : function(offset) {
268 // Launch an indeterminate -> semi-determinate progress
269 // modal. Using a determinate modal that starts counting
270 // on the post-grid holds data retrieval results in a modal
271 // that's stuck at 0% for most of its life, which is aggravating.
272 egProgressDialog.open();
274 itemRetrieved : function(item) {
275 egProgressDialog.increment();
276 if (!cached_details[item.id]) {
277 details_needed[item.id] = item;
280 allItemsRetrieved : function() {
281 flesh_holds().finally(egProgressDialog.close);
286 // Fetches hold detail data for each hold in the grid and links
287 // the detail data to the related grid item so egHoldGridActions
288 // and friends have access to holds data they understand.
289 // Only fetch not-yet-cached data.
290 function flesh_holds() {
291 egProgressDialog.increment();
293 // Start by fleshing hold details from our cached data.
294 var items = $scope.gridControls.allItems();
295 angular.forEach(items, function(item) {
296 if (!cached_details[item.id]) return $q.when();
297 angular.forEach(cached_details[item.id],
298 function(val, key) { item[key] = val })
301 // Exit if all needed details were already cached
302 if (Object.keys(details_needed).length == 0) return $q.when();
304 return egCore.net.request(
306 'open-ils.circ.hold.details.batch.retrieve.authoritative',
307 egCore.auth.token(), Object.keys(details_needed), {
311 ).then(null, null, function(hold_info) {
312 egProgressDialog.increment();
314 // check if this is a staff-created hold
315 // i.e., requestor is not the same as the user
316 hold_info['_is_staff_hold'] = hold_info.hold.requestor() != hold_info.hold.usr().id();
318 var hold_id = hold_info.hold.id();
319 cached_details[hold_id] = hold_info;
320 var item = details_needed[hold_id];
321 delete details_needed[hold_id];
323 // flesh the grid item from the blob of hold data.
324 angular.forEach(hold_info,
325 function(val, key) { item[key] = val });
330 $scope.grid_actions = egHoldGridActions;
331 $scope.grid_actions.refresh = function() {
332 cached_details = {}; // un-cache details after edit actions.
333 $scope.gridControls.refresh();
336 $scope.detail_view = function(action, user_data, items) {
338 $location.path('/circ/holds/pull/' + h.hold.id());
342 $scope.list_view = function(items) {
343 $location.path('/circ/holds/pull');
346 // when the detail hold is fetched (and updated), update the bib
347 // record summary display record id.
348 $scope.set_hold = function(hold_data) {
349 $scope.detail_hold_record_id = hold_data.mvr.doc_id();
352 // By default, this action is hidded from the UI, but leaving it
353 // here in case it's needed in the future
354 $scope.print_list_alt = function() {
355 var url = '/opac/extras/circ/alt_holds_print.html';
356 var win = $window.open(url, '_blank');
357 win.ses = function() {return egCore.auth.token()};
362 $scope.print_full_list = function() {
363 var print_holds = [];
364 egProgressDialog.open({value : 0});
366 // collect the full list of holds
369 'open-ils.circ.hold_pull_list.fleshed.stream',
370 egCore.auth.token(), 10000, 0
373 console.debug('printing ' + print_holds.length + ' holds');
375 // holds fetched, send to print
378 template : 'hold_pull_list',
379 scope : {holds : print_holds}
383 function(hold_data) {
384 egProgressDialog.increment();
385 egHolds.local_flesh(hold_data);
386 print_holds.push(hold_data);
387 hold_data.title = hold_data.mvr.title();
388 hold_data.author = hold_data.mvr.author();
389 hold_data.hold = egCore.idl.toHash(hold_data.hold);
390 hold_data.copy = egCore.idl.toHash(hold_data.copy);
391 hold_data.volume = egCore.idl.toHash(hold_data.volume);
392 hold_data.part = egCore.idl.toHash(hold_data.part);
394 ).finally(egProgressDialog.close);