I have a simple form on a secure website which submits data using an AJAX process. The website's function is to track inventory and clients for a food pantry.
Everything is working great...except for one MAJOR problem...
We use a UPC barcode scanner to scan items while we take inventory and while we "check-out" our clients. However I am experiencing an odd behavior. When we are scanning items to check out clients as they take items from our pantry, on some of the scans the item is counted twice (thus "charging" the client for two items and deducting two from our inventory). On other occasions it'll scan once. The Barcode scanner only beeps one time, so I know it's sending the 12-digit code to the text field just once.
However, this NEVER happens when we manually enter the upc into the field which tells me that the process that occurs between the scan and the website is causing this.
Let me show you my code:
First you will see the form:
<div style="text-align: center">
<b>Scan Client ID</b><br />
<input autocomplete="off" type="text" name="clientid" style="font-size: 30px; width: 200px; height: 40px; color: gray" id="clientid" onchange="checkClient()" /><br />
<div id="cltresult"></div>
</div><br /><br />
<div id="shpitems" style="display: none">
<div style="float: left; width=40%;">
<b>Scan/Enter Item or click "Search"</b><br />
<form autocomplete="off" onkeypress="return alert('e.type: ' + e.type + '; key: ' + key); noenter(event)">
<input type="text" name="itemupc" id="itemupc" onkeyup="sellItem()" style="font-size: 20px; width: 175px; height: 30px; color: gray" />
<input type="button" value=" Enter " onclick="sellItem()" style="font-size: 18px; width: 100px; height: 35px; background-color: green; color: white;" />
<input type="button" value=" Search " name="itemsearch" id="itemsearch" onclick="popUP('https://<?php echo $_SESSION['siteurl'] ?>/scripts/checkout_search.php', 'Search', 600,450,'no'); return false;" style="font-size: 18px; width: 100px; height: 35px; background-color: red; color: white;" />
</form><br />
<input type="button" id="finish" value=" Finish " onclick="closeClient()" style="font-size: 18px; width: 100px; height: 35px; background-color: yellow; color: black;" />
</div>
<div style="float: right; text-align: left; width: 45%">
<b>Items Scanned</b><br />
<div id="shpresult" style="font-size: 10px;"></div>
<div id="closeresult"></div>
</div>
<div style="clear: both"></div>
</div>
</div>
There are actually two forms here; the first form simply verifies that the client exists in DB and if so, displays the second form, the form that accepts the UPCs. There's nothing wrong with the first form or the ajax.
Let me now post for you the two Javascripts with the ajax calls:
<script>
function checkClient()
{ checkClient: {
var clid = document.getElementById('clientid').value;
if(clid == '') {
break checkClient;
}
var xmlhttp;
if(window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function()
{
if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
if(xmlhttp.responseText == 'NULL') {
document.getElementById('clientid').disabled = false;
document.getElementById('cltresult').innerHTML = 'Client not found';
} else {
document.getElementById('clientid').disabled = true;
document.getElementById('shpitems').style.display = "block";
document.getElementById('cltresult').innerHTML = xmlhttp.responseText;
document.getElementById('itemupc').focus();
document.getElementById('itemupc').select();
}
}
}
xmlhttp.open("GET", "../private/jsp/ajax2.php?clientid=" + clid, true);
xmlhttp.send();
break checkClient;
}}
</script>
<script>
function sellItem()
{ sellItem: {
var item = document.getElementById('itemupc').value;
var clid = document.getElementById('clientid').value;
if(item.length != 12) {
break sellItem;
}
if(item == '900000100000') {
document.getElementById('itemupc').disabled = true;
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function()
{
if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
item = '';
document.getElementById('clientid').value = clid;
document.getElementById("shpresult").innerHTML = xmlhttp.responseText;
document.getElementById("itemupc").value = '';
document.getElementById('itemupc').disabled = false;
setTimeout(function() {document.getElementById('itemupc').focus()}, 1000);
document.getElementById('itemupc').select();
}
}
xmlhttp.open("GET","../private/jsp/ajax2.php?delcnt=" + item + "&sdclient=" + clid, true);
xmlhttp.send();
break sellItem;
}
else if(item == '900000120007') {
document.getElementById('itemupc').disabled = true;
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function()
{
if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
item = '';
document.getElementById('clientid').value = clid;
document.getElementById("shpresult").innerHTML = xmlhttp.responseText;
document.getElementById("itemupc").value = '';
document.getElementById('itemupc').disabled = false;
setTimeout(function() {document.getElementById('itemupc').focus()}, 1000);
document.getElementById('itemupc').select();
}
}
xmlhttp.open("GET","../private/jsp/ajax2.php?modpnts=" + item + "&sdclient=" + clid, true);
xmlhttp.send();
break sellItem;
} else {
document.getElementById('itemupc').disabled = true;
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function()
{
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
item = '';
document.getElementById('clientid').value = clid;
if(xmlhttp.responseText == 'PointsNULL') {
alert("The Client is out of points or does not have enough points for this item!");
}
document.getElementById("shpresult").innerHTML = xmlhttp.responseText;
document.getElementById("itemupc").value = '';
document.getElementById('itemupc').disabled = false;
setTimeout(function() {document.getElementById('itemupc').focus()}, 1000);
document.getElementById('itemupc').select();
}
}
xmlhttp.open("GET","../private/jsp/ajax2.php?getitem=" + item + "&sclient=" + clid, true);
xmlhttp.send();
break sellItem;
}
}}
</script>
The first <script> tag simply sends the scanned (or typed) data to the php script which verifies that the client exists, and if so, the result is to display the UPC form so we can scan our products.
The second <script> looks at the UPC scanned and after verifying that it is a 12-digit number, sends the scanned (or typed) data to the php script to be handled. The returned responseText is a running tally of items scanned along with their point value. I added a break function to both of these so that I can stop the script if there's an error, but I will likely strip that out once this problem is resolved.
Finally, the PHP
if(isset($_GET['clientid'])) {
$cnt = $db->fetchOne("SELECT COUNT(*) FROM tbl_clt WHERE reg_number = '$_GET[clientid]'");
if($cnt < 1) {
echo 'NULL';
die;
} else {
$stop = 0;
$clt = $db->fetchOneRow("SELECT * FROM tbl_clt WHERE reg_number = '$_GET[clientid]'");
$cmt = $db->fetchOne("SELECT COUNT(id) FROM tbl_cmt WHERE client_id = $clt->id AND comment_imp = 1 ORDER BY comment_date DESC LIMIT 1");
if($cmt == 1) {
echo '<font style="color: red; font-size: 26px; font-weight: bold;">NO SHOPPING ALLOWED!</font><br />';
die;
} else {
echo $clt->first_name .' '. $clt->last_name .'<br />';
echo 'Available Points: '. ($clt->app_points - $clt->used_points);
echo '<br /><br /><hr /><br />';
}
}
}
if(isset($_GET['getitem'])) {
$cnt = $db->fetchOne("SELECT COUNT(*) FROM tbl_cnt WHERE item_upc = '$_GET[getitem]'");
if($cnt < 1) {
echo 'Click "Override" to add the item manually.</font>';
die;
} else {
$pts = $db->fetchOneRow("SELECT app_points, used_points FROM tbl_clt WHERE reg_number = '$_GET[sclient]'");
$itm = $db->fetchOneRow("SELECT * FROM tbl_cnt WHERE item_upc = '$_GET[getitem]'");
$rempoints = ($pts->app_points - $pts->used_points) - $itm->item_points;
if($rempoints < $itm->item_points) {
echo 'PointsNULL';
die;
}
// Get Client PK
$clt = $db->fetchOneRow("SELECT id FROM tbl_clt WHERE reg_number = '$_GET[sclient]'");
// update client points
$db->update("UPDATE tbl_clt SET used_points = used_points + $itm->item_points WHERE id = '$clt->id'");
// update inventory
$db->update("UPDATE tbl_cnt SET item_act_count = item_act_count - 1 WHERE id = $itm->id");
// Add entry to DB
$db->insert("INSERT INTO tbl_trn (clid, dt, itm) VALUES ($clt->id, now(), $itm->id)");
$totalpts = 0;
echo '<table style="width: 99%">';
foreach($db->iterate("SELECT * FROM tbl_trn WHERE clid = $clt->id AND DATE(dt) = DATE(now()) ORDER BY id ASC") AS $row) {
echo '<tr>';
$col = $db->fetchOneRow("SELECT item_upc, item_brand, item_name, item_points FROM tbl_cnt WHERE id = $row->itm");
echo '<td>'. $col->item_upc .'</td><td>'. $col->item_brand .','. $col->item_name .'</td><td>'. $col->item_points .'</td>';
echo '</tr>';
$totalpts = $totalpts + $col->item_points;
}
echo '<tr><td colspan="2" style="text-align: right">Points Spent:</td><td>'. $totalpts .'</td></tr>';
echo '<tr><td colspan="2" style="text-align: right">Remaining Points:</td><td>'. $rempoints .'</td></tr>';
echo '</table>';
die;
}
}
The first request checks that the client exists and if so, displays the UPC-entering form. When a UPC is scanned, the $_GET['getitem'] script is called and it does what is commented.
Also, on our "Take Inventory" page, I use ajax as well. I was able to solve this scan issue (as we were scanning merchandise for counts, it would double every so often as well) by setting the xmlhttpRequest async to 'false' which I know isn't supposed to be done, but pramatically, it did the job. I haven't done this with the client checkout page yet, but if anyone here is unable to help me figure this out, I may try that next.
Thanks!
PS, because the bar code scanner automatically sends a CR after each scan, I used javascript to disable Key 13 or Enter. When the barcode scanner sends the CR command, it is skipped. The javascript in the form (onkeyup) handles the submission of the form - so that the user doesn't have to press 'enter' each time. Once 12 characters is reached the js goes to work.