1 dojo.require('openils.CGI');
2 dojo.require('openils.Util');
3 dojo.require('openils.User');
4 dojo.require('openils.Event');
6 dojo.requireLocalization('openils.circ', 'selfcheck');
7 var localeStrings = dojo.i18n.getLocalization('openils.circ', 'selfcheck');
10 const SET_BARCODE_REGEX = 'opac.barcode_regex';
11 const SET_PATRON_TIMEOUT = 'circ.selfcheck.patron_login_timeout';
12 const SET_ALERT_ON_CHECKOUT_EVENT = 'circ.selfcheck.alert_on_checkout_event';
13 const SET_AUTO_OVERRIDE_EVENTS = 'circ.selfcheck.auto_override_checkout_events';
14 const SET_PATRON_PASSWORD_REQUIRED = 'circ.selfcheck.patron_password_required';
16 function SelfCheckManager() {
18 this.cgi = new openils.CGI();
20 this.workstation = null;
21 this.authtoken = null;
24 this.patronBarcodeRegex = null;
26 // current item barcode
27 this.itemBarcode = null;
29 // are we currently performing a renewal?
30 this.isRenewal = false;
32 // is a transaction pending?
33 this.pendingXact = false;
35 // dict of org unit settings for "here"
36 this.orgSettings = {};
40 * Fetch the org-unit settings, initialize the display, etc.
42 SelfCheckManager.prototype.init = function() {
44 this.staff = openils.User.user;
45 this.workstation = openils.User.workstation;
46 this.authtoken = openils.User.authtoken;
47 this.loadOrgSettings();
49 if(this.cgi.param('patron')) {
50 // Patron barcode via cgi param. Mainly used for debugging.
51 this.loginPatron(this.cgi.param('patron'));
58 * Loads the org unit settings
60 SelfCheckManager.prototype.loadOrgSettings = function() {
62 var settings = fieldmapper.aou.fetchOrgSettingBatch(
66 SET_ALERT_ON_CHECKOUT_EVENT,
67 SET_AUTO_OVERRIDE_EVENTS,
73 this.orgSettings[k] = settings[k].value;
76 if(settings[SET_BARCODE_REGEX])
77 this.patronBarcodeRegex = new RegExp(settings[SET_BARCODE_REGEX].value);
80 SelfCheckManager.prototype.drawLoginPage = function() {
83 var bcHandler = function(barcode) {
84 // handle patron barcode entry
86 if(self.orgSettings[SET_PATRON_PASSWORD_REQUIRED]) {
88 // password is required. wire up the scan box to read it
90 msg : 'Please enter your password', // TODO i18n
91 handler : function(pw) { self.loginPatron(barcode, ps); }
94 dojo.connect(selfckScanBox, 'onKeyDown', pwHandler);
97 // password is not required, go ahead and login
98 self.loginPatron(barcode);
103 msg : 'Please log in with your library barcode.', // TODO
111 SelfCheckManager.prototype.loginPatron = function(barcode, passwd) {
113 if(this.orgSettings[SET_PATRON_PASSWORD_REQUIRED]) {
115 // patron password is required. Verify it.
117 var res = fieldmapper.standardRequest(
118 ['open-ils.actor', 'open-ils.actor.verify_user_password'],
119 {params : [this.authtoken, barcode, null, hex_md5(passwd)]}
123 return alert('login failed'); // TODO
127 // retrieve the fleshed user by barcode
128 this.patron = fieldmapper.standardRequest(
129 ['open-ils.actor', 'open-ils.actor.user.fleshed.retrieve_by_barcode'],
130 {params : [this.authtoken, barcode]}
133 var evt = openils.Event.parse(this.patron);
136 // User login failed, why?
138 switch(evt.textcode) {
140 case 'ACTOR_USER_NOT_FOUND':
141 return alert('user not found'); // TODO
144 return alert('staff login timed out'); // TODO
147 return alert('unexpected patron login error occured: ' + evt.textcode); // TODO
151 // patron login succeeded
152 dojo.byId('oils-selfck-user-banner').innerHTML = 'Welcome, ' + this.patron.usrname(); // TODO i18n
158 * Manages the main input box
159 * @param msg The context message to display with the box
160 * @param clearOnly Don't update the context message, just clear the value and re-focus
161 * @param handler Optional "on-enter" handler.
163 SelfCheckManager.prototype.updateScanBox = function(args) {
165 selfckScanBox.attr('value', '');
168 selfckScanBox.attr('value', args.value);
171 dojo.byId('oils-selfck-scan-text').innerHTML = args.msg;
173 if(selfckScanBox._lastHandler && (args.handler || args.clearHandler)) {
174 dojo.disconnect(selfckScanBox._lastHandler);
178 selfckScanBox._lastHandler = dojo.connect(
182 if(e.keyCode != dojo.keys.ENTER)
184 args.handler(selfckScanBox.attr('value'));
189 selfckScanBox.focus();
193 * Sets up the checkout/renewal interface
195 SelfCheckManager.prototype.drawCircPage = function() {
199 msg : 'Please enter an item barcode', // TODO i18n
200 handler : function(barcode) { self.checkout(barcode); }
203 openils.Util.show('oils-selfck-circ-page');
205 this.circTbody = dojo.byId('oils-selfck-circ-tbody');
206 if(!this.circTemplate)
207 this.circTemplate = this.circTbody.removeChild(dojo.byId('oils-selfck-circ-row'));
209 // items out, holds, and fines summaries
212 fieldmapper.standardRequest(
213 ['open-ils.actor', 'open-ils.actor.user.fines.summary'],
215 params : [this.authtoken, this.patron.id()],
216 oncomplete : function(r) {
217 var summary = openils.Util.readResponse(r);
218 dojo.byId('oils-selfck-fines-total').innerHTML =
219 dojo.string.substitute(
220 localeStrings.TOTAL_FINES_ACCOUNT,
221 [summary.balance_owed()]
229 this.updateHoldsSummary();
230 this.updateCircSummary();
233 SelfCheckManager.prototype.updateHoldsSummary = function(decrement) {
237 var oncomplete = function() {
238 dojo.byId('oils-selfck-holds-total').innerHTML =
239 dojo.string.substitute(
240 localeStrings.TOTAL_HOLDS,
241 [self.holdsSummary.total]
244 dojo.byId('oils-selfck-holds-ready').innerHTML =
245 dojo.string.substitute(
246 localeStrings.HOLDS_READY_FOR_PICKUP,
247 [self.holdsSummary.ready]
251 if(!this.holdsSummary) {
252 fieldmapper.standardRequest(
253 ['open-ils.circ', 'open-ils.circ.holds.user_summary'],
255 params : [this.authtoken, this.patron.id()],
256 oncomplete : function(r) {
257 var summary = openils.Util.readResponse(r);
258 self.holdsSummary = {};
259 self.holdsSummary.ready = Number(summary['4']);
260 self.holdsSummary.total = 0;
261 for(var i in summary)
262 self.holdsSummary.total += Number(summary[i]);
270 this.holdsSummary.ready -= 1;
277 SelfCheckManager.prototype.updateCircSummary = function(increment) {
280 var oncomplete = function() {
281 dojo.byId('oils-selfck-circ-account-total').innerHTML =
282 dojo.string.substitute(
283 localeStrings.TOTAL_ITEMS_ACCOUNT,
284 [self.circSummary.total]
287 dojo.byId('oils-selfck-circ-session-total').innerHTML =
288 dojo.string.substitute(
289 localeStrings.TOTAL_ITEMS_SESSION,
290 [self.circSummary.session]
294 if(this.circSummary) {
297 // local checkout occurred. Add to the total and the session.
298 this.circSummary.total += 1;
299 this.circSummary.session += 1;
305 // fetch the circ summary for the patron
306 var summary = fieldmapper.standardRequest(
307 ['open-ils.actor', 'open-ils.actor.user.checked_out.count'],
310 params : [this.authtoken, this.patron.id()],
311 oncomplete : function(r) {
312 var summary = openils.Util.readResponse(r);
314 total : Number(summary.out) + Number(summary.overdue),
315 overdue : Number(summary.overdue),
328 * Check out a single item. If the item is already checked
329 * out to the patron, redirect to renew()
331 SelfCheckManager.prototype.checkout = function(barcode, override) {
334 this.updateScanbox(null, true);
338 // TODO see if it's a patron barcode
339 // TODO see if this item has already been checked out in this session
341 var method = 'open-ils.circ.checkout.full';
342 if(override) method += '.override';
344 var result = fieldmapper.standardRequest(
345 ['open-ils.circ', 'open-ils.circ.checkout.full'],
348 patron_id : this.patron.id(),
349 copy_barcode : barcode
355 if(dojo.isArray(result)) {
356 // list of results. See if we can override all of them.
359 var evt = openils.Event.parse(result);
361 switch(evt.textcode) {
362 // standard result events
365 this.displayCheckout(evt);
368 case 'OPEN_CIRCULATION_EXISTS':
378 console.log("Circ resulted in " + js2JSON(result));
384 SelfCheckManager.prototype.renew = function() {
388 * Display the result of a checkout or renewal in the items out table
390 SelfCheckManager.prototype.displayCheckout = function(evt) {
392 var copy = evt.payload.copy;
393 var record = evt.payload.record;
394 var circ = evt.payload.circ;
395 var row = this.circTemplate.cloneNode(true);
399 var pic = $n(template, 'jacket');
400 pic.setAttribute('src', '/opac/ac/jacket/small/' + cleanISBN(record.isbn()));
404 this.byName(row, 'barcode').innerHTML = copy.barcode();
405 this.byName(row, 'title').innerHTML = record.title();
406 this.byName(row, 'author').innerHTML = record.author();
407 this.circTbody.appendChild(row);
411 SelfCheckManager.prototype.byName = function(node, name) {
412 return dojo.query('[name=' + name+']', node)[0];
418 SelfCheckManager.prototype.printReceipt = function() {
422 * Build the patron holds table
424 SelfCheckManager.prototype.displayHolds = function() {
429 * Logout the patron and return to the login page
431 SelfCheckManager.prototype.logoutPatron = function() {
436 * Fire up the manager on page load
438 openils.Util.addOnLoad(
440 new SelfCheckManager().init();