]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/serials/directives/view-items-grid.js
LP#1414197 Serial Item Delete Improvements
[working/Evergreen.git] / Open-ILS / web / js / ui / default / staff / serials / directives / view-items-grid.js
1 angular.module('egSerialsAppDep')
2
3 .directive('egItemGrid', function() {
4     return {
5         transclude: true,
6         restrict:   'E',
7         scope: {
8             bibId  : '=',
9             ssubId : '='
10         },
11         templateUrl: './serials/t_view_items_grid',
12         controller:
13        ['$scope','$q','egSerialsCoreSvc','egCore','egGridDataProvider','orderByFilter',
14         '$uibModal','ngToast','egConfirmDialog','egPromptDialog','$timeout',
15 function($scope , $q , egSerialsCoreSvc , egCore , egGridDataProvider , orderByFilter ,
16          $uibModal , ngToast , egConfirmDialog , egPromptDialog , $timeout) {
17
18     $scope.svc = egSerialsCoreSvc;
19
20     var _paging_filter;
21     function reload(ssubId,filter) {
22         _paging_filter = filter;
23         return egSerialsCoreSvc.fetchItemsForSub(ssubId,filter).then(function() {
24             $scope.itemGridProvider.refresh();
25         });
26     }
27
28     $scope.filter_items_all = function () { return reload($scope.ssubId) }
29     $scope.filter_items_have = function () { return reload($scope.ssubId,{status:['Received','Bindery','Bound']}) }
30     $scope.filter_items_dont_have = function () { return reload($scope.ssubId,{'-not':{status:['Received','Bindery','Bound']}}) }
31     $scope.filter_items_by_status = function (item,status) { return reload($scope.ssubId,{status:status.name}) }
32
33     $scope.$watch('ssubId', function(newVal, oldVal) {
34         if (newVal && newVal != oldVal) return reload(newVal);
35     });
36
37     $scope.itemGridControls = {
38         activateItem : function (item) { } // TODO
39     };
40
41     function compileSort(sort) {
42         if (sort && angular.isArray(sort) && sort.length == 1) {
43             if (angular.isObject(sort[0])) {
44                 for (key in sort[0]) {
45                     return {
46                         'class'   : 'sitem',
47                         field     :  key,
48                         direction : sort[0][key]
49                     };
50                 }
51             } else {
52                 return { 'class': 'sitem', field: sort[0] };
53             }
54         }
55     }
56     var current_sort = [];
57     $scope.itemGridProvider = egGridDataProvider.instance({
58         get : function(offset, count) {
59             var self = this;
60             if (angular.equals(current_sort, self.sort) && egSerialsCoreSvc.itemList.length >= offset + count) { // if there's anything on the requested page, notify
61                 return self.arrayNotifier(egSerialsCoreSvc.itemList, offset, count);
62             } else { // else try to fetch another page
63                 if (angular.equals(current_sort, self.sort)) {
64                     return egSerialsCoreSvc.fetchItemsForSubPaged(
65                         $scope.ssubId,
66                         _paging_filter,
67                         egSerialsCoreSvc.itemList.length,
68                         count + offset - egSerialsCoreSvc.itemList.length,
69                         compileSort(self.sort)
70                     ).then(function() {
71                         return self.arrayNotifier(egSerialsCoreSvc.itemList, offset, count);
72                     });
73                 } else {
74                     current_sort = self.sort;
75                     return egSerialsCoreSvc.fetchItemsForSub(
76                         $scope.ssubId,
77                         _paging_filter,
78                         null,
79                         compileSort(self.sort)
80                     ).then(function() {
81                         return self.arrayNotifier(egSerialsCoreSvc.itemList, offset, count);
82                     });
83                 }
84             }
85         }
86     });
87
88     $scope.delete_items = function (items) {
89         var list = [];
90
91         angular.forEach(items, function (i) {
92             var obj = egSerialsCoreSvc.itemMap[i.id];
93             obj.isdeleted(1);
94             list.push(obj);
95         });
96
97         egConfirmDialog.open(
98             egCore.strings.CONFIRM_CHANGE_ITEMS.delete,
99             egCore.strings.CONFIRM_CHANGE_ITEMS_MESSAGE.delete,
100             {items : list.length}
101         ).result.then(function () {
102             return egCore.net.request(
103                 'open-ils.serial',
104                 'open-ils.serial.item.fleshed.batch.update',
105                 egCore.auth.token(),
106                 list
107             ).then( function(resp) {
108                 var evt = egCore.evt.parse(resp);
109                 if (evt) {
110                     ngToast.danger(egCore.strings.SERIALS_ISSUANCE_FAIL_SAVE);
111                 } else {
112                     ngToast.success(egCore.strings.SERIALS_ISSUANCE_SUCCESS_SAVE);
113                     return reload($scope.ssubId,_paging_filter);
114                 }
115             });
116         });
117     }
118
119     $scope.edit_issuance_holding_code = function (items) {
120         var promises = [];
121         var edits = [];
122         angular.forEach(items.reverse(), function (item) {
123             promises.push( egSerialsCoreSvc.new_holding_code({
124                     title    : egCore.strings.SERIALS_EDIT_SISS_HC,
125                     curr_iss : egCore.idl.fromHash('siss',item.issuance),
126                     label    : item.issuance.label,
127                     type     : item.issuance.type ? item.issuance.type : 'basic',
128                     can_change_adhoc : true
129                 }).then(function(result) {
130                     if (!result.adhoc) {
131                         item.issuance.holding_code = JSON.stringify(result.holding_code);
132                         item.issuance.holding_type = result.type;
133                     } else {
134                         item.issuance.label = result.label;
135                         item.issuance.holding_type = result.type;
136                     }
137
138                     item.issuance.date_published = result.date.toISOString();
139                     item.issuance.editor = egCore.auth.user();
140                     item.issuance.edit_date = 'now';
141
142                     var iss = egCore.idl.fromHash('siss',item.issuance);
143                     if (!result.adhoc) { // not an ad hoc issuance, get predicted label
144                         return egCore.net.request(
145                             'open-ils.serial',
146                             'open-ils.serial.make_prediction_values',
147                             egCore.auth.token(),
148                             { ssub_id : $scope.ssubId,
149                               num_to_predict : 0,
150                               include_base_issuance : 1,
151                               base_issuance : iss
152                             }
153                         ).then( function(resp) {
154                             var evt = egCore.evt.parse(resp);
155                             if (evt) {
156                                 ngToast.danger(egCore.strings.SERIALS_ISSUANCE_FAIL_SAVE);
157                             } else {
158                                 iss.label(resp[0].label);
159                                 edits.push(iss);
160                             }
161                         });
162                     }
163
164                     return $q.when(edits.push(iss));
165                 })
166             );
167         });
168         return $q.all(promises)
169             .finally(function() {
170                 if (edits.length) return update_issuances(edits);
171             });
172     }
173
174
175     function update_issuances (list) {
176         if (!angular.isArray(list)) list = [list];
177
178         return egCore.net.request(
179             'open-ils.serial',
180                 'open-ils.serial.issuance.fleshed.batch.update',
181                 egCore.auth.token(),
182                 list
183             ).then(
184                 function(resp) {
185                     var evt = egCore.evt.parse(resp);
186                     if (evt) {
187                         ngToast.danger(egCore.strings.SERIALS_ISSUANCE_FAIL_SAVE);
188                     } else {
189                         ngToast.success(egCore.strings.SERIALS_ISSUANCE_SUCCESS_SAVE);
190                         return reload($scope.ssubId,_paging_filter);
191                     }
192                 },
193                 function(resp) {
194                     ngToast.danger(egCore.strings.SERIALS_ISSUANCE_FAIL_SAVE);
195                 }
196             );
197     }
198
199
200     $scope.following_issuance = function (items) {
201         return egSerialsCoreSvc.new_holding_code({
202             title : egCore.strings.SERIALS_ISSUANCE_ADD,
203             prev_iss : egCore.idl.fromHash('siss',items[0].issuance),
204             can_change_adhoc : true
205         }).then(function(hc) {
206             if (hc.adhoc) {
207                 var new_iss = new egCore.idl.siss();
208                 new_iss.creator( egCore.auth.user().id() );
209                 new_iss.editor( egCore.auth.user().id() );
210                 new_iss.date_published( hc.date.toISOString() );
211                 new_iss.subscription( $scope.ssubId );
212                 new_iss.label( hc.label );
213                 new_iss.holding_type( hc.type );
214
215                 return egCore.pcrud.create(new_iss).then(function(issuance) {
216                     var new_item = new egCore.idl.sitem();
217                     new_item.creator( egCore.auth.user().id() );
218                     new_item.editor( egCore.auth.user().id() );
219                     new_item.issuance( issuance.id() );
220                     new_item.stream( items[0].stream.id );
221                     new_item.date_expected( hc.date.toISOString() ); // XXX do we have interval math?
222
223                     return egCore.pcrud.create(new_item).then(function() {
224                         ngToast.success(egCore.strings.SERIALS_ISSUANCE_SUCCESS_SAVE);
225                         return reload($scope.ssubId,_paging_filter);
226                     },function (error) {
227                         ngToast.danger(egCore.strings.SERIALS_ISSUANCE_FAIL_SAVE);
228                     });
229                 },function (error) {
230                     ngToast.danger(egCore.strings.SERIALS_ISSUANCE_FAIL_SAVE);
231                 });
232             }
233
234             return egCore.net.request(
235                 'open-ils.serial',
236                 'open-ils.serial.make_predictions',
237                 egCore.auth.token(),
238                 { ssub_id : $scope.ssubId,
239                   num_to_predict : 1,
240                   base_issuance : egCore.idl.fromHash('siss',items[0].issuance)
241                 }
242             ).then(
243                 function(resp) {
244                     var evt = egCore.evt.parse(resp);
245                     if (evt) {
246                         ngToast.danger(egCore.strings.SERIALS_ISSUANCE_FAIL_SAVE);
247                     } else {
248                         ngToast.success(egCore.strings.SERIALS_ISSUANCE_SUCCESS_SAVE);
249                         return reload($scope.ssubId,_paging_filter);
250                     }
251                 },
252                 function(resp) {
253                     ngToast.danger(egCore.strings.SERIALS_ISSUANCE_FAIL_SAVE);
254                 }
255             );
256         });
257     }
258
259     $scope.add_special_issuance = function() {
260         return egSerialsCoreSvc.new_holding_code({
261             title : egCore.strings.SERIALS_SPECIAL_ISSUANCE_ADD,
262             can_change_adhoc : false,
263             adhoc       : true
264         }).then(function(hc) {
265             // perforce add hoc
266             var new_iss = new egCore.idl.siss();
267             new_iss.creator( egCore.auth.user().id() );
268             new_iss.editor( egCore.auth.user().id() );
269             new_iss.date_published( hc.date.toISOString() );
270             new_iss.subscription( $scope.ssubId );
271             new_iss.label( hc.label );
272             new_iss.holding_type( hc.type );
273
274             return egCore.pcrud.create(new_iss).then(function(issuance) {
275                 var new_items = [];
276                 var sub = egSerialsCoreSvc.get_ssub($scope.ssubId);
277                 angular.forEach(sub.distributions(), function(dist) {
278                     angular.forEach(dist.streams(), function(stream) {
279                         var new_item = new egCore.idl.sitem();
280                         new_item.creator( egCore.auth.user().id() );
281                         new_item.editor( egCore.auth.user().id() );
282                         new_item.issuance( issuance.id() );
283                         new_item.stream( stream.id() );
284                         new_item.date_expected( hc.date.toISOString() ); // XXX do we have interval math?
285                         new_items.push(new_item);
286                     });
287                 });
288                 var promises = [];
289                 angular.forEach(new_items, function(item) {
290                     promises.push(egCore.pcrud.create(item));
291                 });
292
293                 $q.all(promises).then(function() {
294                     ngToast.success(egCore.strings.SERIALS_ISSUANCE_SUCCESS_SAVE);
295                     return reload($scope.ssubId,_paging_filter);
296                 },function (error) {
297                     ngToast.danger(egCore.strings.SERIALS_ISSUANCE_FAIL_SAVE);
298                 });
299             });
300         });
301     }
302
303     $scope.do_print_routing_lists = false;
304     egCore.hatch.getItem('eg.serials.items.do_print_routing_lists').then(function(val) {
305         $scope.do_print_routing_lists = val;
306     });
307
308     $scope.receive_and_barcode = false;
309     egCore.hatch.getItem('eg.serials.items.receive_and_barcode').then(function(val) {
310         $scope.receive_and_barcode = val;
311     });
312
313     $scope.checkbox_handler = function(item) {
314         $scope[item.checkbox] = item.checked;
315         egCore.hatch.setItem('eg.serials.items.'+item.checkbox, item.checked);
316     }
317
318     $scope.receive_next = function () {
319         var list = [];
320         var next_per_stream = {};
321         angular.forEach(egSerialsCoreSvc.itemTree, function (item) {
322             if (next_per_stream[item.stream().id()]) return;
323             if (item.status() == 'Expected') {
324                 next_per_stream[item.stream().id()] = item;
325                 list.push(egCore.idl.Clone(item));
326             }
327         });
328
329         return egSerialsCoreSvc.process_items('receive', $scope.bibId, list, $scope.receive_and_barcode, false, $scope.do_print_routing_lists, function(){reload($scope.ssubId,_paging_filter)});
330     }
331
332     $scope.receive_selected = function (list) {
333         var items = list.filter(function(i){
334             return i.status == 'Expected';
335         });
336         return egSerialsCoreSvc.process_items('receive', $scope.bibId, items.map(function(item) {
337             return egCore.idl.Clone(egSerialsCoreSvc.itemMap[item.id]);
338         }), $scope.receive_and_barcode, false, $scope.do_print_routing_lists, function(){reload($scope.ssubId,_paging_filter)});
339     }
340
341     $scope.reset_selected = function (list) {
342         return egSerialsCoreSvc.process_items('reset', $scope.bibId, list.map(function(item) {
343             return egCore.idl.Clone(egSerialsCoreSvc.itemMap[item.id]);
344         }), false, false, false, function(){reload($scope.ssubId,_paging_filter)});
345     }
346
347     $scope.bind_selected = function (list) {
348         return egSerialsCoreSvc.process_items('bind', $scope.bibId, list.map(function(item) {
349             return egCore.idl.Clone(egSerialsCoreSvc.itemMap[item.id]);
350         }), true, true, $scope.do_print_routing_lists, function(){reload($scope.ssubId,_paging_filter)});
351     }
352
353     $scope.set_selected_as_claimed = function(list) {
354         return egSerialsCoreSvc.set_item_status('Claimed', $scope.bibId, list.map(function(item) {
355             return egCore.idl.Clone(egSerialsCoreSvc.itemMap[item.id]);
356         }), function(){reload($scope.ssubId,_paging_filter)});
357     }
358     $scope.set_selected_as_discarded = function(list) {
359         return egSerialsCoreSvc.set_item_status('Discarded', $scope.bibId, list.map(function(item) {
360             return egCore.idl.Clone(egSerialsCoreSvc.itemMap[item.id]);
361         }), function(){reload($scope.ssubId,_paging_filter)});
362     }
363     $scope.set_selected_as_not_published = function(list) {
364         return egSerialsCoreSvc.set_item_status('Not Published', $scope.bibId, list.map(function(item) {
365             return egCore.idl.Clone(egSerialsCoreSvc.itemMap[item.id]);
366         }), function(){reload($scope.ssubId,_paging_filter)});
367     }
368     $scope.set_selected_as_not_held = function(list) {
369         return egSerialsCoreSvc.set_item_status('Not Held', $scope.bibId, list.map(function(item) {
370             return egCore.idl.Clone(egSerialsCoreSvc.itemMap[item.id]);
371         }), function(){reload($scope.ssubId,_paging_filter)});
372     }
373
374     $scope.menu_print_routing_lists = function (items) {
375         items = items.map(function(item) {
376             return egCore.idl.Clone(egSerialsCoreSvc.itemMap[item.id]);
377         });
378         return egSerialsCoreSvc.print_routing_lists($scope.bibId, items, false, true, $scope.do_print_routing_lists);
379     }
380
381     $scope.add_issuances = function () {
382         egSerialsCoreSvc.add_issuances($scope.ssubId).then(function() {
383             return reload($scope.ssubId,_paging_filter);
384         });
385     }
386
387     $scope.need_one_selected = function() {
388         var items = $scope.itemGridControls.selectedItems();
389         if (items.length == 1) return false;
390         return true;
391     };
392
393     $scope.need_many_selected = function() {
394         var items = $scope.itemGridControls.selectedItems();
395         if (items.length > 1) return false;
396         return true;
397     };
398
399     $scope.need_expected = function() {
400         var items = $scope.itemGridControls.selectedItems().filter(function(i){
401             return i.status == 'Expected';
402         });
403         if (items.length) return false;
404         return true;
405     };
406
407     $scope.item_notes = function(rows) {
408         return $scope.notes('item',rows);
409     }
410     // TODO - refactor this, it's duplicated in subscription_manager.js
411     $scope.notes = function(note_type,rows) {
412         if (!rows) { return; }
413
414         function modal(existing_notes) {
415             $uibModal.open({
416                 templateUrl: './serials/t_notes',
417                 animation: true,
418                 controller: 'NotesCtrl',
419                 resolve : {
420                     note_type : function() { return note_type; },
421                     rows : function() {
422                         return rows;
423                     },
424                     notes : function() {
425                         return existing_notes;
426                     }
427                 },
428                 windowClass: 'app-modal-window',
429                 backdrop: 'static',
430                 keyboard: false
431             }).result.then(function(notes) {
432                 egCore.pcrud.apply(notes).then(
433                     function(a) { ngToast.success(egCore.strings.SERIALS_ITEM_NOTE_SUCCESS_SAVE) },
434                     function(a) { ngToast.danger(egCore.strings.SERIALS_ITEM_NOTE_FAIL_SAVE) }
435                 );
436             });
437         }
438
439         if (rows.length == 1) {
440             var fm_hint;
441             var search_hash = {};
442             var search_opt = {};
443             switch(note_type) {
444                 case 'subscription':
445                     fm_hint = 'ssubn';
446                     search_hash.subscription = rows[0]['id'];
447                     search_opt.order_by = { ssubn : 'create_date' };
448                 break;
449                 case 'distribution':
450                     fm_hint = 'sdistn';
451                     search_hash.distribution = rows[0]['sdist.id'];
452                     search_opt.order_by = { sdistn : 'create_date' };
453                 break;
454                 case 'item': default:
455                     fm_hint = 'sin';
456                     search_hash.item = rows[0]['id'];
457                     search_opt.order_by = { sin : 'create_date' };
458                 break;
459             }
460             egCore.pcrud.search(fm_hint, search_hash, search_opt,
461                 { atomic : true }
462             ).then(function(list) {
463                 modal(list);
464             });
465         } else {
466                 // support batch creation of notes across selections,
467                 // but not editing
468                 modal([]);
469         }
470     }
471
472 }]
473
474     }
475 })
476
477 // TODO - refactor this; it's duplicated in subscription_manager.js
478 .controller('NotesCtrl',
479        ['$scope','$uibModalInstance','egCore','note_type','rows','notes',
480 function($scope , $uibModalInstance , egCore , note_type , rows , notes ) {
481     $scope.note_type = note_type;
482     $scope.focusNote = true;
483     $scope.note = {
484         creator : egCore.auth.user().id(),
485         title   : '',
486         value   : '',
487         pub     : false,
488         'alert' : false,
489     };
490
491     $scope.note_list = notes;
492
493     $scope.ok = function(note) {
494
495         var return_notes = [];
496         if (note.initials) note.value += ' [' + note.initials + ']';
497         if (   (typeof note.title != 'undefined' && note.title != '')
498             || (typeof note.value != 'undefined' && note.value != '')) {
499             angular.forEach(rows, function (r) {
500                 var n;
501                 switch(note_type) {
502                     case 'subscription':
503                         n = new egCore.idl.ssubn();
504                         n.subscription(r['id']);
505                         break;
506                     case 'distribution':
507                         n = new egCore.idl.sdistn();
508                         n.distribution(r['sdist.id']);
509                         break;
510                     case 'item':
511                     default:
512                         n = new egCore.idl.sin();
513                         n.item(r['id']);
514                 }
515                 n.isnew(true);
516                 n.creator(note.creator);
517                 n.pub(note.pub);
518                 n['alert'](note['alert']);
519                 n.title(note.title);
520                 n.value(note.value);
521                 return_notes.push( n );
522             });
523         }
524         angular.forEach(notes, function(n) {
525             if (n.ischanged() || n.isdeleted()) {
526                 return_notes.push( n );
527             }
528         });
529         $uibModalInstance.close(return_notes);
530     }
531
532     $scope.cancel = function($event) {
533         $uibModalInstance.dismiss();
534         $event.preventDefault();
535     }
536 }])