From: erickson <erickson@dcc99617-32d9-48b4-a31d-7c20da2025e4> Date: Mon, 21 Dec 2009 16:33:23 +0000 (+0000) Subject: initial support for credit card payments via selfcheck. 2 caveats. 1) the code... X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=c693f665ee64cd9c92c0e01527ae21fbb97ec32d;p=evergreen%2Fmasslnc.git initial support for credit card payments via selfcheck. 2 caveats. 1) the code does not (yet) support cherry-picking transactions to pay, it instead pays the oldest transactions first if the payment amount is less than the total owed. 2) it does not differentiate between transactions that started at (or having billings at) locations that do not support credit card payments. Until (and if) support for differentiation is added, if the current location supports CC payments, all transactions are fair game for payment git-svn-id: svn://svn.open-ils.org/ILS/trunk@15208 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- diff --git a/Open-ILS/web/css/skin/default/selfcheck.css b/Open-ILS/web/css/skin/default/selfcheck.css index d17cf3ae55..986b46f597 100644 --- a/Open-ILS/web/css/skin/default/selfcheck.css +++ b/Open-ILS/web/css/skin/default/selfcheck.css @@ -79,6 +79,9 @@ body { text-align: right; width: 100%; } +#oils-selfck-content-header span { + padding-left: 5px; +} #oils-selfck-info-nav { margin: 15px 10px 15px 10px; @@ -86,9 +89,11 @@ body { border-bottom: 1px dashed #888; } +#oils-selfck-circ-info-div span { + padding-right: 8px; +} #oils-selfck-info-nav span { padding-left: 8px; - padding-right: 8px; } #oils-selfck-info-nav span.selected { @@ -118,4 +123,13 @@ body { color: red; } +#oils-selfck-cc-payment-summary { + font-weight:bold; + padding: 30px; +} + +#oils-selfck-cc-payment-table td { + padding: 5px; +} + diff --git a/Open-ILS/web/js/dojo/openils/Event.js b/Open-ILS/web/js/dojo/openils/Event.js index a37f6ff066..b2bf745682 100644 --- a/Open-ILS/web/js/dojo/openils/Event.js +++ b/Open-ILS/web/js/dojo/openils/Event.js @@ -29,12 +29,15 @@ if(!dojo._hasResource["openils.Event"]) { this.servertime = kwargs.servertime; this.ilsperm = kwargs.ilsperm; this.ilspermloc = kwargs.ilspermloc; + this.note = kwargs.note; }, toString : function() { var s = 'Event: ' + (this.code || '') + ':' + this.textcode + ' -> ' + new String(this.desc); if(this.ilsperm) s += ' ' + this.ilsperm + '@' + this.ilspermloc; + if(this.note) + s += '\n' + this.note; return s; } }); diff --git a/Open-ILS/web/js/dojo/openils/circ/nls/selfcheck.js b/Open-ILS/web/js/dojo/openils/circ/nls/selfcheck.js index a3358ed2c5..cbb1f85b9a 100644 --- a/Open-ILS/web/js/dojo/openils/circ/nls/selfcheck.js +++ b/Open-ILS/web/js/dojo/openils/circ/nls/selfcheck.js @@ -16,6 +16,7 @@ 'MAX_RENEWALS' : 'No more renewals allowed for item ${0}', 'ITEM_NOT_CATALOGED' : 'Item ${0} was not found in the system. Try re-scanning the item.', 'WORKSTATION_REQUIRED' : 'A workstation is required to log in to selfcheck. You can set the workstation name with URL param "ws". \n\nWould you like to register a new workstation for this self-check interface?', - 'WORKSTATION_EXISTS' : 'This workstation has already been registered. Would you like to use it for this self-check station?' + 'WORKSTATION_EXISTS' : 'This workstation has already been registered. Would you like to use it for this self-check station?', + 'CC_PAYABLE_BALANCE' : 'You have \$${0} in fines payable by credit card.' } diff --git a/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js b/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js index be436f855f..493d1c31ec 100644 --- a/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js +++ b/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js @@ -1,5 +1,7 @@ dojo.require('dojo.date.locale'); dojo.require('dojo.date.stamp'); +dojo.require('dijit.form.CheckBox'); +dojo.require('dijit.form.NumberSpinner'); dojo.require('openils.CGI'); dojo.require('openils.Util'); dojo.require('openils.User'); @@ -19,6 +21,7 @@ const SET_AUTO_RENEW_INTERVAL = 'circ.checkout_auto_renew_age'; const SET_WORKSTATION_REQUIRED = 'circ.selfcheck.workstation_required'; const SET_ALERT_POPUP = 'circ.selfcheck.alert.popup'; const SET_ALERT_SOUND = 'circ.selfcheck.alert.sound'; +const SET_CC_PAYMENT_ALLOWED = 'credit.payments.allow'; function SelfCheckManager() { @@ -94,7 +97,8 @@ SelfCheckManager.prototype.init = function() { // connect onclick handlers to the various navigation links var linkHandlers = { 'oils-selfck-hold-details-link' : function() { self.drawHoldsPage(); }, - 'oils-selfck-pay-fines-link' : function() { self.drawFinesPage(); }, + 'oils-selfck-view-fines-link' : function() { self.drawFinesPage(); }, + 'oils-selfck-pay-fines-link' : function() { self.drawPayFinesPage(); }, 'oils-selfck-nav-home' : function() { self.drawCircPage(); }, 'oils-selfck-nav-logout' : function() { self.logoutPatron(); }, 'oils-selfck-nav-logout-print' : function() { self.logoutPatron(true); }, @@ -190,7 +194,8 @@ SelfCheckManager.prototype.loadOrgSettings = function() { SET_AUTO_OVERRIDE_EVENTS, SET_PATRON_PASSWORD_REQUIRED, SET_AUTO_RENEW_INTERVAL, - SET_WORKSTATION_REQUIRED + SET_WORKSTATION_REQUIRED, + SET_CC_PAYMENT_ALLOWED ] ); @@ -364,7 +369,25 @@ SelfCheckManager.prototype.drawCircPage = function() { if(!this.circTemplate) this.circTemplate = this.circTbody.removeChild(dojo.byId('oils-selfck-circ-row')); - // items out, holds, and fines summaries + // fines summary + this.updateFinesSummary(); + + // holds summary + this.updateHoldsSummary(); + + // items out summary + this.updateCircSummary(); + + // render mock checkouts for debugging? + if(this.mockCheckouts) { + for(var i in [1,2,3]) + this.displayCheckout(this.mockCheckout, 'checkout'); + } +} + + +SelfCheckManager.prototype.updateFinesSummary = function() { + var self = this; // fines summary fieldmapper.standardRequest( @@ -372,27 +395,19 @@ SelfCheckManager.prototype.drawCircPage = function() { { async : true, params : [this.authtoken, this.patron.id()], oncomplete : function(r) { + var summary = openils.Util.readResponse(r); + dojo.byId('oils-selfck-fines-total').innerHTML = dojo.string.substitute( localeStrings.TOTAL_FINES_ACCOUNT, [summary.balance_owed()] ); + + self.creditPayableBalance = summary.balance_owed(); } } ); - - // holds summary - this.updateHoldsSummary(); - - // items out summary - this.updateCircSummary(); - - // render mock checkouts for debugging? - if(this.mockCheckouts) { - for(var i in [1,2,3]) - this.displayCheckout(this.mockCheckout, 'checkout'); - } } @@ -446,9 +461,11 @@ SelfCheckManager.prototype.drawItemsOutPage = function() { SelfCheckManager.prototype.goToTab = function(name) { this.tabName = name; + openils.Util.hide('oils-selfck-fines-page'); openils.Util.hide('oils-selfck-payment-page'); openils.Util.hide('oils-selfck-holds-page'); openils.Util.hide('oils-selfck-circ-page'); + openils.Util.hide('oils-selfck-pay-fines-link'); switch(name) { case 'checkout': @@ -461,6 +478,9 @@ SelfCheckManager.prototype.goToTab = function(name) { openils.Util.show('oils-selfck-holds-page'); break; case 'fines': + openils.Util.show('oils-selfck-fines-page'); + break; + case 'payment': openils.Util.show('oils-selfck-payment-page'); break; } @@ -645,6 +665,117 @@ SelfCheckManager.prototype.drawHolds = function(holds) { } +SelfCheckManager.prototype.drawPayFinesPage = function() { + this.goToTab('payment'); + + dojo.byId('oils-selfck-cc-payment-summary').innerHTML = + dojo.string.substitute( + localeStrings.CC_PAYABLE_BALANCE, + [this.creditPayableBalance] + ); + + oilsSelfckCCNumber.attr('value', ''); + oilsSelfckCCMonth.attr('value', '01'); + oilsSelfckCCAmount.attr('value', this.creditPayableBalance); + oilsSelfckCCYear.attr('value', new Date().getFullYear()); + oilsSelfckCCFName.attr('value', this.patron.first_given_name()); + oilsSelfckCCLName.attr('value', this.patron.family_name()); + var addr = this.patron.billing_address() || this.patron.mailing_address(); + + if(addr) { + oilsSelfckCCStreet.attr('value', addr.street1()+' '+addr.street2()); + oilsSelfckCCCity.attr('value', addr.city()); + oilsSelfckCCState.attr('value', addr.state()); + oilsSelfckCCZip.attr('value', addr.post_code()); + } + + dojo.connect(oilsSelfckEditDetails, 'onChange', + function(newVal) { + dojo.forEach( + [ oilsSelfckCCFName, + oilsSelfckCCLName, + oilsSelfckCCStreet, + oilsSelfckCCCity, + oilsSelfckCCState, + oilsSelfckCCZip + ], + function(dij) { dij.attr('disabled', !newVal); } + ); + } + ); + + + var self = this; + dojo.connect(oilsSelfckCCSubmit, 'onClick', + function() { + progressDialog.show(true); + self.sendCCPayment(); + } + ); +} + + +// In this form, this code only supports global on/off credit card +// payments and does not dissallow payments to transactions that started +// at remote locations or transactions that have accumulated billings at +// remote locations that dissalow credit card payments. +// TODO add per-transaction blocks for orgs that do not support CC payments + +SelfCheckManager.prototype.sendCCPayment = function() { + + var args = { + userid : this.patron.id(), + payment_type : 'credit_card_payment', + payments : [], + cc_args : { + where_process : 1, + number : oilsSelfckCCNumber.attr('value'), + expire_year : oilsSelfckCCYear.attr('value'), + expire_month : oilsSelfckCCMonth.attr('value'), + billing_first : oilsSelfckCCFName.attr('value'), + billing_last : oilsSelfckCCLName.attr('value'), + billing_address : oilsSelfckCCStreet.attr('value'), + billing_city : oilsSelfckCCCity.attr('value'), + billing_state : oilsSelfckCCState.attr('value'), + billing_zip : oilsSelfckCCZip.attr('value') + } + } + + var funds = oilsSelfckCCAmount.attr('value'); + + xacts = this.finesData.sort( + function(a, b) { + if(a.transaction.xact_start() < b.transaction.xact_start()) + return -1; + return 1; + } + ); + + for(var i in xacts) { + var xact = xacts[i].transaction; + var paying = Math.min(funds, xact.balance_owed()); + args.payments.push([xact.id(), paying]); + funds -= paying; + if(funds <= 0) break; + } + + var resp = fieldmapper.standardRequest( + ['open-ils.circ', 'open-ils.circ.money.payment'], + {params : [this.authtoken, args]} + ); + + progressDialog.hide(); + + var evt = openils.Event.parse(resp); + if(evt) { + alert(evt); + } else { + this.updateFinesSummary(); + this.drawFinesPage(); + } +} + + SelfCheckManager.prototype.drawFinesPage = function() { // TODO add option to hid scanBox @@ -653,6 +784,10 @@ SelfCheckManager.prototype.drawFinesPage = function() { this.goToTab('fines'); progressDialog.show(true); + if(this.creditPayableBalance > 0 && this.orgSettings[SET_CC_PAYMENT_ALLOWED]) { + openils.Util.show('oils-selfck-pay-fines-link', 'inline'); + } + this.finesTbody = dojo.byId('oils-selfck-fines-tbody'); if(!this.finesTemplate) this.finesTemplate = this.finesTbody.removeChild(dojo.byId('oils-selfck-fines-row')); @@ -662,6 +797,7 @@ SelfCheckManager.prototype.drawFinesPage = function() { var self = this; var handler = function(dataList) { self.finesCount = dataList.length; + self.finesData = dataList; for(var i in dataList) { var data = dataList[i]; var row = self.finesTemplate.cloneNode(true); diff --git a/Open-ILS/web/templates/default/circ/selfcheck/circ_page.tt2 b/Open-ILS/web/templates/default/circ/selfcheck/circ_page.tt2 index b1abfb4c06..36830b8c96 100644 --- a/Open-ILS/web/templates/default/circ/selfcheck/circ_page.tt2 +++ b/Open-ILS/web/templates/default/circ/selfcheck/circ_page.tt2 @@ -7,7 +7,7 @@ <td>Title</td> <td>Author</td> <td>Due Date</td> - <td>Renewals Left</td> + <td class='hidden'>Renewals Left</td> <td>Type</td> </tr> </thead> @@ -18,7 +18,7 @@ <td name='title'></td> <td name='author'></td> <td name='due_date'></td> - <td name='remaining'></td> + <td class='hidden' name='remaining'></td> <td> <div name='checkout' class='hidden'>Checkout</div> <div name='renew' class='hidden'>Renewal</div> diff --git a/Open-ILS/web/templates/default/circ/selfcheck/main.tt2 b/Open-ILS/web/templates/default/circ/selfcheck/main.tt2 index 8e4939cf00..1549a4f792 100644 --- a/Open-ILS/web/templates/default/circ/selfcheck/main.tt2 +++ b/Open-ILS/web/templates/default/circ/selfcheck/main.tt2 @@ -10,7 +10,8 @@ <div id='oils-selfck-bottom-div'> <div id='oils-selfck-content-div'> <div id='oils-selfck-content-header'> - <a id='oils-selfck-print-list-link' href='javascript:void(0);'>Print List</a> + <span><a class='hidden' href='javascript:void(0);' id='oils-selfck-pay-fines-link'>Pay Fines</a></span> + <span><a id='oils-selfck-print-list-link' href='javascript:void(0);'>Print List</a></span> </div> <div id='oils-selfck-circ-page' class='hidden'> <!-- Checkout / renewal and items out interface --> @@ -20,10 +21,14 @@ <!-- Patron holds interface --> [% INCLUDE 'default/circ/selfcheck/holds_page.tt2' %] </div> - <div id='oils-selfck-payment-page' class='hidden'> - <!-- Fines and credit card payments interface --> + <div id='oils-selfck-fines-page' class='hidden'> + <!-- Fines and interface --> [% INCLUDE 'default/circ/selfcheck/fines.tt2' %] </div> + <div id='oils-selfck-payment-page' class='hidden'> + <!-- credit card payments interface --> + [% INCLUDE 'default/circ/selfcheck/payment.tt2' %] + </div> </div> <div id='oils-selfck-summary-div'> [% INCLUDE 'default/circ/selfcheck/summary.tt2' %] diff --git a/Open-ILS/web/templates/default/circ/selfcheck/payment.tt2 b/Open-ILS/web/templates/default/circ/selfcheck/payment.tt2 new file mode 100644 index 0000000000..fef4e0267c --- /dev/null +++ b/Open-ILS/web/templates/default/circ/selfcheck/payment.tt2 @@ -0,0 +1,69 @@ +<div id='oils-selfck-cc-payment-summary'> </div> +<table id='oils-selfck-cc-payment-table'> + <tbody> + <tr> + <td>Amount</td> + <td><input dojoType='dijit.form.TextBox' jsId='oilsSelfckCCAmount' required='true'/></td> + </tr> + <tr> + <td>Credit Card #</td> + <td><input dojoType='dijit.form.TextBox' jsId='oilsSelfckCCNumber' required='true'/></td> + </tr> + <tr> + <td>Exipration Month</td> + <td> + <select dojoType='dijit.form.FilteringSelect' jsId='oilsSelfckCCMonth' required='true'> + <option value='01' selected='selected'>Jan</option> + <option value='02'>Feb</option> + <option value='03'>Mar</option> + <option value='04'>April</option> + <option value='05'>May</option> + <option value='06'>June</option> + <option value='07'>July</option> + <option value='08'>Aug</option> + <option value='09'>Sept</option> + <option value='10'>Oct</option> + <option value='11'>Nov</option> + <option value='12'>Dec</option> + </select> + </td> + </tr> + <tr> + <td>Expiration Year</td> + <td><input dojoType='dijit.form.NumberSpinner' constraints='{pattern:"0000", places:0, maxlength:4}' jsId='oilsSelfckCCYear' required='true'/></td> + </tr> + <tr> + <td>Edit Billing Details</td> + <td><input dojoType='dijit.form.CheckBox' jsId='oilsSelfckEditDetails'/></td> + </tr> + <tr> + <td>First Name</td> + <td><input dojoType='dijit.form.TextBox' jsId='oilsSelfckCCFName' disabled='disabled' required='true'/></td> + </tr> + <tr> + <td>Last Name</td> + <td><input dojoType='dijit.form.TextBox' jsId='oilsSelfckCCLName' disabled='disabled' required='true'/></td> + </tr> + <tr> + <td>Street Address</td> + <td><input dojoType='dijit.form.TextBox' jsId='oilsSelfckCCStreet' disabled='disabled' required='true'/></td> + </tr> + <tr> + <td>City</td> + <td><input dojoType='dijit.form.TextBox' jsId='oilsSelfckCCCity' disabled='disabled' required='true'/></td> + </tr> + <tr> + <td>State or Province</td> + <td><input dojoType='dijit.form.TextBox' jsId='oilsSelfckCCState' disabled='disabled' required='true'/></td> + </tr> + <tr> + <td>ZIP or Postal Code</td> + <td><input dojoType='dijit.form.TextBox' jsId='oilsSelfckCCZip' disabled='disabled' required='true'/></td> + </tr> + <tr> + <td colspan='2' align='center'> + <button dojoType='dijit.form.Button' jsId='oilsSelfckCCSubmit'>Submit Payment</button> + </td> + </tr> + </tbody> +</table> diff --git a/Open-ILS/web/templates/default/circ/selfcheck/summary.tt2 b/Open-ILS/web/templates/default/circ/selfcheck/summary.tt2 index d708073efd..4794e4ec14 100644 --- a/Open-ILS/web/templates/default/circ/selfcheck/summary.tt2 +++ b/Open-ILS/web/templates/default/circ/selfcheck/summary.tt2 @@ -19,7 +19,9 @@ <fieldset> <legend>Fines</legend> <div id='oils-selfck-fines-total'></div> - <div><a href='javascript:void(0);' id='oils-selfck-pay-fines-link'>View Fines</a></div> + <div> + <span><a href='javascript:void(0);' id='oils-selfck-view-fines-link'>View Details</a></span> + </div> </fieldset> </div>