]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/circ/checkin/app.js
965529b4c50cbd7c7ee0f076fff46ade528d0b7b
[working/Evergreen.git] / Open-ILS / web / js / ui / default / staff / circ / checkin / app.js
1 angular.module('egCheckinApp', ['ngRoute', 'ui.bootstrap', 
2     'egCoreMod', 'egUiMod', 'egGridMod', 'egUserMod'])
3
4 .config(function($routeProvider, $locationProvider, $compileProvider) {
5     $locationProvider.html5Mode(true);
6     $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|mailto|blob):/); // grid export
7         
8     var resolver = {delay : 
9         ['egStartup', function(egStartup) {return egStartup.go()}]}
10
11     $routeProvider.when('/circ/checkin/checkin', {
12         templateUrl: './circ/checkin/t_checkin',
13         controller: 'CheckinCtrl',
14         resolve : resolver
15     });
16
17     $routeProvider.when('/circ/checkin/capture', {
18         templateUrl: './circ/checkin/t_checkin',
19         controller: 'CheckinCtrl',
20         resolve : resolver
21     });
22
23     $routeProvider.otherwise({redirectTo : '/circ/checkin/checkin'});
24 })
25
26 .factory('checkinSvc', [function() {
27     var service = {};
28     service.checkins = [];
29     return service;
30 }])
31
32
33 /**
34  * Manages checkin
35  */
36 .controller('CheckinCtrl',
37        ['$scope','$q','$window','$location', '$timeout','egCore','checkinSvc','egGridDataProvider','egCirc', 'egItem',
38 function($scope , $q , $window , $location , $timeout , egCore , checkinSvc , egGridDataProvider , egCirc, itemSvc)  {
39
40     $scope.focusMe = true;
41     $scope.checkins = checkinSvc.checkins;
42     var today = new Date();
43     $scope.checkinArgs = {backdate : today}
44     $scope.using_hatch_printer = egCore.hatch.usePrinting();
45     $scope.modifiers = {};
46     $scope.fine_total = 0;
47     $scope.is_capture = $location.path().match(/capture$/);
48     var suppress_popups = false;
49     $scope.grid_persist_key = $scope.is_capture ? 
50         'circ.checkin.capture' : 'circ.checkin.checkin';
51
52     // TODO: add this to the setting batch lookup below
53     egCore.hatch.getItem('circ.checkin.strict_barcode')
54         .then(function(sb){ $scope.strict_barcode = sb });
55
56     egCore.org.settings([
57         'ui.circ.suppress_checkin_popups' // add other settings as needed
58     ]).then(function(set) {
59         suppress_popups = set['ui.circ.suppress_checkin_popups'];
60     });
61
62     $scope.sort_money = function (a,b) {
63         var ma = parseFloat(a);
64         var mb = parseFloat(b);
65         if (ma < mb) return -1;
66         if (ma > mb) return 1;
67         return 0
68     }
69
70     // checkin & hold capture modifiers
71     var modifiers = [
72         'void_overdues', 
73         'clear_expired',
74         'hold_as_transit',
75         'manual_float',
76         'no_precat_alert',
77         'retarget_holds',
78         'retarget_holds_all'
79     ];
80
81     if ($scope.is_capture) {
82         // in hold capture mode, some values are forced, regardless
83         // of stored preferences.
84         $scope.modifiers.noop = false;
85         $scope.modifiers.auto_print_holds_transits = true;
86     } else {
87         modifiers.push('noop'); // AKA suppress holds and transits
88         modifiers.push('auto_print_holds_transits');
89         modifiers.push('do_inventory_update');
90     }
91
92     // set modifiers from stored preferences
93     var snames = modifiers.map(function(m) {return 'eg.circ.checkin.' + m;});
94     egCore.hatch.getItemBatch(snames).then(function(settings) {
95         angular.forEach(settings, function(val, key) {
96             if (val === true) {
97                 var parts = key.split('.')
98                 var mod = parts.pop();
99                 $scope.modifiers[mod] = true;
100             }
101         })
102     });
103
104     // set / unset a checkin modifier
105     // when set, store the preference
106     $scope.toggle_mod = function(mod) {
107         if ($scope.modifiers[mod]) {
108             $scope.modifiers[mod] = false;
109             egCore.hatch.removeItem('eg.circ.checkin.' + mod);
110         } else {
111             $scope.modifiers[mod] = true;
112             egCore.hatch.setItem('eg.circ.checkin.' + mod, true);
113         }
114     }
115
116
117     // ensure the backdate is not in the future
118     // note: input type=date max=foo not yet supported anywhere
119     $scope.$watch('checkinArgs.backdate', function(newval) {
120         if (newval && newval > today) 
121             $scope.checkinArgs.backdate = today;
122     });
123
124     $scope.is_backdate = function() {
125         return $scope.checkinArgs.backdate < today;
126     }
127
128     var checkinGrid = $scope.gridControls = {};
129
130     $scope.gridDataProvider = egGridDataProvider.instance({
131         get : function(offset, count) {
132             return this.arrayNotifier($scope.checkins, offset, count);
133         }
134     });
135
136     // turns the various inputs (form args, modifiers, etc.) into
137     // checkin params and options.
138     function compile_checkin_args(args) {
139         var params = angular.copy(args);
140
141         // a backdate of 'today' is not really a backdate
142         // (and this particularly matters when checking in hourly
143         // loans, as backdated checkins currently get the time
144         // portion of the checkin time from the due date; this will
145         // stop mattering when FIXME bug 1793817 is dealt with)
146         if (!$scope.is_backdate())
147             delete params.backdate;
148
149         if (params.backdate) {
150             params.backdate = 
151                 params.backdate.toISOString().replace(/T.*/,'');
152         }
153
154         angular.forEach(['noop','void_overdues',
155                 'clear_expired','hold_as_transit','manual_float'],
156             function(opt) {
157                 if ($scope.modifiers[opt]) params[opt] = true;
158             }
159         );
160
161         if ($scope.modifiers.retarget_holds) {
162             if ($scope.modifiers.retarget_holds_all) {
163                 params.retarget_mode = 'retarget.all';
164             } else {
165                 params.retarget_mode = 'retarget';
166             }
167         }
168         if ($scope.modifiers.do_inventory_update) params.do_inventory_update = true;
169
170         egCore.hatch.setItem('circ.checkin.strict_barcode', $scope.strict_barcode);
171         egCore.hatch.setItem('circ.checkin.do_inventory_update', $scope.modifiers.do_inventory_update);
172         var options = {
173             check_barcode : $scope.strict_barcode,
174             no_precat_alert : $scope.modifiers.no_precat_alert,
175             auto_print_holds_transits : 
176                 $scope.modifiers.auto_print_holds_transits,
177             suppress_popups : suppress_popups,
178             do_inventory_update : $scope.modifiers.do_inventory_update
179         };
180
181         return {params : params, options: options};
182     }
183
184     $scope.checkin = function(args) {
185
186         var compiled = compile_checkin_args(args);
187         args.copy_barcode = ''; // reset UI for next scan
188         $scope.focusMe = true;
189         delete $scope.alert;
190         delete $scope.billable_amount;
191         delete $scope.billable_barcode;
192         delete $scope.billable_user_id;
193
194         var params = compiled.params;
195         var options = compiled.options;
196
197         if (!params.copy_barcode) return;
198         delete $scope.alert;
199
200         var row_item = {
201             index : checkinSvc.checkins.length,
202             input_barcode : params.copy_barcode
203         };
204
205         // track the item in the grid before sending the request
206         checkinSvc.checkins.unshift(row_item);
207         egCirc.checkin(params, options).then(
208         function(final_resp) {
209             
210             row_item.evt = final_resp.evt;
211             angular.forEach(final_resp.data, function(val, key) {
212                 row_item[key] = val;
213             });
214             
215             row_item['copy_barcode'] = row_item.acp.barcode();
216
217             if (row_item.acp.latest_inventory() && row_item.acp.latest_inventory().inventory_date() == "now")
218                 row_item.acp.latest_inventory().inventory_date(Date.now());
219
220             if (row_item.mbts) {
221                 var amt = Number(row_item.mbts.balance_owed());
222                 if (amt != 0) {
223                     $scope.billable_barcode = row_item.copy_barcode;
224                     $scope.billable_amount = amt;
225                     $scope.billable_user_id = row_item.circ.usr();
226                     $scope.fine_total = 
227                         ($scope.fine_total * 100 + amt * 100) / 100;
228                 }
229             }
230
231             if (final_resp.evt.textcode == 'NO_CHANGE') {
232                 $scope.alert = 
233                     {already_checked_in : final_resp.evt.copy_barcode};
234             }
235
236             if ($scope.trim_list && checkinSvc.checkins.length > 20) {
237                 //cut array short at 20 items
238                 checkinSvc.checkins.length = 20;
239                 checkinGrid.prepend(20);
240             } else {
241                 checkinGrid.prepend();
242             }
243         },
244         function() {
245             // Checkin was rejected somewhere along the way.
246             // Remove the copy from the grid since there was no action.
247             // note: since checkins are unshifted onto the array, the
248             // index value does not (generally) match the array position.
249             var pos = -1;
250             angular.forEach(checkinSvc.checkins, function(ci, idx) {
251                 if (ci.index == row_item.index) pos = idx;
252             });
253             checkinSvc.checkins.splice(pos, 1);
254
255         })['finally'](function() {
256             // when all is said and done, refocus
257             $scope.focusMe = true;
258         });
259     }
260
261     $scope.print_receipt = function() {
262         var print_data = {checkins : []}
263
264         if (checkinSvc.checkins.length == 0) return $q.when();
265
266         angular.forEach(checkinSvc.checkins, function(checkin) {
267
268             var checkin = {
269                 copy : egCore.idl.toHash(checkin.acp) || {},
270                 call_number : egCore.idl.toHash(checkin.acn) || {},
271                 copy_barcode : checkin.copy_barcode,
272                 title : checkin.title,
273                 author : checkin.author
274             }
275
276             print_data.checkins.push(checkin);
277         });
278
279         return egCore.print.print({
280             template : 'checkin', 
281             scope : print_data,
282             show_dialog : $scope.show_print_dialog
283         });
284     }
285
286
287     // --- context menu actions
288     //
289     $scope.fetchLastCircPatron = function(items) {
290         var checkin = items[0];
291         if (!checkin || !checkin.acp) return;
292
293         egCirc.last_copy_circ(checkin.acp.id())
294         .then(function(circ) {
295
296             if (circ) {
297                 // jump to the patron UI (separate app)
298                 $window.location.href = $location
299                     .path('/circ/patron/' + circ.usr() + '/checkout')
300                     .absUrl();
301                 return;
302             }
303
304             $scope.alert = {item_never_circed : checkin.acp.barcode()};
305         });
306     }
307
308     $scope.showBackdateDialog = function(items) {
309         var circ_ids = [];
310
311         angular.forEach(items, function(item) {
312             if (item.circ) circ_ids.push(item.circ.id());
313         });
314
315         if (circ_ids.length) {
316             egCirc.backdate_dialog(circ_ids).then(function(result) {
317                 angular.forEach(items, function(item) {
318                     item.circ.checkin_time(result.backdate);
319                 })
320             });
321             // TODO: support grid row styling
322             checkinGrid.refresh();
323         }
324     }
325
326     $scope.showMarkDamaged = function(items) {
327         var copy_ids = [];
328         angular.forEach(items, function(item) {
329             if (item.acp) {
330                 egCirc.mark_damaged({
331                     id: item.acp.id(),
332                     barcode: item.acp.barcode()
333                 })
334
335             }
336         });
337
338     }
339
340     $scope.abortTransit = function(items) {
341         var transit_ids = [];
342         angular.forEach(items, function(item) {
343             if (item.transit) transit_ids.push(item.transit.id());
344         });
345
346         egCirc.abort_transits(transit_ids).then(function() {
347             // update grid items?
348         });
349     }
350
351     $scope.add_copies_to_bucket = function(items){
352         var itemsIds = [];
353         angular.forEach(items, function(cp){
354             itemsIds.push(cp.acp.id());
355         });
356
357         itemSvc.add_copies_to_bucket(itemsIds);
358     }
359
360     $scope.showBibHolds = function(items){
361         var recordIds = [];
362         angular.forEach(items, function(i){
363             recordIds.push(i.acn.record());
364         });
365         angular.forEach(recordIds, function (r) {
366             var url = egCore.env.basePath + 'cat/catalog/record/' + r + '/holds';
367             $timeout(function() { $window.open(url, '_blank') });
368         });
369     }
370
371     $scope.showLastCircs = function(items){
372         var itemIds = [];
373         angular.forEach(items, function(cp){
374             itemIds.push(cp.acp.id());
375         });
376         angular.forEach(itemIds, function (id) {
377             var url = egCore.env.basePath + 'cat/item/' + id + '/circs';
378             $timeout(function() { $window.open(url, '_blank') });
379         });
380     }
381
382     $scope.selectedHoldingsVolCopyEdit = function (items) {
383         var itemObjs = [];
384         angular.forEach(items, function(i){
385             var h = egCore.idl.toHash(i);
386             itemObjs.push({
387                 'call_number.record.id': h.record.doc_id,
388                 'id' : h.acp.id
389             });
390         });
391         itemSvc.spawnHoldingsEdit(itemObjs,false,false);
392     }
393
394     $scope.show_mark_missing_pieces = function(items){
395         angular.forEach(items, function(i){
396             i.acp.call_number(i.acn);
397             i.acp.call_number().record(i.record);
398             itemSvc.mark_missing_pieces(i.acp,$scope);
399         });
400     }
401
402     $scope.printSpineLabels = function(items){
403         var copy_ids = [];
404         angular.forEach(items, function(item) {
405             if (item.acp) copy_ids.push(item.acp.id());
406         });
407         itemSvc.print_spine_labels(copy_ids);
408     }
409
410     $scope.addCopyAlerts = function(items) {
411         var copy_ids = [];
412         angular.forEach(items, function(item) {
413             if (item.acp) copy_ids.push(item.acp.id());
414         });
415         egCirc.add_copy_alerts(copy_ids).then(function() {
416             // update grid items?
417         });
418     }
419
420     $scope.manageCopyAlerts = function(items) {
421         var copy_ids = [];
422         angular.forEach(items, function(item) {
423             if (item.acp) copy_ids.push(item.acp.id());
424         });
425         egCirc.manage_copy_alerts(copy_ids).then(function() {
426             // update grid items?
427         });
428     }
429
430 }])
431