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