]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/circ/services/holds.js
LP#1748265 SMS Carrier not an option in the patron's list of holds.
[Evergreen.git] / Open-ILS / web / js / ui / default / staff / circ / services / holds.js
1 /**
2  * Holds, yo
3  */
4
5 angular.module('egCoreMod')
6
7 .factory('egHolds',
8
9        ['$uibModal','$q','egCore','egConfirmDialog','egAlertDialog',
10 function($uibModal , $q , egCore , egConfirmDialog , egAlertDialog) {
11
12     var service = {};
13
14     service.fetch_holds = function(hold_ids) {
15         var deferred = $q.defer();
16
17         // Fetch hold details in batches for better UI responsiveness.
18         var batch_size = 5;
19         var index = 0;
20
21         function one_batch() {
22             var ids = hold_ids.slice(index, index + batch_size)
23                 .filter(function(id) {return Boolean(id)}) // avoid nulls
24
25             console.debug('egHolds.fetch_holds => ' + ids);
26             index += batch_size;
27
28             if (!ids.length) {
29                 deferred.resolve();
30                 return;
31             }
32
33             egCore.net.request(
34                 'open-ils.circ',
35                 'open-ils.circ.hold.details.batch.retrieve.authoritative',
36                 egCore.auth.token(), ids, {
37                     include_current_copy : true,
38                     include_usr          : true,
39                     include_cancel_cause : true,
40                     include_sms_carrier  : true,
41                     include_requestor    : true
42                 }
43
44             ).then(
45                 one_batch,  // kick off the next batch
46                 null, 
47                 function(hold_data) {
48                     var hold = hold_data.hold;
49                     hold_data.id = hold.id();
50                     service.local_flesh(hold_data);
51                     deferred.notify(hold_data);
52                 }
53             );
54         }
55
56         one_batch(); // kick it off
57         return deferred.promise;
58     }
59
60
61     service.cancel_holds = function(hold_ids) {
62        
63         return $uibModal.open({
64             templateUrl : './circ/share/t_cancel_hold_dialog',
65             backdrop: 'static',
66             controller : 
67                 ['$scope', '$uibModalInstance', 'cancel_reasons',
68                 function($scope, $uibModalInstance, cancel_reasons) {
69                     $scope.args = {
70                         cancel_reason : 5,
71                         cancel_reasons : cancel_reasons,
72                         num_holds : hold_ids.length
73                     };
74                     
75                     $scope.cancel = function($event) {
76                         $uibModalInstance.dismiss();
77                         $event.preventDefault();
78                     }
79
80                     $scope.ok = function() {
81
82                         function cancel_one() {
83                             var hold_id = hold_ids.pop();
84                             if (!hold_id) {
85                                 $uibModalInstance.close();
86                                 return;
87                             }
88                             egCore.net.request(
89                                 'open-ils.circ', 'open-ils.circ.hold.cancel',
90                                 egCore.auth.token(), hold_id,
91                                 $scope.args.cancel_reason,
92                                 $scope.args.note
93                             ).then(function(resp) {
94                                 if (evt = egCore.evt.parse(resp)) {
95                                     egCore.audio.play(
96                                         'warning.hold.cancel_failed');
97                                     console.error('unable to cancel hold: ' 
98                                         + evt.toString());
99                                 }
100                                 cancel_one();
101                             });
102                         }
103
104                         cancel_one();
105                     }
106                 }
107             ],
108             resolve : {
109                 cancel_reasons : function() {
110                     return service.get_cancel_reasons();
111                 }
112             }
113         }).result;
114     }
115
116     service.uncancel_holds = function(hold_ids) {
117        
118         return $uibModal.open({
119             templateUrl : './circ/share/t_uncancel_hold_dialog',
120             backdrop: 'static',
121             controller : 
122                 ['$scope', '$uibModalInstance',
123                 function($scope, $uibModalInstance) {
124                     $scope.args = {
125                         num_holds : hold_ids.length
126                     };
127                     
128                     $scope.cancel = function($event) {
129                         $uibModalInstance.dismiss();
130                         $event.preventDefault();
131                     }
132
133                     $scope.ok = function() {
134
135                         function uncancel_one() {
136                             var hold_id = hold_ids.pop();
137                             if (!hold_id) {
138                                 $uibModalInstance.close();
139                                 return;
140                             }
141                             egCore.net.request(
142                                 'open-ils.circ', 'open-ils.circ.hold.uncancel',
143                                 egCore.auth.token(), hold_id
144                             ).then(function(resp) {
145                                 if (evt = egCore.evt.parse(resp)) {
146                                     egCore.audio.play(
147                                         'warning.hold.uncancel_failed');
148                                     console.error('unable to uncancel hold: ' 
149                                         + evt.toString());
150                                 }
151                                 uncancel_one();
152                             });
153                         }
154
155                         uncancel_one();
156                     }
157                 }
158             ]
159         }).result;
160     }
161
162     service.get_cancel_reasons = function() {
163         if (egCore.env.ahrcc) return $q.when(egCore.env.ahrcc.list);
164         return egCore.pcrud.retrieveAll('ahrcc', {}, {atomic : true})
165         .then(function(list) { return egCore.env.absorbList(list, 'ahrcc').list });
166     }
167
168     // Updates a batch of holds, notifies on each response.
169     // new_values = array of hashes describing values to change,
170     // including the id of the hold to change.
171     // e.g. {id : 1, mint_condition : true}
172     service.update_holds = function(new_values) {
173         return egCore.net.request(
174             'open-ils.circ',
175             'open-ils.circ.hold.update.batch',
176             egCore.auth.token(), null, new_values).then(
177             function(resp) {
178                 if (evt = egCore.evt.parse(resp)) {
179                     egCore.audio.play(
180                         'warning.hold.batch_update');
181                     console.error('unable to batch update holds: '
182                         + evt.toString());
183                 } else {
184                     egCore.audio.play(
185                         'success.hold.batch_update');
186                 }
187             }
188         );
189     }
190
191     service.set_copy_quality = function(hold_ids) {
192         if (!hold_ids.length) return $q.when();
193         return $uibModal.open({
194             templateUrl : './circ/share/t_hold_copy_quality_dialog',
195             backdrop: 'static',
196             controller : 
197                 ['$scope', '$uibModalInstance',
198                 function($scope, $uibModalInstance) {
199
200                     function update(val) {
201                         var vals = hold_ids.map(function(hold_id) {
202                             return {id : hold_id, mint_condition : val}})
203                         service.update_holds(vals).finally(function() {
204                             $uibModalInstance.close();
205                         });
206                     }
207                     $scope.good = function() { update(true) }
208                     $scope.any = function() { update(false) }
209                     $scope.cancel = function() { $uibModalInstance.dismiss() }
210                 }
211             ]
212         }).result;
213     }
214
215     service.edit_pickup_lib = function(hold_ids) {
216         if (!hold_ids.length) return $q.when();
217         return $uibModal.open({
218             templateUrl : './circ/share/t_hold_edit_pickup_lib',
219             backdrop: 'static',
220             controller : 
221                 ['$scope', '$uibModalInstance',
222                 function($scope, $uibModalInstance) {
223                     $scope.cant_be_pickup = function (id) { return !egCore.org.CanHaveUsers(id); };
224                     $scope.args = {};
225                     $scope.ok = function() { 
226                         var vals = hold_ids.map(function(hold_id) {
227                             return {
228                                 id : hold_id, 
229                                 pickup_lib : $scope.args.org_unit.id()
230                             }
231                         });
232                         service.update_holds(vals).finally(function() {
233                             $uibModalInstance.close();
234                         });
235                     }
236                     $scope.cancel = function() { $uibModalInstance.dismiss() }
237                 }
238             ]
239         }).result;
240     }
241
242     service.get_sms_carriers = function() {
243         if (egCore.env.csc) return $q.when(egCore.env.csc.list);
244         return egCore.pcrud.retrieveAll('csc', {}, {atomic : true})
245         .then(function(list) { return egCore.env.absorbList(list, 'csc').list });
246     }
247
248     service.edit_notify_prefs = function(hold_ids) {
249         if (!hold_ids.length) return $q.when();
250         return $uibModal.open({
251             templateUrl : './circ/share/t_hold_notification_prefs',
252             backdrop: 'static',
253             controller : 
254                 ['$scope', '$uibModalInstance', 'sms_carriers',
255                 function($scope, $uibModalInstance, sms_carriers) {
256                     $scope.args = {}
257                     $scope.sms_carriers = sms_carriers;
258                     $scope.num_holds = hold_ids.length;
259                     $scope.ok = function() { 
260
261                         var vals = hold_ids.map(function(hold_id) {
262                             var val = {id : hold_id};
263                             angular.forEach(
264                                 ['email', 'phone', 'sms'],
265                                 function(type) {
266                                     var key = type + '_notify';
267                                     if ($scope.args['update_' + key]) 
268                                         val[key] = $scope.args[key];
269                                 }
270                             );
271                             if ($scope.args.update_sms_carrier)
272                                 val.sms_carrier = $scope.args.sms_carrier.id();
273                             return val;
274                         });
275
276                         service.update_holds(vals).finally(function() {
277                             $uibModalInstance.close();
278                         });
279                     }
280                     $scope.cancel = function() { $uibModalInstance.dismiss() }
281                 }
282             ],
283             resolve : {
284                 sms_carriers : service.get_sms_carriers
285             }
286         }).result;
287     }
288
289     service.edit_dates = function(hold_ids) {
290         if (!hold_ids.length) return $q.when();
291
292         // collects the fields from the dialog the user wishes to modify
293         function relay_to_update(modal_scope) {
294             var vals = hold_ids.map(function(hold_id) {
295                 var val = {id : hold_id};
296                 angular.forEach(
297                     ['thaw_date', 'request_time', 'expire_time', 'shelf_expire_time'], 
298                     function(field) {
299                         if (modal_scope.args['modify_' + field]) 
300                             val[field] = modal_scope.args[field].toISOString();
301                     }
302                 );
303
304                 return val;
305             });
306
307             console.log(JSON.stringify(vals,null,2));
308             return service.update_holds(vals);
309         }
310
311         return $uibModal.open({
312             templateUrl : './circ/share/t_hold_dates',
313             backdrop: 'static',
314             controller : 
315                 ['$scope', '$uibModalInstance',
316                 function($scope, $uibModalInstance) {
317                     var today = new Date();
318                     $scope.args = {
319                         thaw_date : today,
320                         request_time : today,
321                         expire_time : today,
322                         shelf_expire_time : today
323                     }
324                     $scope.num_holds = hold_ids.length;
325                     $scope.ok = function() { 
326                         relay_to_update($scope).then($uibModalInstance.close);
327                     }
328                     $scope.cancel = function() { $uibModalInstance.dismiss() }
329                 }
330             ],
331         }).result;
332     }
333
334     service.update_field_with_confirm = function(hold_ids, msg_key, field, value) {
335         if (!hold_ids.length) return $q.when();
336
337         return egConfirmDialog.open(
338             egCore.strings[msg_key], '', {num_holds : hold_ids.length})
339         .result.then(function() {
340
341             var vals = hold_ids.map(function(hold_id) {
342                 val = {id : hold_id};
343                 val[field] = value;
344                 return val;
345             });
346             return service.update_holds(vals);
347         });
348     }
349
350     service.suspend_holds = function(hold_ids) {
351         return service.update_field_with_confirm(
352             hold_ids, 'SUSPEND_HOLDS', 'frozen', true);
353     }
354
355     service.activate_holds = function(hold_ids) {
356         return service.update_field_with_confirm(
357             hold_ids, 'ACTIVATE_HOLDS', 'frozen', false);
358     }
359
360     service.set_top_of_queue = function(hold_ids) {
361         return service.update_field_with_confirm(
362             hold_ids, 'SET_TOP_OF_QUEUE', 'cut_in_line', true);
363     }
364
365     service.clear_top_of_queue = function(hold_ids) {
366         return service.update_field_with_confirm(
367             hold_ids, 'CLEAR_TOP_OF_QUEUE', 'cut_in_line', null);
368     }
369
370     service.transfer_to_marked_title = function(hold_ids) {
371         if (!hold_ids.length) return $q.when();
372
373         var bib_id = egCore.hatch.getLocalItem(
374             'eg.circ.hold.title_transfer_target');
375
376         if (!bib_id) {
377             // no target marked
378             return egAlertDialog.open(
379                 egCore.strings.NO_HOLD_TRANSFER_TITLE_MARKED).result;
380         }
381
382         return egConfirmDialog.open(
383             egCore.strings.TRANSFER_HOLD_TO_TITLE, '', {
384                 num_holds : hold_ids.length,
385                 bib_id : bib_id
386             }
387         ).result.then(function() {
388             return egCore.net.request(
389                 'open-ils.circ',
390                 'open-ils.circ.hold.change_title.specific_holds',
391                 egCore.auth.token(), bib_id, hold_ids);
392         });
393     }
394
395     service.transfer_all_bib_holds_to_marked_title = function(bib_ids) {
396         if (!bib_ids.length) return $q.when();
397
398         var target_bib_id = egCore.hatch.getLocalItem(
399             'eg.circ.hold.title_transfer_target');
400
401         if (!target_bib_id) {
402             // no target marked
403             return egAlertDialog.open(
404                 egCore.strings.NO_HOLD_TRANSFER_TITLE_MARKED).result;
405         }
406
407         return egConfirmDialog.open(
408             egCore.strings.TRANSFER_ALL_BIB_HOLDS_TO_TITLE, '', {
409                 num_bibs : bib_ids.length,
410                 bib_id : target_bib_id
411             }
412         ).result.then(function() {
413             return egCore.net.request(
414                 'open-ils.circ',
415                 'open-ils.circ.hold.change_title',
416                 egCore.auth.token(), target_bib_id, bib_ids);
417         });
418     }
419
420     // serially retargets each hold
421     service.retarget = function(hold_ids) {
422         if (!hold_ids.length) return $q.when();
423         var deferred = $q.defer();
424
425         egConfirmDialog.open(
426             egCore.strings.RETARGET_HOLDS, '', 
427             {hold_ids : hold_ids.join(',')}
428
429         ).result.then(function() {
430
431             function do_one() {
432                 var hold_id = hold_ids.pop();
433                 if (!hold_id) {
434                     deferred.resolve();
435                     return;
436                 }
437
438                 egCore.net.request(
439                     'open-ils.circ',
440                     'open-ils.circ.hold.reset',
441                     egCore.auth.token(), hold_id).finally(do_one);
442             }
443
444             do_one(); // kick it off
445         });
446
447         return deferred.promise;
448     }
449
450     // fleshes orgs, etc. for hold data blobs retrieved from
451     // open-ils.circ.hold.details[.batch].retrieve
452     service.local_flesh = function(hold_data) {
453
454         hold_data.status_string = 
455             egCore.strings['HOLD_STATUS_' + hold_data.status] 
456             || hold_data.status;
457
458         var hold = hold_data.hold;
459         var volume = hold_data.volume;
460         hold.pickup_lib(egCore.org.get(hold.pickup_lib()));
461         hold.current_shelf_lib(egCore.org.get(hold.current_shelf_lib()));
462         hold_data.id = hold.id();
463
464         // TODO: LP#1697954 fleshing calls below are deprecated in favor
465         // of API fleshing.
466
467         if (hold.requestor() && typeof hold.requestor() != 'object') {
468             console.debug('fetching hold requestor');
469             egCore.pcrud.retrieve('au',hold.requestor()).then(function(u) { hold.requestor(u) });
470         }
471
472         if (hold.cancel_cause() && typeof hold.cancel_cause() != 'object') {
473             console.debug('fetching hold cancel cause');
474             egCore.pcrud.retrieve('ahrcc',hold.cancel_cause()).then(function(c) { hold.cancel_cause(c) });
475         }
476
477         if (hold.usr() && typeof hold.usr() != 'object') {
478             console.debug('fetching hold user');
479             egCore.pcrud.retrieve('au',hold.usr()).then(function(u) { hold.usr(u) });
480         }
481
482         if (hold.sms_carrier() && typeof hold.sms_carrier() != 'object') {
483             console.debug('fetching sms carrier');
484             egCore.pcrud.retrieve('csc',hold.sms_carrier()).then(function(c) { hold.sms_carrier(c) });
485         }
486
487         // current_copy is not always fleshed in the API
488         if (hold.current_copy() && typeof hold.current_copy() != 'object') {
489             hold.current_copy(hold_data.copy);
490         }
491
492         if (hold.current_copy()) {
493             // likewise, current_copy's status isn't fleshed in the API
494             if(hold.current_copy().status() !== null &&
495                typeof hold.current_copy().status() != 'object')
496                 egCore.pcrud.retrieve('ccs',hold.current_copy().status()
497                     ).then(function(c) { hold.current_copy().status(c) });
498         
499             // current_copy's shelving location position isn't always accessible
500             if (hold.current_copy().location()) {
501                 //console.debug('fetching hold copy location order');
502                 var location_id;
503                 if (typeof hold.current_copy().location() != 'object') {
504                     location_id = hold.current_copy().location();
505                 } else {
506                     location_id = hold.current_copy().location().id();
507                 }
508                 egCore.pcrud.search(
509                     'acplo',
510                     {location: location_id, org: egCore.auth.user().ws_ou()},
511                     null,
512                     {atomic:true}
513                 ).then(function(orders) {
514                     if(orders[0]){
515                         hold_data.hold._copy_location_position = orders[0].position();
516                     } else {
517                         hold_data.hold._copy_location_position = 999;
518                     }
519                 });
520             }
521
522             //Call number affixes are not always fleshed in the API
523             if (hold_data.volume.prefix) {
524                 //console.debug('fetching call number prefix');
525                 //console.log(hold_data.volume.prefix());
526                 egCore.pcrud.retrieve('acnp',hold_data.volume.prefix())
527                 .then(function(p) {hold_data.volume.prefix = p.label(); hold_data.volume.prefix_sortkey = p.label_sortkey()});
528             }
529             if (hold_data.volume.suffix) {
530                 //console.debug('fetching call number suffix');
531                 //console.log(hold_data.volume.suffix());
532                 egCore.pcrud.retrieve('acns',hold_data.volume.suffix())
533                 .then(function(s) {hold_data.volume.suffix = s.label(); hold_data.volume.suffix_sortkey = s.label_sortkey()});
534             }
535         }
536     }
537
538     return service;
539 }])
540
541 /**  
542  * Action handlers for the common Hold grid UI.
543  * These generally scrub the data for valid input then pass the
544  * holds / copies / etc. off to the relevant action in egHolds or egCirc.
545  *
546  * Caller must apply a reset_page function, which is called after 
547  * most actionis are performed.
548  */
549 .factory('egHoldGridActions', 
550        ['$window','$location','$timeout','egCore','egHolds','egCirc',
551 function($window , $location , $timeout , egCore , egHolds , egCirc) {
552     
553     var service = {};
554
555     service.refresh = function() {
556         console.error('egHoldGridActions.refresh not defined!');
557     }
558
559     service.cancel_hold = function(items) {
560         var hold_ids = items.filter(function(item) {
561             return !item.hold.cancel_time();
562         }).map(function(item) {return item.hold.id()});
563
564         return egHolds.cancel_holds(hold_ids).then(service.refresh);
565     }
566
567     service.uncancel_hold = function(items) {
568         var hold_ids = items.filter(function(item) {
569             return item.hold.cancel_time();
570         }).map(function(item) {return item.hold.id()});
571
572         return egHolds.uncancel_holds(hold_ids).then(service.refresh);
573     }
574
575     // jump to circ list for either 1) the targeted copy or
576     // 2) the hold target copy for copy-level holds
577     service.show_recent_circs = function(items) {
578         var focus = items.length == 1;
579         angular.forEach(items, function(item) {
580             if (item.copy) {
581                 var url = egCore.env.basePath +
582                           '/cat/item/' +
583                           item.copy.id() +
584                           '/circ_list';
585                 $timeout(function() { var x = $window.open(url, '_blank'); if (focus) x.focus() });
586             }
587         });
588     }
589
590     service.show_patrons = function(items) {
591         var focus = items.length == 1;
592         angular.forEach(items, function(item) {
593             var url = egCore.env.basePath +
594                       'circ/patron/' +
595                       item.hold.usr().id() +
596                       '/holds';
597             $timeout(function() { var x = $window.open(url, '_blank'); if (focus) x.focus() });
598         });
599     }
600
601     service.show_holds_for_title = function(items) {
602         var focus = items.length == 1;
603         angular.forEach(items, function(item) {
604             var url = egCore.env.basePath +
605                       'cat/catalog/record/' +
606                       item.mvr.doc_id() +
607                       '/holds';
608             $timeout(function() { var x = $window.open(url, '_blank'); if (focus) x.focus() });
609         });
610     }
611
612
613     function generic_update(items, action) {
614         if (!items.length) return $q.when();
615         var hold_ids = items.map(function(item) {return item.hold.id()});
616         return egHolds[action](hold_ids).then(service.refresh);
617     }
618
619     service.set_copy_quality = function(items) {
620         generic_update(items, 'set_copy_quality'); }
621     service.edit_pickup_lib = function(items) {
622         generic_update(items, 'edit_pickup_lib'); }
623     service.edit_notify_prefs = function(items) {
624         generic_update(items, 'edit_notify_prefs'); }
625     service.edit_dates = function(items) {
626         generic_update(items, 'edit_dates'); }
627     service.suspend = function(items) {
628         generic_update(items, 'suspend_holds'); }
629     service.activate = function(items) {
630         generic_update(items, 'activate_holds'); }
631     service.set_top_of_queue = function(items) {
632         generic_update(items, 'set_top_of_queue'); }
633     service.clear_top_of_queue = function(items) {
634         generic_update(items, 'clear_top_of_queue'); }
635     service.transfer_to_marked_title = function(items) {
636         generic_update(items, 'transfer_to_marked_title'); }
637
638     service.mark_damaged = function(items) {
639         angular.forEach(items, function(item) {
640             if (item.copy) {
641                 egCirc.mark_damaged({
642                     id: item.copy.id(),
643                     barcode: item.copy.barcode()
644                 }).then(service.refresh);
645             }
646         });
647     }
648
649     service.mark_missing = function(items) {
650         var copy_ids = items
651             .filter(function(item) { return Boolean(item.copy) })
652             .map(function(item) { return item.copy.id() });
653         if (copy_ids.length) 
654             egCirc.mark_missing(copy_ids).then(service.refresh);
655     }
656
657     service.retarget = function(items) {
658         var hold_ids = items.map(function(item) { return item.hold.id() });
659         egHolds.retarget(hold_ids).then(service.refresh);
660     }
661
662     return service;
663 }])
664
665 /**
666  * Hold details interface 
667  */
668 .directive('egHoldDetails', function() {
669     return {
670         restrict : 'AE',
671         templateUrl : './circ/share/t_hold_details',
672         scope : {
673             holdId : '=',
674             // if set, called whenever hold details are retrieved.  The
675             // argument is the hold blob returned from hold.details.retrieve
676             holdRetrieved : '=',
677             showPatron : '='
678         },
679         controller : [
680                     '$scope','$uibModal','egCore','egHolds','egCirc',
681             function($scope , $uibModal , egCore , egHolds , egCirc) {
682
683                 function draw() {
684                     if (!$scope.holdId) return;
685
686                     egCore.net.request(
687                         'open-ils.circ',
688                         'open-ils.circ.hold.details.retrieve.authoritative',
689                         egCore.auth.token(), $scope.holdId, {
690                             include_current_copy : true,
691                             include_usr          : true,
692                             include_cancel_cause : true,
693                             include_sms_carrier  : true,
694                             include_requestor    : true
695                         }
696                     ).then(function(hold_data) { 
697                         egHolds.local_flesh(hold_data);
698     
699                         angular.forEach(hold_data, 
700                             function(val, key) { $scope[key] = val });
701
702                         // fetch + flesh the cancel_cause if needed
703                         if ($scope.hold.cancel_time()) {
704                             egHolds.get_cancel_reasons().then(function() {
705                                 // egHolds caches the causes in egEnv
706                                 $scope.hold.cancel_cause(
707                                     egCore.env.ahrcc.map[$scope.hold.cancel_cause()]);
708                             })
709                         }
710
711                         if ($scope.hold.current_copy()) {
712                             egCirc.flesh_copy_location($scope.hold.current_copy());
713                         }
714
715                         if ($scope.holdRetrieved)
716                             $scope.holdRetrieved(hold_data);
717
718                     });
719                 }
720
721                 $scope.show_notify_tab = function() {
722                     $scope.detail_tab = 'notify';
723                     egCore.pcrud.search('ahn',
724                         {hold : $scope.hold.id()}, 
725                         {flesh : 1, flesh_fields : {ahn : ['notify_staff']}}, 
726                         {atomic : true}
727                     ).then(function(nots) {
728                         $scope.hold.notifications(nots);
729                     });
730                 }
731
732                 $scope.delete_note = function(note) {
733                     egCore.pcrud.remove(note).then(function() {
734                         // remove the deleted note from the locally fleshed notes
735                         $scope.hold.notes(
736                             $scope.hold.notes().filter(function(n) {
737                                 return n.id() != note.id()
738                             })
739                         );
740                     });
741                 }
742
743                 $scope.new_note = function() {
744                     return $uibModal.open({
745                         templateUrl : './circ/share/t_hold_note_dialog',
746                         backdrop: 'static',
747                         controller : 
748                             ['$scope', '$uibModalInstance',
749                             function($scope, $uibModalInstance) {
750                                 $scope.args = {};
751                                 $scope.ok = function() {
752                                     $uibModalInstance.close($scope.args)
753                                 },
754                                 $scope.cancel = function($event) {
755                                     $uibModalInstance.dismiss();
756                                     $event.preventDefault();
757                                 }
758                             }
759                         ]
760                     }).result.then(function(args) {
761                         var note = new egCore.idl.ahrn();
762                         note.hold($scope.hold.id());
763                         note.staff(true);
764                         note.slip(args.slip);
765                         note.pub(args.pub); 
766                         note.title(args.title);
767                         note.body(args.body);
768                         return egCore.pcrud.create(note).then(function(n) {
769                             $scope.hold.notes().push(n);
770                         });
771                     });
772                 }
773
774                 $scope.new_notification = function() {
775                     return $uibModal.open({
776                         templateUrl : './circ/share/t_hold_notification_dialog',
777                         backdrop: 'static',
778                         controller : 
779                             ['$scope', '$uibModalInstance',
780                             function($scope, $uibModalInstance) {
781                                 $scope.args = {};
782                                 $scope.ok = function() {
783                                     $uibModalInstance.close($scope.args)
784                                 },
785                                 $scope.cancel = function($event) {
786                                     $uibModalInstance.dismiss();
787                                     $event.preventDefault();
788                                 }
789                             }
790                         ]
791                     }).result.then(function(args) {
792                         var note = new egCore.idl.ahn();
793                         note.hold($scope.hold.id());
794                         note.method(args.method);
795                         note.note(args.note);
796                         note.notify_staff(egCore.auth.user().id());
797                         note.notify_time('now');
798                         return egCore.pcrud.create(note).then(function(n) {
799                             n.notify_staff(egCore.auth.user());
800                             $scope.hold.notifications().push(n);
801                         });
802                     });
803                 }
804
805                 $scope.$watch('holdId', function(newVal, oldVal) {
806                     if (newVal != oldVal) draw();
807                 });
808
809                 draw();
810             }
811         ]
812     }
813 })
814
815