1 angular.module('egHoldingsMod', ['egCoreMod','egGridMod'])
3 .factory('holdingsSvc',
5 function(egCore , $q) {
7 var service = function() {
9 this.copies = []; // record search results
10 this.index = 0; // search grid index
15 service.prototype.flesh = {
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']
25 service.prototype.fetchAgain = function() {
32 empty_org: this.empty_org
36 // resolved with the last received copy
37 service.prototype.fetch = function(opts) {
40 if (svc.ongoing && svc.p) {
42 console.log('Canceling fetch for org '+ svc.org.id());
43 if (svc.p.reject) svc.p.reject();
47 var empty_org = opts.empty_org;
51 var empty = opts.empty;
53 if (!rid) return $q.when();
54 if (!org) return $q.when();
59 svc.empty_org = opts.empty_org;
63 svc.empty = opts.empty;
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);
72 org_list.map(function(o){svc.org_use_map[''+o]=0;})
74 var p = egCore.pcrud.search(
76 {record : rid, owning_lib : org_list, deleted : 'f', label : {'!=' : '##URI##'}},
79 function() { // finished
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();
93 var empty_org_list = [];
94 angular.forEach(svc.org_use_map,function(v,k){
95 if (v == 0) empty_org_list.push(k);
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;
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());
108 var owner_label = owner_list.join(' ... ');
113 call_number: { label : '' },
116 owner_list : owner_list,
117 owner_label: owner_label,
128 svc.copies = svc.copies.sort(
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);
136 if (x[i] < y[i]) { // x is first
138 } else if (x[i] > y[i]) { // y is first
142 } else { // no orgs to compare ...
149 var owner_order = compare_array(a.owner_list, b.owner_list, 0);
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;
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;
160 if (a.copy_number < b.copy_number) return -1;
161 if (a.copy_number > b.copy_number) return 1;
164 if (a.barcode < b.barcode) return -1;
165 if (a.barcode > b.barcode) return 1;
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;
176 else cp.copy_alert_count = 0;
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
186 egCore.pcrud.search('circ',
187 {target_copy: copyIds, checkin_time: null}
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();
201 // create a label using just the unique part of the owner list
204 angular.forEach(svc.copies, function (cp) {
205 if (!prev_owner_list) {
206 cp.owner_label = cp.owner_list.join(' ... ');
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();
213 cp.owner_label = current_owner_list.join(' ... ');
217 prev_owner_list = cp.owner_list.slice();
220 var new_list = svc.copies;
221 if (!copy || !vol) { // collapse copy rows, supply a count instead
226 var current_blob = { copy_count : 0 };
227 angular.forEach(new_list, function (cp) {
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;
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);
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;
261 current_blob.barcode = current_blob.copy_count;
262 cp_list.push(current_blob);
265 if (!vol) { // do the same for vol rows
270 current_blob = { copy_count : 0 };
271 angular.forEach(cp_list, function (cp) {
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;
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);
289 if (current_blob.raw) current_blob.raw = current_blob.raw.concat(cp.raw);
290 else current_blob.raw = cp.raw;
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;
310 current_blob.barcode = current_blob.copy_count;
311 current_blob.call_number = { label : current_blob.cn_count };
312 cn_list.push(current_blob);
318 svc.copies = new_list;
324 // notify reads the stream of copies, one at a time.
326 if (p.cancel) return;
328 var copies = cn.copies().filter(function(cp){ return cp.deleted() == 'f' });
331 angular.forEach(copies, function (cp) {
335 var owner_id = cn.owning_lib();
336 var owner = egCore.org.get(owner_id);
337 svc.org_use_map[''+owner_id] += 1;
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());
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];
356 svc.copies = svc.copies.concat(flat);
361 owner_list : owner_name_list,
362 call_number: egCore.idl.toHash(cn),
376 .directive("egVolumeList", function () {
384 templateUrl: './cat/share/t_volume_list',
386 ['$scope','holdingsSvc','egCore','egGridDataProvider','$uibModal',
387 function($scope , holdingsSvc , egCore , egGridDataProvider, $uibModal) {
388 var holdingsSvcInst = new holdingsSvc();
390 $scope.holdingsGridControls = {};
391 $scope.holdingsGridDataProvider = egGridDataProvider.instance({
392 get : function(offset, count) {
393 return this.arrayNotifier(holdingsSvcInst.copies, offset, count);
397 function gatherHoldingsIds () {
400 $scope.holdingsGridControls.allItems(),
401 function (item) { cp_id_list = cp_id_list.concat(item.id_list) }
406 var spawn_volume_editor = function (copies_too) {
409 'open-ils.actor.anon_cache.set_value',
410 null, 'edit-these-copies', {
411 record_id: $scope.recordId,
412 copies: gatherHoldingsIds(),
414 hide_copies : ((copies_too) ? false : true)
416 ).then(function(key) {
419 templateUrl: './cat/share/t_embedded_volcopy',
422 windowClass: 'eg-wide-modal',
424 ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
426 egCore.env.basePath + 'cat/volcopy/' + key + '/embedded';
427 $scope.ok = function(args) { $uibModalInstance.close(args) }
428 $scope.cancel = function () { $uibModalInstance.dismiss() }
430 }).result.then(function() {
436 $scope.edit_volumes = function() {
437 spawn_volume_editor(false);
439 $scope.edit_copies = function() {
440 spawn_volume_editor(true);
443 function load_holdings() {
444 holdingsSvcInst.fetch({
445 rid : $scope.recordId,
446 org : egCore.org.root(),
451 $scope.holdingsGridDataProvider.refresh();
454 $scope.$watch('recordId',
455 function(newVal, oldVal) {
456 if (newVal && newVal !== oldVal) {
465 .filter('string_pick', function() { return function(i){ return arguments[i] || ''; }; })