5 angular.module('egCoreMod')
9 ['$modal','$q','egCore','egConfirmDialog','egAlertDialog',
10 function($modal , $q , egCore , 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) {
68 cancel_reasons : cancel_reasons,
69 num_holds : hold_ids.length
72 $scope.cancel = function($event) {
73 $modalInstance.dismiss();
74 $event.preventDefault();
77 $scope.ok = function() {
79 function cancel_one() {
80 var hold_id = hold_ids.pop();
82 $modalInstance.close();
86 'open-ils.circ', 'open-ils.circ.hold.cancel',
87 egCore.auth.token(), hold_id,
88 $scope.args.cancel_reason.id(),
90 ).then(function(resp) {
91 if (evt = egCore.evt.parse(resp)) {
92 console.error('unable to cancel hold: '
104 cancel_reasons : function() {
105 return service.get_cancel_reasons();
111 service.uncancel_holds = function(hold_ids) {
114 templateUrl : './circ/share/t_uncancel_hold_dialog',
116 ['$scope', '$modalInstance',
117 function($scope, $modalInstance) {
119 num_holds : hold_ids.length
122 $scope.cancel = function($event) {
123 $modalInstance.dismiss();
124 $event.preventDefault();
127 $scope.ok = function() {
129 function uncancel_one() {
130 var hold_id = hold_ids.pop();
132 $modalInstance.close();
136 'open-ils.circ', 'open-ils.circ.hold.uncancel',
137 egCore.auth.token(), hold_id
138 ).then(function(resp) {
139 if (evt = egCore.evt.parse(resp)) {
140 console.error('unable to uncancel hold: '
154 service.get_cancel_reasons = function() {
155 if (egCore.env.ahrcc) return $q.when(egCore.env.ahrcc.list);
156 return egCore.pcrud.retrieveAll('ahrcc', {}, {atomic : true})
157 .then(function(list) { return egCore.env.absorbList(list, 'ahrcc').list });
160 // Updates a batch of holds, notifies on each response.
161 // new_values = array of hashes describing values to change,
162 // including the id of the hold to change.
163 // e.g. {id : 1, mint_condition : true}
164 service.update_holds = function(new_values) {
165 return egCore.net.request(
167 'open-ils.circ.hold.update.batch',
168 egCore.auth.token(), null, new_values);
171 service.set_copy_quality = function(hold_ids) {
172 if (!hold_ids.length) return $q.when();
174 templateUrl : './circ/share/t_hold_copy_quality_dialog',
176 ['$scope', '$modalInstance',
177 function($scope, $modalInstance) {
179 function update(val) {
180 var vals = hold_ids.map(function(hold_id) {
181 return {id : hold_id, mint_condition : val}})
182 service.update_holds(vals).finally(function() {
183 $modalInstance.close();
186 $scope.good = function() { update(true) }
187 $scope.any = function() { update(false) }
188 $scope.cancel = function() { $modalInstance.dismiss() }
194 service.edit_pickup_lib = function(hold_ids) {
195 if (!hold_ids.length) return $q.when();
197 templateUrl : './circ/share/t_hold_edit_pickup_lib',
199 ['$scope', '$modalInstance',
200 function($scope, $modalInstance) {
201 $scope.cant_be_pickup = function (id) { return !egCore.org.CanHaveUsers(id); };
203 $scope.ok = function() {
204 var vals = hold_ids.map(function(hold_id) {
207 pickup_lib : $scope.args.org_unit.id()
210 service.update_holds(vals).finally(function() {
211 $modalInstance.close();
214 $scope.cancel = function() { $modalInstance.dismiss() }
220 service.get_sms_carriers = function() {
221 if (egCore.env.csc) return $q.when(egCore.env.csc.list);
222 return egCore.pcrud.retrieveAll('csc', {}, {atomic : true})
223 .then(function(list) { return egCore.env.absorbList(list, 'csc').list });
226 service.edit_notify_prefs = function(hold_ids) {
227 if (!hold_ids.length) return $q.when();
229 templateUrl : './circ/share/t_hold_notification_prefs',
231 ['$scope', '$modalInstance', 'sms_carriers',
232 function($scope, $modalInstance, sms_carriers) {
234 $scope.sms_carriers = sms_carriers;
235 $scope.num_holds = hold_ids.length;
236 $scope.ok = function() {
238 var vals = hold_ids.map(function(hold_id) {
239 var val = {id : hold_id};
241 ['email', 'phone', 'sms'],
243 var key = type + '_notify';
244 if ($scope.args['update_' + key])
245 val[key] = $scope.args[key];
248 if ($scope.args.update_sms_carrier)
249 val.sms_carrier = $scope.args.sms_carrier.id();
253 service.update_holds(vals).finally(function() {
254 $modalInstance.close();
257 $scope.cancel = function() { $modalInstance.dismiss() }
261 sms_carriers : service.get_sms_carriers
266 service.edit_dates = function(hold_ids) {
267 if (!hold_ids.length) return $q.when();
269 // collects the fields from the dialog the user wishes to modify
270 function relay_to_update(modal_scope) {
271 var vals = hold_ids.map(function(hold_id) {
272 var val = {id : hold_id};
274 ['thaw_date', 'request_time', 'expire_time', 'shelf_expire_time'],
276 if (modal_scope.args['modify_' + field])
277 val[field] = modal_scope.args[field].toISOString();
284 console.log(JSON.stringify(vals,null,2));
285 return service.update_holds(vals);
289 templateUrl : './circ/share/t_hold_dates',
291 ['$scope', '$modalInstance',
292 function($scope, $modalInstance) {
293 var today = new Date();
296 request_time : today,
298 shelf_expire_time : today
300 $scope.num_holds = hold_ids.length;
301 $scope.ok = function() {
302 relay_to_update($scope).then($modalInstance.close);
304 $scope.cancel = function() { $modalInstance.dismiss() }
310 service.update_field_with_confirm = function(hold_ids, msg_key, field, value) {
311 if (!hold_ids.length) return $q.when();
313 return egConfirmDialog.open(
314 egCore.strings[msg_key], '', {num_holds : hold_ids.length})
315 .result.then(function() {
317 var vals = hold_ids.map(function(hold_id) {
318 val = {id : hold_id};
322 return service.update_holds(vals);
326 service.suspend_holds = function(hold_ids) {
327 return service.update_field_with_confirm(
328 hold_ids, 'SUSPEND_HOLDS', 'frozen', true);
331 service.activate_holds = function(hold_ids) {
332 return service.update_field_with_confirm(
333 hold_ids, 'ACTIVATE_HOLDS', 'frozen', false);
336 service.set_top_of_queue = function(hold_ids) {
337 return service.update_field_with_confirm(
338 hold_ids, 'SET_TOP_OF_QUEUE', 'cut_in_line', true);
341 service.clear_top_of_queue = function(hold_ids) {
342 return service.update_field_with_confirm(
343 hold_ids, 'CLEAR_TOP_OF_QUEUE', 'cut_in_line', null);
346 service.transfer_to_marked_title = function(hold_ids) {
347 if (!hold_ids.length) return $q.when();
349 var bib_id = egCore.hatch.getLocalItem(
350 'eg.circ.hold.title_transfer_target');
354 return egAlertDialog.open(
355 egCore.strings.NO_HOLD_TRANSFER_TITLE_MARKED).result;
358 return egConfirmDialog.open(
359 egCore.strings.TRANSFER_HOLD_TO_TITLE, '', {
360 num_holds : hold_ids.length,
363 ).result.then(function() {
364 return egCore.net.request(
366 'open-ils.circ.hold.change_title.specific_holds',
367 egCore.auth.token(), bib_id, hold_ids);
371 // serially retargets each hold
372 service.retarget = function(hold_ids) {
373 if (!hold_ids.length) return $q.when();
374 var deferred = $q.defer();
376 egConfirmDialog.open(
377 egCore.strings.RETARGET_HOLDS, '',
378 {hold_ids : hold_ids.join(',')}
380 ).result.then(function() {
383 var hold_id = hold_ids.pop();
391 'open-ils.circ.hold.reset',
392 egCore.auth.token(), hold_id).finally(do_one);
395 do_one(); // kick it off
398 return deferred.promise;
401 // fleshes orgs, etc. for hold data blobs retrieved from
402 // open-ils.circ.hold.details[.batch].retrieve
403 service.local_flesh = function(hold_data) {
405 hold_data.status_string =
406 egCore.strings['HOLD_STATUS_' + hold_data.status]
409 var hold = hold_data.hold;
410 hold.pickup_lib(egCore.org.get(hold.pickup_lib()));
411 hold.current_shelf_lib(egCore.org.get(hold.current_shelf_lib()));
412 hold_data.id = hold.id();
414 if (hold.requestor() && typeof hold.requestor() != 'object')
415 egCore.pcrud.retrieve('au',hold.requestor()).then(function(u) { hold.requestor(u) });
417 if (hold.cancel_cause() && typeof hold.cancel_cause() != 'object')
418 egCore.pcrud.retrieve('ahrcc',hold.cancel_cause()).then(function(c) { hold.cancel_cause(c) });
420 if (hold.usr() && typeof hold.usr() != 'object')
421 egCore.pcrud.retrieve('au',hold.usr()).then(function(u) { hold.usr(u) });
423 // current_copy is not always fleshed in the API
424 if (hold.current_copy() && typeof hold.current_copy() != 'object')
425 hold.current_copy(hold_data.copy);
432 * Action handlers for the common Hold grid UI.
433 * These generally scrub the data for valid input then pass the
434 * holds / copies / etc. off to the relevant action in egHolds or egCirc.
436 * Caller must apply a reset_page function, which is called after
437 * most actionis are performed.
439 .factory('egHoldGridActions',
440 ['$window','$location','egCore','egHolds','egCirc',
441 function($window , $location , egCore , egHolds , egCirc) {
445 service.refresh = function() {
446 console.error('egHoldGridActions.refresh not defined!');
449 service.cancel_hold = function(items) {
450 var hold_ids = items.filter(function(item) {
451 return !item.hold.cancel_time();
452 }).map(function(item) {return item.hold.id()});
454 return egHolds.cancel_holds(hold_ids).then(service.refresh);
457 service.uncancel_hold = function(items) {
458 var hold_ids = items.filter(function(item) {
459 return item.hold.cancel_time();
460 }).map(function(item) {return item.hold.id()});
462 return egHolds.uncancel_holds(hold_ids).then(service.refresh);
465 // jump to circ list for either 1) the targeted copy or
466 // 2) the hold target copy for copy-level holds
467 service.show_recent_circs = function(items) {
468 if (items.length && (copy = items[0].copy)) {
469 var url = $location.path(
470 '/cat/item/' + copy.id() + '/circ_list').absUrl();
471 $window.open(url, '_blank').focus();
475 function generic_update(items, action) {
476 if (!items.length) return $q.when();
477 var hold_ids = items.map(function(item) {return item.hold.id()});
478 return egHolds[action](hold_ids).then(service.refresh);
481 service.set_copy_quality = function(items) {
482 generic_update(items, 'set_copy_quality'); }
483 service.edit_pickup_lib = function(items) {
484 generic_update(items, 'edit_pickup_lib'); }
485 service.edit_notify_prefs = function(items) {
486 generic_update(items, 'edit_notify_prefs'); }
487 service.edit_dates = function(items) {
488 generic_update(items, 'edit_dates'); }
489 service.suspend = function(items) {
490 generic_update(items, 'suspend_holds'); }
491 service.activate = function(items) {
492 generic_update(items, 'activate_holds'); }
493 service.set_top_of_queue = function(items) {
494 generic_update(items, 'set_top_of_queue'); }
495 service.clear_top_of_queue = function(items) {
496 generic_update(items, 'clear_top_of_queue'); }
497 service.transfer_to_marked_title = function(items) {
498 generic_update(items, 'transfer_to_marked_title'); }
500 service.mark_damaged = function(items) {
502 .filter(function(item) { return Boolean(item.copy) })
503 .map(function(item) { return item.copy.id() });
505 egCirc.mark_damaged(copy_ids).then(service.refresh);
508 service.mark_missing = function(items) {
510 .filter(function(item) { return Boolean(item.copy) })
511 .map(function(item) { return item.copy.id() });
513 egCirc.mark_missing(copy_ids).then(service.refresh);
516 service.retarget = function(items) {
517 var hold_ids = items.map(function(item) { return item.hold.id() });
518 egHolds.retarget(hold_ids).then(service.refresh);
525 * Hold details interface
527 .directive('egHoldDetails', function() {
530 templateUrl : './circ/share/t_hold_details',
533 // if set, called whenever hold details are retrieved. The
534 // argument is the hold blob returned from hold.details.retrieve
539 '$scope','$modal','egCore','egHolds','egCirc',
540 function($scope , $modal , egCore , egHolds , egCirc) {
543 if (!$scope.holdId) return;
547 'open-ils.circ.hold.details.retrieve.authoritative',
548 egCore.auth.token(), $scope.holdId
550 ).then(function(hold_data) {
551 egHolds.local_flesh(hold_data);
553 angular.forEach(hold_data,
554 function(val, key) { $scope[key] = val });
556 // fetch + flesh the cancel_cause if needed
557 if ($scope.hold.cancel_time()) {
558 egHolds.get_cancel_reasons().then(function() {
559 // egHolds caches the causes in egEnv
560 $scope.hold.cancel_cause(
561 egCore.env.ahrcc.map[$scope.hold.cancel_cause()]);
565 if ($scope.hold.current_copy()) {
566 egCirc.flesh_copy_location($scope.hold.current_copy());
569 if ($scope.holdRetrieved)
570 $scope.holdRetrieved(hold_data);
575 $scope.show_notify_tab = function() {
576 $scope.detail_tab = 'notify';
577 egCore.pcrud.search('ahn',
578 {hold : $scope.hold.id()},
579 {flesh : 1, flesh_fields : {ahn : ['notify_staff']}},
581 ).then(function(nots) {
582 $scope.hold.notifications(nots);
586 $scope.delete_note = function(note) {
587 egCore.pcrud.remove(note).then(function() {
588 // remove the deleted note from the locally fleshed notes
590 $scope.hold.notes().filter(function(n) {
591 return n.id() != note.id()
597 $scope.new_note = function() {
599 templateUrl : './circ/share/t_hold_note_dialog',
601 ['$scope', '$modalInstance',
602 function($scope, $modalInstance) {
604 $scope.ok = function() {
605 $modalInstance.close($scope.args)
607 $scope.cancel = function($event) {
608 $modalInstance.dismiss();
609 $event.preventDefault();
613 }).result.then(function(args) {
614 var note = new egCore.idl.ahrn();
615 note.hold($scope.hold.id());
617 note.slip(args.slip);
619 note.title(args.title);
620 note.body(args.body);
621 return egCore.pcrud.create(note).then(function(n) {
622 $scope.hold.notes().push(n);
627 $scope.new_notification = function() {
629 templateUrl : './circ/share/t_hold_notification_dialog',
631 ['$scope', '$modalInstance',
632 function($scope, $modalInstance) {
634 $scope.ok = function() {
635 $modalInstance.close($scope.args)
637 $scope.cancel = function($event) {
638 $modalInstance.dismiss();
639 $event.preventDefault();
643 }).result.then(function(args) {
644 var note = new egCore.idl.ahn();
645 note.hold($scope.hold.id());
646 note.method(args.method);
647 note.note(args.note);
648 note.notify_staff(egCore.auth.user().id());
649 note.notify_time('now');
650 return egCore.pcrud.create(note).then(function(n) {
651 n.notify_staff(egCore.auth.user());
652 $scope.hold.notifications().push(n);
657 $scope.$watch('holdId', function(newVal, oldVal) {
658 if (newVal != oldVal) draw();