5 angular.module('egCoreMod')
9 ['$modal','$q','egCore','egAlertDialog','egConfirmDialog','egAlertDialog',
10 function($modal , $q , egCore , egAlertDialog , egConfirmDialog , egAlertDialog) {
14 service.fetch_holds = function(hold_ids) {
15 var deferred = $q.defer();
17 // FIXME: large batches using .authoritative result in many
18 // stranded cstore backends on the server. Needs investigation.
19 // For now, collect holds in a series of small batches.
20 // Fetch them serially both to avoid the above problem and
25 function one_batch() {
26 var ids = hold_ids.slice(index, index + batch_size)
27 .filter(function(id) {return Boolean(id)}) // avoid nulls
29 console.debug('egHolds.fetch_holds => ' + ids);
39 'open-ils.circ.hold.details.batch.retrieve.authoritative',
40 egCore.auth.token(), ids
43 one_batch, // kick off the next batch
46 var hold = hold_data.hold;
47 hold_data.id = hold.id();
48 service.local_flesh(hold_data);
49 deferred.notify(hold_data);
54 one_batch(); // kick it off
55 return deferred.promise;
59 service.cancel_holds = function(hold_ids) {
62 templateUrl : './circ/share/t_cancel_hold_dialog',
64 ['$scope', '$modalInstance', 'cancel_reasons',
65 function($scope, $modalInstance, cancel_reasons) {
67 cancel_reasons : cancel_reasons,
68 num_holds : hold_ids.length
71 $scope.cancel = function($event) {
72 $modalInstance.dismiss();
73 $event.preventDefault();
76 $scope.ok = function() {
78 function cancel_one() {
79 var hold_id = hold_ids.pop();
81 $modalInstance.close();
85 'open-ils.circ', 'open-ils.circ.hold.cancel',
86 egCore.auth.token(), hold_id,
87 $scope.args.cancel_reason.id(),
89 ).then(function(resp) {
90 if (evt = egCore.evt.parse(resp)) {
91 console.error('unable to cancel hold: '
103 cancel_reasons : function() {
104 return service.get_cancel_reasons();
110 service.get_cancel_reasons = function() {
111 if (egCore.env.ahrcc) return $q.when(egCore.env.ahrcc.list);
112 return egCore.pcrud.retrieveAll('ahrcc', {}, {atomic : true})
113 .then(function(list) { return egCore.env.absorbList(list, 'ahrcc').list });
116 // Updates a batch of holds, notifies on each response.
117 // new_values = array of hashes describing values to change,
118 // including the id of the hold to change.
119 // e.g. {id : 1, mint_condition : true}
120 service.update_holds = function(new_values) {
121 return egCore.net.request(
123 'open-ils.circ.hold.update.batch',
124 egCore.auth.token(), null, new_values);
127 service.set_copy_quality = function(hold_ids) {
128 if (!hold_ids.length) return $q.when();
130 templateUrl : './circ/share/t_hold_copy_quality_dialog',
132 ['$scope', '$modalInstance',
133 function($scope, $modalInstance) {
135 function update(val) {
136 var vals = hold_ids.map(function(hold_id) {
137 return {id : hold_id, mint_condition : val}})
138 service.update_holds(vals).finally(function() {
139 $modalInstance.close();
142 $scope.good = function() { update(true) }
143 $scope.any = function() { update(false) }
144 $scope.cancel = function() { $modalInstance.dismiss() }
150 service.edit_pickup_lib = function(hold_ids) {
151 if (!hold_ids.length) return $q.when();
153 templateUrl : './circ/share/t_hold_edit_pickup_lib',
155 ['$scope', '$modalInstance',
156 function($scope, $modalInstance) {
158 $scope.ok = function() {
159 var vals = hold_ids.map(function(hold_id) {
162 pickup_lib : $scope.args.org_unit.id()
165 service.update_holds(vals).finally(function() {
166 $modalInstance.close();
169 $scope.cancel = function() { $modalInstance.dismiss() }
175 service.get_sms_carriers = function() {
176 if (egCore.env.csc) return $q.when(egCore.env.csc.list);
177 return egCore.pcrud.retrieveAll('csc', {}, {atomic : true})
178 .then(function(list) { return egCore.env.absorbList(list, 'csc').list });
181 service.edit_notify_prefs = function(hold_ids) {
182 if (!hold_ids.length) return $q.when();
184 templateUrl : './circ/share/t_hold_notification_prefs',
186 ['$scope', '$modalInstance', 'sms_carriers',
187 function($scope, $modalInstance, sms_carriers) {
189 $scope.sms_carriers = sms_carriers;
190 $scope.num_holds = hold_ids.length;
191 $scope.ok = function() {
193 var vals = hold_ids.map(function(hold_id) {
194 var val = {id : hold_id};
196 ['email', 'phone', 'sms'],
198 var key = type + '_notify';
199 if ($scope.args['update_' + key])
200 val[key] = $scope.args[key];
203 if ($scope.args.update_sms_carrier)
204 val.sms_carrier = $scope.args.sms_carrier.id();
208 service.update_holds(vals).finally(function() {
209 $modalInstance.close();
212 $scope.cancel = function() { $modalInstance.dismiss() }
216 sms_carriers : service.get_sms_carriers
221 service.edit_dates = function(hold_ids) {
222 if (!hold_ids.length) return $q.when();
224 // collects the fields from the dialog the user wishes to modify
225 function relay_to_update(modal_scope) {
226 var vals = hold_ids.map(function(hold_id) {
227 var val = {id : hold_id};
229 ['thaw_date', 'request_time', 'expire_time', 'shelf_expire_time'],
231 if (modal_scope.args['modify_' + field])
232 val[field] = modal_scope.args[field].toISOString();
239 console.log(JSON.stringify(vals,null,2));
240 return service.update_holds(vals);
244 templateUrl : './circ/share/t_hold_dates',
246 ['$scope', '$modalInstance',
247 function($scope, $modalInstance) {
248 var today = new Date();
251 request_time : today,
253 shelf_expire_time : today
255 $scope.num_holds = hold_ids.length;
256 $scope.ok = function() {
257 relay_to_update($scope).then($modalInstance.close);
259 $scope.cancel = function() { $modalInstance.dismiss() }
265 service.update_field_with_confirm = function(hold_ids, msg_key, field, value) {
266 if (!hold_ids.length) return $q.when();
268 return egConfirmDialog.open(
269 egCore.strings[msg_key], '', {num_holds : hold_ids.length})
270 .result.then(function() {
272 var vals = hold_ids.map(function(hold_id) {
273 val = {id : hold_id};
277 return service.update_holds(vals);
281 service.suspend_holds = function(hold_ids) {
282 return service.update_field_with_confirm(
283 hold_ids, 'SUSPEND_HOLDS', 'frozen', true);
286 service.activate_holds = function(hold_ids) {
287 return service.update_field_with_confirm(
288 hold_ids, 'ACTIVATE_HOLDS', 'frozen', false);
291 service.set_top_of_queue = function(hold_ids) {
292 return service.update_field_with_confirm(
293 hold_ids, 'SET_TOP_OF_QUEUE', 'cut_in_line', true);
296 service.clear_top_of_queue = function(hold_ids) {
297 return service.update_field_with_confirm(
298 hold_ids, 'CLEAR_TOP_OF_QUEUE', 'cut_in_line', null);
301 service.transfer_to_marked_title = function(hold_ids) {
302 if (!hold_ids.length) return $q.when();
304 var bib_id = egCore.hatch.getLocalItem(
305 'eg.circ.hold.title_transfer_target');
309 return egAlertDialog.open(
310 egCore.strings.NO_HOLD_TRANSFER_TITLE_MARKED).result;
313 return egConfirmDialog.open(
314 egCore.strings.TRANSFER_HOLD_TO_TITLE, '', {
315 num_holds : hold_ids.length,
318 ).result.then(function() {
319 return egCore.net.request(
321 'open-ils.circ.hold.change_title.specific_holds',
322 egCore.auth.token(), bib_id, hold_ids);
326 // serially retargets each hold
327 service.retarget = function(hold_ids) {
328 if (!hold_ids.length) return $q.when();
329 var deferred = $q.defer();
331 egConfirmDialog.open(
332 egCore.strings.RETARGET_HOLDS, '',
333 {hold_ids : hold_ids.join(',')}
335 ).result.then(function() {
338 var hold_id = hold_ids.pop();
346 'open-ils.circ.hold.reset',
347 egCore.auth.token(), hold_id).finally(do_one);
350 do_one(); // kick it off
353 return deferred.promise;
356 // fleshes orgs, etc. for hold data blobs retrieved from
357 // open-ils.circ.hold.details[.batch].retrieve
358 service.local_flesh = function(hold_data) {
360 hold_data.status_string =
361 egCore.strings['HOLD_STATUS_' + hold_data.status]
364 var hold = hold_data.hold;
365 hold.pickup_lib(egCore.org.get(hold.pickup_lib()));
366 hold.current_shelf_lib(egCore.org.get(hold.current_shelf_lib()));
367 hold_data.id = hold.id();
369 // current_copy is not always fleshed in the API
370 if (hold.current_copy() && typeof hold.current_copy() != 'object')
371 hold.current_copy(hold_data.copy);
378 * Action handlers for the common Hold grid UI.
379 * These generally scrub the data for valid input then pass the
380 * holds / copies / etc. off to the relevant action in egHolds or egCirc.
382 * Caller must apply a reset_page function, which is called after
383 * most actionis are performed.
385 .factory('egHoldGridActions',
386 ['$window','$location','egCore','egHolds','egCirc',
387 function($window , $location , egCore , egHolds , egCirc) {
391 service.refresh = function() {
392 console.error('egHoldGridActions.refresh not defined!');
395 service.cancel_hold = function(items) {
396 var hold_ids = items.filter(function(item) {
397 return !item.hold.cancel_time();
398 }).map(function(item) {return item.hold.id()});
400 return egHolds.cancel_holds(hold_ids).then(service.refresh);
403 // jump to circ list for either 1) the targeted copy or
404 // 2) the hold target copy for copy-level holds
405 service.show_recent_circs = function(items) {
406 if (items.length && (copy = items[0].copy)) {
407 var url = $location.path(
408 '/cat/item/' + copy.id() + '/circ_list').absUrl();
409 $window.open(url, '_blank').focus();
413 function generic_update(items, action) {
414 if (!items.length) return $q.when();
415 var hold_ids = items.map(function(item) {return item.hold.id()});
416 return egHolds[action](hold_ids).then(service.refresh);
419 service.set_copy_quality = function(items) {
420 generic_update(items, 'set_copy_quality'); }
421 service.edit_pickup_lib = function(items) {
422 generic_update(items, 'edit_pickup_lib'); }
423 service.edit_notify_prefs = function(items) {
424 generic_update(items, 'edit_notify_prefs'); }
425 service.edit_dates = function(items) {
426 generic_update(items, 'edit_dates'); }
427 service.suspend = function(items) {
428 generic_update(items, 'suspend_holds'); }
429 service.activate = function(items) {
430 generic_update(items, 'activate_holds'); }
431 service.set_top_of_queue = function(items) {
432 generic_update(items, 'set_top_of_queue'); }
433 service.clear_top_of_queue = function(items) {
434 generic_update(items, 'clear_top_of_queue'); }
435 service.transfer_to_marked_title = function(items) {
436 generic_update(items, 'transfer_to_marked_title'); }
438 service.mark_damaged = function(items) {
440 .filter(function(item) { return Boolean(item.copy) })
441 .map(function(item) { return item.copy.id() });
443 egCirc.mark_damaged(copy_ids).then(service.refresh);
446 service.mark_missing = function(items) {
448 .filter(function(item) { return Boolean(item.copy) })
449 .map(function(item) { return item.copy.id() });
451 egCirc.mark_missing(copy_ids).then(service.refresh);
454 service.retarget = function(items) {
455 var hold_ids = items.map(function(item) { return item.hold.id() });
456 egHolds.retarget(hold_ids).then(service.refresh);
463 * Hold details interface
465 .directive('egHoldDetails', function() {
468 templateUrl : './circ/share/t_hold_details',
471 // if set, called whenever hold details are retrieved. The
472 // argument is the hold blob returned from hold.details.retrieve
477 '$scope','$modal','egCore','egHolds','egCirc',
478 function($scope , $modal , egCore , egHolds , egCirc) {
481 if (!$scope.holdId) return;
485 'open-ils.circ.hold.details.retrieve.authoritative',
486 egCore.auth.token(), $scope.holdId
488 ).then(function(hold_data) {
489 egHolds.local_flesh(hold_data);
491 angular.forEach(hold_data,
492 function(val, key) { $scope[key] = val });
494 // fetch + flesh the cancel_cause if needed
495 if ($scope.hold.cancel_time()) {
496 egHolds.get_cancel_reasons().then(function() {
497 // egHolds caches the causes in egEnv
498 $scope.hold.cancel_cause(
499 egCore.env.ahrcc.map[$scope.hold.cancel_cause()]);
503 if ($scope.hold.current_copy()) {
504 egCirc.flesh_copy_location($scope.hold.current_copy());
507 if ($scope.holdRetrieved)
508 $scope.holdRetrieved(hold_data);
513 $scope.show_notify_tab = function() {
514 $scope.detail_tab = 'notify';
515 egCore.pcrud.search('ahn',
516 {hold : $scope.hold.id()},
517 {flesh : 1, flesh_fields : {ahn : ['notify_staff']}},
519 ).then(function(nots) {
520 $scope.hold.notifications(nots);
524 $scope.delete_note = function(note) {
525 egCore.pcrud.remove(note).then(function() {
526 // remove the deleted note from the locally fleshed notes
528 $scope.hold.notes().filter(function(n) {
529 return n.id() != note.id()
535 $scope.new_note = function() {
537 templateUrl : './circ/share/t_hold_note_dialog',
539 ['$scope', '$modalInstance',
540 function($scope, $modalInstance) {
542 $scope.ok = function() {
543 $modalInstance.close($scope.args)
545 $scope.cancel = function($event) {
546 $modalInstance.dismiss();
547 $event.preventDefault();
551 }).result.then(function(args) {
552 var note = new egCore.idl.ahrn();
553 note.hold($scope.hold.id());
555 note.slip(args.slip);
557 note.title(args.title);
558 note.body(args.body);
559 return egCore.pcrud.create(note).then(function(n) {
560 $scope.hold.notes().push(n);
565 $scope.new_notification = function() {
567 templateUrl : './circ/share/t_hold_notification_dialog',
569 ['$scope', '$modalInstance',
570 function($scope, $modalInstance) {
572 $scope.ok = function() {
573 $modalInstance.close($scope.args)
575 $scope.cancel = function($event) {
576 $modalInstance.dismiss();
577 $event.preventDefault();
581 }).result.then(function(args) {
582 var note = new egCore.idl.ahn();
583 note.hold($scope.hold.id());
584 note.method(args.method);
585 note.note(args.note);
586 note.notify_staff(egCore.auth.user().id());
587 note.notify_time('now');
588 return egCore.pcrud.create(note).then(function(n) {
589 n.notify_staff(egCore.auth.user());
590 $scope.hold.notifications().push(n);
595 $scope.$watch('holdId', function(newVal, oldVal) {
596 if (newVal != oldVal) draw();