]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/cat/services/holdings.js
LP2042879 Shelving Location Groups Admin accessibility
[Evergreen.git] / Open-ILS / web / js / ui / default / staff / cat / services / holdings.js
1 angular.module('egHoldingsMod', ['egCoreMod','egGridMod'])
2
3 .factory('holdingsSvc', 
4        ['egCore','$q',
5 function(egCore , $q) {
6
7     var service = function() {
8         this.ongoing = false;
9         this.copies = []; // record search results
10         this.index = 0; // search grid index
11         this.org = null;
12         this.rid = null;
13     };
14
15     service.prototype.flesh = {   
16         flesh : 3,
17         flesh_fields : {
18             acp : ['status','location','circ_lib','parts','age_protect','copy_alerts', 'latest_inventory'],
19             acn : ['prefix','suffix','copies','label_class','record'],
20             bre : ['simple_record'],
21             alci : ['inventory_workstation']
22         }
23     }
24
25     service.prototype.fetchAgain = function() {
26         return this.fetch({
27             rid: this.rid,
28             org: this.org,
29             copy: this.copy,
30             vol: this.vol,
31             empty: this.empty,
32             empty_org: this.empty_org
33         })
34     };
35
36     // resolved with the last received copy
37     service.prototype.fetch = function(opts) {
38         var svc = this;
39
40         if (svc.ongoing && svc.p) {
41             svc.p.cancel = true;
42             console.log('Canceling fetch for org '+ svc.org.id());
43             if (svc.p.reject) svc.p.reject();
44         }
45
46         var rid = opts.rid;
47         var empty_org = opts.empty_org;
48         var org = opts.org;
49         var copy = opts.copy;
50         var vol = opts.vol;
51         var empty = opts.empty;
52
53         if (!rid) return $q.when();
54         if (!org) return $q.when();
55
56         svc.ongoing = true;
57
58         svc.rid = rid;
59         svc.empty_org = opts.empty_org;
60         svc.org = org;
61         svc.copy = opts.copy;
62         svc.vol = opts.vol;
63         svc.empty = opts.empty;
64
65         svc.copies = [];
66         svc.index = 0;
67
68         var org_list = egCore.org.descendants(org.id(), true);
69         console.log('Holdings fetch with: rid='+rid+' org='+org_list+' copy='+copy+' vol='+vol+' empty='+empty);
70
71         svc.org_use_map = {};
72         org_list.map(function(o){svc.org_use_map[''+o]=0;})
73
74         var p = egCore.pcrud.search(
75             'acn',
76             {record : rid, owning_lib : org_list, deleted : 'f', label : {'!=' : '##URI##'}},
77             svc.flesh
78         ).then(
79             function() { // finished
80                 if (p.cancel) return;
81
82                 // create virtual field for displaying active parts
83                 angular.forEach(svc.copies, function (cp) {
84                     cp.monograph_parts = '';
85                     if (cp.parts && cp.parts.length > 0) {
86                         cp.monograph_parts = cp.parts.map(function(obj) { return obj.label; }).join(', ');
87                         cp.monograph_parts_sortkeys = cp.parts.map(function(obj) { return obj.label_sortkey; }).join();
88                     }
89                 });
90
91                 if (empty_org) {
92
93                     var empty_org_list = [];
94                     angular.forEach(svc.org_use_map,function(v,k){
95                         if (v == 0) empty_org_list.push(k);
96                     });
97
98                     angular.forEach(empty_org_list, function (oid) {
99                         var owner = egCore.org.get(oid);
100                         if (owner.ou_type().can_have_vols() != 't') return;
101
102                         var owner_list = [];
103                         while (owner.parent_ou()) { // we're going to skip the top of the tree...
104                             owner_list.unshift(owner.shortname());
105                             owner = egCore.org.get(owner.parent_ou());
106                         }
107
108                         var owner_label = owner_list.join(' ... ');
109
110                         svc.copies.push({
111                             index      : index++,
112                             id_list    : [],
113                             call_number: { label : '' },
114                             barcode    : '',
115                             owner_id   : oid,
116                             owner_list : owner_list,
117                             owner_label: owner_label,
118                             copy_count : 0,
119                             cn_count   : 0,
120                             copy_alert_count : 0
121                         });
122                     });
123                 }
124
125                 svc.ongoing = false;
126
127
128                 svc.copies = svc.copies.sort(
129                     function (a, b) {
130                         function compare_array (x, y, i) {
131                             if (x[i] && y[i]) { // both have values
132                                 if (x[i] == y[i]) { // need to look deeper
133                                     return compare_array(x, y, ++i);
134                                 }
135
136                                 if (x[i] < y[i]) { // x is first
137                                     return -1;
138                                 } else if (x[i] > y[i]) { // y is first
139                                     return 1;
140                                 }
141
142                             } else { // no orgs to compare ...
143                                 if (x[i]) return -1;
144                                 if (y[i]) return 1;
145                             }
146                             return 0;
147                         }
148
149                         var owner_order = compare_array(a.owner_list, b.owner_list, 0);
150                         if (!owner_order) {
151                             // now compare on CN label
152                             if (a.call_number.label < b.call_number.label) return -1;
153                             if (a.call_number.label > b.call_number.label) return 1;
154
155                             // also parts sortkeys combined string
156                             if (a.monograph_parts_sortkeys < b.monograph_parts_sortkeys) return -1;
157                             if (a.monograph_parts_sortkeys > b.monograph_parts_sortkeys) return 1;
158
159                             // try copy number
160                             if (a.copy_number < b.copy_number) return -1;
161                             if (a.copy_number > b.copy_number) return 1;
162
163                             // finally, barcode
164                             if (a.barcode < b.barcode) return -1;
165                             if (a.barcode > b.barcode) return 1;
166                         }
167                         return owner_order;
168                     }
169                 );
170
171                 // create virtual fields for copy alert count and most recent circ
172                 angular.forEach(svc.copies, function (cp) {
173                     if (cp.copy_alerts) {
174                         cp.copy_alert_count = cp.copy_alerts.filter(function(aca) { return aca.ack_time == null ;}).length;
175                     }
176                     else cp.copy_alert_count = 0;
177                 });
178
179                 // Grab the open circulation (i.e. checkin_time=null) for
180                 // all of the copies we're rendering so we can display
181                 // due date info.  There should only ever be one circulation
182                 // at most with checkin_time=null for any copy.
183                 var copyIds = svc.copies.map(function(cp) {return cp.id})
184                     .filter(function(id) {return Boolean(id)}); // avoid nulls
185
186                 egCore.pcrud.search('circ', 
187                     {target_copy: copyIds, checkin_time: null}
188                 ).then(
189                     null, // complete
190                     null, // error
191                     function(circ) {
192                         var cp = svc.copies.filter(function(c) { 
193                             return c.id == circ.target_copy() })[0];
194                         if (!cp) { return; } // can disappear during reloads.
195                         cp._circ = egCore.idl.toHash(circ, true);
196                         cp._circ_lib = circ.circ_lib();
197                         cp._duration = circ.duration();
198                     }
199                 );
200
201                 // create a label using just the unique part of the owner list
202                 var index = 0;
203                 var prev_owner_list;
204                 angular.forEach(svc.copies, function (cp) {
205                     if (!prev_owner_list) {
206                         cp.owner_label = cp.owner_list.join(' ... ');
207                     } else {
208                         var current_owner_list = cp.owner_list.slice();
209                         while (current_owner_list[1] && prev_owner_list[1] && current_owner_list[0] == prev_owner_list[0]) {
210                             current_owner_list.shift();
211                             prev_owner_list.shift();
212                         }
213                         cp.owner_label = current_owner_list.join(' ... ');
214                     }
215
216                     cp.index = index++;
217                     prev_owner_list = cp.owner_list.slice();
218                 });
219
220                 var new_list = svc.copies;
221                 if (!copy || !vol) { // collapse copy rows, supply a count instead
222
223                     index = 0;
224                     var cp_list = [];
225                     var prev_key;
226                     var current_blob = { copy_count : 0 };
227                     angular.forEach(new_list, function (cp) {
228                         if (!prev_key) {
229                             prev_key = cp.owner_list.join('') + cp.call_number.label;
230                             if (cp.barcode) current_blob.copy_count = 1;
231                             current_blob.index = index++;
232                             current_blob.id_list = cp.id_list;
233                             if (cp.raw) current_blob.raw = cp.raw;
234                             current_blob.call_number = cp.call_number;
235                             current_blob.owner_list = cp.owner_list;
236                             current_blob.owner_label = cp.owner_label;
237                             current_blob.owner_id = cp.owner_id;
238                         } else {
239                             var current_key = cp.owner_list.join('') + cp.call_number.label;
240                             if (prev_key == current_key) { // collapse into current_blob
241                                 current_blob.copy_count++;
242                                 current_blob.id_list = current_blob.id_list.concat(cp.id_list);
243                                 current_blob.raw = current_blob.raw.concat(cp.raw);
244                             } else {
245                                 current_blob.barcode = current_blob.copy_count;
246                                 cp_list.push(current_blob);
247                                 prev_key = current_key;
248                                 current_blob = { copy_count : 0 };
249                                 if (cp.barcode) current_blob.copy_count = 1;
250                                 current_blob.index = index++;
251                                 current_blob.id_list = cp.id_list;
252                                 if (cp.raw) current_blob.raw = cp.raw;
253                                 current_blob.owner_label = cp.owner_label;
254                                 current_blob.owner_id = cp.owner_id;
255                                 current_blob.call_number = cp.call_number;
256                                 current_blob.owner_list = cp.owner_list;
257                             }
258                         }
259                     });
260
261                     current_blob.barcode = current_blob.copy_count;
262                     cp_list.push(current_blob);
263                     new_list = cp_list;
264
265                     if (!vol) { // do the same for vol rows
266
267                         index = 0;
268                         var cn_list = [];
269                         prev_key = '';
270                         current_blob = { copy_count : 0 };
271                         angular.forEach(cp_list, function (cp) {
272                             if (!prev_key) {
273                                 prev_key = cp.owner_list.join('');
274                                 current_blob.index = index++;
275                                 current_blob.id_list = cp.id_list;
276                                 if (cp.raw) current_blob.raw = cp.raw;
277                                 current_blob.cn_count = 1;
278                                 current_blob.copy_count = cp.copy_count;
279                                 current_blob.owner_list = cp.owner_list;
280                                 current_blob.owner_label = cp.owner_label;
281                                 current_blob.owner_id = cp.owner_id;
282                             } else {
283                                 var current_key = cp.owner_list.join('');
284                                 if (prev_key == current_key) { // collapse into current_blob
285                                     current_blob.cn_count++;
286                                     current_blob.copy_count += cp.copy_count;
287                                     current_blob.id_list = current_blob.id_list.concat(cp.id_list);
288                                     if (cp.raw) {
289                                         if (current_blob.raw) current_blob.raw = current_blob.raw.concat(cp.raw);
290                                         else current_blob.raw = cp.raw;
291                                     }
292                                 } else {
293                                     current_blob.barcode = current_blob.copy_count;
294                                     current_blob.call_number = { label : current_blob.cn_count };
295                                     cn_list.push(current_blob);
296                                     prev_key = current_key;
297                                     current_blob = { copy_count : 0 };
298                                     current_blob.index = index++;
299                                     current_blob.id_list = cp.id_list;
300                                     if (cp.raw) current_blob.raw = cp.raw;
301                                     current_blob.owner_label = cp.owner_label;
302                                     current_blob.owner_id = cp.owner_id;
303                                     current_blob.cn_count = 1;
304                                     current_blob.copy_count = cp.copy_count;
305                                     current_blob.owner_list = cp.owner_list;
306                                 }
307                             }
308                         });
309     
310                         current_blob.barcode = current_blob.copy_count;
311                         current_blob.call_number = { label : current_blob.cn_count };
312                         cn_list.push(current_blob);
313                         new_list = cn_list;
314     
315                     }
316                 }
317
318                 svc.copies = new_list;
319                 svc.ongoing = false;
320             },
321
322             null, // error
323
324             // notify reads the stream of copies, one at a time.
325             function(cn) {
326                 if (p.cancel) return;
327
328                 var copies = cn.copies().filter(function(cp){ return cp.deleted() == 'f' });
329                 cn.copies([]);
330
331                 angular.forEach(copies, function (cp) {
332                     cp.call_number(cn);
333                 });
334
335                 var owner_id = cn.owning_lib();
336                 var owner = egCore.org.get(owner_id);
337                 svc.org_use_map[''+owner_id] += 1;
338
339                 var owner_name_list = [];
340                 while (owner.parent_ou()) { // we're going to skip the top of the tree...
341                     owner_name_list.unshift(owner.shortname());
342                     owner = egCore.org.get(owner.parent_ou());
343                 }
344
345                 if (copies[0]) {
346                     var flat = [];
347                     angular.forEach(copies, function (cp) {
348                         var flat_cp = egCore.idl.toHash(cp);
349                         flat_cp.owner_id = owner_id;
350                         flat_cp.owner_list = owner_name_list;
351                         flat_cp.id_list = [flat_cp.id];
352                         flat_cp.raw = [cp];
353                         flat.push(flat_cp);
354                     });
355
356                     svc.copies = svc.copies.concat(flat);
357                 } else if (empty) {
358                     svc.copies.push({
359                         id_list    : [],
360                         owner_id   : owner_id,
361                         owner_list : owner_name_list,
362                         call_number: egCore.idl.toHash(cn),
363                         raw_call_number: cn
364                     });
365                 }
366
367                 return cn;
368             }
369         );
370
371         return svc.p = p;
372     };
373
374     return service;
375 }])
376 .directive("egVolumeList", function () {
377     return {
378         restrict:   'AE',
379         scope: {
380             recordId : '=',
381             editVolumes : '@',
382             editCopies  : '@'
383         },
384         templateUrl: './cat/share/t_volume_list',
385         controller:
386                    ['$scope','holdingsSvc','egCore','egGridDataProvider','$uibModal',
387             function($scope , holdingsSvc , egCore , egGridDataProvider,  $uibModal) {
388                 var holdingsSvcInst = new holdingsSvc();
389
390                 $scope.holdingsGridControls = {};
391                 $scope.holdingsGridDataProvider = egGridDataProvider.instance({
392                     get : function(offset, count) {
393                         return this.arrayNotifier(holdingsSvcInst.copies, offset, count);
394                     }
395                 });
396
397                 function gatherHoldingsIds () {
398                     var cp_id_list = [];
399                     angular.forEach(
400                         $scope.holdingsGridControls.allItems(),
401                         function (item) { cp_id_list = cp_id_list.concat(item.id_list) }
402                     );
403                     return cp_id_list;
404                 }
405
406                 var spawn_volume_editor = function (copies_too) {
407                     egCore.net.request(
408                         'open-ils.actor',
409                         'open-ils.actor.anon_cache.set_value',
410                         null, 'edit-these-copies', {
411                             record_id: $scope.recordId,
412                             copies: gatherHoldingsIds(),
413                             hide_vols : false,
414                             hide_copies : ((copies_too) ? false : true)
415                         }
416                     ).then(function(key) {
417                         if (key) {
418                             $uibModal.open({
419                                 templateUrl: './cat/share/t_embedded_volcopy',
420                                 backdrop: 'static',
421                                 size: 'lg',
422                                 windowClass: 'eg-wide-modal',
423                                 controller:
424                                     ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
425                                     $scope.volcopy_url = 
426                                         egCore.env.basePath + 'cat/volcopy/' + key + '/embedded';
427                                     $scope.ok = function(args) { $uibModalInstance.close(args) }
428                                     $scope.cancel = function () { $uibModalInstance.dismiss() }
429                                 }]
430                             }).result.then(function() {
431                                 load_holdings();
432                             });
433                         }
434                     });
435                 }
436                 $scope.edit_volumes = function() {
437                     spawn_volume_editor(false);
438                 }
439                 $scope.edit_copies = function() {
440                     spawn_volume_editor(true);
441                 }
442
443                 function load_holdings() {
444                     holdingsSvcInst.fetch({
445                         rid   : $scope.recordId,
446                         org   : egCore.org.root(),
447                         copy  : false,
448                         vol   : true,
449                         empty : true
450                     }).then(function() {
451                         $scope.holdingsGridDataProvider.refresh();
452                     });
453                 };
454                 $scope.$watch('recordId',
455                     function(newVal, oldVal) {
456                         if (newVal && newVal !== oldVal) {
457                             load_holdings();
458                         }
459                     }
460                 );
461                 load_holdings();
462             }]
463     }
464 })
465 .filter('string_pick', function() { return function(i){ return arguments[i] || ''; }; })
466 ;