]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/circ/patron/checkout.js
LP#1356477: add quick receipt button
[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','$routeParams','egCore','egUser','patronSvc',
8         'egGridDataProvider','$location','$timeout','egCirc','ngToast',
9
10 function($scope , $q , $routeParams , egCore , egUser , patronSvc , 
11          egGridDataProvider , $location , $timeout , egCirc , ngToast) {
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     function setting_value (user, setting) {
38         if (user) {
39             var list = user.settings().filter(function(s){
40                 return s.name() == setting;
41             });
42
43             if (list.length) return list[0].value();
44         }
45     }
46
47     $scope.has_email_address = function() {
48         return (
49             patronSvc.current &&
50             patronSvc.current.email() &&
51             patronSvc.current.email().match(/.*@.*/).length
52         );
53     }
54
55     $scope.may_email_receipt = function() {
56         return (
57             $scope.has_email_address() &&
58             setting_value(
59                 patronSvc.current,
60                 'circ.send_email_checkout_receipts'
61             ) == 'true'
62         );
63     }
64
65     $scope.using_hatch = egCore.hatch.usingHatch();
66
67     egCore.hatch.getItem('circ.checkout.strict_barcode')
68         .then(function(sb){ $scope.strict_barcode = sb });
69
70     // avoid multiple, in-flight attempts on the same barcode
71     var pending_barcodes = {};
72
73     var printOnComplete = true;
74     egCore.org.settings([
75         'circ.staff_client.do_not_auto_attempt_print'
76     ]).then(function(settings) { 
77         printOnComplete = !Boolean(
78             settings['circ.staff_client.do_not_auto_attempt_print']);
79     });
80
81     egCirc.get_noncat_types().then(function(list) {
82         $scope.nonCatTypes = list;
83     });
84
85     $scope.selectedNcType = function() {
86         if (!egCore.env.cnct) return null; // too soon
87         var type = egCore.env.cnct.map[$scope.checkoutArgs.noncat_type];
88         return type ? type.name() : null;
89     }
90
91     $scope.checkout = function(args) {
92         var params = angular.copy(args);
93         params.patron_id = patronSvc.current.id();
94
95         if (args.sticky_date) {
96             params.due_date = args.due_date.toISOString();
97         } else {
98             delete params.due_date;
99         }
100         delete params.sticky_date;
101
102         if (params.noncat_type == 'barcode') {
103             if (!args.copy_barcode) return;
104
105             args.copy_barcode = ''; // reset UI input
106             params.noncat_type = ''; // "barcode"
107
108             if (pending_barcodes[params.copy_barcode]) {
109                 console.log(
110                     "Skipping checkout of redundant barcode " 
111                     + params.copy_barcode
112                 );
113                 return;
114             }
115
116             pending_barcodes[params.copy_barcode] = true;
117             send_checkout(params);
118
119         } else {
120             egCirc.noncat_dialog(params).then(function() {
121                 send_checkout(params)
122             });
123         }
124
125         $scope.focusMe = true; // return focus to barcode input
126     }
127
128     function send_checkout(params) {
129
130         params.noncat_type = params.noncat ? params.noncat_type : '';
131
132         // populate the grid row before we send the request so that the
133         // order of actions is maintained and so the user gets an 
134         // immediate reaction to their barcode input action.
135         var row_item = {
136             index : $scope.checkouts.length,
137             copy_barcode : params.copy_barcode,
138             noncat_type : params.noncat_type
139         };
140
141         $scope.checkouts.unshift(row_item);
142         $scope.gridDataProvider.refresh();
143
144         egCore.hatch.setItem('circ.checkout.strict_barcode', $scope.strict_barcode);
145         var options = {check_barcode : $scope.strict_barcode};
146
147         egCirc.checkout(params, options).then(
148             function(co_resp) {
149                 // update stats locally so we don't have to fetch them w/
150                 // each checkout.
151                 patronSvc.patron_stats.checkouts.out++;
152                 patronSvc.patron_stats.checkouts.total_out++;
153
154                 // copy the response event into the original grid row item
155                 // note: angular.copy clobbers the destination
156                 row_item.evt = co_resp.evt;
157                 angular.forEach(co_resp.data, function(val, key) {
158                     row_item[key] = val;
159                 });
160                 munge_checkout_resp(co_resp, row_item);
161             },
162             function() {
163                 // Circ was rejected somewhere along the way.
164                 // Remove the copy from the grid since there was no action.
165                 // note: since checkouts are unshifted onto the array, the
166                 // index value does not (generally) match the array position.
167                 var pos = -1;
168                 angular.forEach($scope.checkouts, function(co, idx) {
169                     if (co.index == row_item.index) pos = idx;
170                 });
171                 $scope.checkouts.splice(pos, 1);
172                 $scope.gridDataProvider.refresh();
173             }
174
175         ).finally(function() {
176
177             // regardless of the outcome of the circ, remove the 
178             // barcode from the pending list.
179             if (params.copy_barcode)
180                 delete pending_barcodes[params.copy_barcode];
181
182             $scope.focusMe = true; // return focus to barcode input
183         });
184     }
185
186     // add some checkout-specific additions for display
187     function munge_checkout_resp(co_resp, row_item) {
188         var params = co_resp.params;
189         if (params.noncat) {
190             row_item.title = egCore.env.cnct.map[params.noncat_type].name();
191             row_item.noncat_count = params.noncat_count;
192             row_item.circ = new egCore.idl.circ();
193             row_item.circ.due_date(co_resp.evt.payload.noncat_circ.duedate());
194         }
195     }
196
197     $scope.print_receipt = function() {
198         var print_data = {circulations : []}
199
200         if ($scope.checkouts.length == 0) return $q.when();
201
202         angular.forEach($scope.checkouts, function(co) {
203             if (co.circ) {
204                 print_data.circulations.push({
205                     circ : egCore.idl.toHash(co.circ),
206                     copy : egCore.idl.toHash(co.acp),
207                     call_number : egCore.idl.toHash(co.acn),
208                     title : co.title,
209                     author : co.author
210                 })
211             };
212         });
213
214         return egCore.print.print({
215             context : 'default', 
216             template : 'checkout', 
217             scope : print_data,
218             show_dialog : $scope.show_print_dialog
219         });
220     }
221
222     $scope.email_receipt = function() {
223         if ($scope.has_email_address() && $scope.checkouts.length) {
224             return egCore.net.request(
225                 'open-ils.circ',
226                 'open-ils.circ.checkout.batch_notify.session.atomic',
227                 egCore.auth.token(),
228                 patronSvc.current.id(),
229                 $scope.checkouts.map(function (c) { return c.circ.id() })
230             ).then(function() {
231                 ngToast.create(egCore.strings.EMAILED_CHECKOUT_RECEIPT);
232                 return $q.when();
233             });
234         }
235         return $q.when();
236     }
237
238     $scope.print_or_email_receipt = function() {
239         if ($scope.may_email_receipt()) return $scope.email_receipt();
240         $scope.print_receipt();
241     }
242
243     // set of functions to issue a receipt (if desired), then
244     // redirect
245     $scope.done_auto_receipt = function() {
246         if ($scope.may_email_receipt()) {
247             $scope.email_receipt().then(function() {
248                 $scope.done_redirect();
249             });
250         } else {
251             if (printOnComplete) {
252
253                 $scope.print_receipt().then(function() {
254                     $scope.done_redirect();
255                 });
256
257             } else {
258                 $scope.done_redirect();
259             }
260         }
261     }
262     $scope.done_print_receipt = function() {
263         $scope.print_receipt().then( function () {
264             $scope.done_redirect();
265         });
266     }
267     $scope.done_email_receipt = function() {
268         $scope.email_receipt().then( function () {
269             $scope.done_redirect();
270         });
271     }
272     $scope.done_no_receipt = function() {
273         $scope.done_redirect();
274     }
275
276     // Redirect the user to the barcode entry page to load a new patron.
277     $scope.done_redirect = function() {
278         $location.path('/circ/patron/bcsearch');
279     }
280 }])
281