osCommerce fails to check stock level correctly when going through the order process. The problem is two fold, and is worsened by the use of PayPal. If you use PayPal for payments, this code will not work as expected for you.
The checkout_process.php file is the final script in the checkout workflow, and performs the actual order processing itself, including inserting the required entries into the database.
The process code *does* check stock level, but fails to do something useful with it.
The $stock_level variable, is the total number of items in stock *after* the item is checked out. osCommerce will allow you to order a product with quantity 2, even if there is only one left.
The below code ensures this does not occur. If stock level after the current order is processed is below 0 (i.e. -1 or lower), then the user is redirected to the shopping cart page.
In osCommerce v2.3.1, this code below must be placed between line 173 and line 174 of checkout_process.php.
######## added by aknowles - go back to cart, no stock left!!
if ( ($stock_left < 0) && (STOCK_ALLOW_CHECKOUT == 'false') ) {
if (tep_session_is_registered('shipping')) tep_session_unregister('shipping');
if (tep_session_is_registered('payment')) tep_session_unregister('payment');
tep_redirect(tep_href_link(FILENAME_SHOPPING_CART));
}
######## end of code added by aknowles
When completed, it should look like this:
if (tep_db_num_rows($stock_query) > 0) {
$stock_values = tep_db_fetch_array($stock_query);
// do not decrement quantities if products_attributes_filename exists
if ((DOWNLOAD_ENABLED != 'true') || (!$stock_values['products_attributes_filename'])) {
$stock_left = $stock_values['products_quantity'] - $order->products[$i]['qty'];
} else {
$stock_left = $stock_values['products_quantity'];
}
######## added by aknowles – go back to cart, no stock left!!
if ( ($stock_left < 0) && (STOCK_ALLOW_CHECKOUT == ‘false’) ) { if (tep_session_is_registered(‘shipping’)) tep_session_unregister(‘shipping’); if (tep_session_is_registered(‘payment’)) tep_session_unregister(‘payment’); tep_redirect(tep_href_link(FILENAME_SHOPPING_CART)); } ######## end of code added by aknowles tep_db_query(“update ” . TABLE_PRODUCTS . ” set products_quantity = ‘” . $stock_left . “‘ where products_id = ‘” . tep_get_prid($order->products[$i][‘id’]) . “‘”);
if ( ($stock_left < 1) && (STOCK_ALLOW_CHECKOUT == 'false') ) { tep_db_query("update " . TABLE_PRODUCTS . " set products_status = '0' where products_id = '" . tep_get_prid($order->products[$i]['id']) . "'");
}
}
The problem, and why it won’t work with PayPal:
The workflow of the osCommerce checkout is bizzare. My client’s business posts a number of items for sale, and opens them up at a specified time, at which point, her online store is bumrushed for the same product. This results in multiple people checking out the same product, and the stock level not being updated properly by osCommerce. The problem lies in the use of PayPal specifically. The user’s order is confirmed only when payment is processed by PayPal. The problem here, however, is that stock is not adjusted before the PayPal process is started. So if multiple people are going through the same checkout workflow at the same time for the same item, once they have reached the PayPal checkout, no matter how long it takes them to confirm the payment, the order will still go through, regardless of stock level, when PayPal returns to osCommerce.
IF you use this code with PayPal, it will not process the order, despite the user having paid.
This code is best used with manual payment processing only.
To fix the stock issue, and utilise PayPal, you must go through a larger re-development process. Next option for this is for me to update the checkout confirmation page, to check stock and deduct appropriately – confirming the order in osCommerce and adjusting stock levels – and then allowing the user to pay via PayPal. Cancelled payments via PayPal will then reverse the order.
That is a great post Ashley, but did you follow on the last paragraph? PayPal and stock deduction is a big issue and one that has reared its ugly head many times.