]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/circ/holds/app.js
fc9ea34c515b9c05916dee9e7e0e8685f5ef1275
[working/Evergreen.git] / Open-ILS / web / js / ui / default / staff / circ / holds / app.js
1 angular.module('egHoldsApp', 
2     ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'egGridMod'])
3
4 .config(function($routeProvider, $locationProvider, $compileProvider) {
5     $locationProvider.html5Mode(true);
6     $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob):/); // grid export
7
8     var resolver = {delay : 
9         ['egStartup', function(egStartup) {return egStartup.go()}]}
10
11     $routeProvider.when('/circ/holds/shelf', {
12         templateUrl: './circ/holds/t_shelf',
13         controller: 'HoldsShelfCtrl',
14         resolve : resolver
15     });
16
17     $routeProvider.when('/circ/holds/shelf/:hold_id', {
18         templateUrl: './circ/holds/t_shelf',
19         controller: 'HoldsShelfCtrl',
20         resolve : resolver
21     });
22
23     $routeProvider.when('/circ/holds/pull', {
24         templateUrl: './circ/holds/t_pull',
25         controller: 'HoldsPullListCtrl',
26         resolve : resolver
27     });
28
29     $routeProvider.when('/circ/holds/pull/:hold_id', {
30         templateUrl: './circ/holds/t_pull',
31         controller: 'HoldsPullListCtrl',
32         resolve : resolver
33     });
34
35     $routeProvider.otherwise({redirectTo : '/circ/holds/shelf'});
36 })
37
38
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;
43
44     var hold_ids = [];
45     var holds = [];
46     var clear_mode = false;
47     $scope.gridControls = {};
48     $scope.grid_actions = egHoldGridActions;
49
50     function fetch_holds(offset, count) {
51         var ids = hold_ids.slice(offset, offset + count);
52         return egHolds.fetch_holds(ids).then(null, null,
53             function(hold_data) { 
54                 holds.push(hold_data);
55                 return hold_data; // to the grid
56             }
57         );
58     }
59
60     var provider = egGridDataProvider.instance({});
61     $scope.gridDataProvider = provider;
62
63     function refresh_page() {
64         holds = [];
65         hold_ids = [];
66         provider.refresh();
67     }
68     // called after any egHoldGridActions action occurs
69     $scope.grid_actions.refresh = refresh_page;
70
71     provider.get = function(offset, count) {
72
73         // see if we have the requested range cached
74         if (holds[offset]) {
75             return provider.arrayNotifier(holds, offset, count);
76         }
77
78         // see if we have the holds IDs for this range already loaded
79         if (hold_ids[offset]) {
80             return fetch_holds(offset, count);
81         }
82
83         var deferred = $q.defer();
84         hold_ids = [];
85         holds = [];
86
87         var method = 'open-ils.circ.captured_holds.id_list.on_shelf.retrieve.authoritative.atomic';
88         if (clear_mode) 
89             method = 'open-ils.circ.captured_holds.id_list.expired_on_shelf_or_wrong_shelf.retrieve.atomic';
90
91         egCore.net.request(
92             'open-ils.circ', method,
93             egCore.auth.token(), $scope.pickup_ou.id()
94
95         ).then(function(ids) {
96             if (!ids.length) { 
97                 deferred.resolve(); 
98                 return; 
99             }
100
101             hold_ids = ids;
102             fetch_holds(offset, count)
103             .then(deferred.resolve, null, deferred.notify);
104         });
105
106         return deferred.promise;
107     }
108
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) 
113             refresh_page();
114     });
115
116     $scope.detail_view = function(action, user_data, items) {
117         if (h = items[0]) {
118             $location.path('/circ/holds/shelf/' + h.hold.id());
119         }
120     }
121
122     $scope.list_view = function(items) {
123         $location.path('/circ/holds/shelf');
124     }
125
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();
130     }
131
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 }
140
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) {
145             if (info.action) {
146                 var grid_item = holds.filter(function(item) {
147                     return item.hold.id() == info.hold_details.id
148                 })[0];
149
150                 // there will be no grid item if the hold is off-page
151                 if (grid_item) {
152                     grid_item.post_clear = 
153                         egCore.strings['CLEAR_SHELF_ACTION_' + info.action];
154                 }
155             }
156         });
157     }
158
159     $scope.clear_holds = function() {
160         clearing = true;
161         $scope.clear_progress = {max : 0, value : 0};
162
163         // we want to see all processed holds, so (effectively) remove
164         // the grid limit.
165         $scope.gridControls.setLimit(1000, true); 
166
167         // initiate clear shelf and grab cache key
168         egCore.net.request(
169             'open-ils.circ',
170             'open-ils.circ.hold.clear_shelf.process',
171             egCore.auth.token(), $scope.pickup_ou.id(),
172             null, 1
173
174         // request responses from the clear shelf cache
175         ).then(
176             
177             // clear shelf done; fetch the cached results.
178             function(resp) {
179                 clearing = false;
180                 egCore.net.request(
181                     'open-ils.circ',
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);
185             }, 
186
187             null,
188
189             // handle streamed clear_shelf progress updates
190             function(resp) {
191                 if (resp.maximum) 
192                     $scope.clear_progress.max = resp.maximum;
193                 if (resp.progress)
194                     $scope.clear_progress.value = resp.progress;
195             }
196
197         );
198     }
199
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;
205
206         // collect the full list of holds
207         var method = 'open-ils.circ.captured_holds.id_list.on_shelf.retrieve.authoritative.atomic';
208         if (clear_mode)
209             method = 'open-ils.circ.captured_holds.id_list.expired_on_shelf_or_wrong_shelf.retrieve.atomic';
210         egCore.net.request(
211             'open-ils.circ',
212             method,
213             egCore.auth.token(), $scope.pickup_ou.id()
214         ).then( function(idlist) {
215
216             egHolds.fetch_holds(idlist).then(
217                 function () {
218                     console.debug('printing ' + print_holds.length + ' holds');
219                     // holds fetched, send to print
220                     egCore.print.print({
221                         context : 'default', 
222                         template : 'hold_shelf_list', 
223                         scope : {holds : print_holds}
224                     })
225                 },
226                 null,
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);
237                 }
238             )
239         }).finally(function() {
240             $scope.print_list_loading = false;
241             $scope.print_list_progress = null;
242         });
243     }
244
245     refresh_page();
246
247 }])
248
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) {
254
255     $scope.detail_hold_id = $routeParams.hold_id;
256
257     var cached_details = {};
258     var details_needed = {};
259
260     $scope.gridControls = {
261         setQuery : function() {
262             return {'copy_circ_lib_id' : egCore.auth.user().ws_ou()}
263         },
264         setSort : function() {
265             return ['copy_location_order_position','call_number_sort_key']
266         },
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();
273         },
274         itemRetrieved : function(item) {
275             egProgressDialog.increment();
276             if (!cached_details[item.id]) {
277                 details_needed[item.id] = item;
278             }
279         },
280         allItemsRetrieved : function() {
281             flesh_holds().finally(egProgressDialog.close);
282         }
283     }
284
285
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();
292
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 })
299         });
300
301         // Exit if all needed details were already cached
302         if (Object.keys(details_needed).length == 0) return $q.when();
303
304         return egCore.net.request(
305             'open-ils.circ',
306             'open-ils.circ.hold.details.batch.retrieve.authoritative',
307             egCore.auth.token(), Object.keys(details_needed), {
308                 include_usr : true
309             }
310
311         ).then(null, null, function(hold_info) {
312             egProgressDialog.increment();
313
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();
317
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];
322
323             // flesh the grid item from the blob of hold data.
324             angular.forEach(hold_info, 
325                 function(val, key) { item[key] = val });
326
327         });
328     }
329
330     $scope.grid_actions = egHoldGridActions;
331     $scope.grid_actions.refresh = function() {
332         cached_details = {}; // un-cache details after edit actions.
333         $scope.gridControls.refresh();
334     }
335
336     $scope.detail_view = function(action, user_data, items) {
337         if (h = items[0]) {
338             $location.path('/circ/holds/pull/' + h.hold.id());
339         }
340     }
341
342     $scope.list_view = function(items) {
343         $location.path('/circ/holds/pull');
344     }
345
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();
350     }
351
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()};
358         win.open();
359         win.focus();
360     }
361
362     $scope.print_full_list = function() {
363         var print_holds = [];
364         egProgressDialog.open({value : 0});
365
366         // collect the full list of holds
367         egCore.net.request(
368             'open-ils.circ',
369             'open-ils.circ.hold_pull_list.fleshed.stream',
370             egCore.auth.token(), 10000, 0
371         ).then(
372             function() {
373                 console.debug('printing ' + print_holds.length + ' holds');
374
375                 // holds fetched, send to print
376                 egCore.print.print({
377                     context : 'default', 
378                     template : 'hold_pull_list', 
379                     scope : {holds : print_holds}
380                 });
381             },
382             null, 
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);
393             }
394         ).finally(egProgressDialog.close);
395     }
396
397 }])
398