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