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 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);
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
76 $scope.add_distribution = function(ssub, grab_focus) {
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 = [];
88 new_sdist._focus_me = true;
89 ssub._focus_me = false;
91 ssub.distributions.push(new_sdist);
92 $scope.add_stream(new_sdist); // since we know we want at least one stream
95 $scope.remove_pending_distribution = function(ssub, sdist) {
97 for (var i = 0; i < ssub.distributions.length; i++) {
98 if (ssub.distributions[i] === sdist) {
103 if (to_remove > -1) {
104 ssub.distributions.splice(to_remove, 1);
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;
112 new_sstr._focus_me = true;
113 sdist._has_focus = false; // and take focus away from a newly created sdist
115 if (!angular.isArray(sdist.streams)){
118 sdist.streams.push(new_sstr);
121 $scope.remove_pending_stream = function(sdist, sstr) {
123 for (var i = 0; i < sdist.streams.length; i++) {
124 if (sdist.streams[i] === sstr) {
129 if (to_remove > -1) {
130 sdist.streams.splice(to_remove, 1);
134 $scope.abort_changes = function(form) {
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)
145 $scope.dirtyForm = function () {
146 $scope.ssubform.$dirty = true;
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);
163 var obj = egCore.idl.fromTypedHash($scope.subscriptions);
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
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);
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();
200 sdist._cud_done.resolve();
202 ssub._cud_done.resolve();
205 $q.all(promises).then(function(resolutions) {
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';
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 () {
222 angular.forEach(s_rows, function(el) {
226 'open-ils.serial.subscription.safe_delete',
229 ).then(function(resp){
230 var evt = egCore.evt.parse(resp);
232 ngToast.danger(egCore.strings.SERIALS_SUBSCRIPTION_FAIL_DELETE + ' : ' + evt.desc);
234 ngToast.success(egCore.strings.SERIALS_SUBSCRIPTION_SUCCESS_DELETE);
239 $q.all(promises).then(function() {
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';
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 () {
256 angular.forEach(d_rows, function(el) {
260 'open-ils.serial.distribution.safe_delete',
263 ).then(function(resp){
264 var evt = egCore.evt.parse(resp);
266 ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_DELETE + ' : ' + evt.desc);
268 ngToast.success(egCore.strings.SERIALS_DISTRIBUTION_SUCCESS_DELETE);
273 $q.all(promises).then(function() {
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';
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 () {
290 angular.forEach(s_rows, function(el) {
294 'open-ils.serial.stream.safe_delete',
297 ).then(function(resp){
298 var evt = egCore.evt.parse(resp);
300 ngToast.danger(egCore.strings.SERIALS_STREAM_FAIL_DELETE + ' : ' + evt.desc);
302 ngToast.success(egCore.strings.SERIALS_STREAM_SUCCESS_DELETE);
307 $q.all(promises).then(function() {
312 $scope.additional_routing = function(rows) {
313 if (!rows) { return; }
315 if (!row) { row = $scope.distStreamGridControls.selectedItems()[0]; }
316 if (row && row['sstr.id']) {
317 egCore.pcrud.search('srlu', {
318 stream : row['sstr.id']
323 'au' : ['mailing_address','billing_address','home_ou']
325 order_by : { srlu : 'pos' }
328 ).then(function(list) {
330 templateUrl: './serials/t_routing_list',
332 controller: 'RoutingCtrl',
334 rowInfo : function() {
337 routes : function() {
338 return egCore.idl.toHash(list);
341 }).result.then(function(routes) {
342 // delete all of the routes first;
343 // it's easiest given the constraints
346 angular.forEach(routes, function(r) {
347 var srlu = new egCore.idl.srlu();
348 srlu.stream(r.stream);
351 srlu.reader(r.reader.id);
353 srlu.department(r.department);
357 var srlu_copy = angular.copy(srlu);
358 srlu_copy.isdeleted(true);
359 deletions.push(srlu_copy);
363 creations.push(srlu);
366 egCore.pcrud.apply(deletions.concat(creations)).then(function(){
373 $scope.clone_subscription = function(rows) {
374 if (!rows) { return; }
377 templateUrl: './serials/t_clone_subscription',
378 controller: 'CloneCtrl',
384 windowClass: 'app-modal-window',
387 }).result.then(function(args) {
389 var some_failure = false;
390 var some_success = false;
392 angular.forEach(rows, function(row) {
399 'open-ils.serial.subscription.clone',
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);
409 ngToast.danger(egCore.strings.SERIALS_SUBSCRIPTION_FAIL_CLONE);
411 console.log('success',resp);
413 ngToast.success(egCore.strings.SERIALS_SUBSCRIPTION_SUCCESS_CLONE);
417 console.log('failure',resp);
419 ngToast.danger(egCore.strings.SERIALS_SUBSCRIPTION_FAIL_CLONE);
425 $q.all(promises).then(function() {
430 $scope.link_mfhd = function(rows) {
431 if (!rows) { return; }
433 if (!row['sdist.id']) { return; }
435 templateUrl: './serials/t_link_mfhd',
436 controller: 'LinkMFHDCtrl',
445 windowClass: 'app-modal-window',
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);
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);
471 console.log('success',resp);
472 ngToast.success(egCore.strings.SERIALS_DISTRIBUTION_SUCCESS_LINK_MFHD);
477 console.log('failure',resp);
478 ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_LINK_MFHD);
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';
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;
495 id: el['sdist.holding_lib.id'],
496 name: el['sdist.holding_lib.name'],
501 templateUrl: './serials/t_apply_binding_template',
502 controller: 'ApplyBindingTemplateCtrl',
511 windowClass: 'app-modal-window',
514 }).result.then(function(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);
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'
533 : args.bind_unit_template[sdist.holding_lib()]
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);
543 console.log('success',resp2);
544 ngToast.success(egCore.strings.SERIALS_DISTRIBUTION_SUCCESS_BINDING_TEMPLATE);
549 console.log('failure',resp2);
550 ngToast.danger(egCore.strings.SERIALS_DISTRIBUTION_FAIL_BINDING_TEMPLATE);
555 $q.all(promises).then(function() {
561 $scope.subscription_notes = function(rows) {
562 return $scope.notes('subscription',rows);
564 $scope.distribution_notes = function(rows) {
565 return $scope.notes('distribution',rows);
567 $scope.notes = function(note_type,rows) {
568 if (!rows) { return; }
570 function modal(existing_notes) {
572 templateUrl: './serials/t_notes',
574 controller: 'NotesCtrl',
576 note_type : function() { return note_type; },
581 return existing_notes;
584 windowClass: 'app-modal-window',
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); }
596 if (rows.length == 1) {
598 var search_hash = {};
603 search_hash.subscription = rows[0]['id'];
604 search_opt.order_by = { ssubn : 'create_date' };
608 search_hash.distribution = rows[0]['sdist.id'];
609 search_opt.order_by = { sdistn : 'create_date' };
611 case 'item': default:
613 search_hash.item = rows[0]['si.id'];
614 search_opt.order_by = { sin : 'create_date' };
617 egCore.pcrud.search(fm_hint, search_hash, search_opt,
619 ).then(function(list) {
623 // support batch creation of notes across selections,
633 .controller('ApplyBindingTemplateCtrl',
634 ['$scope','$q','$uibModalInstance','egCore','egSerialsCoreSvc',
636 function($scope , $q , $uibModalInstance , egCore , egSerialsCoreSvc ,
638 $scope.ok = function(count) { $uibModalInstance.close($scope.args) }
639 $scope.cancel = function () { $uibModalInstance.dismiss() }
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);
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);
657 summary_method: row['sdist.summary_method'] || 'add_to_sre',
659 if (row['sdist.record_entry']) {
660 $scope.args.which_mfhd = row['sdist.record_entry'].id;
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 }
669 function(resp) { // maybe success
670 var evt; if (evt = egCore.evt.parse(resp)) { console.error(evt.toString()); return; }
671 if (!resp) { return; }
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: [] };
684 'open-ils.search.biblio.record.mods_slim.retrieve.authoritative',
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);
695 'open-ils.search.serial.record.bib.retrieve',
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) );
708 if (typeof $scope.legacies[sre.record()].sres == 'undefined') {
709 $scope.legacies[sre.record()].sres = {};
711 $scope.legacies[sre.record()].sres[sre.id()] = egCore.idl.toHash(sre);
714 $q.all(promises).then(function(){
715 console.log('done',$scope.legacies);
718 function(resp) { // outright failure
719 console.error('failure',resp);
724 .controller('CloneCtrl',
725 ['$scope','$uibModalInstance','egCore','subs',
726 function($scope , $uibModalInstance , egCore , subs ) {
728 $scope.ok = function(count) { $uibModalInstance.close($scope.args) }
729 $scope.cancel = function () { $uibModalInstance.dismiss() }
731 $scope.find_bib = function () {
733 $scope.bibNotFound = null;
735 if (!$scope.args.bib_id) return;
737 return egCore.net.request(
739 'open-ils.search.biblio.record.mods_slim.retrieve.authoritative',
742 function(resp) { // maybe success
744 if (evt = egCore.evt.parse(resp)) {
745 $scope.bibNotFound = $scope.args.bib_id;
746 console.error(evt.toString());
751 $scope.bibNotFound = $scope.args.bib_id;
755 $scope.mvr = egCore.idl.toHash(resp);
756 //console.log($scope.mvr);
758 function(resp) { // outright failure
760 $scope.bibNotFound = $scope.args.bib_id;
765 $scope.$watch("args.bib_id", function(newVal, oldVal) {
766 if (newVal && newVal != oldVal) {
772 .controller('RoutingCtrl',
773 ['$scope','$uibModalInstance','egCore','rowInfo','routes',
774 function($scope , $uibModalInstance , egCore , rowInfo , routes ) {
776 which_radio_button: 'reader'
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 () {
790 $scope.readerNotFound = null;
791 $scope.reader_obj = null;
792 if (!$scope.args.reader) return;
796 'open-ils.actor.get_barcodes',
797 egCore.auth.token(), egCore.auth.user().ws_ou(),
798 'actor', $scope.args.reader)
800 .then(function(resp) { // get_barcodes
802 if (evt = egCore.evt.parse(resp)) {
803 console.error(evt.toString());
807 if (!resp || !resp[0]) {
808 $scope.readerNotFound = $scope.args.reader;
812 egCore.pcrud.search('au', {
817 'au' : ['mailing_address','billing_address','home_ou']
821 ).then(function(usr) {
822 $scope.reader_obj = egCore.idl.toHash(usr[0]);
826 $scope.add_route = function () {
828 stream: $scope.stream_id
829 ,pos: $scope.routes.length
830 ,note: $scope.args.note
832 if ($scope.args.which_radio_button == 'reader') {
833 new_route.reader = $scope.reader_obj;
835 new_route.department = $scope.args.department;
837 $scope.routes.push(new_route);
838 $scope.model_has_changed = true;
840 function adjust_pos_field() {
842 for (var i = 0; i < $scope.routes.length; i++) {
843 $scope.routes[i].pos = $scope.routes[i].delete_me ? idx : idx++;
845 $scope.model_has_changed = true;
847 $scope.move_route_up = function(r) {
850 var temp = $scope.routes[ pos - 1 ];
851 $scope.routes[ pos - 1 ] = $scope.routes[ pos ];
852 $scope.routes[ pos ] = temp;
856 $scope.move_route_down = function(r) {
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;
865 $scope.toggle_delete = function(r) {
866 r.delete_me = ! r.delete_me;
869 $scope.$watch("args.reader", function(newVal, oldVal) {
870 if (newVal && newVal != oldVal) {
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;
882 creator : egCore.auth.user().id(),
889 $scope.note_list = notes;
891 $scope.ok = function(note) {
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) {
903 n = new egCore.idl.ssubn();
904 n.subscription(r['id']);
907 n = new egCore.idl.sdistn();
908 n.distribution(r['sdist.id']);
912 n = new egCore.idl.sin();
916 n.creator(note.creator);
918 n['alert'](note['alert']);
921 return_notes.push( n );
924 angular.forEach(notes, function(n) {
925 if (n.ischanged() || n.isdeleted()) {
926 return_notes.push( n );
929 window.return_notes = return_notes;
930 $uibModalInstance.close(return_notes);
933 $scope.cancel = function($event) {
934 $uibModalInstance.dismiss();
935 $event.preventDefault();