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