]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js
4ac16d30c561a908d0e9be6116b2b5f1761e1b0b
[Evergreen.git] / Open-ILS / web / js / ui / default / circ / selfcheck / selfcheck.js
1 dojo.require('dojo.date.locale');
2 dojo.require('dojo.date.stamp');
3 dojo.require('openils.CGI');
4 dojo.require('openils.Util');
5 dojo.require('openils.User');
6 dojo.require('openils.Event');
7
8 dojo.requireLocalization('openils.circ', 'selfcheck');
9 var localeStrings = dojo.i18n.getLocalization('openils.circ', 'selfcheck');
10
11
12 const SET_BARCODE_REGEX = 'opac.barcode_regex';
13 const SET_PATRON_TIMEOUT = 'circ.selfcheck.patron_login_timeout';
14 const SET_ALERT_ON_CHECKOUT_EVENT = 'circ.selfcheck.alert_on_checkout_event';
15 const SET_AUTO_OVERRIDE_EVENTS = 'circ.selfcheck.auto_override_checkout_events';
16 const SET_PATRON_PASSWORD_REQUIRED = 'circ.selfcheck.patron_password_required';
17
18 function SelfCheckManager() {
19
20     this.cgi = new openils.CGI();
21     this.staff = null; 
22     this.workstation = null;
23     this.authtoken = null;
24
25     this.patron = null; 
26     this.patronBarcodeRegex = null;
27
28     // current item barcode
29     this.itemBarcode = null; 
30
31     // are we currently performing a renewal?
32     this.isRenewal = false; 
33
34     // is a transaction pending?
35     this.pendingXact = false; 
36
37     // dict of org unit settings for "here"
38     this.orgSettings = {};
39
40     
41     // Construct a mock checkout for debugging purposes
42     this.mockCheckout = {
43         payload : {
44             record : new fieldmapper.mvr(),
45             copy : new fieldmapper.acp(),
46             circ : new fieldmapper.circ()
47         }
48     };
49
50     this.mockCheckout.payload.record.title('Jazz improvisation for guitar');
51     this.mockCheckout.payload.record.author('Wise, Les');
52     this.mockCheckout.payload.record.isbn('0634033565');
53     this.mockCheckout.payload.copy.barcode('123456789');
54     this.mockCheckout.payload.circ.renewal_remaining(1);
55     this.mockCheckout.payload.circ.parent_circ(1);
56     this.mockCheckout.payload.circ.due_date('2012-12-21');
57 }
58
59
60
61 /**
62  * Fetch the org-unit settings, initialize the display, etc.
63  */
64 SelfCheckManager.prototype.init = function() {
65
66     this.staff = openils.User.user;
67     this.workstation = openils.User.workstation;
68     this.authtoken = openils.User.authtoken;
69     this.loadOrgSettings();
70
71     // add onclick handlers for nav links
72
73     var self = this;
74     dojo.connect(
75         dojo.byId('oils-selfck-hold-details-link'),
76         'onclick',
77         function() { self.drawHoldsPage(); }
78     );
79
80     dojo.connect(
81         dojo.byId('oils-selfck-pay-fines-link'),
82         'onclick',
83         function() { self.drawPayFinesPage(); }
84     );
85
86
87     if(this.cgi.param('patron')) {
88         
89         // Patron barcode via cgi param.  Mainly used for debugging and
90         // only works if password is not required by policy
91         this.loginPatron(this.cgi.param('patron'));
92
93     } else {
94         this.drawLoginPage();
95     }
96 }
97
98 /**
99  * Loads the org unit settings
100  */
101 SelfCheckManager.prototype.loadOrgSettings = function() {
102
103     var settings = fieldmapper.aou.fetchOrgSettingBatch(
104         this.staff.ws_ou(), [
105             SET_BARCODE_REGEX,
106             SET_PATRON_TIMEOUT,
107             SET_ALERT_ON_CHECKOUT_EVENT,
108             SET_AUTO_OVERRIDE_EVENTS,
109         ]
110     );
111
112     for(k in settings) {
113         if(settings[k])
114             this.orgSettings[k] = settings[k].value;
115     }
116
117     if(settings[SET_BARCODE_REGEX]) 
118         this.patronBarcodeRegex = new RegExp(settings[SET_BARCODE_REGEX].value);
119 }
120
121 SelfCheckManager.prototype.drawLoginPage = function() {
122     var self = this;
123
124     var bcHandler = function(barcode) {
125         // handle patron barcode entry
126
127         if(self.orgSettings[SET_PATRON_PASSWORD_REQUIRED]) {
128             
129             // password is required.  wire up the scan box to read it
130             self.updateScanBox({
131                 msg : 'Please enter your password', // TODO i18n 
132                 handler : function(pw) { self.loginPatron(barcode, ps); }
133             });
134
135             dojo.connect(selfckScanBox, 'onKeyDown', pwHandler);
136
137         } else {
138             // password is not required, go ahead and login
139             self.loginPatron(barcode);
140         }
141     };
142
143     this.updateScanBox({
144         msg : 'Please log in with your library barcode.', // TODO
145         handler : bcHandler
146     });
147 }
148
149 /**
150  * Login the patron.  
151  */
152 SelfCheckManager.prototype.loginPatron = function(barcode, passwd) {
153
154     if(this.orgSettings[SET_PATRON_PASSWORD_REQUIRED]) {
155
156         // patron password is required.  Verify it.
157
158         var res = fieldmapper.standardRequest(
159             ['open-ils.actor', 'open-ils.actor.verify_user_password'],
160             {params : [this.authtoken, barcode, null, hex_md5(passwd)]}
161         );
162
163         if(res == 0) {
164             return alert('login failed'); // TODO
165         }
166     } 
167
168     // retrieve the fleshed user by barcode
169     this.patron = fieldmapper.standardRequest(
170         ['open-ils.actor', 'open-ils.actor.user.fleshed.retrieve_by_barcode'],
171         {params : [this.authtoken, barcode]}
172     );
173
174     var evt = openils.Event.parse(this.patron);
175     if(evt) {
176
177         // User login failed, why?
178         
179         switch(evt.textcode) {
180
181             case 'ACTOR_USER_NOT_FOUND':
182                 return alert('user not found'); // TODO
183
184             case 'NO_SESSION':
185                 return alert('staff login timed out'); // TODO
186
187             default:
188                 return alert('unexpected patron login error occured: ' + evt.textcode); // TODO
189         }
190     }
191
192     // patron login succeeded
193     dojo.byId('oils-selfck-user-banner').innerHTML = 'Welcome, ' + this.patron.usrname(); // TODO i18n
194     this.drawCircPage();
195 }
196
197
198 /**
199  * Manages the main input box
200  * @param msg The context message to display with the box
201  * @param clearOnly Don't update the context message, just clear the value and re-focus
202  * @param handler Optional "on-enter" handler.  
203  */
204 SelfCheckManager.prototype.updateScanBox = function(args) {
205
206     selfckScanBox.attr('value', '');
207
208     if(args.value)
209         selfckScanBox.attr('value', args.value);
210
211     if(args.msg) 
212         dojo.byId('oils-selfck-scan-text').innerHTML = args.msg;
213
214     if(selfckScanBox._lastHandler && (args.handler || args.clearHandler)) {
215         dojo.disconnect(selfckScanBox._lastHandler);
216     }
217
218     if(args.handler) {
219         selfckScanBox._lastHandler = dojo.connect(
220             selfckScanBox, 
221             'onKeyDown', 
222             function(e) {
223                 if(e.keyCode != dojo.keys.ENTER) 
224                     return;
225                 args.handler(selfckScanBox.attr('value'));
226             }
227         );
228     }
229
230     selfckScanBox.focus();
231 }
232
233 /**
234  *  Sets up the checkout/renewal interface
235  */
236 SelfCheckManager.prototype.drawCircPage = function() {
237
238     var self = this;
239     this.updateScanBox({
240         msg : 'Please enter an item barcode', // TODO i18n
241         handler : function(barcode) { self.checkout(barcode); }
242     });
243
244     openils.Util.show('oils-selfck-circ-page');
245
246     this.circTbody = dojo.byId('oils-selfck-circ-tbody');
247     if(!this.circTemplate)
248         this.circTemplate = this.circTbody.removeChild(dojo.byId('oils-selfck-circ-row'));
249
250     // items out, holds, and fines summaries
251
252     // fines summary
253     fieldmapper.standardRequest(
254         ['open-ils.actor', 'open-ils.actor.user.fines.summary'],
255         {   async : true,
256             params : [this.authtoken, this.patron.id()],
257             oncomplete : function(r) {
258                 var summary = openils.Util.readResponse(r);
259                 dojo.byId('oils-selfck-fines-total').innerHTML = 
260                     dojo.string.substitute(
261                         localeStrings.TOTAL_FINES_ACCOUNT, 
262                         [summary.balance_owed()]
263                     );
264             }
265         }
266     );
267
268     // holds summary
269     this.updateHoldsSummary();
270
271     // items out summary
272     this.updateCircSummary();
273
274     // render mock checkouts for debugging?
275     if(this.cgi.param('mock-circ')) {
276         for(var i in [1,2,3]) 
277             this.displayCheckout(this.mockCheckout);
278     }
279 }
280
281 SelfCheckManager.prototype.updateHoldsSummary = function(decrement) {
282
283     if(!this.holdsSummary) {
284         var summary = fieldmapper.standardRequest(
285             ['open-ils.circ', 'open-ils.circ.holds.user_summary'],
286             {params : [this.authtoken, this.patron.id()]}
287         );
288
289         this.holdsSummary = {};
290         this.holdsSummary.ready = Number(summary['4']);
291         this.holdsSummary.total = 0;
292
293         for(var i in summary) 
294             this.holdsSummary.total += Number(summary[i]);
295     }
296
297     if(this.decrement) 
298         this.holdsSummary.ready -= 1;
299
300     dojo.byId('oils-selfck-holds-total').innerHTML = 
301         dojo.string.substitute(
302             localeStrings.TOTAL_HOLDS, 
303             [this.holdsSummary.total]
304         );
305
306     dojo.byId('oils-selfck-holds-ready').innerHTML = 
307         dojo.string.substitute(
308             localeStrings.HOLDS_READY_FOR_PICKUP, 
309             [this.holdsSummary.ready]
310         );
311 }
312
313
314 SelfCheckManager.prototype.updateCircSummary = function(increment) {
315
316     if(!this.circSummary) {
317
318         var summary = fieldmapper.standardRequest(
319             ['open-ils.actor', 'open-ils.actor.user.checked_out.count'],
320             {params : [this.authtoken, this.patron.id()]}
321         );
322
323         this.circSummary = {
324             total : Number(summary.out) + Number(summary.overdue),
325             overdue : Number(summary.overdue),
326             session : 0
327         };
328     }
329
330     if(increment) {
331         // local checkout occurred.  Add to the total and the session.
332         this.circSummary.total += 1;
333         this.circSummary.session += 1;
334     }
335
336     dojo.byId('oils-selfck-circ-account-total').innerHTML = 
337         dojo.string.substitute(
338             localeStrings.TOTAL_ITEMS_ACCOUNT, 
339             [this.circSummary.total]
340         );
341
342     dojo.byId('oils-selfck-circ-session-total').innerHTML = 
343         dojo.string.substitute(
344             localeStrings.TOTAL_ITEMS_SESSION, 
345             [this.circSummary.session]
346         );
347 }
348
349
350 SelfCheckManager.prototype.drawHoldsPage = function() {
351
352     // TODO add option to hid scanBox
353     // this.updateScanBox(...)
354
355     openils.Util.hide('oils-selfck-circ-page');
356     openils.Util.hide('oils-selfck-payment-page');
357     openils.Util.show('oils-selfck-holds-page');
358 }
359
360
361
362
363 /**
364  * Check out a single item.  If the item is already checked 
365  * out to the patron, redirect to renew()
366  */
367 SelfCheckManager.prototype.checkout = function(barcode, override) {
368
369     if(!barcode) {
370         this.updateScanbox(null, true);
371         return;
372     }
373
374     // TODO see if it's a patron barcode
375     // TODO see if this item has already been checked out in this session
376
377     var method = 'open-ils.circ.checkout.full';
378     if(override) method += '.override';
379
380     var result = fieldmapper.standardRequest(
381         ['open-ils.circ', 'open-ils.circ.checkout.full'],
382         {params: [
383             this.authtoken, {
384                 patron_id : this.patron.id(),
385                 copy_barcode : barcode
386             }
387         ]}
388     );
389
390
391     if(dojo.isArray(result)) {
392         // list of results.  See if we can override all of them.
393
394     } else {
395         var evt = openils.Event.parse(result);
396
397         switch(evt.textcode) {
398             // standard result events
399             
400             case 'SUCCESS':
401                 this.displayCheckout(evt);
402                 break;
403
404             case 'OPEN_CIRCULATION_EXISTS':
405                 // TODO renewal
406                 break;
407
408             case 'NO_SESSION':
409                 // TODO logout staff
410                 break;
411         }
412     }
413
414     console.log("Circ resulted in " + js2JSON(result));
415 }
416
417 /**
418  * Renew an item
419  */
420 SelfCheckManager.prototype.renew = function() {
421 }
422
423 /**
424  * Display the result of a checkout or renewal in the items out table
425  */
426 SelfCheckManager.prototype.displayCheckout = function(evt) {
427
428     var copy = evt.payload.copy;
429     var record = evt.payload.record;
430     var circ = evt.payload.circ;
431     var row = this.circTemplate.cloneNode(true);
432
433     if(record.isbn()) {
434         this.byName(row, 'jacket').setAttribute('src', '/opac/extras/ac/jacket/small/' + record.isbn());
435     }
436
437     this.byName(row, 'barcode').innerHTML = copy.barcode();
438     this.byName(row, 'title').innerHTML = record.title();
439     this.byName(row, 'author').innerHTML = record.author();
440     this.byName(row, 'remaining').innerHTML = circ.renewal_remaining();
441
442     var date = dojo.date.stamp.fromISOString(circ.due_date());
443     this.byName(row, 'due_date').innerHTML = 
444         dojo.date.locale.format(date, {selector : 'date'});
445
446     this.circTbody.appendChild(row);
447 }
448
449
450 SelfCheckManager.prototype.byName = function(node, name) {
451     return dojo.query('[name=' + name+']', node)[0];
452 }
453
454 /**
455  * Print a receipt
456  */
457 SelfCheckManager.prototype.printReceipt = function() {
458 }
459
460 /**
461  * Build the patron holds table
462  */
463 SelfCheckManager.prototype.displayHolds = function() {
464 }
465
466
467 /**
468  * Logout the patron and return to the login page
469  */
470 SelfCheckManager.prototype.logoutPatron = function() {
471 }
472
473
474 /**
475  * Fire up the manager on page load
476  */
477 openils.Util.addOnLoad(
478     function() {
479         new SelfCheckManager().init();
480     }
481 );