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