play sound for login success
[evergreen-equinox.git] / Open-ILS / web / js / ui / kcls / circ / selfcheck / selfcheck.js
1 dojo.require('dojo.date.locale');\r
2 dojo.require('dojo.date.stamp');\r
3 dojo.require('dijit.form.CheckBox');\r
4 dojo.require('dijit.form.NumberSpinner');\r
5 dojo.require('openils.CGI');\r
6 dojo.require('openils.Util');\r
7 dojo.require('openils.User');\r
8 dojo.require('openils.Event');\r
9 dojo.require('openils.widget.ProgressDialog');\r
10 dojo.require('openils.widget.OrgUnitFilteringSelect');\r
11 \r
12 dojo.requireLocalization('openils.circ', 'selfcheck');\r
13 var localeStrings = dojo.i18n.getLocalization('openils.circ', 'selfcheck');\r
14 var selfCheckMgr;\r
15 var itemsOutCirc = [];\r
16 var itemsOutMod = [];\r
17 var itemsOutCopy = [];\r
18 var TIMEOUT = 60; // logout timer\r
19 \r
20 \r
21 const SET_BARCODE_REGEX = 'opac.barcode_regex';\r
22 const SET_PATRON_TIMEOUT = 'circ.selfcheck.patron_login_timeout';\r
23 const SET_AUTO_OVERRIDE_EVENTS = 'circ.selfcheck.auto_override_checkout_events';\r
24 const SET_PATRON_PASSWORD_REQUIRED = 'circ.selfcheck.patron_password_required';\r
25 const SET_AUTO_RENEW_INTERVAL = 'circ.checkout_auto_renew_age';\r
26 const SET_WORKSTATION_REQUIRED = 'circ.selfcheck.workstation_required';\r
27 const SET_ALERT_POPUP = 'circ.selfcheck.alert.popup';\r
28 const SET_ALERT_SOUND = 'circ.selfcheck.alert.sound';\r
29 const SET_CC_PAYMENT_ALLOWED = 'credit.payments.allow';\r
30 // This setting only comes into play if COPY_NOT_AVAILABLE is in the SET_AUTO_OVERRIDE_EVENTS list\r
31 const SET_BLOCK_CHECKOUT_ON_COPY_STATUS = 'circ.selfcheck.block_checkout_on_copy_status';\r
32 \r
33 function SelfCheckManager() {\r
34         selfCheckMgr = this;\r
35         switchTo('step1');\r
36         \r
37         this.timer = null;\r
38     this.cgi = new openils.CGI();\r
39     this.staff = null; \r
40     this.workstation = null;\r
41     this.authtoken = null;\r
42 \r
43     this.patron = null; \r
44     this.patronBarcodeRegex = null;\r
45 \r
46     this.checkouts = [];\r
47     this.itemsOut = [];\r
48 \r
49     // During renewals, keep track of the ID of the previous circulation. \r
50     // Previous circ is used for tracking failed renewals (for receipts).\r
51     this.prevCirc = null;\r
52 \r
53     // current item barcode\r
54     this.itemBarcode = null; \r
55 \r
56     // are we currently performing a renewal?\r
57     this.isRenewal = false; \r
58 \r
59     // dict of org unit settings for "here"\r
60     this.orgSettings = {};\r
61 \r
62     // Construct a mock checkout for debugging purposes\r
63     if(this.mockCheckouts = this.cgi.param('mock-circ')) {\r
64 \r
65         this.mockCheckout = {\r
66             payload : {\r
67                 record : new fieldmapper.mvr(),\r
68                 copy : new fieldmapper.acp(),\r
69                 circ : new fieldmapper.circ()\r
70             }\r
71         };\r
72 \r
73         this.mockCheckout.payload.record.title('Jazz improvisation for guitar');\r
74         this.mockCheckout.payload.record.author('Wise, Les');\r
75         this.mockCheckout.payload.record.isbn('0634033565');\r
76         this.mockCheckout.payload.copy.barcode('123456789');\r
77         this.mockCheckout.payload.circ.renewal_remaining(1);\r
78         this.mockCheckout.payload.circ.parent_circ(1);\r
79         this.mockCheckout.payload.circ.due_date('2012-12-21');\r
80     }\r
81 \r
82     this.initPrinter();\r
83 }\r
84 \r
85 SelfCheckManager.prototype.keepMeLoggedIn = function() {\r
86         //alert(this.timer);\r
87         if(this.timer) try {clearTimeout(this.timer)} catch(e){}\r
88         this.timer = setTimeout('selfCheckMgr.logoutPatron();', TIMEOUT*1000);\r
89 }\r
90 \r
91 /**\r
92  * Fetch the org-unit settings, initialize the display, etc.\r
93  */\r
94 SelfCheckManager.prototype.init = function() {\r
95     this.staff = openils.User.user;\r
96     this.workstation = openils.User.workstation;\r
97     this.authtoken = openils.User.authtoken;\r
98     this.loadOrgSettings();\r
99 \r
100     this.circTbody = dojo.byId('oils-selfck-circ-tbody');\r
101     this.itemsOutTbody = dojo.byId('oils-selfck-circ-out-tbody');\r
102 \r
103     // workstation is required but none provided\r
104     if(this.orgSettings[SET_WORKSTATION_REQUIRED] && !this.workstation) {\r
105         if(confirm(dojo.string.substitute(localeStrings.WORKSTATION_REQUIRED))) {\r
106             this.registerWorkstation();\r
107         }\r
108         return;\r
109     }\r
110     \r
111     var self = this;\r
112     // connect onclick handlers to the various navigation links\r
113     var linkHandlers = {\r
114         'oils-selfck-hold-details-link' : function() { self.drawHoldsPage(true); },\r
115         'oils-selfck-view-fines-link' : function() { self.drawFinesPage(); openils.Util.show('oils-selfck-fines-tbody'); openils.Util.hide('pay_fines'); },\r
116         'oils-selfck-pay-fines-link' : function() {\r
117             switchTo('step3','step3c');\r
118                         openils.Util.hide('oils-selfck-fines-tbody');\r
119                         openils.Util.show('pay_fines');\r
120                         self.keepMeLoggedIn();\r
121             self.drawPayFinesPage(\r
122                 self.patron,\r
123                 self.getSelectedFinesTotal(),\r
124                 self.getSelectedFineTransactions(),\r
125                 function(resp) {\r
126                     var evt = openils.Event.parse(resp);\r
127                     if(evt) {\r
128                         var message = evt + '';\r
129                         if(evt.textcode == 'CREDIT_PROCESSOR_DECLINED_TRANSACTION' && evt.payload)\r
130                             message += '\n' + evt.payload.error_message;\r
131                         self.handleAlert(message, true, 'payment-failure');\r
132                         return;\r
133                     }\r
134                                         self.patron.last_xact_id(resp.last_xact_id);\r
135                     self.printPaymentReceipt(\r
136                         resp,\r
137                         function() {\r
138                             self.updateFinesSummary();\r
139                             self.drawFinesPage();\r
140                         }\r
141                     );\r
142                 }\r
143             );\r
144         },\r
145         //'oils-selfck-nav-home' : function() { self.drawCircPage(); },\r
146         'oils-selfck-nav-logout' : function() { self.logoutPatron(); },\r
147         'oils-selfck-nav-logout-print' : function() { self.logoutPatron(true); },\r
148         'oils-selfck-items-out-details-link' : function() { self.drawItemsOutPage(); },\r
149         //'oils-selfck-print-list-link' : function() { self.printList(); }\r
150     }\r
151 \r
152     for(var id in linkHandlers) {\r
153                 //var obj1 = dojo.byId(id);\r
154                 //obj1.onclick = linkHandlers[id];\r
155         dojo.connect(dojo.byId(id), 'onclick', linkHandlers[id]);\r
156         }\r
157 \r
158 \r
159     if(this.cgi.param('patron')) {\r
160         \r
161         // Patron barcode via cgi param.  Mainly used for debugging and\r
162         // only works if password is not required by policy\r
163         this.loginPatron(this.cgi.param('patron'));\r
164 \r
165     } else {\r
166         this.drawLoginPage();\r
167     }\r
168 \r
169     /**\r
170      * To test printing, pass a URL param of 'testprint'.  The value for the param\r
171      * should be a JSON string like so:  [{circ:<circ_id>}, ...]\r
172      */\r
173     var testPrint = this.cgi.param('testprint');\r
174     if(testPrint) {\r
175         this.checkouts = JSON2js(testPrint);\r
176         this.printSessionReceipt();\r
177         this.checkouts = [];\r
178     }\r
179 }\r
180 \r
181 \r
182 SelfCheckManager.prototype.getSelectedFinesTotal = function() {\r
183     var total = 0;\r
184     dojo.forEach(\r
185         dojo.query("[name=selector]", this.finesTbody),\r
186         function(input) {\r
187             if(input.checked)\r
188                 total += Number(input.balance_owed);\r
189         }\r
190     );\r
191     return total.toFixed(2);\r
192 };\r
193 \r
194 SelfCheckManager.prototype.getSelectedFineTransactions = function() {\r
195     return dojo.query("[name=selector]", this.finesTbody).\r
196         filter(function (o) { return o.checked }).\r
197         map(\r
198             function (o) {\r
199                 return [\r
200                     o.getAttribute("xact"),\r
201                     Number(o.balance_owed).toFixed(2)\r
202                 ];\r
203             }\r
204         );\r
205 };\r
206 \r
207 /**\r
208  * Registers a new workstion\r
209  */\r
210 SelfCheckManager.prototype.registerWorkstation = function() {\r
211     \r
212     oilsSelfckWsDialog.show();\r
213 \r
214     new openils.User().buildPermOrgSelector(\r
215         'REGISTER_WORKSTATION', \r
216         oilsSelfckWsLocSelector, \r
217         this.staff.home_ou()\r
218     );\r
219 \r
220 \r
221     var self = this;\r
222     dojo.connect(oilsSelfckWsSubmit, 'onClick', \r
223 \r
224         function() {\r
225             oilsSelfckWsDialog.hide();\r
226             var name = oilsSelfckWsLocSelector.attr('displayedValue') + '-' + oilsSelfckWsName.attr('value');\r
227 \r
228             var res = fieldmapper.standardRequest(\r
229                 ['open-ils.actor', 'open-ils.actor.workstation.register'],\r
230                 { params : [\r
231                         self.authtoken, name, oilsSelfckWsLocSelector.attr('value')\r
232                     ]\r
233                 }\r
234             );\r
235 \r
236             if(evt = openils.Event.parse(res)) {\r
237                 if(evt.textcode == 'WORKSTATION_NAME_EXISTS') {\r
238                     if(confirm(localeStrings.WORKSTATION_EXISTS)) {\r
239                         location.href = location.href.replace(/\?.*/, '') + '?ws=' + name;\r
240                     } else {\r
241                         self.registerWorkstation();\r
242                     }\r
243                     return;\r
244                 } else {\r
245                     alert(evt);\r
246                 }\r
247             } else {\r
248                 location.href = location.href.replace(/\?.*/, '') + '?ws=' + name;\r
249             }\r
250         }\r
251     );\r
252 }\r
253 \r
254 /**\r
255  * Loads the org unit settings\r
256  */\r
257 SelfCheckManager.prototype.loadOrgSettings = function() {\r
258 \r
259     var settings = fieldmapper.aou.fetchOrgSettingBatch(\r
260         this.staff.ws_ou(), [\r
261             SET_BARCODE_REGEX,\r
262             SET_PATRON_TIMEOUT,\r
263             SET_ALERT_POPUP,\r
264             SET_ALERT_SOUND,\r
265             SET_AUTO_OVERRIDE_EVENTS,\r
266             SET_BLOCK_CHECKOUT_ON_COPY_STATUS,\r
267             SET_PATRON_PASSWORD_REQUIRED,\r
268             SET_AUTO_RENEW_INTERVAL,\r
269             SET_WORKSTATION_REQUIRED,\r
270             SET_CC_PAYMENT_ALLOWED\r
271         ]\r
272     );\r
273 \r
274     for(k in settings) {\r
275         if(settings[k])\r
276             this.orgSettings[k] = settings[k].value;\r
277     }\r
278 \r
279     if(settings[SET_BARCODE_REGEX]) \r
280         this.patronBarcodeRegex = new RegExp(settings[SET_BARCODE_REGEX].value);\r
281 }\r
282 \r
283 SelfCheckManager.prototype.drawLoginPage = function() {\r
284     var self = this;\r
285     var bcHandler = function(barcode) {\r
286         // handle patron barcode entry\r
287 \r
288         if(self.orgSettings[SET_PATRON_PASSWORD_REQUIRED]) {\r
289             \r
290             // password is required.  wire up the scan box to read it\r
291             self.updateScanBox({\r
292                 msg : 'Please enter your password', // TODO i18n \r
293                 handler : function(pw) { self.loginPatron(barcode, pw); },\r
294                 password : true\r
295             });\r
296 \r
297         } else {\r
298             // password is not required, go ahead and login\r
299             self.loginPatron(barcode);\r
300         }\r
301     };\r
302 \r
303     this.updateScanBox({\r
304         msg : 'Please log in with your library barcode.', // TODO\r
305         handler : bcHandler\r
306     });\r
307         \r
308         var txtBox = (dojo.byId('step2').style.display=='none') ? 'patron-login-username' : 'patron-login-password';\r
309         try{var a=dojo.byId(txtBox);a.focus();a.select();}catch(e){}\r
310 }\r
311 \r
312 /**\r
313  * Login the patron.  \r
314  */\r
315 SelfCheckManager.prototype.loginPatron = function(barcode, passwd) {\r
316         \r
317     //if(this.orgSettings[SET_PATRON_PASSWORD_REQUIRED]) { // password always reqired, per KCLS - fail safe\r
318         if(!passwd) {\r
319             // would only happen in dev/debug mode when using the patron= param\r
320             alert('password required by org setting.  remove patron= from URL'); \r
321             return;\r
322         }\r
323 \r
324         // patron password is required.  Verify it.\r
325 \r
326         var res = fieldmapper.standardRequest(\r
327             ['open-ils.actor', 'open-ils.actor.verify_user_password'],\r
328             {params : [this.authtoken, barcode, null, hex_md5(passwd)]}\r
329         );\r
330 \r
331         if(res == 0) {\r
332             // user-not-found results in login failure\r
333             this.handleAlert(\r
334                 dojo.string.substitute(localeStrings.LOGIN_FAILED, [barcode]),\r
335                 false, 'login-failure'\r
336             );\r
337             this.drawLoginPage();\r
338                         openils.Util.show('back_to_login');\r
339             return;\r
340         }\r
341     //} \r
342 \r
343     // retrieve the fleshed user by barcode\r
344     this.patron = fieldmapper.standardRequest(\r
345         ['open-ils.actor', 'open-ils.actor.user.fleshed.retrieve_by_barcode'],\r
346         {params : [this.authtoken, barcode]}\r
347     );\r
348 \r
349     var evt = openils.Event.parse(this.patron);\r
350     if(evt) {\r
351         this.handleAlert(\r
352             dojo.string.substitute(localeStrings.LOGIN_FAILED, [barcode]),\r
353             false, 'login-failure'\r
354         );\r
355         this.drawLoginPage();\r
356                 openils.Util.show('back_to_login');\r
357 \r
358     } else {\r
359 \r
360         this.handleAlert('', true, 'login-success');\r
361         dojo.byId('user_name').innerHTML = \r
362             dojo.string.substitute(localeStrings.WELCOME_BANNER, [this.patron.first_given_name()]);\r
363                 dojo.byId('oils-selfck-status-div').innerHTML = '';\r
364                 dojo.byId('oils-selfck-status-div2').innerHTML = '';\r
365                 dojo.byId('oils-selfck-status-div3').innerHTML = '';\r
366                 openils.Util.hide('back_to_login');\r
367         this.drawCircPage();\r
368     }\r
369 }\r
370 \r
371 \r
372 SelfCheckManager.prototype.handleAlert = function(message, shouldPopup, sound) {\r
373     console.log("Handling alert " + message);\r
374 \r
375     dojo.byId('oils-selfck-status-div').innerHTML = message;\r
376         if(!this.patron){\r
377                 dojo.byId('oils-selfck-status-div2').innerHTML = message;\r
378                 dojo.byId('oils-selfck-status-div3').innerHTML = message;\r
379         }\r
380 \r
381     if(shouldPopup)\r
382         openils.Util.addCSSClass( dojo.byId('oils-selfck-status-div'), 'checkout_failure' );\r
383     else\r
384         openils.Util.removeCSSClass( dojo.byId('oils-selfck-status-div'), 'checkout_failure' );\r
385         \r
386     if(message && shouldPopup && this.orgSettings[SET_ALERT_POPUP]) \r
387         alert(message);\r
388 \r
389     if(this.orgSettings[SET_ALERT_SOUND])\r
390         openils.Util.playAudioUrl(SelfCheckManager.audioConfig[sound]);\r
391 }\r
392 \r
393 \r
394 /**\r
395  * Manages the main input box\r
396  * @param msg The context message to display with the box\r
397  * @param clearOnly Don't update the context message, just clear the value and re-focus\r
398  * @param handler Optional "on-enter" handler.  \r
399  */\r
400 SelfCheckManager.prototype.updateScanBox = function(args) {\r
401     args = args || {};\r
402 \r
403     if(args.select) {\r
404         selfckScanBox.domNode.select();\r
405     } else {\r
406         selfckScanBox.attr('value', '');\r
407     }\r
408 \r
409     if(args.password) {\r
410         selfckScanBox.domNode.setAttribute('type', 'password');\r
411     } else {\r
412         selfckScanBox.domNode.setAttribute('type', '');\r
413     }\r
414 \r
415     if(args.value)\r
416         selfckScanBox.attr('value', args.value);\r
417 \r
418     if(args.msg) \r
419         dojo.byId('oils-selfck-scan-text').innerHTML = args.msg;\r
420 \r
421     if(selfckScanBox._lastHandler && (args.handler || args.clearHandler)) {\r
422         dojo.disconnect(selfckScanBox._lastHandler);\r
423     }\r
424 \r
425     if(args.handler) {\r
426 \r
427         selfckScanBox._lastHandler = dojo.connect(\r
428             selfckScanBox, \r
429             'onKeyDown', \r
430             function(e) {\r
431                 if(e.keyCode == dojo.keys.ESCAPE)\r
432                     selfCheckMgr.logoutPatron(true);\r
433                 if(e.keyCode != dojo.keys.ENTER) \r
434                     return;\r
435                 args.handler(selfckScanBox.attr('value'));\r
436             }\r
437         );\r
438     }\r
439 \r
440     selfckScanBox.focus();\r
441 }\r
442 \r
443 /**\r
444  *  Sets up the checkout/renewal interface\r
445  */\r
446 SelfCheckManager.prototype.drawCircPage = function() {\r
447         this.keepMeLoggedIn();\r
448     openils.Util.show('oils-selfck-circ-tbody', 'table-row-group');\r
449         switchTo('step3');\r
450 \r
451     var self = this;\r
452     this.updateScanBox({\r
453         msg : 'Please enter an item barcode', // TODO i18n\r
454         handler : function(barcode) { \r
455                 openils.Util.show('oils-selfck-fines-tbody'); \r
456                 openils.Util.hide('pay_fines'); switchTo('step3'); \r
457                 self.checkout(barcode); }\r
458     });\r
459 \r
460     if(!this.circTemplate)\r
461         this.circTemplate = this.circTbody.removeChild(dojo.byId('oils-selfck-circ-row'));\r
462 \r
463     // fines summary\r
464     this.updateFinesSummary();\r
465 \r
466     // holds summary\r
467     this.updateHoldsSummary();\r
468 \r
469     // items out summary\r
470     this.updateCircSummary();\r
471 \r
472     // render mock checkouts for debugging?\r
473     if(this.mockCheckouts) {\r
474         for(var i in [1,2,3]) \r
475             this.displayCheckout(this.mockCheckout, 'checkout');\r
476     }\r
477 }\r
478 \r
479 \r
480 SelfCheckManager.prototype.updateFinesSummary = function() {\r
481     var self = this; \r
482 \r
483     // fines summary\r
484     fieldmapper.standardRequest(\r
485         ['open-ils.actor', 'open-ils.actor.user.fines.summary'],\r
486         {   async : true,\r
487             params : [this.authtoken, this.patron.id()],\r
488             oncomplete : function(r) {\r
489                 var summary = openils.Util.readResponse(r);\r
490                                 var finesSum = dojo.byId('acct_fines');\r
491                                 var bal = summary.balance_owed();\r
492                                 var bal2 = parseFloat(bal);\r
493                                 \r
494                                 if(bal2>0) {finesSum.style.color="red"; openils.Util.show('oils-selfck-pay-fines-link');}\r
495                 finesSum.innerHTML = dojo.string.substitute(localeStrings.TOTAL_FINES_ACCOUNT, [bal2.toFixed(2)]);\r
496                 self.creditPayableBalance = bal2+'';\r
497             }\r
498         }\r
499     );\r
500 }\r
501 \r
502 \r
503 SelfCheckManager.prototype.drawItemsOutPage = function() {\r
504         this.keepMeLoggedIn();\r
505         switchTo('step3','step3d');\r
506 \r
507         if(!this.outTemplate)\r
508         this.outTemplate = this.itemsOutTbody.removeChild(dojo.byId('oils-selfck-circ-out-row'));\r
509     while(this.itemsOutTbody.childNodes[0])\r
510         this.itemsOutTbody.removeChild(this.itemsOutTbody.childNodes[0]);\r
511 \r
512     progressDialog.show(true);\r
513     var self = this;\r
514         \r
515     fieldmapper.standardRequest(\r
516         ['open-ils.circ', 'open-ils.circ.actor.user.checked_out.atomic'],\r
517         {\r
518             async : true,\r
519             params : [this.authtoken, this.patron.id()],\r
520             oncomplete : function(r) {\r
521                 var resp = openils.Util.readResponse(r);\r
522 \r
523                 var circs = resp.sort(\r
524                     function(a, b) {\r
525                         if(a.circ.due_date() > b.circ.due_date())\r
526                             return -1;\r
527                         return 1;\r
528                     }\r
529                 );\r
530 \r
531                 self.itemsOut = [];\r
532                 dojo.forEach(circs,\r
533                     function(circ) {\r
534                         self.itemsOut.push(circ.circ.id());\r
535                         handleCheckedItems(circ);\r
536                     }\r
537                 );\r
538                                 progressDialog.hide();\r
539             }\r
540         }\r
541     );\r
542 }\r
543 \r
544 function handleCheckedItems(circ) {\r
545         var self = selfCheckMgr;\r
546         var row = self.outTemplate.cloneNode(true);\r
547         \r
548         self.byName(row,'barcode').innerHTML = circ.copy.barcode();\r
549         self.byName(row,'title').innerHTML = circ.record.title();\r
550         self.byName(row,'author').innerHTML = circ.record.author();\r
551         if(dojo.date.stamp.fromISOString(circ.circ.due_date())<(new Date())) self.byName(row,'due_date').style.color="red";\r
552         self.byName(row,'due_date').innerHTML = dojo.date.locale.format(dojo.date.stamp.fromISOString(circ.circ.due_date()), {selector: 'date', fullYear: true});\r
553         self.byName(row,'format').innerHTML = circ.record.types_of_resource()[0];\r
554         \r
555         self.itemsOutTbody.appendChild(row);\r
556 }\r
557 \r
558 SelfCheckManager.prototype.goToTab = function(name) {\r
559     this.tabName = name;\r
560 \r
561     openils.Util.hide('oils-selfck-fines-page');\r
562     openils.Util.hide('oils-selfck-payment-page');\r
563     openils.Util.hide('oils-selfck-holds-page');\r
564     openils.Util.hide('oils-selfck-circ-page');\r
565     openils.Util.hide('oils-selfck-pay-fines-link');\r
566     \r
567     switch(name) {\r
568         case 'checkout':\r
569             openils.Util.show('oils-selfck-circ-page');\r
570             break;\r
571         case 'items_out':\r
572             openils.Util.show('oils-selfck-circ-page');\r
573             break;\r
574         case 'holds':\r
575             openils.Util.show('oils-selfck-holds-page');\r
576             break;\r
577         case 'fines':\r
578             openils.Util.show('oils-selfck-fines-page');\r
579             break;\r
580         case 'payment':\r
581             openils.Util.show('oils-selfck-payment-page');\r
582             break;\r
583     }\r
584 }\r
585 \r
586 \r
587 SelfCheckManager.prototype.printList = function(which) {\r
588         this.keepMeLoggedIn();\r
589     switch(which) {\r
590         case 'checkout':\r
591             this.printSessionReceipt();\r
592             break;\r
593         case 'items_out':\r
594             this.printItemsOutReceipt();\r
595             break;\r
596         case 'holds':\r
597             this.printHoldsReceipt();\r
598             break;\r
599         case 'fines':\r
600             this.printFinesReceipt();\r
601             break;\r
602     }\r
603 }\r
604 \r
605 SelfCheckManager.prototype.updateHoldsSummary = function() {\r
606     if(!this.holdsSummary) {\r
607         var summary = fieldmapper.standardRequest(\r
608             ['open-ils.circ', 'open-ils.circ.holds.user_summary'],\r
609             {params : [this.authtoken, this.patron.id()]}\r
610         );\r
611 \r
612         this.holdsSummary = {};\r
613         this.holdsSummary.ready = Number(summary['4']);\r
614         this.holdsSummary.total = 0;\r
615 \r
616         for(var i in summary)\r
617             this.holdsSummary.total += Number(summary[i]);\r
618     }\r
619 \r
620     dojo.byId('oils-selfck-holds-total').innerHTML =dojo.string.substitute("${0}) Item"+(this.holdsSummary.total==1?"":"s"),[this.holdsSummary.total]);\r
621     dojo.byId('oils-selfck-holds-ready').innerHTML =dojo.string.substitute("${0}) Item"+(this.holdsSummary.ready==1?"":"s"),[this.holdsSummary.ready]);\r
622 }\r
623 \r
624 \r
625 SelfCheckManager.prototype.updateCircSummary = function(increment) {\r
626     if(!this.circSummary) {\r
627 \r
628         var summary = fieldmapper.standardRequest(\r
629             ['open-ils.actor', 'open-ils.actor.user.checked_out.count'],\r
630             {params : [this.authtoken, this.patron.id()]}\r
631         );\r
632 \r
633         this.circSummary = {\r
634             total : Number(summary.out) + Number(summary.overdue),\r
635             overdue : Number(summary.overdue),\r
636             session : 0\r
637         };\r
638     }\r
639 \r
640     if(increment) {\r
641         // local checkout occurred.  Add to the total and the session.\r
642         this.circSummary.total += 1;\r
643         this.circSummary.session += 1;\r
644     }\r
645 \r
646     dojo.byId('oils-selfck-circ-account-total').innerHTML = dojo.string.substitute("${0}) Item"+(this.circSummary.total==1?"":"s"), [this.circSummary.total]);\r
647 \r
648     /*\r
649         dojo.byId('oils-selfck-circ-session-total').innerHTML = \r
650         dojo.string.substitute(\r
651             localeStrings.TOTAL_ITEMS_SESSION, \r
652             [this.circSummary.session]\r
653         );\r
654         */\r
655 }\r
656 \r
657 \r
658 SelfCheckManager.prototype.drawHoldsPage = function(bool) {\r
659         this.keepMeLoggedIn();\r
660         if(bool) switchTo('step3','step3f'); else switchTo('step3','step3e');\r
661 \r
662     this.holdTbody = dojo.byId('oils-selfck-hold-tbody');\r
663         this.readyTbody = dojo.byId('oils-selfck-rdy-tbody');\r
664         if(!this.readyTemplate)\r
665         this.readyTemplate = this.readyTbody.removeChild(dojo.byId('oils-selfck-rdy-row'));\r
666     if(!this.holdTemplate)\r
667         this.holdTemplate = this.holdTbody.removeChild(dojo.byId('oils-selfck-hold-row'));\r
668     while(this.holdTbody.childNodes[0])\r
669         this.holdTbody.removeChild(this.holdTbody.childNodes[0]);\r
670         while(this.readyTbody.childNodes[0])\r
671         this.readyTbody.removeChild(this.readyTbody.childNodes[0]);\r
672 \r
673     progressDialog.show(true);\r
674 \r
675     var self = this;\r
676     fieldmapper.standardRequest( // fetch the hold IDs\r
677 \r
678         ['open-ils.circ', 'open-ils.circ.holds.id_list.retrieve'],\r
679         {   async : true,\r
680             params : [this.authtoken, this.patron.id()],\r
681 \r
682             oncomplete : function(r) { \r
683                 var ids = openils.Util.readResponse(r);\r
684                 if(!ids || ids.length == 0) {\r
685                     progressDialog.hide();\r
686                     return;\r
687                 }\r
688 \r
689                 fieldmapper.standardRequest( // fetch the hold objects with fleshed details\r
690                     ['open-ils.circ', 'open-ils.circ.hold.details.batch.retrieve'],\r
691                     {   async : true,\r
692                         params : [self.authtoken, ids],\r
693 \r
694                         onresponse : function(rr) {\r
695                                                         progressDialog.hide(); \r
696                             self.drawHolds(openils.Util.readResponse(rr));\r
697                         }\r
698                     }\r
699                 );\r
700             }\r
701         }\r
702     );\r
703 }\r
704 \r
705 /**\r
706  * Fetch and add a single hold to the list of holds\r
707  */\r
708 SelfCheckManager.prototype.drawHolds = function(holds) {\r
709         //this.keepMeLoggedIn();\r
710     this.holds = holds;\r
711     progressDialog.hide();\r
712         \r
713         var data = holds;\r
714         if(!data) return;\r
715         var row = this.holdTemplate.cloneNode(true);\r
716         var row2 = this.readyTemplate.cloneNode(true);\r
717 \r
718         //if(data.mvr.isbn()) {\r
719         //    this.byName(row, 'jacket').setAttribute('src', '/opac/extras/ac/jacket/small/' + data.mvr.isbn());\r
720         //}\r
721         \r
722         if(data.status == 4) {\r
723                 this.byName(row2, 'title').innerHTML = data.mvr.title();\r
724                 this.byName(row2, 'format').innerHTML = data.mvr.types_of_resource()[0];\r
725                 this.byName(row2, 'lib').innerHTML = fieldmapper.aou.findOrgUnit(data.hold.pickup_lib()).name();\r
726                 if(dojo.date.stamp.fromISOString(data.hold.capture_time())<(new Date())) this.byName(row2, 'date').style.color="red";\r
727                 this.byName(row2, 'date').innerHTML = dojo.date.locale.format(dojo.date.stamp.fromISOString(data.hold.capture_time()), {selector: 'date', fullYear: true});\r
728                 this.readyTbody.appendChild(row2);\r
729         } else {\r
730 \r
731                 this.byName(row, 'title').innerHTML = data.mvr.title();\r
732                 this.byName(row, 'author').innerHTML = data.mvr.author();\r
733                 this.byName(row, 'format').innerHTML = data.mvr.types_of_resource()[0];\r
734 \r
735                         // hold is still pending\r
736                 this.byName(row, 'status').innerHTML = dojo.string.substitute(localeStrings.HOLD_STATUS_WAITING,[data.queue_position, data.potential_copies]);\r
737                 this.holdTbody.appendChild(row);\r
738         }\r
739 }\r
740 \r
741 \r
742 SelfCheckManager.prototype.drawFinesPage = function() {\r
743         this.keepMeLoggedIn();\r
744     // TODO add option to hid scanBox\r
745     // this.updateScanBox(...)\r
746 \r
747     //this.goToTab('fines');\r
748         switchTo('step3','step3c');\r
749     progressDialog.show(true);\r
750 \r
751     //if(this.creditPayableBalance > 0 && this.orgSettings[SET_CC_PAYMENT_ALLOWED])\r
752     //  openils.Util.show('oils-selfck-pay-fines-link', 'inline');\r
753     \r
754 \r
755     this.finesTbody = dojo.byId('oils-selfck-fines-tbody');\r
756     if(!this.finesTemplate)\r
757         this.finesTemplate = this.finesTbody.removeChild(dojo.byId('oils-selfck-fines-row'));\r
758     while(this.finesTbody.childNodes[0])\r
759         this.finesTbody.removeChild(this.finesTbody.childNodes[0]);\r
760 \r
761 /*\r
762     // when user clicks on a selector checkbox, update the total owed\r
763     var updateSelected = function() {\r
764         var total = 0;\r
765         dojo.forEach(\r
766             dojo.query('[name=selector]', this.finesTbody),\r
767             function(input) {\r
768                 if(input.checked)\r
769                     total += Number(input.getAttribute('balance_owed'));\r
770             }\r
771         );\r
772 \r
773         total = total.toFixed(2);\r
774         dojo.byId('oils-selfck-selected-total').innerHTML = \r
775             dojo.string.substitute(localeStrings.TOTAL_FINES_SELECTED, [total]);\r
776     }\r
777 \r
778     // wire up the batch on/off selector\r
779     var sel = dojo.byId('oils-selfck-fines-selector');\r
780     sel.onchange = function() {\r
781         dojo.forEach(\r
782             dojo.query('[name=selector]', this.finesTbody),\r
783             function(input) {\r
784                 input.checked = sel.checked;\r
785             }\r
786         );\r
787     };\r
788 */\r
789     var self = this;\r
790     var handler = function(dataList) {\r
791 \r
792         self.finesCount = dataList.length;\r
793         self.finesData = dataList;\r
794 \r
795         for(var i in dataList) {\r
796 \r
797             var data = dataList[i];\r
798             var row = self.finesTemplate.cloneNode(true);\r
799             var type = data.transaction.xact_type();\r
800 \r
801             if(type == 'circulation') {\r
802                 self.byName(row, 'title').innerHTML = data.record.title();\r
803                                 if(dojo.date.stamp.fromISOString(data.circ.due_date())<(new Date())) self.byName(row, 'due_date').style.color="red";\r
804                 self.byName(row, 'due_date').innerHTML = dojo.date.locale.format(dojo.date.stamp.fromISOString(data.circ.due_date()), {selector: 'date', fullYear: true});\r
805                                 self.byName(row, 'date_return').innerHTML = (data.circ.checkin_time())?dojo.date.locale.format(dojo.date.stamp.fromISOString(data.circ.checkin_time()), {selector: 'date', fullYear: true}):"";\r
806 \r
807             } else if(type == 'grocery') {\r
808                 self.byName(row, 'title').innerHTML = (data.transaction.last_billing_type())?("Miscellaneous - "+data.transaction.last_billing_type()):"Miscellaneous"; // Go ahead and head off any confusion around "grocery".  TODO i18n\r
809             }\r
810 \r
811             //self.byName(row, 'total_owed').innerHTML = data.transaction.total_owed();\r
812             //self.byName(row, 'total_paid').innerHTML = data.transaction.total_paid();\r
813             self.byName(row, 'balance').innerHTML = data.transaction.balance_owed();\r
814                         self.byName(row, 'selector').balance_owed = data.transaction.balance_owed();\r
815                         self.byName(row, 'selector').setAttribute('xact', data.transaction.id());\r
816 /*\r
817             // row selector\r
818             var selector = self.byName(row, 'selector')\r
819             selector.onchange = updateSelected;\r
820             selector.setAttribute('xact', data.transaction.id());\r
821             selector.setAttribute('balance_owed', data.transaction.balance_owed());\r
822             selector.checked = true;\r
823 */\r
824             self.finesTbody.appendChild(row);\r
825         }\r
826 \r
827         //updateSelected();\r
828     }\r
829 \r
830 \r
831     fieldmapper.standardRequest( \r
832         ['open-ils.actor', 'open-ils.actor.user.transactions.have_balance.fleshed'],\r
833         {   async : true,\r
834             params : [this.authtoken, this.patron.id()],\r
835             oncomplete : function(r) { \r
836                 progressDialog.hide();\r
837                 handler(openils.Util.readResponse(r));\r
838             }\r
839         }\r
840     );\r
841 }\r
842 \r
843 SelfCheckManager.prototype.checkin = function(barcode, abortTransit) {\r
844     var resp = fieldmapper.standardRequest(\r
845         ['open-ils.circ', 'open-ils.circ.transit.abort'],\r
846         {params : [this.authtoken, {barcode : barcode}]}\r
847     );\r
848 \r
849     // resp == 1 on success\r
850     if(openils.Event.parse(resp))\r
851         return false;\r
852 \r
853     var resp = fieldmapper.standardRequest(\r
854         ['open-ils.circ', 'open-ils.circ.checkin.override'],\r
855         {params : [\r
856             this.authtoken, {\r
857                 patron_id : this.patron.id(),\r
858                 copy_barcode : barcode,\r
859                 noop : true\r
860             }\r
861         ]}\r
862     );\r
863 \r
864     if(!resp.length) resp = [resp];\r
865     for(var i = 0; i < resp.length; i++) {\r
866         var tc = openils.Event.parse(resp[i]).textcode;\r
867         if(tc == 'SUCCESS' || tc == 'NO_CHANGE') {\r
868             continue;\r
869         } else {\r
870             return false;\r
871         }\r
872     }\r
873 \r
874     return true;\r
875 }\r
876 \r
877 /**\r
878  * Check out a single item.  If the item is already checked \r
879  * out to the patron, redirect to renew()\r
880  */\r
881 SelfCheckManager.prototype.checkout = function(barcode, override) {\r
882         this.keepMeLoggedIn();\r
883     this.prevCirc = null;\r
884 \r
885     if(!barcode) {\r
886         this.updateScanbox(null, true);\r
887         return;\r
888     }\r
889 \r
890     if(this.mockCheckouts) {\r
891         // if we're in mock-checkout mode, just insert another\r
892         // fake circ into the table and get out of here.\r
893         this.displayCheckout(this.mockCheckout, 'checkout');\r
894         return;\r
895     }\r
896 \r
897     // TODO see if it's a patron barcode\r
898     // TODO see if this item has already been checked out in this session\r
899 \r
900     var method = 'open-ils.circ.checkout.full';\r
901     if(override) method += '.override';\r
902 \r
903     console.log("Checkout out item " + barcode + " with method " + method);\r
904 \r
905     var result = fieldmapper.standardRequest(\r
906         ['open-ils.circ', method],\r
907         {params: [\r
908             this.authtoken, {\r
909                 patron_id : this.patron.id(),\r
910                 copy_barcode : barcode\r
911             }\r
912         ]}\r
913     );\r
914 \r
915     var stat = this.handleXactResult('checkout', barcode, result);\r
916 \r
917     if(stat.override) {\r
918         this.checkout(barcode, true);\r
919     } else if(stat.doOver) {\r
920         this.checkout(barcode);\r
921     } else if(stat.renew) {\r
922         this.renew(barcode);\r
923     }\r
924 }\r
925 \r
926 SelfCheckManager.prototype.failPartMessage = function(result) {\r
927     if (result.payload && result.payload.fail_part) {\r
928         var stringKey = "FAIL_PART_" +\r
929             result.payload.fail_part.replace(/\./g, "_");\r
930         return localeStrings[stringKey];\r
931     } else {\r
932         return null;\r
933     }\r
934 }\r
935 \r
936 SelfCheckManager.prototype.handleXactResult = function(action, item, result) {\r
937     var displayText = '';\r
938 \r
939     // If true, the display message is important enough to pop up.  Whether or not\r
940     // an alert() actually occurs, depends on org unit settings\r
941     var popup = false;  \r
942     var sound = ''; // sound file reference\r
943     var payload = result.payload || {};\r
944     var overrideEvents = this.orgSettings[SET_AUTO_OVERRIDE_EVENTS];\r
945     var blockStatuses = this.orgSettings[SET_BLOCK_CHECKOUT_ON_COPY_STATUS];\r
946         result.payload = payload;\r
947         \r
948     if(result.textcode == 'NO_SESSION') {\r
949 \r
950         return this.logoutStaff();\r
951 \r
952     } else if(result.textcode == 'SUCCESS') {\r
953 \r
954         if(action == 'checkout') {\r
955 \r
956             displayText = dojo.string.substitute(localeStrings.CHECKOUT_SUCCESS, [item]);\r
957             this.displayCheckout(result, 'checkout');\r
958 \r
959             if(payload.holds_fulfilled && payload.holds_fulfilled.length) {\r
960                 // A hold was fulfilled, update the hold numbers in the circ summary\r
961                 console.log("fulfilled hold " + payload.holds_fulfilled + " during checkout");\r
962                 this.holdsSummary = null;\r
963                 this.updateHoldsSummary();\r
964             }\r
965 \r
966             this.updateCircSummary(true);\r
967 \r
968         } else if(action == 'renew') {\r
969 \r
970             displayText = dojo.string.substitute(localeStrings.RENEW_SUCCESS, [item]);\r
971             this.displayCheckout(result, 'renew');\r
972         }\r
973 \r
974         this.checkouts.push({circ : result.payload.circ.id()});\r
975         sound = 'checkout-success';\r
976         this.updateScanBox();\r
977 \r
978     } else if(result.textcode == 'OPEN_CIRCULATION_EXISTS' && action == 'checkout') {\r
979 \r
980         // Server says the item is already checked out.  If it's checked out to the\r
981         // current user, we may need to renew it.  \r
982 \r
983         if(payload.old_circ) { \r
984 \r
985             /*\r
986             old_circ refers to the previous checkout IFF it's for the same user. \r
987             If no auto-renew interval is not defined, assume we should renew it\r
988             If an auto-renew interval is defined and the payload comes back with\r
989             auto_renew set to true, do the renewal.  Otherwise, let the patron know\r
990             the item is already checked out to them.  */\r
991 \r
992             if( !this.orgSettings[SET_AUTO_RENEW_INTERVAL] ||\r
993                 (this.orgSettings[SET_AUTO_RENEW_INTERVAL] && payload.auto_renew) ) {\r
994                 this.prevCirc = payload.old_circ.id();\r
995                 return { renew : true };\r
996             }\r
997 \r
998             popup = true;\r
999             sound = 'checkout-failure';\r
1000             displayText = dojo.string.substitute(localeStrings.ALREADY_OUT, [item]);\r
1001 \r
1002         } else {\r
1003 \r
1004             if( // copy is marked lost.  if configured to do so, check it in and try again.\r
1005                 result.payload.copy && \r
1006                 result.payload.copy.status() == /* LOST */ 3 &&\r
1007                 overrideEvents && overrideEvents.length &&\r
1008                 overrideEvents.indexOf('COPY_STATUS_LOST') != -1) {\r
1009 \r
1010                     if(this.checkin(item)) {\r
1011                         return { doOver : true };\r
1012                     }\r
1013             }\r
1014 \r
1015             \r
1016             // item is checked out to some other user\r
1017             popup = true;\r
1018             sound = 'checkout-failure';\r
1019             displayText = dojo.string.substitute(localeStrings.OPEN_CIRCULATION_EXISTS, [item]);\r
1020         }\r
1021 \r
1022         this.updateScanBox();\r
1023 \r
1024     } else {\r
1025 \r
1026     \r
1027         if(overrideEvents && overrideEvents.length) {\r
1028             \r
1029             // see if the events we received are all in the list of\r
1030             // events to override\r
1031     \r
1032             if(!result.length) result = [result];\r
1033     \r
1034             var override = true;\r
1035             for(var i = 0; i < result.length; i++) {\r
1036 \r
1037                 var match = overrideEvents.filter(function(e) { return (e == result[i].textcode); })[0];\r
1038 \r
1039                 if(!match) {\r
1040                     override = false;\r
1041                     break;\r
1042                 }\r
1043 \r
1044                 if(result[i].textcode == 'COPY_NOT_AVAILABLE' && blockStatuses && blockStatuses.length) {\r
1045 \r
1046                     var stat = result[i].payload.status(); // copy status\r
1047                     if(typeof stat == 'object') stat = stat.id();\r
1048 \r
1049                     var match2 = blockStatuses.filter(function(e) { return (e == stat); })[0];\r
1050 \r
1051                     if(match2) { // copy is in a blocked status\r
1052                         override = false;\r
1053                         break;\r
1054                     }\r
1055                 }\r
1056 \r
1057                 if(result[i].textcode == 'COPY_IN_TRANSIT') {\r
1058                     // to override a transit, we have to abort the transit and check it in first\r
1059                     if(this.checkin(item, true)) {\r
1060                         return { doOver : true };\r
1061                     } else {\r
1062                         override = false;\r
1063                     }\r
1064                 }\r
1065             }\r
1066 \r
1067             if(override) \r
1068                 return { override : true };\r
1069         }\r
1070     \r
1071         this.updateScanBox();\r
1072         popup = true;\r
1073         sound = 'checkout-failure';\r
1074 \r
1075         if(action == 'renew')\r
1076             this.checkouts.push({circ : this.prevCirc, renewal_failure : true});\r
1077 \r
1078         if(result.length) \r
1079             result = result[0];\r
1080 \r
1081         switch(result.textcode) {\r
1082 \r
1083             // TODO custom handler for blocking penalties\r
1084 \r
1085             case 'MAX_RENEWALS_REACHED' :\r
1086                 displayText = dojo.string.substitute(\r
1087                     localeStrings.MAX_RENEWALS, [item]);\r
1088                 break;\r
1089 \r
1090             case 'ITEM_NOT_CATALOGED' :\r
1091                 displayText = dojo.string.substitute(\r
1092                     localeStrings.ITEM_NOT_CATALOGED, [item]);\r
1093                 break;\r
1094 \r
1095             case 'OPEN_CIRCULATION_EXISTS' :\r
1096                 displayText = dojo.string.substitute(\r
1097                     localeStrings.OPEN_CIRCULATION_EXISTS, [item]);\r
1098 \r
1099                 break;\r
1100 \r
1101             default:\r
1102                 console.error('Unhandled event ' + result.textcode);\r
1103 \r
1104                 if (!(displayText = this.failPartMessage(result))) {\r
1105                     if (action == 'checkout' || action == 'renew') {\r
1106                         displayText = dojo.string.substitute(\r
1107                             localeStrings.GENERIC_CIRC_FAILURE, [item]);\r
1108                     } else {\r
1109                         displayText = dojo.string.substitute(\r
1110                             localeStrings.UNKNOWN_ERROR, [result.textcode]);\r
1111                     }\r
1112                 }\r
1113         }\r
1114     }\r
1115 \r
1116     this.handleAlert(displayText, popup, sound);\r
1117     return {};\r
1118 }\r
1119 \r
1120 \r
1121 /**\r
1122  * Renew an item\r
1123  */\r
1124 SelfCheckManager.prototype.renew = function(barcode, override) {\r
1125 \r
1126     var method = 'open-ils.circ.renew';\r
1127     if(override) method += '.override';\r
1128 \r
1129     console.log("Renewing item " + barcode + " with method " + method);\r
1130 \r
1131     var result = fieldmapper.standardRequest(\r
1132         ['open-ils.circ', method],\r
1133         {params: [\r
1134             this.authtoken, {\r
1135                 patron_id : this.patron.id(),\r
1136                 copy_barcode : barcode\r
1137             }\r
1138         ]}\r
1139     );\r
1140 \r
1141     console.log(js2JSON(result));\r
1142 \r
1143     var stat = this.handleXactResult('renew', barcode, result);\r
1144 \r
1145     if(stat.override)\r
1146         this.renew(barcode, true);\r
1147 }\r
1148 \r
1149 /**\r
1150  * Display the result of a checkout or renewal in the items out table\r
1151  */\r
1152 SelfCheckManager.prototype.displayCheckout = function(evt, type, itemsOut) {\r
1153     var copy = evt.payload.copy;\r
1154     var record = evt.payload.record;\r
1155     var circ = evt.payload.circ;\r
1156     var row = this.circTemplate.cloneNode(true);\r
1157 \r
1158     //if(record.isbn()) {\r
1159     //    this.byName(row, 'jacket').setAttribute('src', '/opac/extras/ac/jacket/small/' + record.isbn());\r
1160     //}\r
1161 \r
1162     this.byName(row, 'barcode').innerHTML = copy.barcode();\r
1163     this.byName(row, 'title').innerHTML = record.title();\r
1164     //this.byName(row, 'author').innerHTML = record.author();\r
1165     //this.byName(row, 'remaining').innerHTML = circ.renewal_remaining();\r
1166     openils.Util.show(this.byName(row, type));\r
1167 \r
1168     var date = dojo.date.stamp.fromISOString(circ.due_date());\r
1169     this.byName(row, 'due_date').innerHTML = \r
1170         dojo.date.locale.format(date, {selector : 'date'});\r
1171 \r
1172     // put new circs at the top of the list\r
1173     var tbody = this.circTbody;\r
1174     if(itemsOut) tbody = this.itemsOutTbody;\r
1175     tbody.insertBefore(row, tbody.getElementsByTagName('tr')[0]);\r
1176 }\r
1177 \r
1178 \r
1179 SelfCheckManager.prototype.byName = function(node, name) {\r
1180     return dojo.query('[name=' + name+']', node)[0];\r
1181 }\r
1182 \r
1183 \r
1184 SelfCheckManager.prototype.initPrinter = function() {\r
1185     try { // Mozilla only\r
1186                 netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");\r
1187         netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');\r
1188         netscape.security.PrivilegeManager.enablePrivilege('UniversalPreferencesRead');\r
1189         netscape.security.PrivilegeManager.enablePrivilege('UniversalPreferencesWrite');\r
1190         var pref = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);\r
1191         if (pref)\r
1192             pref.setBoolPref('print.always_print_silent', true);\r
1193     } catch(E) {\r
1194         console.log("Unable to initialize auto-printing"); \r
1195     }\r
1196 }\r
1197 \r
1198 /**\r
1199  * Print a receipt for this session's checkouts\r
1200  */\r
1201 SelfCheckManager.prototype.printSessionReceipt = function(callback) {\r
1202     var circIds = [];\r
1203     var circCtx = []; // circ context data.  in this case, renewal_failure info\r
1204 \r
1205     // collect the circs and failure info\r
1206     dojo.forEach(\r
1207         this.checkouts, \r
1208         function(blob) {\r
1209             circIds.push(blob.circ);\r
1210             circCtx.push({renewal_failure:blob.renewal_failure});\r
1211         }\r
1212     );\r
1213 \r
1214     var params = [\r
1215         this.authtoken, \r
1216         this.staff.ws_ou(),\r
1217         null,\r
1218         'format.selfcheck.checkout',\r
1219         'print-on-demand',\r
1220         circIds,\r
1221         circCtx\r
1222     ];\r
1223 \r
1224     var self = this;\r
1225     fieldmapper.standardRequest(\r
1226         ['open-ils.circ', 'open-ils.circ.fire_circ_trigger_events'],\r
1227         {   \r
1228             async : true,\r
1229             params : params,\r
1230             oncomplete : function(r) {\r
1231                 var resp = openils.Util.readResponse(r);\r
1232                 var output = resp.template_output();\r
1233                 if(output) {\r
1234                     self.printData(output.data(), self.checkouts.length, callback); \r
1235                 } else {\r
1236                     var error = resp.error_output();\r
1237                     if(error) {\r
1238                         throw new Error("Error creating receipt: " + error.data());\r
1239                     } else {\r
1240                         throw new Error("No receipt data returned from server");\r
1241                     }\r
1242                 }\r
1243             }\r
1244         }\r
1245     );\r
1246 }\r
1247 \r
1248 SelfCheckManager.prototype.printData = function(data, numItems, callback) {\r
1249     var win = window.open('', '', 'resizable,width=350,height=250,scrollbars=1'); \r
1250     win.document.body.innerHTML = data;\r
1251     win.print();\r
1252 \r
1253     /*\r
1254      * There is no way to know when the browser is done printing.\r
1255      * Make a best guess at when to close the print window by basing\r
1256      * the setTimeout wait on the number of items to be printed plus\r
1257      * a small buffer\r
1258      */\r
1259     var sleepTime = 1000;\r
1260     if(numItems > 0) \r
1261         sleepTime += (numItems / 2) * 1000;\r
1262 \r
1263     setTimeout(\r
1264         function() { \r
1265             win.close(); // close the print window\r
1266             if(callback) callback(); // fire optional post-print callback\r
1267         },\r
1268         sleepTime \r
1269     );\r
1270 }\r
1271 \r
1272 \r
1273 /**\r
1274  * Print a receipt for this user's items out\r
1275  */\r
1276 SelfCheckManager.prototype.printItemsOutReceipt = function(callback) {\r
1277     if(!this.itemsOut.length) return;\r
1278 \r
1279     progressDialog.show(true);\r
1280 \r
1281     var params = [\r
1282         this.authtoken, \r
1283         this.staff.ws_ou(),\r
1284         null,\r
1285         'format.selfcheck.items_out',\r
1286         'print-on-demand',\r
1287         this.itemsOut\r
1288     ];\r
1289 \r
1290     var self = this;\r
1291     fieldmapper.standardRequest(\r
1292         ['open-ils.circ', 'open-ils.circ.fire_circ_trigger_events'],\r
1293         {   \r
1294             async : true,\r
1295             params : params,\r
1296             oncomplete : function(r) {\r
1297                 progressDialog.hide();\r
1298                 var resp = openils.Util.readResponse(r);\r
1299                 var output = resp.template_output();\r
1300                 if(output) {\r
1301                     self.printData(output.data(), self.itemsOut.length, callback); \r
1302                 } else {\r
1303                     var error = resp.error_output();\r
1304                     if(error) {\r
1305                         throw new Error("Error creating receipt: " + error.data());\r
1306                     } else {\r
1307                         throw new Error("No receipt data returned from server");\r
1308                     }\r
1309                 }\r
1310             }\r
1311         }\r
1312     );\r
1313 }\r
1314 \r
1315 /**\r
1316  * Print a receipt for this user's items out\r
1317  */\r
1318 SelfCheckManager.prototype.printHoldsReceipt = function(callback) {\r
1319     if(!this.holds.length) return;\r
1320 \r
1321     progressDialog.show(true);\r
1322 \r
1323     var holdIds = [];\r
1324     var holdData = [];\r
1325 \r
1326     dojo.forEach(this.holds,\r
1327         function(data) {\r
1328             holdIds.push(data.hold.id());\r
1329             if(data.status == 4) {\r
1330                 holdData.push({ready : true});\r
1331             } else {\r
1332                 holdData.push({\r
1333                     queue_position : data.queue_position, \r
1334                     potential_copies : data.potential_copies\r
1335                 });\r
1336             }\r
1337         }\r
1338     );\r
1339 \r
1340     var params = [\r
1341         this.authtoken, \r
1342         this.staff.ws_ou(),\r
1343         null,\r
1344         'format.selfcheck.holds',\r
1345         'print-on-demand',\r
1346         holdIds,\r
1347         holdData\r
1348     ];\r
1349 \r
1350     var self = this;\r
1351     fieldmapper.standardRequest(\r
1352         ['open-ils.circ', 'open-ils.circ.fire_hold_trigger_events'],\r
1353         {   \r
1354             async : true,\r
1355             params : params,\r
1356             oncomplete : function(r) {\r
1357                 progressDialog.hide();\r
1358                 var resp = openils.Util.readResponse(r);\r
1359                 var output = resp.template_output();\r
1360                 if(output) {\r
1361                     self.printData(output.data(), self.holds.length, callback); \r
1362                 } else {\r
1363                     var error = resp.error_output();\r
1364                     if(error) {\r
1365                         throw new Error("Error creating receipt: " + error.data());\r
1366                     } else {\r
1367                         throw new Error("No receipt data returned from server");\r
1368                     }\r
1369                 }\r
1370             }\r
1371         }\r
1372     );\r
1373 }\r
1374 \r
1375 \r
1376 SelfCheckManager.prototype.printPaymentReceipt = function(paymentIds, callback) {\r
1377     var self = this;\r
1378     progressDialog.show(true);\r
1379 \r
1380     fieldmapper.standardRequest(\r
1381         ['open-ils.circ', 'open-ils.circ.money.payment_receipt.print'],\r
1382         {\r
1383             async : true,\r
1384             params : [this.authtoken, paymentIds],\r
1385             oncomplete : function(r) {\r
1386                 var resp = openils.Util.readResponse(r);\r
1387                 var output = resp.template_output();\r
1388                 progressDialog.hide();\r
1389                 if(output) {\r
1390                     self.printData(output.data(), 1, callback); \r
1391                 } else {\r
1392                     var error = resp.error_output();\r
1393                     if(error) {\r
1394                         throw new Error("Error creating receipt: " + error.data());\r
1395                     } else {\r
1396                         throw new Error("No receipt data returned from server");\r
1397                     }\r
1398                 }\r
1399             }\r
1400         }\r
1401     );\r
1402 }\r
1403 \r
1404 /**\r
1405  * Print a receipt for this user's items out\r
1406  */\r
1407 SelfCheckManager.prototype.printFinesReceipt = function(callback) {\r
1408     progressDialog.show(true);\r
1409 \r
1410     var params = [\r
1411         this.authtoken, \r
1412         this.staff.ws_ou(),\r
1413         null,\r
1414         'format.selfcheck.fines',\r
1415         'print-on-demand',\r
1416         [this.patron.id()]\r
1417     ];\r
1418 \r
1419     var self = this;\r
1420     fieldmapper.standardRequest(\r
1421         ['open-ils.circ', 'open-ils.circ.fire_user_trigger_events'],\r
1422         {   \r
1423             async : true,\r
1424             params : params,\r
1425             oncomplete : function(r) {\r
1426                 progressDialog.hide();\r
1427                 var resp = openils.Util.readResponse(r);\r
1428                 var output = resp.template_output();\r
1429                 if(output) {\r
1430                     self.printData(output.data(), self.finesCount, callback); \r
1431                 } else {\r
1432                     var error = resp.error_output();\r
1433                     if(error) {\r
1434                         throw new Error("Error creating receipt: " + error.data());\r
1435                     } else {\r
1436                         throw new Error("No receipt data returned from server");\r
1437                     }\r
1438                 }\r
1439             }\r
1440         }\r
1441     );\r
1442 }\r
1443 \r
1444 \r
1445 /**\r
1446  * Logout the patron and return to the login page\r
1447  */\r
1448 SelfCheckManager.prototype.logoutPatron = function(print) {\r
1449     progressDialog.show(true); // prevent patron from clicking logout link twice\r
1450     if(print && this.checkouts.length) {\r
1451         this.printSessionReceipt(\r
1452             function() {\r
1453                 location.href = location.href;\r
1454             }\r
1455         );\r
1456     } else {\r
1457         location.href = location.href;\r
1458     }\r
1459 }\r
1460 \r
1461 \r
1462 function checkLogin() {\r
1463         selfCheckMgr.keepMeLoggedIn();\r
1464         if(selfCheckMgr.orgSettings[SET_PATRON_PASSWORD_REQUIRED]) {\r
1465                 switchTo('step2');\r
1466                 try{dojo.byId('patron-login-password').focus();}catch(e){}\r
1467         } else {\r
1468                 selfCheckMgr.loginPatron(dojo.byId('patron-login-username').value);\r
1469         }\r
1470 }\r
1471 \r
1472 \r
1473 function cancelLogin() {\r
1474         dojo.byId('oils-selfck-status-div').innerHTML = '';\r
1475         dojo.byId('oils-selfck-status-div2').innerHTML = '';\r
1476         dojo.byId('oils-selfck-status-div3').innerHTML = '';\r
1477         dojo.byId('patron-login-password').value = '';\r
1478         openils.Util.hide('back_to_login');\r
1479         switchTo('step1');\r
1480         try {\r
1481                 dojo.byId('patron-login-username').focus();\r
1482                 dojo.byId('patron-login-username').select();\r
1483         } catch(e) {}\r
1484 }\r
1485 \r
1486 /**\r
1487  * Fire up the manager on page load\r
1488  */\r
1489 openils.Util.addOnLoad(\r
1490     function() {\r
1491         new SelfCheckManager().init();\r
1492                 openils.Util.registerEnterHandler(dojo.byId('patron-login-username'), function(){checkLogin();});\r
1493                 openils.Util.registerEnterHandler(dojo.byId('patron-login-password'), function(){selfCheckMgr.loginPatron(dojo.byId('patron-login-username').value,dojo.byId('patron-login-password').value);});\r
1494     }\r
1495 );\r