Example #1
0
 /**
  * Prepare subtotal and line-item detail content to send to PayPal
  */
 function getLineItemDetails($restrictedCurrency)
 {
     global $order, $currencies, $order_totals, $order_total_modules;
     // if not default currency, do not send subtotals or line-item details
     if (DEFAULT_CURRENCY != $order->info['currency'] || $restrictedCurrency != DEFAULT_CURRENCY) {
         $this->zcLog('getLineItemDetails 1', 'Not using default currency. Thus, no line-item details can be submitted.');
         return array();
     }
     if ($currencies->currencies[$_SESSION['currency']]['value'] != 1 || $currencies->currencies[$order->info['currency']]['value'] != 1) {
         $this->zcLog('getLineItemDetails 2', 'currency val not equal to 1.0000 - cannot proceed without coping with currency conversions. Aborting line-item details.');
         return array();
     }
     $optionsST = array();
     $optionsLI = array();
     $optionsNB = array();
     $numberOfLineItemsProcessed = 0;
     $creditsApplied = 0;
     $surcharges = 0;
     $sumOfLineItems = 0;
     $sumOfLineTax = 0;
     $optionsST['AMT'] = 0;
     $optionsST['ITEMAMT'] = 0;
     $optionsST['TAXAMT'] = 0;
     $optionsST['SHIPPINGAMT'] = 0;
     $optionsST['SHIPDISCAMT'] = 0;
     $optionsST['HANDLINGAMT'] = 0;
     $optionsST['INSURANCEAMT'] = 0;
     $flagSubtotalsUnknownYet = true;
     $subTotalLI = 0;
     $subTotalTax = 0;
     $subTotalShipping = 0;
     $subtotalPRE = array('no data');
     $discountProblemsFlag = FALSE;
     $flag_treat_as_partial = FALSE;
     if (sizeof($order_totals)) {
         // prepare subtotals
         for ($i = 0, $n = sizeof($order_totals); $i < $n; $i++) {
             if ($order_totals[$i]['code'] == '') {
                 continue;
             }
             if (in_array($order_totals[$i]['code'], array('ot_total', 'ot_subtotal', 'ot_tax', 'ot_shipping')) || strstr($order_totals[$i]['code'], 'insurance')) {
                 if ($order_totals[$i]['code'] == 'ot_shipping') {
                     $optionsST['SHIPPINGAMT'] = round($order_totals[$i]['value'], 2);
                 }
                 if ($order_totals[$i]['code'] == 'ot_total') {
                     $optionsST['AMT'] = round($order_totals[$i]['value'], 2);
                 }
                 if ($order_totals[$i]['code'] == 'ot_tax') {
                     $optionsST['TAXAMT'] = round($order_totals[$i]['value'], 2);
                 }
                 if ($order_totals[$i]['code'] == 'ot_subtotal') {
                     $optionsST['ITEMAMT'] = round($order_totals[$i]['value'], 2);
                 }
                 if (strstr($order_totals[$i]['code'], 'insurance')) {
                     $optionsST['INSURANCEAMT'] += round($order_totals[$i]['value'], 2);
                 }
                 //$optionsST['SHIPDISCAMT'] = '';  // Not applicable
             } else {
                 // handle other order totals:
                 global ${$order_totals[$i]['code']};
                 if (substr($order_totals[$i]['text'], 0, 1) == '-' || isset(${$order_totals[$i]['code']}->credit_class) && ${$order_totals[$i]['code']}->credit_class == true) {
                     // handle credits
                     $creditsApplied += round($order_totals[$i]['value'], 2);
                 } else {
                     // treat all other OT's as if they're related to handling fees or other extra charges to be added/included
                     $surcharges += $order_totals[$i]['value'];
                 }
             }
         }
         if ($creditsApplied > 0) {
             $optionsST['ITEMAMT'] -= $creditsApplied;
         }
         if ($surcharges > 0) {
             $optionsST['ITEMAMT'] += $surcharges;
         }
         // Handle tax-included scenario
         if (DISPLAY_PRICE_WITH_TAX == 'true') {
             $optionsST['TAXAMT'] = 0;
         }
         $subtotalPRE = $optionsST;
         // Move shipping tax amount from Tax subtotal into Shipping subtotal for submission to PayPal, since PayPal applies tax to each line-item individually
         $module = substr($_SESSION['shipping']['id'], 0, strpos($_SESSION['shipping']['id'], '_'));
         if (zen_not_null($order->info['shipping_method']) && DISPLAY_PRICE_WITH_TAX != 'true') {
             if ($GLOBALS[$module]->tax_class > 0) {
                 $shipping_tax_basis = !isset($GLOBALS[$module]->tax_basis) ? STORE_SHIPPING_TAX_BASIS : $GLOBALS[$module]->tax_basis;
                 $shippingOnBilling = zen_get_tax_rate($GLOBALS[$module]->tax_class, $order->billing['country']['id'], $order->billing['zone_id']);
                 $shippingOnDelivery = zen_get_tax_rate($GLOBALS[$module]->tax_class, $order->delivery['country']['id'], $order->delivery['zone_id']);
                 if ($shipping_tax_basis == 'Billing') {
                     $shipping_tax = $shippingOnBilling;
                 } elseif ($shipping_tax_basis == 'Shipping') {
                     $shipping_tax = $shippingOnDelivery;
                 } else {
                     if (STORE_ZONE == $order->billing['zone_id']) {
                         $shipping_tax = $shippingOnBilling;
                     } elseif (STORE_ZONE == $order->delivery['zone_id']) {
                         $shipping_tax = $shippingOnDelivery;
                     } else {
                         $shipping_tax = 0;
                     }
                 }
                 $taxAdjustmentForShipping = zen_round(zen_calculate_tax($order->info['shipping_cost'], $shipping_tax), $currencies->currencies[$_SESSION['currency']]['decimal_places']);
                 $optionsST['SHIPPINGAMT'] += $taxAdjustmentForShipping;
                 $optionsST['TAXAMT'] -= $taxAdjustmentForShipping;
             }
         }
         $flagSubtotalsUnknownYet = $optionsST['SHIPPINGAMT'] + $optionsST['SHIPDISCAMT'] + $optionsST['AMT'] + $optionsST['TAXAMT'] + $optionsST['ITEMAMT'] + $optionsST['INSURANCEAMT'] == 0;
     } else {
         // if we get here, we don't have any order-total information yet because the customer has clicked Express before starting normal checkout flow
         // thus, we must make a note to manually calculate subtotals, rather than relying on the more robust order-total infrastructure
         $flagSubtotalsUnknownYet = TRUE;
     }
     // loop thru all products to prepare details of quantity and price.
     for ($i = 0, $n = sizeof($order->products), $k = 0; $i < $n; $i++, $k++) {
         // PayPal won't accept zero-value line-items, so skip this entry if price is zero
         if ($order->products[$i]['final_price'] == 0) {
             continue;
         }
         $optionsLI["L_NUMBER{$k}"] = $order->products[$i]['model'];
         $optionsLI["L_NAME{$k}"] = $order->products[$i]['name'];
         // Append *** if out-of-stock.
         $optionsLI["L_NAME{$k}"] .= zen_get_products_stock($order->products[$i]['id']) - $order->products[$i]['qty'] < 0 ? STOCK_MARK_PRODUCT_OUT_OF_STOCK : '';
         // if there are attributes, loop thru them and add to description
         if (isset($order->products[$i]['attributes']) && sizeof($order->products[$i]['attributes']) > 0) {
             $optionsLI["L_DESC{$k}"] = '';
             for ($j = 0, $n2 = sizeof($order->products[$i]['attributes']); $j < $n2; $j++) {
                 $optionsLI["L_DESC{$k}"] .= "\n " . $order->products[$i]['attributes'][$j]['option'] . ': ' . $order->products[$i]['attributes'][$j]['value'];
             }
             // end loop
         }
         // endif attribute-info
         // check for rounding problems with taxes
         $m1 = zen_round($order->products[$i]['qty'] * $order->products[$i]['final_price'], $currencies->currencies[$_SESSION['currency']]['decimal_places']);
         $m2 = $order->products[$i]['qty'] * zen_round($order->products[$i]['final_price'], $currencies->currencies[$_SESSION['currency']]['decimal_places']);
         $n1 = $order->products[$i]['qty'] * zen_calculate_tax($order->products[$i]['final_price'], $order->products[$i]['tax']);
         $n2 = zen_calculate_tax($order->products[$i]['qty'] * $order->products[$i]['final_price'], $order->products[$i]['tax']);
         if ($m1 != $m2 || zen_round($n1, $currencies->currencies[$_SESSION['currency']]['decimal_places']) != zen_round($n2, $currencies->currencies[$_SESSION['currency']]['decimal_places'])) {
             $flag_treat_as_partial = true;
         }
         // PayPal can't handle partial-quantity values, so fudge it here
         if ($flag_treat_as_partial || $order->products[$i]['qty'] != (int) $order->products[$i]['qty']) {
             $optionsLI["L_NAME{$k}"] = '(' . $order->products[$i]['qty'] . ' x ) ' . $optionsLI["L_NAME{$k}"];
             $optionsLI["L_AMT{$k}"] = zen_round($order->products[$i]['qty'] * $order->products[$i]['final_price'], $currencies->currencies[$_SESSION['currency']]['decimal_places']);
             $optionsLI["L_TAXAMT{$k}"] = zen_calculate_tax(zen_round($order->products[$i]['qty'] * $order->products[$i]['final_price'], $currencies->currencies[$_SESSION['currency']]['decimal_places']), $order->products[$i]['tax']);
             $optionsLI["L_QTY{$k}"] = 1;
         } else {
             $optionsLI["L_AMT{$k}"] = $order->products[$i]['final_price'];
             $optionsLI["L_QTY{$k}"] = $order->products[$i]['qty'];
             $optionsLI["L_TAXAMT{$k}"] = zen_calculate_tax(1 * $order->products[$i]['final_price'], $order->products[$i]['tax']);
         }
         // For tax-included pricing, combine tax with price instead of treating separately:
         if (DISPLAY_PRICE_WITH_TAX == 'true') {
             $optionsLI["L_AMT{$k}"] += $optionsLI["L_TAXAMT{$k}"];
             $optionsLI["L_TAXAMT{$k}"] = 0;
         }
         $subTotalLI += $optionsLI["L_QTY{$k}"] * $optionsLI["L_AMT{$k}"];
         $subTotalTax += $optionsLI["L_QTY{$k}"] * $optionsLI["L_TAXAMT{$k}"];
         // add line-item for one-time charges on this product
         if ($order->products[$i]['onetime_charges'] != 0) {
             $k++;
             $optionsLI["L_NAME{$k}"] = MODULES_PAYMENT_PAYPALWPP_LINEITEM_TEXT_ONETIME_CHARGES_PREFIX . substr(htmlentities($order->products[$i]['name'], ENT_QUOTES, 'UTF-8'), 0, 120);
             $optionsLI["L_AMT{$k}"] = $order->products[$i]['onetime_charges'];
             $optionsLI["L_QTY{$k}"] = 1;
             $optionsLI["L_TAXAMT{$k}"] = zen_calculate_tax($order->products[$i]['onetime_charges'], $order->products[$i]['tax']);
             $subTotalLI += $order->products[$i]['onetime_charges'];
             $subTotalTax += $optionsLI["L_TAXAMT{$k}"];
         }
         $numberOfLineItemsProcessed = $k;
     }
     // end for loopthru all products
     // add line items for any surcharges added by order-total modules
     if ($surcharges > 0) {
         $numberOfLineItemsProcessed++;
         $k = $numberOfLineItemsProcessed;
         $optionsLI["L_NAME{$k}"] = MODULES_PAYMENT_PAYPALWPP_LINEITEM_TEXT_SURCHARGES_SHORT;
         $optionsLI["L_DESC{$k}"] = MODULES_PAYMENT_PAYPALWPP_LINEITEM_TEXT_SURCHARGES_LONG;
         $optionsLI["L_AMT{$k}"] = $surcharges;
         $optionsLI["L_QTY{$k}"] = 1;
         $subTotalLI += $surcharges;
     }
     // add line items for discounts such as gift certificates and coupons
     if ($creditsApplied > 0) {
         $numberOfLineItemsProcessed++;
         $k = $numberOfLineItemsProcessed;
         $optionsLI["L_NAME{$k}"] = MODULES_PAYMENT_PAYPALWPP_LINEITEM_TEXT_DISCOUNTS_SHORT;
         $optionsLI["L_DESC{$k}"] = MODULES_PAYMENT_PAYPALWPP_LINEITEM_TEXT_DISCOUNTS_LONG;
         $optionsLI["L_AMT{$k}"] = -1 * $creditsApplied;
         $optionsLI["L_QTY{$k}"] = 1;
         $subTotalLI -= $creditsApplied;
     }
     // Reformat properly
     for ($k = 0, $n = $numberOfLineItemsProcessed + 1; $k < $n; $k++) {
         // Replace & and = and % with * if found.
         $optionsLI["L_NAME{$k}"] = str_replace(array('&', '=', '%'), '*', $optionsLI["L_NAME{$k}"]);
         if (isset($optionsLI["L_DESC{$k}"])) {
             $optionsLI["L_DESC{$k}"] = str_replace(array('&', '=', '%'), '*', $optionsLI["L_DESC{$k}"]);
         }
         if (isset($optionsLI["L_NUMBER{$k}"])) {
             $optionsLI["L_NUMBER{$k}"] = str_replace(array('&', '=', '%'), '*', $optionsLI["L_NUMBER{$k}"]);
         }
         // Remove HTML markup if found
         $optionsLI["L_NAME{$k}"] = zen_clean_html($optionsLI["L_NAME{$k}"], 'strong');
         if (isset($optionsLI["L_DESC{$k}"])) {
             $optionsLI["L_DESC{$k}"] = zen_clean_html($optionsLI["L_DESC{$k}"], 'strong');
         }
         // reformat properly according to API specs
         $optionsLI["L_NAME{$k}"] = substr($optionsLI["L_NAME{$k}"], 0, 127);
         if (isset($optionsLI["L_NUMBER{$k}"])) {
             $optionsLI["L_NUMBER{$k}"] = substr($optionsLI["L_NUMBER{$k}"], 0, 127);
         }
         if (isset($optionsLI["L_DESC{$k}"]) && $optionsLI["L_DESC{$k}"] == '') {
             unset($optionsLI["L_DESC{$k}"]);
         }
         if (isset($optionsLI["L_DESC{$k}"])) {
             $optionsLI["L_DESC{$k}"] = substr($optionsLI["L_DESC{$k}"], 0, 127);
         }
         if (isset($optionsLI["L_TAXAMT{$k}"]) && ($optionsLI["L_TAXAMT{$k}"] != '' || $optionsLI["L_TAXAMT{$k}"] > 0)) {
             $optionsLI["L_TAXAMT{$k}"] = round($optionsLI["L_TAXAMT{$k}"], 2);
         }
     }
     /**
      * PayPal says their math works like this:
      * a) ITEMAMT = L_AMTn * L_QTYn
      * b) TAXAMT = L_QTYn * L_TAXAMTn
      * c) AMT = ITEMAMT + SHIPPINGAMT + HANDLINGAMT + TAXAMT
      */
     // Sanity Check of line-item subtotals
     for ($j = 0; $j < $k; $j++) {
         $itemAMT = $optionsLI["L_AMT{$j}"];
         $itemQTY = $optionsLI["L_QTY{$j}"];
         $itemTAX = isset($optionsLI["L_TAXAMT{$j}"]) ? $optionsLI["L_TAXAMT{$j}"] : 0;
         $sumOfLineItems += $itemQTY * $itemAMT;
         $sumOfLineTax += $itemQTY * $itemTAX;
     }
     $sumOfLineItems = round($sumOfLineItems, 2);
     $sumOfLineTax = round($sumOfLineTax, 2);
     if ($sumOfLineItems == 0) {
         $sumOfLineTax = 0;
         $optionsLI = array();
         $discountProblemsFlag = TRUE;
         if ($optionsST['SHIPPINGAMT'] == $optionsST['AMT']) {
             $optionsST['SHIPPINGAMT'] = 0;
         }
     }
     // Sanity check -- if tax-included pricing is causing problems, remove the numbers and put them in a comment instead:
     $stDiffTaxOnly = strval($sumOfLineItems - $sumOfLineTax - round($optionsST['AMT'], 2)) + 0;
     if (DISPLAY_PRICE_WITH_TAX == 'true' && $stDiffTaxOnly == 0) {
         $optionsNB['DESC'] = 'Tax included in prices: ' . $sumOfLineTax . ' (' . $optionsST['TAXAMT'] . ') ';
         $optionsST['TAXAMT'] = 0;
         for ($k = 0, $n = $numberOfLineItemsProcessed + 1; $k < $n; $k++) {
             if (isset($optionsLI["L_TAXAMT{$k}"])) {
                 unset($optionsLI["L_TAXAMT{$k}"]);
             }
         }
     }
     // Do sanity check -- if any of the line-item subtotal math doesn't add up properly, skip line-item details,
     // so that the order can go through even though PayPal isn't being flexible to handle Zen Cart's diversity
     if (strval($subTotalTax) - strval($sumOfLineTax) > 0.02) {
         $this->zcLog('getLineItemDetails 3', 'Tax Subtotal does not match sum of taxes for line-items. Tax details are being removed from line-item submission data.' . "\n" . $sumOfLineTax . ' ' . $subTotalTax . print_r(array_merge($optionsST, $optionsLI), true));
         for ($k = 0, $n = $numberOfLineItemsProcessed + 1; $k < $n; $k++) {
             if (isset($optionsLI["L_TAXAMT{$k}"])) {
                 unset($optionsLI["L_TAXAMT{$k}"]);
             }
         }
         $subTotalTax = 0;
         $sumOfLineTax = 0;
     }
     // If coupons exist and there's a calculation problem, then it's likely that taxes are incorrect, so reset L_TAXAMTn values
     if ($creditsApplied > 0 && strval($optionsST['TAXAMT']) != strval($sumOfLineTax)) {
         $pre = $optionsLI;
         for ($k = 0, $n = $numberOfLineItemsProcessed + 1; $k < $n; $k++) {
             if (isset($optionsLI["L_TAXAMT{$k}"])) {
                 unset($optionsLI["L_TAXAMT{$k}"]);
             }
         }
         $this->zcLog('getLineItemDetails 4', 'Coupons/Discounts have affected tax calculations, so tax details are being removed from line-item submission data.' . "\n" . $sumOfLineTax . ' ' . $optionsST['TAXAMT'] . "\n" . print_r(array_merge($optionsST, $pre, $optionsNB), true) . "\nAFTER:" . print_r(array_merge($optionsST, $optionsLI, $optionsNB), TRUE));
         $subTotalTax = 0;
         $sumOfLineTax = 0;
     }
     if (TRUE) {
         // disable line-item tax details, leaving only TAXAMT subtotal as tax indicator
         for ($k = 0, $n = $numberOfLineItemsProcessed + 1; $k < $n; $k++) {
             if (isset($optionsLI["L_TAXAMT{$k}"])) {
                 unset($optionsLI["L_TAXAMT{$k}"]);
             }
         }
     }
     // check subtotals
     if (strval($optionsST['ITEMAMT']) > 0 && strval($subTotalLI) > 0 && strval($subTotalLI) != strval($optionsST['ITEMAMT']) || strval($subTotalLI) - strval($sumOfLineItems) != 0) {
         $this->zcLog('getLineItemDetails 5', 'Line-item subtotals do not add up properly. Line-item-details skipped.' . "\n" . (double) $sumOfLineItems . ' ' . (double) $subTotalTax . print_r(array_merge($optionsST, $optionsLI), true));
         $optionsLI = array();
     }
     // check whether discounts are causing a problem
     if (strval($optionsST['ITEMAMT']) < 0) {
         $pre = array_merge($optionsST, $optionsLI);
         $optionsLI = array();
         $optionsST['ITEMAMT'] = $optionsST['AMT'];
         if ($optionsST['AMT'] < $optionsST['TAXAMT']) {
             $optionsST['TAXAMT'] = 0;
         }
         if ($optionsST['AMT'] < $optionsST['SHIPPINGAMT']) {
             $optionsST['SHIPPINGAMT'] = 0;
         }
         $discountProblemsFlag = TRUE;
         $this->zcLog('getLineItemDetails 6', 'Discounts have caused the subtotal to calculate incorrectly. Line-item-details cannot be submitted.' . "\nBefore:" . print_r($pre, TRUE) . "\nAfter:" . print_r(array_merge($optionsST, $optionsLI), true));
     }
     // if AMT or ITEMAMT values are 0 (ie: certain OT modules disabled) or we've started express checkout without going through normal checkout flow, we have to get subtotals manually
     if ((!isset($optionsST['AMT']) || $optionsST['AMT'] == 0 || $flagSubtotalsUnknownYet == TRUE || $optionsST['ITEMAMT'] == 0) && $discountProblemsFlag != TRUE) {
         $optionsST['ITEMAMT'] = $sumOfLineItems;
         $optionsST['TAXAMT'] = $sumOfLineTax;
         if ($subTotalShipping > 0) {
             $optionsST['SHIPPINGAMT'] = $subTotalShipping;
         }
         $optionsST['AMT'] = $sumOfLineItems + $optionsST['TAXAMT'] + $optionsST['SHIPPINGAMT'];
     }
     $this->zcLog('getLineItemDetails 7 - subtotal comparisons', 'BEFORE line-item calcs: ' . print_r($subtotalPRE, true) . ($flagSubtotalsUnknownYet == TRUE ? 'Subtotals Unknown Yet' : '') . ' - AFTER doing line-item calcs: ' . print_r(array_merge($optionsST, $optionsLI, $optionsNB), true));
     // if subtotals are not adding up correctly, then skip sending any line-item or subtotal details to PayPal
     $stAll = round(strval($optionsST['ITEMAMT'] + $optionsST['TAXAMT'] + $optionsST['SHIPPINGAMT'] + $optionsST['SHIPDISCAMT'] + $optionsST['HANDLINGAMT'] + $optionsST['INSURANCEAMT']), 2);
     $stDiff = strval($optionsST['AMT'] - $stAll);
     $stDiffRounded = strval($stAll - round($optionsST['AMT'], 2)) + 0;
     // unset any subtotal values that are zero
     if (isset($optionsST['ITEMAMT']) && $optionsST['ITEMAMT'] == 0) {
         unset($optionsST['ITEMAMT']);
     }
     if (isset($optionsST['TAXAMT']) && $optionsST['TAXAMT'] == 0) {
         unset($optionsST['TAXAMT']);
     }
     if (isset($optionsST['SHIPPINGAMT']) && $optionsST['SHIPPINGAMT'] == 0) {
         unset($optionsST['SHIPPINGAMT']);
     }
     if (isset($optionsST['SHIPDISCAMT']) && $optionsST['SHIPDISCAMT'] == 0) {
         unset($optionsST['SHIPDISCAMT']);
     }
     if (isset($optionsST['HANDLINGAMT']) && $optionsST['HANDLINGAMT'] == 0) {
         unset($optionsST['HANDLINGAMT']);
     }
     if (isset($optionsST['INSURANCEAMT']) && $optionsST['INSURANCEAMT'] == 0) {
         unset($optionsST['INSURANCEAMT']);
     }
     // tidy up all values so that they comply with proper format (number_format(xxxx,2) for PayPal US use )
     if (!defined('PAYPALWPP_SKIP_LINE_ITEM_DETAIL_FORMATTING') || PAYPALWPP_SKIP_LINE_ITEM_DETAIL_FORMATTING != 'true' || in_array($order->info['currency'], array('JPY', 'NOK'))) {
         if (is_array($optionsST)) {
             foreach ($optionsST as $key => $value) {
                 $optionsST[$key] = number_format($value, $order->info['currency'] == 'JPY' ? 0 : 2);
             }
         }
         if (is_array($optionsLI)) {
             foreach ($optionsLI as $key => $value) {
                 if (substr($key, 0, 8) == 'L_TAXAMT' && ($optionsLI[$key] == '' || $optionsLI[$key] == 0)) {
                     unset($optionsLI[$key]);
                 } else {
                     if (strstr($key, 'AMT')) {
                         $optionsLI[$key] = number_format($value, $order->info['currency'] == 'JPY' ? 0 : 2);
                     }
                 }
             }
         }
     }
     $this->zcLog('getLineItemDetails 8', 'checking subtotals... ' . "\n" . print_r(array_merge(array('calculated total' => number_format($stAll, $order->info['currency'] == 'JPY' ? 0 : 2)), $optionsST), true) . "\n-------------------\ndifference: " . ($stDiff + 0) . '  (abs+rounded: ' . ($stDiffRounded + 0) . ')');
     if ($stDiffRounded != 0) {
         $this->zcLog('getLineItemDetails 9', 'Subtotals Bad. Skipping line-item/subtotal details');
         return array();
     }
     $this->zcLog('getLineItemDetails 10', 'subtotals balance - okay');
     // Send Subtotal and LineItem results back to be submitted to PayPal
     return array_merge($optionsST, $optionsLI, $optionsNB);
 }
 /**
  * Prepare subtotal and line-item detail content to send to PayPal
  */
 function getLineItemDetails()
 {
     global $order, $currencies, $order_totals, $order_total_modules;
     $optionsST = array();
     $optionsLI = array();
     $onetimeSum = 0;
     $onetimeTax = 0;
     $creditsApplied = 0;
     $creditsTax_applied = 0;
     $sumOfLineItems = 0;
     $sumOfLineTax = 0;
     // prepare subtotals
     for ($i = 0, $n = sizeof($order_totals); $i < $n; $i++) {
         if ($order_totals[$i]['code'] == 'ot_subtotal') {
             $optionsST['ITEMAMT'] = round($order_totals[$i]['value'], 2);
         }
         if ($order_totals[$i]['code'] == 'ot_tax') {
             $optionsST['TAXAMT'] = round($order_totals[$i]['value'], 2);
         }
         if ($order_totals[$i]['code'] == 'ot_shipping') {
             $optionsST['SHIPPINGAMT'] = round($order_totals[$i]['value'], 2);
         }
         if ($order_totals[$i]['code'] == 'ot_total') {
             $optionsST['AMT'] = round($order_totals[$i]['value'], 2);
         }
         $optionsST['HANDLINGAMT'] = 0;
         global ${$order_totals[$i]['code']};
         if (isset(${$order_totals[$i]['code']}->credit_class) && ${$order_totals[$i]['code']}->credit_class == true) {
             $creditsApplied += round($order_totals[$i]['value'], 2);
         }
         // treat all other OT's as if they're related to handling fees
         if (!in_array($order_totals[$i]['code'], array('ot_total', 'ot_subtotal', 'ot_tax', 'ot_shipping')) && !(isset(${$order_totals[$i]['code']}->credit_class) && ${$order_totals[$i]['code']}->credit_class == true)) {
             $optionsST['HANDLINGAMT'] += $order_totals[$i]['value'];
         }
     }
     // Move shipping tax amount from Tax subtotal into Shipping subtotal for submission to PayPal
     $module = substr($_SESSION['shipping']['id'], 0, strpos($_SESSION['shipping']['id'], '_'));
     if (zen_not_null($order->info['shipping_method'])) {
         if ($GLOBALS[$module]->tax_class > 0) {
             $shipping_tax_basis = !isset($GLOBALS[$module]->tax_basis) ? STORE_SHIPPING_TAX_BASIS : $GLOBALS[$module]->tax_basis;
             $shippingOnBilling = zen_get_tax_rate($GLOBALS[$module]->tax_class, $order->billing['country']['id'], $order->billing['zone_id']);
             $shippingOnDelivery = zen_get_tax_rate($GLOBALS[$module]->tax_class, $order->delivery['country']['id'], $order->delivery['zone_id']);
             if ($shipping_tax_basis == 'Billing') {
                 $shipping_tax = $shippingOnBilling;
             } elseif ($shipping_tax_basis == 'Shipping') {
                 $shipping_tax = $shippingOnDelivery;
             } else {
                 if (STORE_ZONE == $order->billing['zone_id']) {
                     $shipping_tax = $shippingOnBilling;
                 } elseif (STORE_ZONE == $order->delivery['zone_id']) {
                     $shipping_tax = $shippingOnDelivery;
                 } else {
                     $shipping_tax = 0;
                 }
             }
             $taxAdjustmentForShipping = zen_calculate_tax($order->info['shipping_cost'], $shipping_tax);
             $optionsST['SHIPPINGAMT'] += $taxAdjustmentForShipping;
             $optionsST['TAXAMT'] -= $taxAdjustmentForShipping;
         }
     }
     // loop thru all products to display quantity and price. Appends *** if out-of-stock.
     for ($i = 0, $n = sizeof($order->products), $k = 0; $i < $n; $i++, $k++) {
         $optionsLI["L_NUMBER{$k}"] = $order->products[$i]['model'];
         $optionsLI["L_QTY{$k}"] = (int) $order->products[$i]['qty'];
         $optionsLI["L_NAME{$k}"] = $order->products[$i]['name'];
         $optionsLI["L_NAME{$k}"] .= zen_get_products_stock($order->products[$i]['id']) - $order->products[$i]['qty'] < 0 ? STOCK_MARK_PRODUCT_OUT_OF_STOCK : '';
         // if there are attributes, loop thru them and add to description
         if (isset($order->products[$i]['attributes']) && sizeof($order->products[$i]['attributes']) > 0) {
             for ($j = 0, $n2 = sizeof($order->products[$i]['attributes']); $j < $n2; $j++) {
                 $optionsLI["L_NAME{$k}"] .= "\n " . $order->products[$i]['attributes'][$j]['option'] . ': ' . $order->products[$i]['attributes'][$j]['value'];
             }
             // end loop
         }
         // endif attribute-info
         $optionsLI["L_AMT{$k}"] = $order->products[$i]['final_price'];
         $optionsLI["L_TAXAMT{$k}"] = zen_calculate_tax($order->products[$i]['final_price'], $order->products[$i]['tax']);
         // track one-time charges
         if ($order->products[$i]['onetime_charges'] != 0) {
             $onetimeSum += $order->products[$i]['onetime_charges'];
             $onetimeTax += zen_calculate_tax($order->products[$i]['onetime_charges'], $order->products[$i]['tax']);
         }
         // Replace & and = with * if found.
         $optionsLI["L_NAME{$k}"] = str_replace(array('&', '='), '*', $optionsLI["L_NAME{$k}"]);
         $optionsLI["L_NAME{$k}"] = zen_clean_html($optionsLI["L_NAME{$k}"], 'strong');
         // reformat properly
         $optionsLI["L_NUMBER{$k}"] = substr($optionsLI["L_NUMBER{$k}"], 0, 127);
         $optionsLI["L_NAME{$k}"] = substr($optionsLI["L_NAME{$k}"], 0, 127);
         $optionsLI["L_AMT{$k}"] = $optionsLI["L_AMT{$k}"];
         $optionsLI["L_TAXAMT{$k}"] = round($optionsLI["L_TAXAMT{$k}"], 2);
     }
     // end for loopthru all products
     if ($onetimeSum > 0) {
         $i++;
         $k++;
         $optionsLI["L_NUMBER{$k}"] = $k;
         $optionsLI["L_NAME{$k}"] = 'One-Time Charges';
         $optionsLI["L_AMT{$k}"] = $onetimeSum;
         $optionsLI["L_TAXAMT{$k}"] = $onetimeTax;
         $optionsLI["L_QTY{$k}"] = 1;
     }
     // handle discounts such as gift certificates and coupons
     if ($creditsApplied > 0) {
         $optionsST['HANDLINGAMT'] -= $creditsApplied;
     }
     // add all one-time charges
     $optionsST['ITEMAMT'] += $onetimeSum;
     //ensure things are not negative
     $optionsST['HANDLINGAMT'] = abs(strval($optionsST['HANDLINGAMT']));
     // ensure all numbers are non-negative
     if (is_array($optionsST)) {
         foreach ($optionsST as $key => $value) {
             $optionsST[$key] = abs(strval($value));
         }
     }
     if (is_array($optionsLI)) {
         foreach ($optionsLI as $key => $value) {
             if (strstr($key, 'AMT')) {
                 $optionsLI[$key] = abs(strval($value));
             }
         }
     }
     // subtotals have to add up to AMT
     // Thus, if there is a discrepancy, make adjustment to HANDLINGAMT:
     $st = $optionsST['ITEMAMT'] + $optionsST['TAXAMT'] + $optionsST['SHIPPINGAMT'] + $optionsST['HANDLINGAMT'];
     if ($st != $optionsST['AMT']) {
         $optionsST['HANDLINGAMT'] += strval($optionsST['AMT'] - $st);
     }
     /*  //PayPal API spec contradicts itself ... and apparently neither of these "requirements" are enforced. 
         //Thus skipping this section for now:
     
         // according to API specs, these cannot be set if they contain zero values, so unset if they are zero:
         if ($optionsST['TAXAMT'] == 0)      unset($optionsST['TAXAMT']);
         if ($optionsST['SHIPPINGAMT'] == 0) unset($optionsST['SHIPPINGAMT']);
         if ($optionsST['HANDLINGAMT'] == 0) unset($optionsST['HANDLINGAMT']);
         // set missing subtotals if they are zero values, since all must be submitted
         if (!isset($optionsST['TAXAMT']))      $optionsST['TAXAMT'] = 0;
         if (!isset($optionsST['SHIPPINGAMT'])) $optionsST['SHIPPINGAMT'] = 0;
         if (!isset($optionsST['HANDLINGAMT'])) $optionsST['HANDLINGAMT'] = 0;
     */
     // Since the PayPal spec cannot handle mathematically mismatched values caused by one-time charges,
     // must drop line-item details if any one-time charges apply to this order:
     // if there are any discounts in this order, do NOT supply line-item details
     if ($onetimeSum > 0) {
         $optionsLI = array();
     }
     // Do sanity check -- if any of the line-item subtotal math doesn't add up properly, skip line-item details,
     // so that the order can go through even though PayPal isn't being flexible to handle Zen Cart's diversity
     for ($j = 0; $j < $k; $j++) {
         $itemAMT = $optionsLI["L_AMT{$j}"];
         $itemTAX = $optionsLI["L_TAXAMT{$j}"];
         $itemQTY = $optionsLI["L_QTY{$j}"];
         $sumOfLineItems += $itemQTY * $itemAMT;
         $sumOfLineTax += round($itemQTY * $itemTAX, 2);
     }
     if ((double) $optionsST['ITEMAMT'] != (double) strval($sumOfLineItems)) {
         $optionsLI = array();
         $this->zcLog('getLineItemDetails 1', 'Order Subtotal does not match sum of line-item prices. Line-item-details skipped.' . "\n" . (double) $optionsST['ITEMAMT'] . ' ' . (double) $sumOfLineItems);
         //die('ITEMAMT != $sumOfLineItems ' . $optionsST['ITEMAMT'] . ' ' . $sumOfLineItems);
     }
     if ((double) $optionsST['TAXAMT'] != (double) strval($sumOfLineTax)) {
         $optionsLI = array();
         $this->zcLog('getLineItemDetails 2', 'Tax Subtotal does not match sum of taxes for line-items. Line-item-details skipped.' . "\n" . $optionsST['TAXAMT'] . ' ' . $sumOfLineTax);
         //die('TAXAMT != $sumofLineTax ' . $optionsST['TAXAMT'] . ' ' . $sumOfLineTax);
     }
     $this->zcLog('getLineItemDetails 3', 'LineItemDetails: ' . "\n" . ($creditsApplied ? 'Credits apply to this order, so all line-item details are NOT being submitted. Thus, the following data is REDUNDANT' . "\n" : '') . 'Details:' . print_r(array_merge($optionsST, $optionsLI), true) . "\n\n" . 'DEFAULT_CURRENCY = ' . DEFAULT_CURRENCY . "\nSESSION['currency'] = " . $_SESSION['currency'] . "\n" . "order->info['currency'] = " . $order->info['currency'] . "\n\$currencies->currencies[\$_SESSION['currency']]['value'] = " . $currencies->currencies[$_SESSION['currency']]['value'] . "\n" . print_r($currencies, true));
     // if not default currency, do not send subtotals or line-item details
     if (DEFAULT_CURRENCY != $order->info['currency']) {
         $this->zcLog('getLineItemDetails 4', 'Not using default currency. Thus, no line-item details can be submitted.');
         return array();
     }
     if ($currencies->currencies[$_SESSION['currency']]['value'] != 1) {
         $this->zcLog('getLineItemDetails 5', 'currency val not equal to 1.0000 - cannot proceed without coping with currency conversions. Aborting line-item details.');
         return array();
     }
     // if there are any discounts in this order, do not supply subtotals or line-item details
     if (strval($creditsApplied) > 0) {
         return array();
     }
     //$this->zcLog('getLineItemDetails 6', 'no credits - okay');
     // if subtotals are not adding up correctly, then skip sending any line-item or subtotal details to PayPal
     $st = round(strval($optionsST['ITEMAMT'] + $optionsST['TAXAMT'] + $optionsST['SHIPPINGAMT'] + $optionsST['HANDLINGAMT']), 2);
     $stDiff = strval($optionsST['AMT'] - $st);
     $stDiffRounded = strval(abs($st) - abs(round($optionsST['AMT'], 2)));
     // tidy up all values so that they comply with proper format (number_format(xxxx,2) for PayPal US use )
     if (!defined('PAYPALWPP_SKIP_LINE_ITEM_DETAIL_FORMATTING') || PAYPALWPP_SKIP_LINE_ITEM_DETAIL_FORMATTING != 'true') {
         if (is_array($optionsST)) {
             foreach ($optionsST as $key => $value) {
                 $optionsST[$key] = number_format(abs($value), 2);
             }
         }
         if (is_array($optionsLI)) {
             foreach ($optionsLI as $key => $value) {
                 if (strstr($key, 'AMT')) {
                     $optionsLI[$key] = number_format(abs($value), 2);
                 }
             }
         }
     }
     $this->zcLog('getLineItemDetails 7', 'checking subtotals... ' . "\nitemamt: " . $optionsST['ITEMAMT'] . "\ntaxamt: " . $optionsST['TAXAMT'] . "\nshippingamt: " . $optionsST['SHIPPINGAMT'] . "\nhandlingamt: " . $optionsST['HANDLINGAMT'] . "\n-------------------\nsubtotal: " . number_format($st, 2) . "\nAMT: " . $optionsST['AMT'] . "\n-------------------\ndifference: " . $stDiff . '  (abs+rounded: ' . $stDiffRounded . ')');
     if ($stDiffRounded != 0) {
         return array();
     }
     //die('bad subtotals'); //return array();
     $this->zcLog('getLineItemDetails 8', 'subtotals balance - okay');
     if (abs($optionsST['HANDLINGAMT']) == 0) {
         unset($optionsST['HANDLINGAMT']);
     }
     // Send Subtotal and LineItem results back to be submitted to PayPal
     return array_merge($optionsST, $optionsLI);
 }
/**
 * Check if the required stock is available.
 *
 * If insufficent stock is available return an out of stock message
 *
 * @param int The product id of the product whos's stock is to be checked
 * @param int Is this amount of stock available
 *
 * @TODO naughty html in a function
*/
function zen_check_stock($products_id, $products_quantity)
{
    $stock_left = zen_get_products_stock($products_id) - $products_quantity;
    $out_of_stock = '';
    if ($stock_left < 0) {
        $out_of_stock = '<span class="markProductOutOfStock">' . STOCK_MARK_PRODUCT_OUT_OF_STOCK . '</span>';
    }
    return $out_of_stock;
}
 /**
  * Method to handle cart Action - multiple add products
  *
  * @param string forward destination
  * @param url parameters
  * @todo change while loop to a foreach
  */
 function actionMultipleAddProduct($goto, $parameters)
 {
     global $messageStack;
     if ($this->display_debug_messages) {
         $messageStack->add_session('header', 'FUNCTION ' . __FUNCTION__, 'caution');
     }
     $addCount = 0;
     if (is_array($_POST['products_id']) && sizeof($_POST['products_id']) > 0) {
         //echo '<pre>'; echo var_dump($_POST['products_id']); echo '</pre>';
         while (list($key, $val) = each($_POST['products_id'])) {
             $prodId = preg_replace('/[^0-9a-f:.]/', '', $key);
             if (is_numeric($val) && $val > 0) {
                 $adjust_max = false;
                 $qty = $val;
                 $add_max = zen_get_products_quantity_order_max($prodId);
                 $cart_qty = $this->in_cart_mixed($prodId);
                 $new_qty = $this->adjust_quantity($qty, $prodId, 'shopping_cart');
                 // bof: adjust new quantity to be same as current in stock
                 $chk_current_qty = zen_get_products_stock($prodId);
                 if (STOCK_ALLOW_CHECKOUT == 'false' && $new_qty > $chk_current_qty) {
                     $new_qty = $chk_current_qty;
                     $messageStack->add_session('shopping_cart', ($this->display_debug_messages ? 'FUNCTION ' . __FUNCTION__ . ': ' : '') . WARNING_PRODUCT_QUANTITY_ADJUSTED . zen_get_products_name($prodId), 'caution');
                 }
                 // eof: adjust new quantity to be same as current in stock
                 if ($add_max == 1 and $cart_qty == 1) {
                     // do not add
                     $adjust_max = 'true';
                 } else {
                     // bof: adjust new quantity to be same as current in stock
                     if (STOCK_ALLOW_CHECKOUT == 'false' && $new_qty + $cart_qty > $chk_current_qty) {
                         $adjust_new_qty = 'true';
                         $alter_qty = $chk_current_qty - $cart_qty;
                         $new_qty = $alter_qty > 0 ? $alter_qty : 0;
                         $messageStack->add_session('shopping_cart', ($this->display_debug_messages ? 'FUNCTION ' . __FUNCTION__ . ': ' : '') . WARNING_PRODUCT_QUANTITY_ADJUSTED . zen_get_products_name($prodId), 'caution');
                     }
                     // eof: adjust new quantity to be same as current in stock
                     // adjust quantity if needed
                     if ($new_qty + $cart_qty > $add_max and $add_max != 0) {
                         $adjust_max = 'true';
                         $new_qty = $add_max - $cart_qty;
                     }
                     $this->add_cart($prodId, $this->get_quantity($prodId) + $new_qty);
                     $addCount++;
                 }
                 if ($adjust_max == 'true') {
                     if ($this->display_debug_messages) {
                         $messageStack->add_session('header', 'FUNCTION ' . __FUNCTION__ . '<br>' . ERROR_MAXIMUM_QTY . zen_get_products_name($prodId), 'caution');
                     }
                     $messageStack->add_session('shopping_cart', ERROR_MAXIMUM_QTY . zen_get_products_name($prodId), 'caution');
                 }
             }
             if (!is_numeric($val) || $val < 0) {
                 // adjust quantity when not a value
                 $chk_link = '<a href="' . zen_href_link(zen_get_info_page($prodId), 'cPath=' . zen_get_generated_category_path_rev(zen_get_products_category_id($prodId)) . '&products_id=' . $prodId) . '">' . zen_get_products_name($prodId) . '</a>';
                 $messageStack->add_session('header', ERROR_CORRECTIONS_HEADING . ERROR_PRODUCT_QUANTITY_UNITS_SHOPPING_CART . $chk_link . ' ' . PRODUCTS_ORDER_QTY_TEXT . zen_output_string_protected($val), 'caution');
                 $val = 0;
             }
         }
         // display message if all is good and not on shopping_cart page
         if ($addCount && DISPLAY_CART == 'false' && $_GET['main_page'] != FILENAME_SHOPPING_CART && $messageStack->size('shopping_cart') == 0) {
             $messageStack->add_session('header', ($this->display_debug_messages ? 'FUNCTION ' . __FUNCTION__ . ': ' : '') . SUCCESS_ADDED_TO_CART_PRODUCTS, 'success');
         } else {
             if (DISPLAY_CART == 'false') {
                 zen_redirect(zen_href_link(FILENAME_SHOPPING_CART));
             }
         }
         zen_redirect(zen_href_link($goto, zen_get_all_get_params($parameters)));
     }
 }
/**
 * Check if the required stock is available.
 *
 * If insufficent stock is available return an out of stock message
 *
 * @param int The product id of the product whos's stock is to be checked
 * @param int Is this amount of stock available
 *
 * @TODO naughty html in a function
*/
function zen_check_stock($products_id, $products_quantity, $attributes = null, $from = 'products')
{
    // START "Stock by Attributes"
    if ($from == 'order' && is_array($attributes)) {
        $tmp_attrib = array();
        foreach ($attributes as $attrib) {
            $tmp_attrib[$attrib['option_id']] = $attrib['value_id'];
        }
        $attributes = $tmp_attrib;
    }
    $stock_left = zen_get_products_stock($products_id, $attributes) - $products_quantity;
    // END "Stock by Attributes"
    $out_of_stock = '';
    if ($stock_left < 0) {
        $out_of_stock = '<span class="markProductOutOfStock">' . STOCK_MARK_PRODUCT_OUT_OF_STOCK . '</span>';
    }
    return $out_of_stock;
}
Example #6
0
$_SESSION['valid_to_checkout'] = true;
$_SESSION['cart']->get_products(true);
if ($_SESSION['valid_to_checkout'] == false) {
    $messageStack->add('header', ERROR_CART_UPDATE, 'error');
    zen_redirect(zen_href_link(FILENAME_SHOPPING_CART));
}
// Stock Check
if (STOCK_CHECK == 'true' && STOCK_ALLOW_CHECKOUT != 'true') {
    $products = $_SESSION['cart']->get_products();
    for ($i = 0, $n = sizeof($products); $i < $n; $i++) {
        if (zen_check_stock($products[$i]['id'], $products[$i]['quantity'])) {
            zen_redirect(zen_href_link(FILENAME_SHOPPING_CART));
            break;
        } else {
            // extra check on stock for mixed YES
            if (zen_get_products_stock($products[$i]['id']) - $_SESSION['cart']->in_cart_mixed($products[$i]['id']) < 0) {
                zen_redirect(zen_href_link(FILENAME_SHOPPING_CART));
                break;
            }
        }
    }
}
// if no shipping destination address was selected, use the customers own address as default
if (!$_SESSION['sendto']) {
    $_SESSION['sendto'] = $_SESSION['customer_default_address_id'];
} else {
    // verify the selected shipping address
    $check_address_query = "SELECT count(*) AS total\n                            FROM   " . TABLE_ADDRESS_BOOK . "\n                            WHERE  customers_id = :customersID\n                            AND    address_book_id = :addressBookID";
    $check_address_query = $db->bindVars($check_address_query, ':customersID', $_SESSION['customer_id'], 'integer');
    $check_address_query = $db->bindVars($check_address_query, ':addressBookID', $_SESSION['sendto'], 'integer');
    $check_address = $db->Execute($check_address_query);
Example #7
0
}
//echo $messageStack->size('checkout_payment');
//die('here');
// Stock Check
$flagAnyOutOfStock = false;
$stock_check = array();
if (STOCK_CHECK == 'true') {
    // bof numinix products variants  - check stock
    if (NMX_PRODUCT_VARIANTS_STATUS == 'true') {
        // get option_id/option_value_id array
        $products_attributes = array();
        for ($i = 0, $n = sizeof($order->products); $i < $n; $i++) {
            foreach ($order->products[$i]['attributes'] as $products_attribute) {
                $products_attributes[$products_attribute['option_id']] = $products_attribute['value_id'];
            }
            $stockUpdate = zen_get_products_stock($order->products[$i]['id'], $products_attributes);
            $stockAvailable = is_array($stockUpdate) ? $stockUpdate['quantity'] : $stockUpdate;
            if ($stockAvailable - $order->products[$i]['qty'] < 0) {
                $flagAnyOutOfStock = true;
                $flagStockCheck = STOCK_MARK_PRODUCT_OUT_OF_STOCK;
            }
        }
    }
    // eof numinix products variants  - check stock
    for ($i = 0, $n = sizeof($order->products); $i < $n; $i++) {
        if ($stock_check[$i] = zen_check_stock($order->products[$i]['id'], $order->products[$i]['qty'])) {
            $flagAnyOutOfStock = true;
        }
    }
    // Out of Stock
    if (STOCK_ALLOW_CHECKOUT != 'true' && $flagAnyOutOfStock == true) {
 $stock_id = $db->Execute($query, false, false, 0, true);
 if ($stock_id->RecordCount() > 0) {
     $query = 'select stock_id from ' . TABLE_PRODUCTS_WITH_ATTRIBUTES_STOCK . ' where products_id = :products_id:';
     $query = $db->bindVars($query, ':products_id:', $_POST['products_id'], 'integer');
     $stock_id = $db->Execute($query, false, false, 0, true);
     //  $_SESSION['stock_idquery'] = $stock_id->RecordCount();
     //Check if item is an SBA tracked item, if so, then perform analysis of whether to add or not.
 }
 if ($stock_id->RecordCount() > 0) {
     //Looks like $_SESSION['cart']->in_cart_mixed($prodId) could be used here to pull the attribute related product information to verify same product is being added to cart... This also may help in the shopping_cart routine added for SBA as all SBA products will have this modifier.
     //      $cart_qty = 0;
     $new_qty = $_POST['cart_quantity'];
     //Number of items being added (Known to be SBA tracked already)
     $new_qty = $_SESSION['cart']->adjust_quantity($new_qty, $_POST['products_id'], 'header');
     // bof: adjust new quantity to be same as current in stock
     $chk_current_qty = zen_get_products_stock($product_id, $attributes);
     $_SESSION['cart']->flag_duplicate_msgs_set = FALSE;
     $productAttrAreSBA = zen_get_sba_stock_attribute_id($product_id, $attributes, 'products');
     if ($productAttrAreSBA === false) {
         $the_list .= PWA_COMBO_OUT_OF_STOCK . "<br />";
         foreach ($_POST['id'] as $key2 => $value2) {
             $the_list .= TEXT_ERROR_OPTION_FOR . '<span class="alertBlack">' . zen_options_name($key2) . '</span>' . TEXT_INVALID_SELECTION . '<span class="alertBlack">' . ($value == (int) PRODUCTS_OPTIONS_VALUES_TEXT_ID ? TEXT_INVALID_USER_INPUT : zen_values_name($value2)) . '</span>' . '<br />';
         }
     }
     if (STOCK_ALLOW_CHECKOUT == 'false' && $cart_qty + $new_qty > $chk_current_qty) {
         $new_qty = $chk_current_qty;
         $messageStack->add_session('shopping_cart', ($_SESSION['cart']->display_debug_messages ? 'C: FUNCTION ' . __FUNCTION__ . ': ' : '') . WARNING_PRODUCT_QUANTITY_ADJUSTED . zen_get_products_name($_POST['products_id']), 'caution');
         $_SESSION['cart']->flag_duplicate_msgs_set = TRUE;
     }
     // eof: adjust new quantity to be same as current in stock
     if ($add_max == 1 and $cart_qty == 1) {
 function _draw_js_stock_array($combinations)
 {
     if (!(isset($combinations) && is_array($combinations) && sizeof($combinations) > 0)) {
         return '{}';
     }
     $out = '';
     foreach ($combinations[0]['comb'] as $oid => $ovid) {
         $out .= '{' . '"_' . zen_output_string_protected($ovid) . '"' . ':';
         $ovids[] = $ovid;
         $opts[] = $oid;
     }
     if (STOCK_SHOW_ATTRIB_LEVEL_STOCK == 'true') {
         //Search for quantity in the SBA table...
         $numbadd = zen_get_products_stock($_GET['products_id'], $ovids);
         if ($numbadd == 0) {
             $numbadd = '0';
         }
         $out .= $numbadd;
     } else {
         $out .= '1';
     }
     for ($combindex = 1; $combindex < sizeof($combinations); $combindex++) {
         $comb = $combinations[$combindex]['comb'];
         for ($i = 0; $i < sizeof($opts) - 1; $i++) {
             if ($comb[$opts[$i]] != $combinations[$combindex - 1]['comb'][$opts[$i]]) {
                 break;
             }
         }
         $out .= str_repeat('}', sizeof($opts) - 1 - $i) . ',';
         if ($i < sizeof($opts) - 1) {
             for ($j = $i; $j < sizeof($opts) - 1; $j++) {
                 $out .= '"_' . zen_output_string_protected($comb[$opts[$j]]) . '"' . ':{';
             }
         }
         $out .= '"_' . zen_output_string_protected($comb[$opts[sizeof($opts) - 1]]) . '"' . ':';
         if (STOCK_SHOW_ATTRIB_LEVEL_STOCK == 'true') {
             $idvals = array();
             foreach ($comb as $ids => $idvalsadd) {
                 $idvals[] = $idvalsadd;
             }
             $numadd = zen_get_products_stock($_GET['products_id'], $idvals);
             if ($numadd == 0) {
                 $numadd = '0';
             }
             $out .= $numadd;
         } else {
             $out .= '1';
         }
     }
     $out .= str_repeat('}', sizeof($opts));
     return $out;
 }