]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/serials/directives/subscription_manager.js
db0e00961eb8923b36cf6acf7a7d97064e74af21
[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                     controller: 'RoutingCtrl',
332                     resolve : {
333                         rowInfo : function() {
334                             return row;
335                         },
336                         routes : function() {
337                             return egCore.idl.toHash(list);
338                         }
339                     }
340                 }).result.then(function(routes) {
341                     // delete all of the routes first;
342                     // it's easiest given the constraints
343                     var deletions = [];
344                     var creations = [];
345                     angular.forEach(routes, function(r) {
346                         var srlu = new egCore.idl.srlu();
347                         srlu.stream(r.stream);
348                         srlu.pos(r.pos);
349                         if (r.reader) {
350                             srlu.reader(r.reader.id);
351                         }
352                         srlu.department(r.department);
353                         srlu.note(r.note);
354                         if (r.id) {
355                             srlu.id(r.id);
356                             var srlu_copy = angular.copy(srlu);
357                             srlu_copy.isdeleted(true);
358                             deletions.push(srlu_copy);
359                         }
360                         if (!r.delete_me) {
361                             srlu.isnew(true);
362                             creations.push(srlu);
363                         }
364                     });
365                     egCore.pcrud.apply(deletions.concat(creations)).then(function(){
366                         reload();
367                     });
368                 });
369             });
370         }
371     }
372     $scope.clone_subscription = function(rows) {
373         if (!rows) { return; }
374         var row = rows[0];
375         $uibModal.open({
376             templateUrl: './serials/t_clone_subscription',
377             controller: 'CloneCtrl',
378             resolve : {
379                 subs : function() {
380                     return rows;
381                 }
382             },
383             windowClass: 'app-modal-window',
384             backdrop: 'static',
385             keyboard: false
386         }).result.then(function(args) {
387             var promises = [];
388             var some_failure = false;
389             var some_success = false;
390             var seen = {};
391             angular.forEach(rows, function(row) { 
392                 //console.log(row);
393                 if (!seen[row.id]) {
394                     seen[row.id] = 1;
395                     promises.push(
396                         egCore.net.request(
397                             'open-ils.serial',
398                             'open-ils.serial.subscription.clone',
399                             egCore.auth.token(),
400                             row.id,
401                             args.bib_id
402                         ).then(
403                             function(resp) {
404                                 var evt = egCore.evt.parse(resp);
405                                 if (evt) { // any way to just throw or return this to the error handler?
406                                     console.log('failure',resp);
407                                     some_failure = true;
408                                     ngToast.danger(egCore.strings.SERIALS_SUBSCRIPTION_FAIL_CLONE);
409                                 } else {
410                                     console.log('success',resp);
411                                     some_success = true;
412                                     ngToast.success(egCore.strings.SERIALS_SUBSCRIPTION_SUCCESS_CLONE);
413                                 }
414                             },
415                             function(resp) {
416                                 console.log('failure',resp);
417                                 some_failure = true;
418                                 ngToast.danger(egCore.strings.SERIALS_SUBSCRIPTION_FAIL_CLONE);
419                             }
420                         )
421                     );
422                 }
423             });
424             $q.all(promises).then(function() {
425                 reload();
426             });
427         });
428     }
429     $scope.link_mfhd = function(rows) {
430         if (!rows) { return; }
431         var row = rows[0];
432         if (!row['sdist.id']) { return; }
433         $uibModal.open({
434             templateUrl: './serials/t_link_mfhd',
435             controller: 'LinkMFHDCtrl',
436             resolve : {
437                 row : function() {
438                     return rows[0];
439                 },
440                 bibId : function() {
441                     return $scope.bibId;
442                 }
443             },
444             windowClass: 'app-modal-window',
445             backdrop: 'static',
446             keyboard: false
447         }).result.then(function(args) {
448             console.log('modal done', args);
449             egCore.pcrud.search('sdist', {
450                     id: rows[0]['sdist.id']
451                 }, {}, { atomic : true }
452             ).then(function(resp){
453                 var evt = egCore.evt.parse(resp);
454                 if (evt) { // any way to just throw or return this to the error handler?
455                     console.log('failure',resp);
456                     ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_LINK_MFHD);
457                 }
458                 var sdist = resp[0];
459                 sdist.ischanged(true);
460                 sdist.summary_method( args.summary_method );
461                 sdist.record_entry( args.which_mfhd );
462                 egCore.pcrud.apply(sdist).then(
463                     function(resp) { // maybe success
464                         console.log('apply',resp);
465                         var evt = egCore.evt.parse(resp);
466                         if (evt) { // any way to just throw or return this to the error handler?
467                             console.log('failure',resp);
468                             ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_LINK_MFHD);
469                         } else {
470                             console.log('success',resp);
471                             ngToast.success(egCore.strings.SERIALS_DISTRIBUTION_SUCCESS_LINK_MFHD);
472                             reload();
473                         }
474                     },
475                     function(resp) {
476                         console.log('failure',resp);
477                         ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_LINK_MFHD);
478                     }
479                 );
480             });
481         });
482     }
483     $scope.apply_binding_template = function(rows) {
484         if (rows.length == 0) { return; }
485         var d_rows = rows.filter(function(el) {
486             return typeof el['sdist.id'] != 'undefined';
487         });
488         if (d_rows.length == 0) { return; }
489         var libs = []; var seen_lib = {};
490         angular.forEach(d_rows, function(el) {
491             if (el['sdist.holding_lib.id'] && !seen_lib[el['sdist.holding_lib.id']]) {
492                 seen_lib[el['sdist.holding_lib.id']] = 1;
493                 libs.push({
494                       id: el['sdist.holding_lib.id'],
495                     name: el['sdist.holding_lib.name'],
496                 });
497             }
498         });
499         $uibModal.open({
500             templateUrl: './serials/t_apply_binding_template',
501             controller: 'ApplyBindingTemplateCtrl',
502             resolve : {
503                 rows : function() {
504                     return d_rows;
505                 },
506                 libs : function() {
507                     return libs;
508                 }
509             },
510             windowClass: 'app-modal-window',
511             backdrop: 'static',
512             keyboard: false
513         }).result.then(function(args) {
514             console.log(args);
515             egCore.pcrud.search('sdist', {
516                     id: d_rows.map(function(el) { return el['sdist.id']; })
517                 }, {}, { atomic : true }
518             ).then(function(resp){
519                 var evt = egCore.evt.parse(resp);
520                 if (evt) { // any way to just throw or return this to the error handler?
521                     console.log('failure',resp);
522                     ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_BINDING_TEMPLATE);
523                 }
524                 var promises = [];
525                 angular.forEach(resp,function(sdist) {
526                     var promise = $q.defer();
527                     promises.push(promise.promise);
528                     sdist.ischanged(true);
529                     sdist.bind_unit_template(
530                         typeof args.bind_unit_template[sdist.holding_lib()] == 'undefined'
531                         ? null
532                         : args.bind_unit_template[sdist.holding_lib()]
533                     );
534                     egCore.pcrud.apply(sdist).then(
535                         function(resp2) { // maybe success
536                             console.log('apply',resp2);
537                             var evt = egCore.evt.parse(resp2);
538                             if (evt) { // any way to just throw or return this to the error handler?
539                                 console.log('failure',resp2);
540                                 ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_BINDING_TEMPLATE);
541                             } else {
542                                 console.log('success',resp2);
543                                 ngToast.success(egCore.strings.SERIALS_DISTRIBUTION_SUCCESS_BINDING_TEMPLATE);
544                             }
545                             promise.resolve();
546                         },
547                         function(resp2) {
548                             console.log('failure',resp2);
549                             ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_BINDING_TEMPLATE);
550                             promise.resolve();
551                         }
552                     );
553                 });
554                 $q.all(promises).then(function() {
555                     reload();
556                 });
557             });
558         });
559     }
560     $scope.subscription_notes = function(rows) {
561         return $scope.notes('subscription',rows);
562     }
563     $scope.distribution_notes = function(rows) {
564         return $scope.notes('distribution',rows);
565     }
566     $scope.notes = function(note_type,rows) {
567         if (!rows) { return; }
568
569         function modal(existing_notes) {
570             $uibModal.open({
571                 templateUrl: './serials/t_notes',
572                 animation: true,
573                 controller: 'NotesCtrl',
574                 resolve : {
575                     note_type : function() { return note_type; },
576                     rows : function() {
577                         return rows;
578                     },
579                     notes : function() {
580                         return existing_notes;
581                     }
582                 },
583                 windowClass: 'app-modal-window',
584                 backdrop: 'static',
585                 keyboard: false
586             }).result.then(function(notes) {
587                 console.log('results',notes);
588                 egCore.pcrud.apply(notes).then(
589                     function(a) { console.log('toast here 1',a); },
590                     function(a) { console.log('toast here 2',a); }
591                 );
592             });
593         }
594
595         if (rows.length == 1) {
596             var fm_hint;
597             var search_hash = {};
598             var search_opt = {};
599             switch(note_type) {
600                 case 'subscription':
601                     fm_hint = 'ssubn';
602                     search_hash.subscription = rows[0]['id'];
603                     search_opt.order_by = { ssubn : 'create_date' };
604                 break;
605                 case 'distribution':
606                     fm_hint = 'sdistn';
607                     search_hash.distribution = rows[0]['sdist.id'];
608                     search_opt.order_by = { sdistn : 'create_date' };
609                 break;
610                 case 'item': default:
611                     fm_hint = 'sin';
612                     search_hash.item = rows[0]['si.id'];
613                     search_opt.order_by = { sin : 'create_date' };
614                 break;
615             }
616             egCore.pcrud.search(fm_hint, search_hash, search_opt,
617                 { atomic : true }
618             ).then(function(list) {
619                 modal(list);
620             });
621         } else {
622                 // support batch creation of notes across selections,
623                 // but not editing
624                 modal([]);
625         }
626     }
627
628 }]
629     }
630 })
631
632 .controller('ApplyBindingTemplateCtrl',
633        ['$scope','$q','$uibModalInstance','egCore','egSerialsCoreSvc',
634         'rows','libs',
635 function($scope , $q , $uibModalInstance , egCore , egSerialsCoreSvc ,
636          rows , libs ) {
637     $scope.ok = function(count) { $uibModalInstance.close($scope.args) }
638     $scope.cancel = function () { $uibModalInstance.dismiss() }
639     $scope.libs = libs;
640     $scope.rows = rows;
641     $scope.args = { bind_unit_template : {} };
642     $scope.templates = {};
643     angular.forEach(libs, function(org) {
644         egSerialsCoreSvc.fetch_templates(org.id).then(function(list){
645             $scope.templates[org.id] = egCore.idl.toTypedHash(list);
646         });
647     });
648 }])
649
650 .controller('LinkMFHDCtrl',
651        ['$scope','$q','$uibModalInstance','egCore','row','bibId',
652 function($scope , $q , $uibModalInstance , egCore , row , bibId ) {
653     console.log('row',row);
654     console.log('bibId',bibId);
655     $scope.args = {
656         summary_method: row['sdist.summary_method'] || 'add_to_sre',
657     };
658     if (row['sdist.record_entry']) {
659         $scope.args.which_mfhd = row['sdist.record_entry'].id;
660     }
661     $scope.ok = function(count) { $uibModalInstance.close($scope.args) }
662     $scope.cancel = function () { $uibModalInstance.dismiss() }
663     $scope.legacies = {};
664     egCore.pcrud.search('sre', {
665             record: bibId, owning_lib : row['sdist.holding_lib.id'], active: 't', deleted: 'f'
666         }, {}, { atomic : true }
667     ).then(
668         function(resp) { // maybe success
669             var evt; if (evt = egCore.evt.parse(resp)) { console.error(evt.toString()); return; }
670             if (!resp) { return; }
671
672             var promises = [];
673             var seen = {};
674
675             angular.forEach(resp, function(sre) {
676                 console.log('sre',sre);
677                 if (!seen[sre.record()]) {
678                     seen[sre.record()] = 1;
679                     $scope.legacies[sre.record()] = { mvr: null, svrs: [] };
680                     promises.push(
681                         egCore.net.request(
682                             'open-ils.search',
683                             'open-ils.search.biblio.record.mods_slim.retrieve.authoritative',
684                             sre.record()
685                         ).then(function(resp2) {
686                             var evt; if (evt = egCore.evt.parse(resp2)) { console.error(evt.toString()); return; }
687                             if (!resp2) { return; }
688                             $scope.legacies[sre.record()].mvr = egCore.idl.toHash(resp2);
689                         })
690                     );
691                     promises.push(
692                         egCore.net.request(
693                             'open-ils.search',
694                             'open-ils.search.serial.record.bib.retrieve',
695                             sre.record(),
696                             row['owning_lib.id']
697                         ).then(function(resp2) {
698                             angular.forEach(resp2,function(r) {
699                                 if (r.sre_id() > 0) {
700                                     console.log('svr',egCore.idl.toHash(r));
701                                     $scope.legacies[sre.record()].svrs.push( egCore.idl.toHash(r) );
702                                 }
703                             });
704                         })
705                     );
706                 }
707                 if (typeof $scope.legacies[sre.record()].sres == 'undefined') {
708                     $scope.legacies[sre.record()].sres = {};
709                 }
710                 $scope.legacies[sre.record()].sres[sre.id()] = egCore.idl.toHash(sre);
711             });
712
713             $q.all(promises).then(function(){
714                 console.log('done',$scope.legacies);
715             });
716         },
717         function(resp) { // outright failure
718             console.error('failure',resp);
719         }
720     )
721 }])
722
723 .controller('CloneCtrl',
724        ['$scope','$uibModalInstance','egCore','subs',
725 function($scope , $uibModalInstance , egCore , subs ) {
726     $scope.args = {};
727     $scope.ok = function(count) { $uibModalInstance.close($scope.args) }
728     $scope.cancel = function () { $uibModalInstance.dismiss() }
729     $scope.subs = subs;
730     $scope.find_bib = function () {
731
732         $scope.bibNotFound = null;
733         $scope.mvr = null;
734         if (!$scope.args.bib_id) return;
735
736         return egCore.net.request(
737             'open-ils.search',
738             'open-ils.search.biblio.record.mods_slim.retrieve.authoritative',
739             $scope.args.bib_id
740         ).then(
741             function(resp) { // maybe success 
742
743                 if (evt = egCore.evt.parse(resp)) {
744                     $scope.bibNotFound = $scope.args.bib_id;
745                     console.error(evt.toString());
746                     return;
747                 }
748
749                 if (!resp) {
750                     $scope.bibNotFound = $scope.args.bib_id;
751                     return;
752                 }
753
754                 $scope.mvr = egCore.idl.toHash(resp);
755                 //console.log($scope.mvr);
756             },
757             function(resp) { // outright failure
758                 console.error(resp);
759                 $scope.bibNotFound = $scope.args.bib_id;
760                 return;
761             }
762         );
763     }
764     $scope.$watch("args.bib_id", function(newVal, oldVal) {
765         if (newVal && newVal != oldVal) {
766             $scope.find_bib();
767         }
768     });
769 }])
770
771 .controller('RoutingCtrl',
772        ['$scope','$uibModalInstance','egCore','rowInfo','routes',
773 function($scope , $uibModalInstance , egCore , rowInfo , routes ) {
774     $scope.args = {
775          which_radio_button: 'reader'
776         ,reader: ''
777         ,department: ''
778         ,delete_me: false
779     };
780     $scope.stream_id = rowInfo['sstr.id'];
781     $scope.stream_label = rowInfo['sstr.routing_label'];
782     $scope.routes = routes;
783     $scope.readerInFocus = true;
784     $scope.ok = function(count) { $uibModalInstance.close($scope.routes) }
785     $scope.cancel = function () { $uibModalInstance.dismiss() }
786     $scope.model_has_changed = false;
787     $scope.find_user = function () {
788
789         $scope.readerNotFound = null;
790         $scope.reader_obj = null;
791         if (!$scope.args.reader) return;
792
793         egCore.net.request(
794             'open-ils.actor',
795             'open-ils.actor.get_barcodes',
796             egCore.auth.token(), egCore.auth.user().ws_ou(),
797             'actor', $scope.args.reader)
798
799         .then(function(resp) { // get_barcodes
800
801             if (evt = egCore.evt.parse(resp)) {
802                 console.error(evt.toString());
803                 return;
804             }
805
806             if (!resp || !resp[0]) {
807                 $scope.readerNotFound = $scope.args.reader;
808                 return;
809             }
810
811             egCore.pcrud.search('au', {
812                     id : resp[0].id
813                 }, {
814                     flesh : 1,
815                     flesh_fields : {
816                         'au'  : ['mailing_address','billing_address','home_ou']
817                     }
818                 },
819                 { atomic : true }
820             ).then(function(usr) {
821                 $scope.reader_obj = egCore.idl.toHash(usr[0]);
822             });
823         });
824     }
825     $scope.add_route = function () {
826         var new_route = {
827              stream: $scope.stream_id
828             ,pos: $scope.routes.length
829             ,note: $scope.args.note
830         }
831         if ($scope.args.which_radio_button == 'reader') {
832             new_route.reader = $scope.reader_obj;
833         } else {
834             new_route.department = $scope.args.department;
835         }
836         $scope.routes.push(new_route);
837         $scope.model_has_changed = true;
838     }
839     function adjust_pos_field() {
840         var idx = 0;
841         for (var i = 0; i < $scope.routes.length; i++) {
842             $scope.routes[i].pos = $scope.routes[i].delete_me ? idx : idx++;
843         }
844         $scope.model_has_changed = true;
845     }
846     $scope.move_route_up = function(r) {
847         var pos = r.pos;
848         if (pos > 0) {
849             var temp = $scope.routes[ pos - 1 ];
850             $scope.routes[ pos - 1 ] = $scope.routes[ pos ];
851             $scope.routes[ pos ] = temp;
852             adjust_pos_field();
853         }
854     }
855     $scope.move_route_down = function(r) {
856         var pos = r.pos;
857         if (pos < $scope.routes.length - 1) {
858             var temp = $scope.routes[ pos + 1 ];
859             $scope.routes[ pos + 1 ] = $scope.routes[ pos ];
860             $scope.routes[ pos ] = temp;
861             adjust_pos_field();
862         }
863     }
864     $scope.toggle_delete = function(r) {
865         r.delete_me = ! r.delete_me;
866         adjust_pos_field();
867     }
868     $scope.$watch("args.reader", function(newVal, oldVal) {
869         if (newVal && newVal != oldVal) {
870             $scope.find_user();
871         }
872     });
873 }])
874
875 .controller('NotesCtrl',
876        ['$scope','$uibModalInstance','egCore','note_type','rows','notes',
877 function($scope , $uibModalInstance , egCore , note_type , rows , notes ) {
878     $scope.note_type = note_type;
879     $scope.focusNote = true;
880     $scope.note = {
881         creator : egCore.auth.user().id(),
882         title   : '',
883         value   : '',
884         pub     : false,
885         'alert' : false,
886     };
887
888     $scope.note_list = notes;
889
890     $scope.ok = function(note) {
891
892         var return_notes = [];
893         if (note.initials) note.value += ' [' + note.initials + ']';
894         if (   (typeof note.title != 'undefined' && note.title != '')
895             || (typeof note.value != 'undefined' && note.value != '')) {
896             angular.forEach(rows, function (r) {
897                 console.log('r',r);
898                 window.my_r = r;
899                 var n;
900                 switch(note_type) {
901                     case 'subscription':
902                         n = new egCore.idl.ssubn();
903                         n.subscription(r['id']);
904                         break;
905                     case 'distribution':
906                         n = new egCore.idl.sdistn();
907                         n.distribution(r['sdist.id']);
908                         break;
909                     case 'item':
910                     default:
911                         n = new egCore.idl.sin();
912                         n.item(r['si.id']);
913                 }
914                 n.isnew(true);
915                 n.creator(note.creator);
916                 n.pub(note.pub);
917                 n['alert'](note['alert']);
918                 n.title(note.title);
919                 n.value(note.value);
920                 return_notes.push( n );
921             });
922         }
923         angular.forEach(notes, function(n) {
924             if (n.ischanged() || n.isdeleted()) {
925                 return_notes.push( n );
926             }
927         });
928         window.return_notes = return_notes;
929         $uibModalInstance.close(return_notes);
930     }
931
932     $scope.cancel = function($event) {
933         $uibModalInstance.dismiss();
934         $event.preventDefault();
935     }
936 }])