]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/serials/directives/subscription_manager.js
LP#1689325 - require most modals have explicit 'exit' or 'cancel' action inside the...
[working/Evergreen.git] / Open-ILS / web / js / ui / default / staff / serials / directives / subscription_manager.js
1 angular.module('egSerialsAppDep')
2
3 .directive('egSubscriptionManager', function() {
4     return {
5         transclude: true,
6         restrict:   'E',
7         scope: {
8             bibId : '='
9         },
10         templateUrl: './serials/t_subscription_manager',
11         controller:
12        ['$scope','$q','egSerialsCoreSvc','egCore','egGridDataProvider',
13         '$uibModal','ngToast','egConfirmDialog',
14 function($scope , $q , egSerialsCoreSvc , egCore , egGridDataProvider ,
15          $uibModal , ngToast , egConfirmDialog ) {
16
17     $scope.selected_owning_ou = null;
18     $scope.owning_ou_changed = function(org) {
19         $scope.selected_owning_ou = org.id();
20         reload();
21     }
22
23     function reload() {
24         egSerialsCoreSvc.fetch($scope.bibId, $scope.selected_owning_ou).then(function() {
25             $scope.subscriptions = egCore.idl.toTypedHash(egSerialsCoreSvc.subTree);
26             // un-flesh receive unit template so that we can use
27             // it as a model of a select
28             angular.forEach($scope.subscriptions, function(ssub) {
29                 angular.forEach(ssub.distributions, function(sdist) {
30                     if (angular.isObject(sdist.receive_unit_template)) {
31                         sdist.receive_unit_template = sdist.receive_unit_template.id;
32                     }
33                 });
34             });
35             $scope.distStreamGridDataProvider.refresh();
36         });
37     }
38     reload();
39
40     $scope.localStreamNames = [];
41     egCore.hatch.getItem('eg.serials.stream_names')
42     .then(function(list) {
43         if (list) $scope.localStreamNames = list;
44     });
45
46     $scope.distStreamGridControls = {
47         activateItem : function (item) { } // TODO
48     };
49     $scope.distStreamGridDataProvider = egGridDataProvider.instance({
50         get : function(offset, count) {
51             return this.arrayNotifier(egSerialsCoreSvc.subList, offset, count);
52         }
53     });
54
55     $scope.need_one_selected = function() {
56         var items = $scope.distStreamGridControls.selectedItems();
57         if (items.length == 1) return false;
58         return true;
59     };
60
61     $scope.receiving_templates = {};
62     angular.forEach(egCore.org.list(), function(org) {
63         egSerialsCoreSvc.fetch_templates(org.id()).then(function(list){
64             $scope.receiving_templates[org.id()] = egCore.idl.toTypedHash(list);
65         });
66     });
67
68     $scope.add_subscription = function() {
69         var new_ssub = egCore.idl.toTypedHash(new egCore.idl.ssub());
70         new_ssub._isnew = true;
71         new_ssub.record_entry = $scope.bibId;
72         new_ssub._focus_me = true;
73         $scope.subscriptions.push(new_ssub);
74         $scope.add_distribution(new_ssub); // since we know we want at least one distribution
75     }
76     $scope.add_distribution = function(ssub, grab_focus) {
77         egCore.org.settings([
78             'serial.default_display_grouping'
79         ]).then(function(set) {
80             var new_sdist = egCore.idl.toTypedHash(new egCore.idl.sdist());
81             new_sdist._isnew = true;
82             new_sdist.subscription = ssub.id;
83             new_sdist.display_grouping = set['serial.default_display_grouping'] || 'chron';
84             if (!angular.isArray(ssub.distributions)){
85                 ssub.distributions = [];
86             }
87             if (grab_focus) {
88                 new_sdist._focus_me = true;
89                 ssub._focus_me = false;
90             }
91             ssub.distributions.push(new_sdist);
92             $scope.add_stream(new_sdist); // since we know we want at least one stream
93         });
94     }
95     $scope.remove_pending_distribution = function(ssub, sdist) {
96         var to_remove = -1;
97         for (var i = 0; i < ssub.distributions.length; i++) {
98             if (ssub.distributions[i] === sdist) {
99                 to_remove = i;
100                 break;
101             }
102         }
103         if (to_remove > -1) {
104             ssub.distributions.splice(to_remove, 1);
105         }
106     }
107     $scope.add_stream = function(sdist, grab_focus) {
108         var new_sstr = egCore.idl.toTypedHash(new egCore.idl.sstr());
109         new_sstr.distribution = sdist.id;
110         new_sstr._isnew = true;
111         if (grab_focus) {
112             new_sstr._focus_me = true;
113             sdist._has_focus = false; // and take focus away from a newly created sdist
114         }
115         if (!angular.isArray(sdist.streams)){
116             sdist.streams = [];
117         }
118         sdist.streams.push(new_sstr);
119         $scope.dirtyForm();
120     }
121     $scope.remove_pending_stream = function(sdist, sstr) {
122         var to_remove = -1;
123         for (var i = 0; i < sdist.streams.length; i++) {
124             if (sdist.streams[i] === sstr) {
125                 to_remove = i;
126                 break;
127             }
128         }
129         if (to_remove > -1) {
130             sdist.streams.splice(to_remove, 1);
131         }
132     }
133
134     $scope.abort_changes = function(form) {
135         reload();
136         form.$setPristine();
137     }
138     function updateLocalStreamNames (new_name) {
139         if (new_name && $scope.localStreamNames.filter(function(x){ return x == new_name}).length == 0) {
140             $scope.localStreamNames.push(new_name);
141             egCore.hatch.setItem('eg.serials.stream_names', $scope.localStreamNames)
142         }
143     }
144
145     $scope.dirtyForm = function () {
146         $scope.ssubform.$dirty = true;
147     }
148
149     $scope.save_subscriptions = function(form) {
150         // traverse through structure and set _ischanged
151         // TODO add more granular dirty input detection
152         angular.forEach($scope.subscriptions, function(ssub) {
153             if (!ssub._isnew) ssub._ischanged = true;
154             angular.forEach(ssub.distributions, function(sdist) {
155                 if (!sdist._isnew) sdist._ischanged = true;
156                 angular.forEach(sdist.streams, function(sstr) {
157                     if (!sstr._isnew) sstr._ischanged = true;
158                     updateLocalStreamNames(sstr.routing_label);
159                 });
160             });
161         });
162
163         var obj = egCore.idl.fromTypedHash($scope.subscriptions);
164
165         // create a bunch of promises that each get resolved upon each
166         // CUD update; that way, we can know when the entire save
167         // operation is completed
168         var promises = [];
169         angular.forEach(obj, function(ssub) {
170             ssub._cud_done = $q.defer();
171             promises.push(ssub._cud_done.promise);
172             angular.forEach(ssub.distributions(), function(sdist) {
173                 sdist._cud_done = $q.defer();
174                 promises.push(sdist._cud_done.promise);
175                 angular.forEach(sdist.streams(), function(sstr) {
176                     sstr._cud_done = $q.defer();
177                     promises.push(sstr._cud_done.promise);
178                 });
179             });
180         });
181
182         angular.forEach(obj, function(ssub) {
183             ssub.owning_lib(ssub.owning_lib().id()); // deflesh
184             egCore.pcrud.apply(ssub).then(function(res) {
185                 var ssub_id = (ssub.isnew() && angular.isObject(res)) ? res.id() : ssub.id();
186                 angular.forEach(ssub.distributions(), function(sdist) {
187                     // set subscription ID just in case it's new
188                     sdist.holding_lib(sdist.holding_lib().id()); // deflesh
189                     sdist.subscription(ssub_id);
190                     egCore.pcrud.apply(sdist).then(function(res) {
191                         var sdist_id = (sdist.isnew() && angular.isObject(res)) ? res.id() : sdist.id();
192                         angular.forEach(sdist.streams(), function(sstr) {
193                             // set distribution ID just in case it's new
194                             sstr.distribution(sdist_id);
195                             egCore.pcrud.apply(sstr).then(function(res) {
196                                 sstr._cud_done.resolve();
197                             });
198                         });
199                     });
200                     sdist._cud_done.resolve();
201                 });
202                 ssub._cud_done.resolve();
203             });
204         });
205         $q.all(promises).then(function(resolutions) {
206             reload();
207             form.$setPristine();
208         });
209     }
210     $scope.delete_subscription = function(rows) {
211         if (rows.length == 0) { return; }
212         var s_rows = rows.filter(function(el) {
213             return typeof el['id'] != 'undefined';
214         });
215         if (s_rows.length == 0) { return; }
216         egConfirmDialog.open(
217             egCore.strings.CONFIRM_DELETE_SUBSCRIPTION,
218             egCore.strings.CONFIRM_DELETE_SUBSCRIPTION_MESSAGE,
219             {count : s_rows.length}
220         ).result.then(function () {
221             var promises = [];
222             angular.forEach(s_rows, function(el) {
223                 promises.push(
224                     egCore.net.request(
225                         'open-ils.serial',
226                         'open-ils.serial.subscription.safe_delete',
227                         egCore.auth.token(),
228                         el['id']
229                     ).then(function(resp){
230                         var evt = egCore.evt.parse(resp);
231                         if (evt) {
232                             ngToast.danger(egCore.strings.SERIALS_SUBSCRIPTION_FAIL_DELETE + ' : ' + evt.desc);
233                         } else {
234                             ngToast.success(egCore.strings.SERIALS_SUBSCRIPTION_SUCCESS_DELETE);
235                         }
236                     })
237                 );
238             });
239             $q.all(promises).then(function() {
240                 reload();
241             });
242         });
243     }
244     $scope.delete_distribution = function(rows) {
245         if (rows.length == 0) { return; }
246         var d_rows = rows.filter(function(el) {
247             return typeof el['sdist.id'] != 'undefined';
248         });
249         if (d_rows.length == 0) { return; }
250         egConfirmDialog.open(
251             egCore.strings.CONFIRM_DELETE_DISTRIBUTION,
252             egCore.strings.CONFIRM_DELETE_DISTRIBUTION_MESSAGE,
253             {count : d_rows.length}
254         ).result.then(function () {
255             var promises = [];
256             angular.forEach(d_rows, function(el) {
257                 promises.push(
258                     egCore.net.request(
259                         'open-ils.serial',
260                         'open-ils.serial.distribution.safe_delete',
261                         egCore.auth.token(),
262                         el['sdist.id']
263                     ).then(function(resp){
264                         var evt = egCore.evt.parse(resp);
265                         if (evt) {
266                             ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_DELETE + ' : ' + evt.desc);
267                         } else {
268                             ngToast.success(egCore.strings.SERIALS_DISTRIBUTION_SUCCESS_DELETE);
269                         }
270                     })
271                 );
272             });
273             $q.all(promises).then(function() {
274                 reload();
275             });
276         });
277     }
278     $scope.delete_stream = function(rows) {
279         if (rows.length == 0) { return; }
280         var s_rows = rows.filter(function(el) {
281             return typeof el['sstr.id'] != 'undefined';
282         });
283         if (s_rows.length == 0) { return; }
284         egConfirmDialog.open(
285             egCore.strings.CONFIRM_DELETE_STREAM,
286             egCore.strings.CONFIRM_DELETE_STREAM_MESSAGE,
287             {count : s_rows.length}
288         ).result.then(function () {
289             var promises = [];
290             angular.forEach(s_rows, function(el) {
291                 promises.push(
292                     egCore.net.request(
293                         'open-ils.serial',
294                         'open-ils.serial.stream.safe_delete',
295                         egCore.auth.token(),
296                         el['sstr.id']
297                     ).then(function(resp){
298                         var evt = egCore.evt.parse(resp);
299                         if (evt) {
300                             ngToast.danger(egCore.strings.SERIALS_STREAM_FAIL_DELETE + ' : ' + evt.desc);
301                         } else {
302                             ngToast.success(egCore.strings.SERIALS_STREAM_SUCCESS_DELETE);
303                         }
304                     })
305                 );
306             });
307             $q.all(promises).then(function() {
308                 reload();
309             });
310         });
311     }
312     $scope.additional_routing = function(rows) {
313         if (!rows) { return; }
314         var row = rows[0];
315         if (!row) { row = $scope.distStreamGridControls.selectedItems()[0]; }
316         if (row && row['sstr.id']) {
317             egCore.pcrud.search('srlu', {
318                     stream : row['sstr.id']
319                 }, {
320                     flesh : 2,
321                     flesh_fields : {
322                         'srlu' : ['reader'],
323                         'au'  : ['mailing_address','billing_address','home_ou']
324                     },
325                     order_by : { srlu : 'pos' }
326                 },
327                 { atomic : true }
328             ).then(function(list) {
329                 $uibModal.open({
330                     templateUrl: './serials/t_routing_list',
331                     backdrop: 'static',
332                     controller: 'RoutingCtrl',
333                     resolve : {
334                         rowInfo : function() {
335                             return row;
336                         },
337                         routes : function() {
338                             return egCore.idl.toHash(list);
339                         }
340                     }
341                 }).result.then(function(routes) {
342                     // delete all of the routes first;
343                     // it's easiest given the constraints
344                     var deletions = [];
345                     var creations = [];
346                     angular.forEach(routes, function(r) {
347                         var srlu = new egCore.idl.srlu();
348                         srlu.stream(r.stream);
349                         srlu.pos(r.pos);
350                         if (r.reader) {
351                             srlu.reader(r.reader.id);
352                         }
353                         srlu.department(r.department);
354                         srlu.note(r.note);
355                         if (r.id) {
356                             srlu.id(r.id);
357                             var srlu_copy = angular.copy(srlu);
358                             srlu_copy.isdeleted(true);
359                             deletions.push(srlu_copy);
360                         }
361                         if (!r.delete_me) {
362                             srlu.isnew(true);
363                             creations.push(srlu);
364                         }
365                     });
366                     egCore.pcrud.apply(deletions.concat(creations)).then(function(){
367                         reload();
368                     });
369                 });
370             });
371         }
372     }
373     $scope.clone_subscription = function(rows) {
374         if (!rows) { return; }
375         var row = rows[0];
376         $uibModal.open({
377             templateUrl: './serials/t_clone_subscription',
378             controller: 'CloneCtrl',
379             resolve : {
380                 subs : function() {
381                     return rows;
382                 }
383             },
384             windowClass: 'app-modal-window',
385             backdrop: 'static',
386             keyboard: false
387         }).result.then(function(args) {
388             var promises = [];
389             var some_failure = false;
390             var some_success = false;
391             var seen = {};
392             angular.forEach(rows, function(row) { 
393                 //console.log(row);
394                 if (!seen[row.id]) {
395                     seen[row.id] = 1;
396                     promises.push(
397                         egCore.net.request(
398                             'open-ils.serial',
399                             'open-ils.serial.subscription.clone',
400                             egCore.auth.token(),
401                             row.id,
402                             args.bib_id
403                         ).then(
404                             function(resp) {
405                                 var evt = egCore.evt.parse(resp);
406                                 if (evt) { // any way to just throw or return this to the error handler?
407                                     console.log('failure',resp);
408                                     some_failure = true;
409                                     ngToast.danger(egCore.strings.SERIALS_SUBSCRIPTION_FAIL_CLONE);
410                                 } else {
411                                     console.log('success',resp);
412                                     some_success = true;
413                                     ngToast.success(egCore.strings.SERIALS_SUBSCRIPTION_SUCCESS_CLONE);
414                                 }
415                             },
416                             function(resp) {
417                                 console.log('failure',resp);
418                                 some_failure = true;
419                                 ngToast.danger(egCore.strings.SERIALS_SUBSCRIPTION_FAIL_CLONE);
420                             }
421                         )
422                     );
423                 }
424             });
425             $q.all(promises).then(function() {
426                 reload();
427             });
428         });
429     }
430     $scope.link_mfhd = function(rows) {
431         if (!rows) { return; }
432         var row = rows[0];
433         if (!row['sdist.id']) { return; }
434         $uibModal.open({
435             templateUrl: './serials/t_link_mfhd',
436             controller: 'LinkMFHDCtrl',
437             resolve : {
438                 row : function() {
439                     return rows[0];
440                 },
441                 bibId : function() {
442                     return $scope.bibId;
443                 }
444             },
445             windowClass: 'app-modal-window',
446             backdrop: 'static',
447             keyboard: false
448         }).result.then(function(args) {
449             console.log('modal done', args);
450             egCore.pcrud.search('sdist', {
451                     id: rows[0]['sdist.id']
452                 }, {}, { atomic : true }
453             ).then(function(resp){
454                 var evt = egCore.evt.parse(resp);
455                 if (evt) { // any way to just throw or return this to the error handler?
456                     console.log('failure',resp);
457                     ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_LINK_MFHD);
458                 }
459                 var sdist = resp[0];
460                 sdist.ischanged(true);
461                 sdist.summary_method( args.summary_method );
462                 sdist.record_entry( args.which_mfhd );
463                 egCore.pcrud.apply(sdist).then(
464                     function(resp) { // maybe success
465                         console.log('apply',resp);
466                         var evt = egCore.evt.parse(resp);
467                         if (evt) { // any way to just throw or return this to the error handler?
468                             console.log('failure',resp);
469                             ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_LINK_MFHD);
470                         } else {
471                             console.log('success',resp);
472                             ngToast.success(egCore.strings.SERIALS_DISTRIBUTION_SUCCESS_LINK_MFHD);
473                             reload();
474                         }
475                     },
476                     function(resp) {
477                         console.log('failure',resp);
478                         ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_LINK_MFHD);
479                     }
480                 );
481             });
482         });
483     }
484     $scope.apply_binding_template = function(rows) {
485         if (rows.length == 0) { return; }
486         var d_rows = rows.filter(function(el) {
487             return typeof el['sdist.id'] != 'undefined';
488         });
489         if (d_rows.length == 0) { return; }
490         var libs = []; var seen_lib = {};
491         angular.forEach(d_rows, function(el) {
492             if (el['sdist.holding_lib.id'] && !seen_lib[el['sdist.holding_lib.id']]) {
493                 seen_lib[el['sdist.holding_lib.id']] = 1;
494                 libs.push({
495                       id: el['sdist.holding_lib.id'],
496                     name: el['sdist.holding_lib.name'],
497                 });
498             }
499         });
500         $uibModal.open({
501             templateUrl: './serials/t_apply_binding_template',
502             controller: 'ApplyBindingTemplateCtrl',
503             resolve : {
504                 rows : function() {
505                     return d_rows;
506                 },
507                 libs : function() {
508                     return libs;
509                 }
510             },
511             windowClass: 'app-modal-window',
512             backdrop: 'static',
513             keyboard: false
514         }).result.then(function(args) {
515             console.log(args);
516             egCore.pcrud.search('sdist', {
517                     id: d_rows.map(function(el) { return el['sdist.id']; })
518                 }, {}, { atomic : true }
519             ).then(function(resp){
520                 var evt = egCore.evt.parse(resp);
521                 if (evt) { // any way to just throw or return this to the error handler?
522                     console.log('failure',resp);
523                     ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_BINDING_TEMPLATE);
524                 }
525                 var promises = [];
526                 angular.forEach(resp,function(sdist) {
527                     var promise = $q.defer();
528                     promises.push(promise.promise);
529                     sdist.ischanged(true);
530                     sdist.bind_unit_template(
531                         typeof args.bind_unit_template[sdist.holding_lib()] == 'undefined'
532                         ? null
533                         : args.bind_unit_template[sdist.holding_lib()]
534                     );
535                     egCore.pcrud.apply(sdist).then(
536                         function(resp2) { // maybe success
537                             console.log('apply',resp2);
538                             var evt = egCore.evt.parse(resp2);
539                             if (evt) { // any way to just throw or return this to the error handler?
540                                 console.log('failure',resp2);
541                                 ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_BINDING_TEMPLATE);
542                             } else {
543                                 console.log('success',resp2);
544                                 ngToast.success(egCore.strings.SERIALS_DISTRIBUTION_SUCCESS_BINDING_TEMPLATE);
545                             }
546                             promise.resolve();
547                         },
548                         function(resp2) {
549                             console.log('failure',resp2);
550                             ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_BINDING_TEMPLATE);
551                             promise.resolve();
552                         }
553                     );
554                 });
555                 $q.all(promises).then(function() {
556                     reload();
557                 });
558             });
559         });
560     }
561     $scope.subscription_notes = function(rows) {
562         return $scope.notes('subscription',rows);
563     }
564     $scope.distribution_notes = function(rows) {
565         return $scope.notes('distribution',rows);
566     }
567     $scope.notes = function(note_type,rows) {
568         if (!rows) { return; }
569
570         function modal(existing_notes) {
571             $uibModal.open({
572                 templateUrl: './serials/t_notes',
573                 animation: true,
574                 controller: 'NotesCtrl',
575                 resolve : {
576                     note_type : function() { return note_type; },
577                     rows : function() {
578                         return rows;
579                     },
580                     notes : function() {
581                         return existing_notes;
582                     }
583                 },
584                 windowClass: 'app-modal-window',
585                 backdrop: 'static',
586                 keyboard: false
587             }).result.then(function(notes) {
588                 console.log('results',notes);
589                 egCore.pcrud.apply(notes).then(
590                     function(a) { console.log('toast here 1',a); },
591                     function(a) { console.log('toast here 2',a); }
592                 );
593             });
594         }
595
596         if (rows.length == 1) {
597             var fm_hint;
598             var search_hash = {};
599             var search_opt = {};
600             switch(note_type) {
601                 case 'subscription':
602                     fm_hint = 'ssubn';
603                     search_hash.subscription = rows[0]['id'];
604                     search_opt.order_by = { ssubn : 'create_date' };
605                 break;
606                 case 'distribution':
607                     fm_hint = 'sdistn';
608                     search_hash.distribution = rows[0]['sdist.id'];
609                     search_opt.order_by = { sdistn : 'create_date' };
610                 break;
611                 case 'item': default:
612                     fm_hint = 'sin';
613                     search_hash.item = rows[0]['si.id'];
614                     search_opt.order_by = { sin : 'create_date' };
615                 break;
616             }
617             egCore.pcrud.search(fm_hint, search_hash, search_opt,
618                 { atomic : true }
619             ).then(function(list) {
620                 modal(list);
621             });
622         } else {
623                 // support batch creation of notes across selections,
624                 // but not editing
625                 modal([]);
626         }
627     }
628
629 }]
630     }
631 })
632
633 .controller('ApplyBindingTemplateCtrl',
634        ['$scope','$q','$uibModalInstance','egCore','egSerialsCoreSvc',
635         'rows','libs',
636 function($scope , $q , $uibModalInstance , egCore , egSerialsCoreSvc ,
637          rows , libs ) {
638     $scope.ok = function(count) { $uibModalInstance.close($scope.args) }
639     $scope.cancel = function () { $uibModalInstance.dismiss() }
640     $scope.libs = libs;
641     $scope.rows = rows;
642     $scope.args = { bind_unit_template : {} };
643     $scope.templates = {};
644     angular.forEach(libs, function(org) {
645         egSerialsCoreSvc.fetch_templates(org.id).then(function(list){
646             $scope.templates[org.id] = egCore.idl.toTypedHash(list);
647         });
648     });
649 }])
650
651 .controller('LinkMFHDCtrl',
652        ['$scope','$q','$uibModalInstance','egCore','row','bibId',
653 function($scope , $q , $uibModalInstance , egCore , row , bibId ) {
654     console.log('row',row);
655     console.log('bibId',bibId);
656     $scope.args = {
657         summary_method: row['sdist.summary_method'] || 'add_to_sre',
658     };
659     if (row['sdist.record_entry']) {
660         $scope.args.which_mfhd = row['sdist.record_entry'].id;
661     }
662     $scope.ok = function(count) { $uibModalInstance.close($scope.args) }
663     $scope.cancel = function () { $uibModalInstance.dismiss() }
664     $scope.legacies = {};
665     egCore.pcrud.search('sre', {
666             record: bibId, owning_lib : row['sdist.holding_lib.id'], active: 't', deleted: 'f'
667         }, {}, { atomic : true }
668     ).then(
669         function(resp) { // maybe success
670             var evt; if (evt = egCore.evt.parse(resp)) { console.error(evt.toString()); return; }
671             if (!resp) { return; }
672
673             var promises = [];
674             var seen = {};
675
676             angular.forEach(resp, function(sre) {
677                 console.log('sre',sre);
678                 if (!seen[sre.record()]) {
679                     seen[sre.record()] = 1;
680                     $scope.legacies[sre.record()] = { mvr: null, svrs: [] };
681                     promises.push(
682                         egCore.net.request(
683                             'open-ils.search',
684                             'open-ils.search.biblio.record.mods_slim.retrieve.authoritative',
685                             sre.record()
686                         ).then(function(resp2) {
687                             var evt; if (evt = egCore.evt.parse(resp2)) { console.error(evt.toString()); return; }
688                             if (!resp2) { return; }
689                             $scope.legacies[sre.record()].mvr = egCore.idl.toHash(resp2);
690                         })
691                     );
692                     promises.push(
693                         egCore.net.request(
694                             'open-ils.search',
695                             'open-ils.search.serial.record.bib.retrieve',
696                             sre.record(),
697                             row['owning_lib.id']
698                         ).then(function(resp2) {
699                             angular.forEach(resp2,function(r) {
700                                 if (r.sre_id() > 0) {
701                                     console.log('svr',egCore.idl.toHash(r));
702                                     $scope.legacies[sre.record()].svrs.push( egCore.idl.toHash(r) );
703                                 }
704                             });
705                         })
706                     );
707                 }
708                 if (typeof $scope.legacies[sre.record()].sres == 'undefined') {
709                     $scope.legacies[sre.record()].sres = {};
710                 }
711                 $scope.legacies[sre.record()].sres[sre.id()] = egCore.idl.toHash(sre);
712             });
713
714             $q.all(promises).then(function(){
715                 console.log('done',$scope.legacies);
716             });
717         },
718         function(resp) { // outright failure
719             console.error('failure',resp);
720         }
721     )
722 }])
723
724 .controller('CloneCtrl',
725        ['$scope','$uibModalInstance','egCore','subs',
726 function($scope , $uibModalInstance , egCore , subs ) {
727     $scope.args = {};
728     $scope.ok = function(count) { $uibModalInstance.close($scope.args) }
729     $scope.cancel = function () { $uibModalInstance.dismiss() }
730     $scope.subs = subs;
731     $scope.find_bib = function () {
732
733         $scope.bibNotFound = null;
734         $scope.mvr = null;
735         if (!$scope.args.bib_id) return;
736
737         return egCore.net.request(
738             'open-ils.search',
739             'open-ils.search.biblio.record.mods_slim.retrieve.authoritative',
740             $scope.args.bib_id
741         ).then(
742             function(resp) { // maybe success 
743
744                 if (evt = egCore.evt.parse(resp)) {
745                     $scope.bibNotFound = $scope.args.bib_id;
746                     console.error(evt.toString());
747                     return;
748                 }
749
750                 if (!resp) {
751                     $scope.bibNotFound = $scope.args.bib_id;
752                     return;
753                 }
754
755                 $scope.mvr = egCore.idl.toHash(resp);
756                 //console.log($scope.mvr);
757             },
758             function(resp) { // outright failure
759                 console.error(resp);
760                 $scope.bibNotFound = $scope.args.bib_id;
761                 return;
762             }
763         );
764     }
765     $scope.$watch("args.bib_id", function(newVal, oldVal) {
766         if (newVal && newVal != oldVal) {
767             $scope.find_bib();
768         }
769     });
770 }])
771
772 .controller('RoutingCtrl',
773        ['$scope','$uibModalInstance','egCore','rowInfo','routes',
774 function($scope , $uibModalInstance , egCore , rowInfo , routes ) {
775     $scope.args = {
776          which_radio_button: 'reader'
777         ,reader: ''
778         ,department: ''
779         ,delete_me: false
780     };
781     $scope.stream_id = rowInfo['sstr.id'];
782     $scope.stream_label = rowInfo['sstr.routing_label'];
783     $scope.routes = routes;
784     $scope.readerInFocus = true;
785     $scope.ok = function(count) { $uibModalInstance.close($scope.routes) }
786     $scope.cancel = function () { $uibModalInstance.dismiss() }
787     $scope.model_has_changed = false;
788     $scope.find_user = function () {
789
790         $scope.readerNotFound = null;
791         $scope.reader_obj = null;
792         if (!$scope.args.reader) return;
793
794         egCore.net.request(
795             'open-ils.actor',
796             'open-ils.actor.get_barcodes',
797             egCore.auth.token(), egCore.auth.user().ws_ou(),
798             'actor', $scope.args.reader)
799
800         .then(function(resp) { // get_barcodes
801
802             if (evt = egCore.evt.parse(resp)) {
803                 console.error(evt.toString());
804                 return;
805             }
806
807             if (!resp || !resp[0]) {
808                 $scope.readerNotFound = $scope.args.reader;
809                 return;
810             }
811
812             egCore.pcrud.search('au', {
813                     id : resp[0].id
814                 }, {
815                     flesh : 1,
816                     flesh_fields : {
817                         'au'  : ['mailing_address','billing_address','home_ou']
818                     }
819                 },
820                 { atomic : true }
821             ).then(function(usr) {
822                 $scope.reader_obj = egCore.idl.toHash(usr[0]);
823             });
824         });
825     }
826     $scope.add_route = function () {
827         var new_route = {
828              stream: $scope.stream_id
829             ,pos: $scope.routes.length
830             ,note: $scope.args.note
831         }
832         if ($scope.args.which_radio_button == 'reader') {
833             new_route.reader = $scope.reader_obj;
834         } else {
835             new_route.department = $scope.args.department;
836         }
837         $scope.routes.push(new_route);
838         $scope.model_has_changed = true;
839     }
840     function adjust_pos_field() {
841         var idx = 0;
842         for (var i = 0; i < $scope.routes.length; i++) {
843             $scope.routes[i].pos = $scope.routes[i].delete_me ? idx : idx++;
844         }
845         $scope.model_has_changed = true;
846     }
847     $scope.move_route_up = function(r) {
848         var pos = r.pos;
849         if (pos > 0) {
850             var temp = $scope.routes[ pos - 1 ];
851             $scope.routes[ pos - 1 ] = $scope.routes[ pos ];
852             $scope.routes[ pos ] = temp;
853             adjust_pos_field();
854         }
855     }
856     $scope.move_route_down = function(r) {
857         var pos = r.pos;
858         if (pos < $scope.routes.length - 1) {
859             var temp = $scope.routes[ pos + 1 ];
860             $scope.routes[ pos + 1 ] = $scope.routes[ pos ];
861             $scope.routes[ pos ] = temp;
862             adjust_pos_field();
863         }
864     }
865     $scope.toggle_delete = function(r) {
866         r.delete_me = ! r.delete_me;
867         adjust_pos_field();
868     }
869     $scope.$watch("args.reader", function(newVal, oldVal) {
870         if (newVal && newVal != oldVal) {
871             $scope.find_user();
872         }
873     });
874 }])
875
876 .controller('NotesCtrl',
877        ['$scope','$uibModalInstance','egCore','note_type','rows','notes',
878 function($scope , $uibModalInstance , egCore , note_type , rows , notes ) {
879     $scope.note_type = note_type;
880     $scope.focusNote = true;
881     $scope.note = {
882         creator : egCore.auth.user().id(),
883         title   : '',
884         value   : '',
885         pub     : false,
886         'alert' : false,
887     };
888
889     $scope.note_list = notes;
890
891     $scope.ok = function(note) {
892
893         var return_notes = [];
894         if (note.initials) note.value += ' [' + note.initials + ']';
895         if (   (typeof note.title != 'undefined' && note.title != '')
896             || (typeof note.value != 'undefined' && note.value != '')) {
897             angular.forEach(rows, function (r) {
898                 console.log('r',r);
899                 window.my_r = r;
900                 var n;
901                 switch(note_type) {
902                     case 'subscription':
903                         n = new egCore.idl.ssubn();
904                         n.subscription(r['id']);
905                         break;
906                     case 'distribution':
907                         n = new egCore.idl.sdistn();
908                         n.distribution(r['sdist.id']);
909                         break;
910                     case 'item':
911                     default:
912                         n = new egCore.idl.sin();
913                         n.item(r['si.id']);
914                 }
915                 n.isnew(true);
916                 n.creator(note.creator);
917                 n.pub(note.pub);
918                 n['alert'](note['alert']);
919                 n.title(note.title);
920                 n.value(note.value);
921                 return_notes.push( n );
922             });
923         }
924         angular.forEach(notes, function(n) {
925             if (n.ischanged() || n.isdeleted()) {
926                 return_notes.push( n );
927             }
928         });
929         window.return_notes = return_notes;
930         $uibModalInstance.close(return_notes);
931     }
932
933     $scope.cancel = function($event) {
934         $uibModalInstance.dismiss();
935         $event.preventDefault();
936     }
937 }])