]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/circ/patron/checkout.js
9806d8f04b87f65e7dc8a3219792f5c8a53d4215
[Evergreen.git] / Open-ILS / web / js / ui / default / staff / circ / patron / checkout.js
1 /**
2  * Checkout items to patrons
3  */
4
5 angular.module('egPatronApp').controller('PatronCheckoutCtrl',
6
7        ['$scope','$q','$modal','$routeParams','egCore','egUser','patronSvc',
8         'egGridDataProvider','$location','$timeout','egCirc',
9
10 function($scope , $q , $modal , $routeParams , egCore , egUser , patronSvc , 
11          egGridDataProvider , $location , $timeout , egCirc) {
12
13     $scope.initTab('checkout', $routeParams.id).finally(function(){
14         $scope.focusMe = true;
15     });
16     $scope.checkouts = patronSvc.checkouts;
17     $scope.checkoutArgs = {
18         noncat_type : 'barcode',
19         due_date : new Date()
20     };
21
22     $scope.gridDataProvider = egGridDataProvider.instance({
23         get : function(offset, count) {
24             return this.arrayNotifier($scope.checkouts, offset, count);
25         }
26     });
27
28     $scope.disable_checkout = function() {
29         return (
30             !patronSvc.current ||
31             patronSvc.current.active() == 'f' ||
32             patronSvc.current.deleted() == 't' ||
33             patronSvc.current.card().active() == 'f'
34         );
35     }
36
37     $scope.using_hatch = egCore.hatch.usingHatch();
38
39     egCore.hatch.getItem('circ.checkout.strict_barcode')
40         .then(function(sb){ $scope.strict_barcode = sb });
41
42     // avoid multiple, in-flight attempts on the same barcode
43     var pending_barcodes = {};
44
45     var printOnComplete = true;
46     egCore.org.settings([
47         'circ.staff_client.do_not_auto_attempt_print'
48     ]).then(function(settings) { 
49         printOnComplete = !Boolean(
50             settings['circ.staff_client.do_not_auto_attempt_print']);
51     });
52
53     egCirc.get_noncat_types().then(function(list) {
54         $scope.nonCatTypes = list;
55     });
56
57     $scope.selectedNcType = function() {
58         if (!egCore.env.cnct) return null; // too soon
59         var type = egCore.env.cnct.map[$scope.checkoutArgs.noncat_type];
60         return type ? type.name() : null;
61     }
62
63     $scope.checkout = function(args) {
64         var params = angular.copy(args);
65         params.patron_id = patronSvc.current.id();
66
67         if (args.sticky_date) {
68             params.due_date = args.due_date.toISOString();
69         } else {
70             delete params.due_date;
71         }
72         delete params.sticky_date;
73
74         if (params.noncat_type == 'barcode') {
75             if (!args.copy_barcode) return;
76
77             args.copy_barcode = ''; // reset UI input
78             params.noncat_type = ''; // "barcode"
79
80             if (pending_barcodes[params.copy_barcode]) {
81                 console.log(
82                     "Skipping checkout of redundant barcode " 
83                     + params.copy_barcode
84                 );
85                 return;
86             }
87
88             pending_barcodes[params.copy_barcode] = true;
89             send_checkout(params);
90
91         } else {
92             egCirc.noncat_dialog(params).then(function() {
93                 send_checkout(params)
94             });
95         }
96
97         $scope.focusMe = true; // return focus to barcode input
98     }
99
100     function send_checkout(params) {
101
102         params.noncat_type = params.noncat ? params.noncat_type : '';
103
104         // populate the grid row before we send the request so that the
105         // order of actions is maintained and so the user gets an 
106         // immediate reaction to their barcode input action.
107         var row_item = {
108             index : $scope.checkouts.length,
109             copy_barcode : params.copy_barcode,
110             noncat_type : params.noncat_type
111         };
112
113         $scope.checkouts.unshift(row_item);
114         $scope.gridDataProvider.refresh();
115
116         egCore.hatch.setItem('circ.checkout.strict_barcode', $scope.strict_barcode);
117         var options = {check_barcode : $scope.strict_barcode};
118
119         egCirc.checkout(params, options).then(
120             function(co_resp) {
121                 // update stats locally so we don't have to fetch them w/
122                 // each checkout.
123                 patronSvc.patron_stats.checkouts.out++;
124                 patronSvc.patron_stats.checkouts.total_out++;
125
126                 // copy the response event into the original grid row item
127                 // note: angular.copy clobbers the destination
128                 row_item.evt = co_resp.evt;
129                 angular.forEach(co_resp.data, function(val, key) {
130                     row_item[key] = val;
131                 });
132                 munge_checkout_resp(co_resp, row_item);
133             },
134             function() {
135                 // Circ was rejected somewhere along the way.
136                 // Remove the copy from the grid since there was no action.
137                 // note: since checkouts are unshifted onto the array, the
138                 // index value does not (generally) match the array position.
139                 var pos = -1;
140                 angular.forEach($scope.checkouts, function(co, idx) {
141                     if (co.index == row_item.index) pos = idx;
142                 });
143                 $scope.checkouts.splice(pos, 1);
144                 $scope.gridDataProvider.refresh();
145             }
146
147         ).finally(function() {
148
149             // regardless of the outcome of the circ, remove the 
150             // barcode from the pending list.
151             if (params.copy_barcode)
152                 delete pending_barcodes[params.copy_barcode];
153
154             $scope.focusMe = true; // return focus to barcode input
155         });
156     }
157
158     // add some checkout-specific additions for display
159     function munge_checkout_resp(co_resp, row_item) {
160         var params = co_resp.params;
161         if (params.noncat) {
162             row_item.title = egCore.env.cnct.map[params.noncat_type].name();
163             row_item.noncat_count = params.noncat_count;
164             row_item.circ = new egCore.idl.circ();
165             row_item.circ.due_date(co_resp.evt.payload.noncat_circ.duedate());
166         }
167     }
168
169     $scope.print_receipt = function() {
170         var print_data = {circulations : []}
171
172         if ($scope.checkouts.length == 0) return $q.when();
173
174         angular.forEach($scope.checkouts, function(co) {
175             if (co.circ) {
176                 print_data.circulations.push({
177                     circ : egCore.idl.toHash(co.circ),
178                     copy : egCore.idl.toHash(co.acp),
179                     call_number : egCore.idl.toHash(co.acn),
180                     title : co.title,
181                     author : co.author
182                 })
183             };
184         });
185
186         return egCore.print.print({
187             context : 'default', 
188             template : 'checkout', 
189             scope : print_data,
190             show_dialog : $scope.show_print_dialog
191         });
192     }
193
194     // Redirect the user to the barcode entry page to load a new patron.
195     // If configured to do so, print the receipt first
196     $scope.done = function() {
197         if (printOnComplete) {
198
199             $scope.print_receipt().then(function() {
200                 $location.path('/circ/patron/bcsearch');
201             });
202
203         } else {
204             $location.path('/circ/patron/bcsearch');
205         }
206     }
207 }])
208