1 angular.module('egSerialsAppDep')
3 .directive('egSubscriptionManager', function() {
10 templateUrl: './serials/t_subscription_manager',
12 ['$scope','$q','egSerialsCoreSvc','egCore','egGridDataProvider',
13 '$uibModal','ngToast','egConfirmDialog',
14 function($scope , $q , egSerialsCoreSvc , egCore , egGridDataProvider ,
15 $uibModal , ngToast , egConfirmDialog ) {
17 $scope.selected_owning_ou = null;
18 $scope.owning_ou_changed = function(org) {
19 $scope.selected_owning_ou = org.id();
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;
35 $scope.distStreamGridDataProvider.refresh();
40 $scope.localStreamNames = [];
41 egCore.hatch.getItem('eg.serials.stream_names')
42 .then(function(list) {
43 if (list) $scope.localStreamNames = list;
46 $scope.distStreamGridControls = {
47 activateItem : function (item) { } // TODO
49 $scope.distStreamGridDataProvider = egGridDataProvider.instance({
50 get : function(offset, count) {
51 return this.arrayNotifier(egSerialsCoreSvc.subList, offset, count);
55 $scope.need_one_selected = function() {
56 var items = $scope.distStreamGridControls.selectedItems();
57 if (items.length == 1) return false;
61 $scope.receiving_templates = {};
62 egSerialsCoreSvc.fetch_templates(egCore.org.list()).then(function(templates){
63 $scope.receiving_templates = templates;
66 $scope.add_subscription = function() {
67 var new_ssub = egCore.idl.toTypedHash(new egCore.idl.ssub());
68 new_ssub._isnew = true;
69 new_ssub.record_entry = $scope.bibId;
70 new_ssub._focus_me = true;
71 $scope.subscriptions.push(new_ssub);
72 $scope.add_distribution(new_ssub); // since we know we want at least one distribution
74 $scope.add_distribution = function(ssub, grab_focus) {
76 'serial.default_display_grouping'
77 ]).then(function(set) {
78 var new_sdist = egCore.idl.toTypedHash(new egCore.idl.sdist());
79 new_sdist._isnew = true;
80 new_sdist.subscription = ssub.id;
81 new_sdist.display_grouping = set['serial.default_display_grouping'] || 'chron';
82 if (!angular.isArray(ssub.distributions)){
83 ssub.distributions = [];
86 new_sdist._focus_me = true;
87 ssub._focus_me = false;
89 ssub.distributions.push(new_sdist);
90 $scope.add_stream(new_sdist); // since we know we want at least one stream
93 $scope.remove_pending_distribution = function(ssub, sdist) {
95 for (var i = 0; i < ssub.distributions.length; i++) {
96 if (ssub.distributions[i] === sdist) {
101 if (to_remove > -1) {
102 ssub.distributions.splice(to_remove, 1);
105 $scope.add_stream = function(sdist, grab_focus) {
106 var new_sstr = egCore.idl.toTypedHash(new egCore.idl.sstr());
107 new_sstr.distribution = sdist.id;
108 new_sstr._isnew = true;
110 new_sstr._focus_me = true;
111 sdist._has_focus = false; // and take focus away from a newly created sdist
113 if (!angular.isArray(sdist.streams)){
116 sdist.streams.push(new_sstr);
119 $scope.remove_pending_stream = function(sdist, sstr) {
121 for (var i = 0; i < sdist.streams.length; i++) {
122 if (sdist.streams[i] === sstr) {
127 if (to_remove > -1) {
128 sdist.streams.splice(to_remove, 1);
132 $scope.abort_changes = function(form) {
136 function updateLocalStreamNames (new_name) {
137 if (new_name && $scope.localStreamNames.filter(function(x){ return x == new_name}).length == 0) {
138 $scope.localStreamNames.push(new_name);
139 egCore.hatch.setItem('eg.serials.stream_names', $scope.localStreamNames)
143 $scope.dirtyForm = function () {
144 $scope.ssubform.$dirty = true;
147 $scope.save_subscriptions = function(form) {
148 // traverse through structure and set _ischanged
149 // TODO add more granular dirty input detection
150 angular.forEach($scope.subscriptions, function(ssub) {
151 if (!ssub._isnew) ssub._ischanged = true;
152 angular.forEach(ssub.distributions, function(sdist) {
153 if (!sdist._isnew) sdist._ischanged = true;
154 angular.forEach(sdist.streams, function(sstr) {
155 if (!sstr._isnew) sstr._ischanged = true;
156 updateLocalStreamNames(sstr.routing_label);
161 var obj = egCore.idl.fromTypedHash($scope.subscriptions);
163 // create a bunch of promises that each get resolved upon each
164 // CUD update; that way, we can know when the entire save
165 // operation is completed
167 angular.forEach(obj, function(ssub) {
168 ssub._cud_done = $q.defer();
169 promises.push(ssub._cud_done.promise);
170 angular.forEach(ssub.distributions(), function(sdist) {
171 sdist._cud_done = $q.defer();
172 promises.push(sdist._cud_done.promise);
173 angular.forEach(sdist.streams(), function(sstr) {
174 sstr._cud_done = $q.defer();
175 promises.push(sstr._cud_done.promise);
180 angular.forEach(obj, function(ssub) {
181 ssub.owning_lib(ssub.owning_lib().id()); // deflesh
182 egCore.pcrud.apply(ssub).then(function(res) {
183 var ssub_id = (ssub.isnew() && angular.isObject(res)) ? res.id() : ssub.id();
184 angular.forEach(ssub.distributions(), function(sdist) {
185 // set subscription ID just in case it's new
186 sdist.holding_lib(sdist.holding_lib().id()); // deflesh
187 sdist.subscription(ssub_id);
188 egCore.pcrud.apply(sdist).then(function(res) {
189 var sdist_id = (sdist.isnew() && angular.isObject(res)) ? res.id() : sdist.id();
190 angular.forEach(sdist.streams(), function(sstr) {
191 // set distribution ID just in case it's new
192 sstr.distribution(sdist_id);
193 egCore.pcrud.apply(sstr).then(function(res) {
194 sstr._cud_done.resolve();
198 sdist._cud_done.resolve();
200 ssub._cud_done.resolve();
203 $q.all(promises).then(function(resolutions) {
208 $scope.delete_subscription = function(rows) {
209 if (rows.length == 0) { return; }
210 var s_rows = rows.filter(function(el) {
211 return typeof el['id'] != 'undefined';
213 if (s_rows.length == 0) { return; }
214 egConfirmDialog.open(
215 egCore.strings.CONFIRM_DELETE_SUBSCRIPTION,
216 egCore.strings.CONFIRM_DELETE_SUBSCRIPTION_MESSAGE,
217 {count : s_rows.length}
218 ).result.then(function () {
220 angular.forEach(s_rows, function(el) {
224 'open-ils.serial.subscription.safe_delete',
227 ).then(function(resp){
228 var evt = egCore.evt.parse(resp);
230 ngToast.danger(egCore.strings.SERIALS_SUBSCRIPTION_FAIL_DELETE + ' : ' + evt.desc);
232 ngToast.success(egCore.strings.SERIALS_SUBSCRIPTION_SUCCESS_DELETE);
237 $q.all(promises).then(function() {
242 $scope.delete_distribution = function(rows) {
243 if (rows.length == 0) { return; }
244 var d_rows = rows.filter(function(el) {
245 return typeof el['sdist.id'] != 'undefined';
247 if (d_rows.length == 0) { return; }
248 egConfirmDialog.open(
249 egCore.strings.CONFIRM_DELETE_DISTRIBUTION,
250 egCore.strings.CONFIRM_DELETE_DISTRIBUTION_MESSAGE,
251 {count : d_rows.length}
252 ).result.then(function () {
254 angular.forEach(d_rows, function(el) {
258 'open-ils.serial.distribution.safe_delete',
261 ).then(function(resp){
262 var evt = egCore.evt.parse(resp);
264 ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_DELETE + ' : ' + evt.desc);
266 ngToast.success(egCore.strings.SERIALS_DISTRIBUTION_SUCCESS_DELETE);
271 $q.all(promises).then(function() {
276 $scope.delete_stream = function(rows) {
277 if (rows.length == 0) { return; }
278 var s_rows = rows.filter(function(el) {
279 return typeof el['sstr.id'] != 'undefined';
281 if (s_rows.length == 0) { return; }
282 egConfirmDialog.open(
283 egCore.strings.CONFIRM_DELETE_STREAM,
284 egCore.strings.CONFIRM_DELETE_STREAM_MESSAGE,
285 {count : s_rows.length}
286 ).result.then(function () {
288 angular.forEach(s_rows, function(el) {
292 'open-ils.serial.stream.safe_delete',
295 ).then(function(resp){
296 var evt = egCore.evt.parse(resp);
298 ngToast.danger(egCore.strings.SERIALS_STREAM_FAIL_DELETE + ' : ' + evt.desc);
300 ngToast.success(egCore.strings.SERIALS_STREAM_SUCCESS_DELETE);
305 $q.all(promises).then(function() {
310 $scope.additional_routing = function(rows) {
311 if (!rows) { return; }
313 if (!row) { row = $scope.distStreamGridControls.selectedItems()[0]; }
314 if (row && row['sstr.id']) {
315 egCore.pcrud.search('srlu', {
316 stream : row['sstr.id']
321 'au' : ['mailing_address','billing_address','home_ou']
323 order_by : { srlu : 'pos' }
326 ).then(function(list) {
328 templateUrl: './serials/t_routing_list',
330 controller: 'RoutingCtrl',
332 rowInfo : function() {
335 routes : function() {
336 return egCore.idl.toHash(list);
339 }).result.then(function(routes) {
340 // delete all of the routes first;
341 // it's easiest given the constraints
344 angular.forEach(routes, function(r) {
345 var srlu = new egCore.idl.srlu();
346 srlu.stream(r.stream);
349 srlu.reader(r.reader.id);
351 srlu.department(r.department);
355 var srlu_copy = angular.copy(srlu);
356 srlu_copy.isdeleted(true);
357 deletions.push(srlu_copy);
361 creations.push(srlu);
364 egCore.pcrud.apply(deletions.concat(creations)).then(function(){
371 $scope.clone_subscription = function(rows) {
372 if (!rows) { return; }
375 templateUrl: './serials/t_clone_subscription',
376 controller: 'CloneCtrl',
382 windowClass: 'app-modal-window',
385 }).result.then(function(args) {
387 var some_failure = false;
388 var some_success = false;
390 angular.forEach(rows, function(row) {
397 'open-ils.serial.subscription.clone',
403 var evt = egCore.evt.parse(resp);
404 if (evt) { // any way to just throw or return this to the error handler?
405 console.log('failure',resp);
407 ngToast.danger(egCore.strings.SERIALS_SUBSCRIPTION_FAIL_CLONE);
409 console.log('success',resp);
411 ngToast.success(egCore.strings.SERIALS_SUBSCRIPTION_SUCCESS_CLONE);
415 console.log('failure',resp);
417 ngToast.danger(egCore.strings.SERIALS_SUBSCRIPTION_FAIL_CLONE);
423 $q.all(promises).then(function() {
428 $scope.link_mfhd = function(rows) {
429 if (!rows) { return; }
431 if (!row['sdist.id']) { return; }
433 templateUrl: './serials/t_link_mfhd',
434 controller: 'LinkMFHDCtrl',
443 windowClass: 'app-modal-window',
446 }).result.then(function(args) {
447 console.log('modal done', args);
448 egCore.pcrud.search('sdist', {
449 id: rows[0]['sdist.id']
450 }, {}, { atomic : true }
451 ).then(function(resp){
452 var evt = egCore.evt.parse(resp);
453 if (evt) { // any way to just throw or return this to the error handler?
454 console.log('failure',resp);
455 ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_LINK_MFHD);
458 sdist.ischanged(true);
459 sdist.summary_method( args.summary_method );
460 sdist.record_entry( args.which_mfhd );
461 egCore.pcrud.apply(sdist).then(
462 function(resp) { // maybe success
463 console.log('apply',resp);
464 var evt = egCore.evt.parse(resp);
465 if (evt) { // any way to just throw or return this to the error handler?
466 console.log('failure',resp);
467 ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_LINK_MFHD);
469 console.log('success',resp);
470 ngToast.success(egCore.strings.SERIALS_DISTRIBUTION_SUCCESS_LINK_MFHD);
475 console.log('failure',resp);
476 ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_LINK_MFHD);
482 $scope.apply_binding_template = function(rows) {
483 if (rows.length == 0) { return; }
484 var d_rows = rows.filter(function(el) {
485 return typeof el['sdist.id'] != 'undefined';
487 if (d_rows.length == 0) { return; }
488 var libs = []; var seen_lib = {};
489 angular.forEach(d_rows, function(el) {
490 if (el['sdist.holding_lib.id'] && !seen_lib[el['sdist.holding_lib.id']]) {
491 seen_lib[el['sdist.holding_lib.id']] = 1;
493 id: el['sdist.holding_lib.id'],
494 name: el['sdist.holding_lib.name'],
499 templateUrl: './serials/t_apply_binding_template',
500 controller: 'ApplyBindingTemplateCtrl',
509 windowClass: 'app-modal-window',
512 }).result.then(function(args) {
514 egCore.pcrud.search('sdist', {
515 id: d_rows.map(function(el) { return el['sdist.id']; })
516 }, {}, { atomic : true }
517 ).then(function(resp){
518 var evt = egCore.evt.parse(resp);
519 if (evt) { // any way to just throw or return this to the error handler?
520 console.log('failure',resp);
521 ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_BINDING_TEMPLATE);
524 angular.forEach(resp,function(sdist) {
525 var promise = $q.defer();
526 promises.push(promise.promise);
527 sdist.ischanged(true);
528 sdist.bind_unit_template(
529 typeof args.bind_unit_template[sdist.holding_lib()] == 'undefined'
531 : args.bind_unit_template[sdist.holding_lib()]
533 egCore.pcrud.apply(sdist).then(
534 function(resp2) { // maybe success
535 console.log('apply',resp2);
536 var evt = egCore.evt.parse(resp2);
537 if (evt) { // any way to just throw or return this to the error handler?
538 console.log('failure',resp2);
539 ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_BINDING_TEMPLATE);
541 console.log('success',resp2);
542 ngToast.success(egCore.strings.SERIALS_DISTRIBUTION_SUCCESS_BINDING_TEMPLATE);
547 console.log('failure',resp2);
548 ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_BINDING_TEMPLATE);
553 $q.all(promises).then(function() {
559 $scope.subscription_notes = function(rows) {
560 return $scope.notes('subscription',rows);
562 $scope.distribution_notes = function(rows) {
563 return $scope.notes('distribution',rows);
565 $scope.notes = function(note_type,rows) {
566 if (!rows) { return; }
568 function modal(existing_notes) {
570 templateUrl: './serials/t_notes',
572 controller: 'NotesCtrl',
574 note_type : function() { return note_type; },
579 return existing_notes;
582 windowClass: 'app-modal-window',
585 }).result.then(function(notes) {
586 console.log('results',notes);
587 egCore.pcrud.apply(notes).then(
588 function(a) { console.log('toast here 1',a); },
589 function(a) { console.log('toast here 2',a); }
594 if (rows.length == 1) {
596 var search_hash = {};
601 search_hash.subscription = rows[0]['id'];
602 search_opt.order_by = { ssubn : 'create_date' };
606 search_hash.distribution = rows[0]['sdist.id'];
607 search_opt.order_by = { sdistn : 'create_date' };
609 case 'item': default:
611 search_hash.item = rows[0]['si.id'];
612 search_opt.order_by = { sin : 'create_date' };
615 egCore.pcrud.search(fm_hint, search_hash, search_opt,
617 ).then(function(list) {
621 // support batch creation of notes across selections,
631 .controller('ApplyBindingTemplateCtrl',
632 ['$scope','$q','$uibModalInstance','egCore','egSerialsCoreSvc',
634 function($scope , $q , $uibModalInstance , egCore , egSerialsCoreSvc ,
636 $scope.ok = function(count) { $uibModalInstance.close($scope.args) }
637 $scope.cancel = function () { $uibModalInstance.dismiss() }
640 $scope.args = { bind_unit_template : {} };
641 $scope.templates = {};
642 var _libs = libs.map(function(x) {
645 egSerialsCoreSvc.fetch_templates(_libs).then(function(templates){
646 $scope.templates = templates;
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);
656 summary_method: row['sdist.summary_method'] || 'add_to_sre',
658 if (row['sdist.record_entry']) {
659 $scope.args.which_mfhd = row['sdist.record_entry'].id;
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 }
668 function(resp) { // maybe success
669 var evt; if (evt = egCore.evt.parse(resp)) { console.error(evt.toString()); return; }
670 if (!resp) { return; }
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: [] };
683 'open-ils.search.biblio.record.mods_slim.retrieve.authoritative',
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);
694 'open-ils.search.serial.record.bib.retrieve',
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) );
707 if (typeof $scope.legacies[sre.record()].sres == 'undefined') {
708 $scope.legacies[sre.record()].sres = {};
710 $scope.legacies[sre.record()].sres[sre.id()] = egCore.idl.toHash(sre);
713 $q.all(promises).then(function(){
714 console.log('done',$scope.legacies);
717 function(resp) { // outright failure
718 console.error('failure',resp);
723 .controller('CloneCtrl',
724 ['$scope','$uibModalInstance','egCore','subs',
725 function($scope , $uibModalInstance , egCore , subs ) {
727 $scope.ok = function(count) { $uibModalInstance.close($scope.args) }
728 $scope.cancel = function () { $uibModalInstance.dismiss() }
730 $scope.find_bib = function () {
732 $scope.bibNotFound = null;
734 if (!$scope.args.bib_id) return;
736 return egCore.net.request(
738 'open-ils.search.biblio.record.mods_slim.retrieve.authoritative',
741 function(resp) { // maybe success
743 if (evt = egCore.evt.parse(resp)) {
744 $scope.bibNotFound = $scope.args.bib_id;
745 console.error(evt.toString());
750 $scope.bibNotFound = $scope.args.bib_id;
754 $scope.mvr = egCore.idl.toHash(resp);
755 //console.log($scope.mvr);
757 function(resp) { // outright failure
759 $scope.bibNotFound = $scope.args.bib_id;
764 $scope.$watch("args.bib_id", function(newVal, oldVal) {
765 if (newVal && newVal != oldVal) {
771 .controller('RoutingCtrl',
772 ['$scope','$uibModalInstance','egCore','rowInfo','routes',
773 function($scope , $uibModalInstance , egCore , rowInfo , routes ) {
775 which_radio_button: 'reader'
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 () {
789 $scope.readerNotFound = null;
790 $scope.reader_obj = null;
791 if (!$scope.args.reader) return;
795 'open-ils.actor.get_barcodes',
796 egCore.auth.token(), egCore.auth.user().ws_ou(),
797 'actor', $scope.args.reader)
799 .then(function(resp) { // get_barcodes
801 if (evt = egCore.evt.parse(resp)) {
802 console.error(evt.toString());
806 if (!resp || !resp[0]) {
807 $scope.readerNotFound = $scope.args.reader;
811 egCore.pcrud.search('au', {
816 'au' : ['mailing_address','billing_address','home_ou']
820 ).then(function(usr) {
821 $scope.reader_obj = egCore.idl.toHash(usr[0]);
825 $scope.add_route = function () {
827 stream: $scope.stream_id
828 ,pos: $scope.routes.length
829 ,note: $scope.args.note
831 if ($scope.args.which_radio_button == 'reader') {
832 new_route.reader = $scope.reader_obj;
834 new_route.department = $scope.args.department;
836 $scope.routes.push(new_route);
837 $scope.model_has_changed = true;
839 function adjust_pos_field() {
841 for (var i = 0; i < $scope.routes.length; i++) {
842 $scope.routes[i].pos = $scope.routes[i].delete_me ? idx : idx++;
844 $scope.model_has_changed = true;
846 $scope.move_route_up = function(r) {
849 var temp = $scope.routes[ pos - 1 ];
850 $scope.routes[ pos - 1 ] = $scope.routes[ pos ];
851 $scope.routes[ pos ] = temp;
855 $scope.move_route_down = function(r) {
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;
864 $scope.toggle_delete = function(r) {
865 r.delete_me = ! r.delete_me;
868 $scope.$watch("args.reader", function(newVal, oldVal) {
869 if (newVal && newVal != oldVal) {
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;
881 creator : egCore.auth.user().id(),
888 $scope.note_list = notes;
890 $scope.ok = function(note) {
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) {
902 n = new egCore.idl.ssubn();
903 n.subscription(r['id']);
906 n = new egCore.idl.sdistn();
907 n.distribution(r['sdist.id']);
911 n = new egCore.idl.sin();
915 n.creator(note.creator);
917 n['alert'](note['alert']);
920 return_notes.push( n );
923 angular.forEach(notes, function(n) {
924 if (n.ischanged() || n.isdeleted()) {
925 return_notes.push( n );
928 window.return_notes = return_notes;
929 $uibModalInstance.close(return_notes);
932 $scope.cancel = function($event) {
933 $uibModalInstance.dismiss();
934 $event.preventDefault();