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