예제 #1
0
 static function getJavascriptArray($groupCustomerId = 0, $isReseller = false)
 {
     global $objDatabase;
     // create javascript array containing all products;
     // used to update the display when changing the product ID.
     // we need the VAT rate in there as well in order to be able to correctly change the products,
     // and the flag indicating whether the VAT is included in the prices already.
     $strJsArrProduct = 'var vat_included = ' . intval(Vat::isIncluded()) . ";\nvar arrProducts = new Array();\n";
     $arrSql = \Text::getSqlSnippets('`product`.`id`', FRONTEND_LANG_ID, 'Shop', array('name' => Product::TEXT_NAME, 'code' => Product::TEXT_CODE));
     $query = "\n            SELECT `product`.`id`,\n                   `product`.`resellerprice`, `product`.`normalprice`,\n                   `product`.`discountprice`, `product`.`discount_active`,\n                   `product`.`weight`, `product`.`vat_id`,\n                   `product`.`distribution`,\n                   `product`.`group_id`, `product`.`article_id`, " . $arrSql['field'] . "\n              FROM `" . DBPREFIX . "module_shop" . MODULE_INDEX . "_products` AS `product`" . $arrSql['join'] . "\n             WHERE `product`.`active`=1";
     $objResult = $objDatabase->Execute($query);
     if (!$objResult) {
         return Product::errorHandler();
     }
     while (!$objResult->EOF) {
         $id = $objResult->fields['id'];
         $distribution = $objResult->fields['distribution'];
         $strCode = $objResult->fields['code'];
         if ($strCode === null) {
             $strCode = \Text::getById($id, 'Shop', Product::TEXT_CODE)->content();
         }
         $strName = $objResult->fields['name'];
         if ($strName === null) {
             $strName = \Text::getById($id, 'Shop', Product::TEXT_NAME)->content();
         }
         $price = $objResult->fields['normalprice'];
         if ($objResult->fields['discount_active']) {
             $price = $objResult->fields['discountprice'];
         } elseif ($isReseller) {
             $price = $objResult->fields['resellerprice'];
         }
         // Determine discounted price from customer and article group matrix
         $discountCustomerRate = Discount::getDiscountRateCustomer($groupCustomerId, $objResult->fields['article_id']);
         $price -= $price * $discountCustomerRate * 0.01;
         // Determine prices for various count discounts, if any
         $arrDiscountCountRate = Discount::getDiscountCountRateArray($objResult->fields['group_id']);
         //\DBG::log("Products::getJavascriptArray($groupCustomerId, $isReseller): Discount rate array: ".var_export($arrDiscountCountRate, true));
         // Order the counts in reverse, from highest to lowest
         $strJsArrPrice = '';
         if (is_array($arrDiscountCountRate)) {
             foreach ($arrDiscountCountRate as $count => $rate) {
                 // Deduct the customer type discount right away
                 //\DBG::log("Products::getJavascriptArray(): price $price, rate $rate");
                 $discountPrice = $price - $price * $rate * 0.01;
                 $strJsArrPrice .= ($strJsArrPrice ? ',' : '') . $count . ',' . Currency::getCurrencyPrice($discountPrice);
             }
         }
         $strJsArrPrice .= ($strJsArrPrice ? ',' : '') . '0,' . Currency::getCurrencyPrice($price);
         $strJsArrProduct .= 'arrProducts[' . $id . '] = {' . 'id:' . $id . ',' . 'code:"' . $strCode . '",' . 'title:"' . htmlspecialchars($strName, ENT_QUOTES, CONTREXX_CHARSET) . '",' . 'percent:' . Vat::getRate($objResult->fields['vat_id']) . ',' . 'weight:' . ($distribution == 'delivery' ? '"' . Weight::getWeightString($objResult->fields['weight']) . '"' : '0') . ',' . 'price:[' . $strJsArrPrice . "]};\n";
         $objResult->MoveNext();
     }
     return $strJsArrProduct;
 }
예제 #2
0
 /**
  * Processes the Order
  *
  * Verifies all data, updates and stores it in the database, and
  * initializes payment
  * @return  boolean         True on successs, false otherwise
  */
 static function process()
 {
     global $objDatabase, $_ARRAYLANG;
     // FOR TESTING ONLY (repeatedly process/store the order, also disable self::destroyCart())
     //$_SESSION['shop']['order_id'] = NULL;
     // Verify that the order hasn't yet been saved
     // (and has thus not yet been confirmed)
     if (isset($_SESSION['shop']['order_id'])) {
         return \Message::error($_ARRAYLANG['TXT_ORDER_ALREADY_PLACED']);
     }
     // No more confirmation
     self::$objTemplate->hideBlock('shopConfirm');
     // Store the customer, register the order
     $customer_ip = $_SERVER['REMOTE_ADDR'];
     $customer_host = substr(@gethostbyaddr($_SERVER['REMOTE_ADDR']), 0, 100);
     $customer_browser = substr(getenv('HTTP_USER_AGENT'), 0, 100);
     $new_customer = false;
     //\DBG::log("Shop::process(): E-Mail: ".$_SESSION['shop']['email']);
     if (self::$objCustomer) {
         //\DBG::log("Shop::process(): Existing User username ".$_SESSION['shop']['username'].", email ".$_SESSION['shop']['email']);
     } else {
         // Registered Customers are required to be logged in!
         self::$objCustomer = Customer::getRegisteredByEmail($_SESSION['shop']['email']);
         if (self::$objCustomer) {
             \Message::error($_ARRAYLANG['TXT_SHOP_CUSTOMER_REGISTERED_EMAIL']);
             \Cx\Core\Csrf\Controller\Csrf::redirect(\Cx\Core\Routing\Url::fromModuleAndCmd('Shop', 'login') . '?redirect=' . base64_encode(\Cx\Core\Routing\Url::fromModuleAndCmd('Shop', 'confirm')));
         }
         // Unregistered Customers are stored as well, as their information is needed
         // nevertheless.  Their active status, however, is set to false.
         self::$objCustomer = Customer::getUnregisteredByEmail($_SESSION['shop']['email']);
         if (!self::$objCustomer) {
             self::$objCustomer = new Customer();
             // Currently, the e-mail address is set as the user name
             $_SESSION['shop']['username'] = $_SESSION['shop']['email'];
             //\DBG::log("Shop::process(): New User username ".$_SESSION['shop']['username'].", email ".$_SESSION['shop']['email']);
             self::$objCustomer->username($_SESSION['shop']['username']);
             self::$objCustomer->email($_SESSION['shop']['email']);
             // Note that the password is unset when the Customer chooses
             // to order without registration.  The generated one
             // defaults to length 8, fulfilling the requirements for
             // complex passwords.  And it's kept absolutely secret.
             $password = empty($_SESSION['shop']['password']) ? \User::make_password() : $_SESSION['shop']['password'];
             //\DBG::log("Password: $password (session: {$_SESSION['shop']['password']})");
             if (!self::$objCustomer->password($password)) {
                 \Message::error($_ARRAYLANG['TXT_INVALID_PASSWORD']);
                 \Cx\Core\Csrf\Controller\Csrf::redirect(\Cx\Core\Routing\Url::fromModuleAndCmd('Shop', 'account'));
             }
             self::$objCustomer->active(empty($_SESSION['shop']['dont_register']));
             $new_customer = true;
         }
     }
     // Update the Customer object from the session array
     // (whether new or not -- it may have been edited)
     self::$objCustomer->gender($_SESSION['shop']['gender']);
     self::$objCustomer->firstname($_SESSION['shop']['firstname']);
     self::$objCustomer->lastname($_SESSION['shop']['lastname']);
     self::$objCustomer->company($_SESSION['shop']['company']);
     self::$objCustomer->address($_SESSION['shop']['address']);
     self::$objCustomer->city($_SESSION['shop']['city']);
     self::$objCustomer->zip($_SESSION['shop']['zip']);
     self::$objCustomer->country_id($_SESSION['shop']['countryId']);
     self::$objCustomer->phone($_SESSION['shop']['phone']);
     self::$objCustomer->fax($_SESSION['shop']['fax']);
     $arrGroups = self::$objCustomer->getAssociatedGroupIds();
     $usergroup_id = \Cx\Core\Setting\Controller\Setting::getValue('usergroup_id_reseller', 'Shop');
     if (empty($usergroup_id)) {
         //\DBG::log("Shop::process(): ERROR: Missing reseller group");
         \Message::error($_ARRAYLANG['TXT_SHOP_ERROR_USERGROUP_INVALID']);
         \Cx\Core\Csrf\Controller\Csrf::redirect(\Cx\Core\Routing\Url::fromModuleAndCmd('Shop', ''));
     }
     if (!in_array($usergroup_id, $arrGroups)) {
         //\DBG::log("Shop::process(): Customer is not in Reseller group (ID $usergroup_id)");
         // Not a reseller.  See if she's a final customer
         $usergroup_id = \Cx\Core\Setting\Controller\Setting::getValue('usergroup_id_customer', 'Shop');
         if (empty($usergroup_id)) {
             //\DBG::log("Shop::process(): ERROR: Missing final customer group");
             \Message::error($_ARRAYLANG['TXT_SHOP_ERROR_USERGROUP_INVALID']);
             \Cx\Core\Csrf\Controller\Csrf::redirect(\Cx\Core\Routing\Url::fromModuleAndCmd('Shop', ''));
         }
         if (!in_array($usergroup_id, $arrGroups)) {
             //\DBG::log("Shop::process(): Customer is not in final customer group (ID $usergroup_id), either");
             // Neither one, add to the final customer group (default)
             $arrGroups[] = $usergroup_id;
             self::$objCustomer->setGroups($arrGroups);
             //\DBG::log("Shop::process(): Added Customer to final customer group (ID $usergroup_id): ".var_export(self::$objCustomer->getAssociatedGroupIds(), true));
         } else {
             //\DBG::log("Shop::process(): Customer is a final customer (ID $usergroup_id) already: ".var_export(self::$objCustomer->getAssociatedGroupIds(), true));
         }
     } else {
         //\DBG::log("Shop::process(): Customer is a Reseller (ID $usergroup_id) already: ".var_export(self::$objCustomer->getAssociatedGroupIds(), true));
     }
     // Insert or update the customer
     //\DBG::log("Shop::process(): Storing Customer: ".var_export(self::$objCustomer, true));
     if (!self::$objCustomer->store()) {
         return \Message::error($_ARRAYLANG['TXT_SHOP_CUSTOMER_ERROR_STORING']);
     }
     // Authenticate new Customer
     if ($new_customer) {
         // Fails for "unregistered" Customers!
         if (self::$objCustomer->auth($_SESSION['shop']['username'], $_SESSION['shop']['password'], false, true)) {
             if (!self::_authenticate()) {
                 return \Message::error($_ARRAYLANG['TXT_SHOP_CUSTOMER_ERROR_STORING']);
             }
         }
     }
     //die();
     // Clear the ship-to country if there is no shipping
     if (!Cart::needs_shipment()) {
         $_SESSION['shop']['countryId2'] = 0;
     }
     $shipper_id = empty($_SESSION['shop']['shipperId']) ? null : $_SESSION['shop']['shipperId'];
     $payment_id = empty($_SESSION['shop']['paymentId']) ? null : $_SESSION['shop']['paymentId'];
     $objOrder = new Order();
     $objOrder->customer_id(self::$objCustomer->id());
     $objOrder->billing_gender($_SESSION['shop']['gender']);
     $objOrder->billing_firstname($_SESSION['shop']['firstname']);
     $objOrder->billing_lastname($_SESSION['shop']['lastname']);
     $objOrder->billing_company($_SESSION['shop']['company']);
     $objOrder->billing_address($_SESSION['shop']['address']);
     $objOrder->billing_city($_SESSION['shop']['city']);
     $objOrder->billing_zip($_SESSION['shop']['zip']);
     $objOrder->billing_country_id($_SESSION['shop']['countryId']);
     $objOrder->billing_phone($_SESSION['shop']['phone']);
     $objOrder->billing_fax($_SESSION['shop']['fax']);
     $objOrder->billing_email($_SESSION['shop']['email']);
     $objOrder->currency_id($_SESSION['shop']['currencyId']);
     $objOrder->sum($_SESSION['shop']['grand_total_price']);
     $objOrder->date_time(date(ASCMS_DATE_FORMAT_INTERNATIONAL_DATETIME));
     $objOrder->status(0);
     $objOrder->company($_SESSION['shop']['company2']);
     $objOrder->gender($_SESSION['shop']['gender2']);
     $objOrder->firstname($_SESSION['shop']['firstname2']);
     $objOrder->lastname($_SESSION['shop']['lastname2']);
     $objOrder->address($_SESSION['shop']['address2']);
     $objOrder->city($_SESSION['shop']['city2']);
     $objOrder->zip($_SESSION['shop']['zip2']);
     $objOrder->country_id($_SESSION['shop']['countryId2']);
     $objOrder->phone($_SESSION['shop']['phone2']);
     $objOrder->vat_amount($_SESSION['shop']['vat_price']);
     $objOrder->shipment_amount($_SESSION['shop']['shipment_price']);
     $objOrder->shipment_id($shipper_id);
     $objOrder->payment_id($payment_id);
     $objOrder->payment_amount($_SESSION['shop']['payment_price']);
     $objOrder->ip($customer_ip);
     $objOrder->host($customer_host);
     $objOrder->lang_id(FRONTEND_LANG_ID);
     $objOrder->browser($customer_browser);
     $objOrder->note($_SESSION['shop']['note']);
     if (!$objOrder->insert()) {
         // $order_id is unset!
         return \Message::error($_ARRAYLANG['TXT_SHOP_ORDER_ERROR_STORING']);
     }
     $order_id = $objOrder->id();
     $_SESSION['shop']['order_id'] = $order_id;
     // The products will be tested one by one below.
     // If any single one of them requires delivery, this
     // flag will be set to true.
     // This is used to determine the order status at the
     // end of the shopping process.
     $_SESSION['shop']['isDelivery'] = false;
     // Try to redeem the Coupon, if any
     $coupon_code = isset($_SESSION['shop']['coupon_code']) ? $_SESSION['shop']['coupon_code'] : null;
     //\DBG::log("Cart::update(): Coupon Code: $coupon_code");
     $items_total = 0;
     // Suppress Coupon messages (see Coupon::available())
     \Message::save();
     foreach (Cart::get_products_array() as $arrProduct) {
         $objProduct = Product::getById($arrProduct['id']);
         if (!$objProduct) {
             unset($_SESSION['shop']['order_id']);
             return \Message::error($_ARRAYLANG['TXT_ERROR_LOOKING_UP_ORDER']);
         }
         $product_id = $arrProduct['id'];
         $name = $objProduct->name();
         $priceOptions = !empty($arrProduct['optionPrice']) ? $arrProduct['optionPrice'] : 0;
         $quantity = $arrProduct['quantity'];
         $price = $objProduct->get_custom_price(self::$objCustomer, $priceOptions, $quantity);
         $item_total = $price * $quantity;
         $items_total += $item_total;
         $productVatId = $objProduct->vat_id();
         $vat_rate = $productVatId && Vat::getRate($productVatId) ? Vat::getRate($productVatId) : '0.00';
         // Test the distribution method for delivery
         $productDistribution = $objProduct->distribution();
         if ($productDistribution == 'delivery') {
             $_SESSION['shop']['isDelivery'] = true;
         }
         $weight = $productDistribution == 'delivery' ? $objProduct->weight() : 0;
         // grams
         if ($weight == '') {
             $weight = 0;
         }
         // Add to order items table
         $result = $objOrder->insertItem($order_id, $product_id, $name, $price, $quantity, $vat_rate, $weight, $arrProduct['options']);
         if (!$result) {
             unset($_SESSION['shop']['order_id']);
             // TODO: Verify error message set by Order::insertItem()
             return false;
         }
         // Store the Product Coupon, if applicable.
         // Note that it is not redeemed yet (uses=0)!
         if ($coupon_code) {
             $objCoupon = Coupon::available($coupon_code, $item_total, self::$objCustomer->id(), $product_id, $payment_id);
             if ($objCoupon) {
                 //\DBG::log("Shop::process(): Got Coupon for Product ID $product_id: ".var_export($objCoupon, true));
                 if (!$objCoupon->redeem($order_id, self::$objCustomer->id(), $price * $quantity, 0)) {
                     // TODO: Do something if the Coupon does not work
                     \DBG::log("Shop::process(): ERROR: Failed to store Coupon for Product ID {$product_id}");
                 }
                 $coupon_code = null;
             }
         }
     }
     // foreach product in cart
     // Store the Global Coupon, if applicable.
     // Note that it is not redeemed yet (uses=0)!
     //\DBG::log("Shop::process(): Looking for global Coupon $coupon_code");
     if ($coupon_code) {
         $objCoupon = Coupon::available($coupon_code, $items_total, self::$objCustomer->id(), null, $payment_id);
         if ($objCoupon) {
             //\DBG::log("Shop::process(): Got global Coupon: ".var_export($objCoupon, true));
             if (!$objCoupon->redeem($order_id, self::$objCustomer->id(), $items_total, 0)) {
                 \DBG::log("Shop::process(): ERROR: Failed to store global Coupon");
             }
         }
     }
     \Message::restore();
     $processor_id = Payment::getProperty($_SESSION['shop']['paymentId'], 'processor_id');
     $processor_name = PaymentProcessing::getPaymentProcessorName($processor_id);
     // other payment methods
     PaymentProcessing::initProcessor($processor_id);
     // TODO: These arguments are no longer valid.  Set them up later?
     //            Currency::getActiveCurrencyCode(),
     //            FWLanguage::getLanguageParameter(FRONTEND_LANG_ID, 'lang'));
     // if the processor is Internal_LSV, and there is account information,
     // store the information.
     if ($processor_name == 'internal_lsv') {
         if (!self::lsv_complete()) {
             // Missing mandatory data; return to payment
             unset($_SESSION['shop']['order_id']);
             \Message::error($_ARRAYLANG['TXT_ERROR_ACCOUNT_INFORMATION_NOT_AVAILABLE']);
             \Cx\Core\Csrf\Controller\Csrf::redirect(\Cx\Core\Routing\Url::fromModuleAndCmd('Shop', 'payment'));
         }
         $query = "\n                INSERT INTO " . DBPREFIX . "module_shop" . MODULE_INDEX . "_lsv (\n                    order_id, holder, bank, blz\n                ) VALUES (\n                    {$order_id},\n                    '" . contrexx_raw2db($_SESSION['shop']['account_holder']) . "',\n                    '" . contrexx_raw2db($_SESSION['shop']['account_bank']) . "',\n                    '" . contrexx_raw2db($_SESSION['shop']['account_blz']) . "'\n                )";
         $objResult = $objDatabase->Execute($query);
         if (!$objResult) {
             // Return to payment
             unset($_SESSION['shop']['order_id']);
             \Message::error($_ARRAYLANG['TXT_ERROR_INSERTING_ACCOUNT_INFORMATION']);
             \Cx\Core\Csrf\Controller\Csrf::redirect(\Cx\Core\Routing\Url::fromModuleAndCmd('Shop', 'payment'));
         }
     }
     $_SESSION['shop']['order_id_checkin'] = $order_id;
     $strProcessorType = PaymentProcessing::getCurrentPaymentProcessorType();
     // Test whether the selected payment method can be
     // considered an instant or deferred one.
     // This is used to set the order status at the end
     // of the shopping process.
     // TODO: Invert this flag, as it may no longer be present after paying
     // online using one of the external payment methods!  Ensure that it is set
     // instead when paying "deferred".
     $_SESSION['shop']['isInstantPayment'] = false;
     if ($strProcessorType == 'external') {
         // For the sake of simplicity, all external payment
         // methods are considered to be 'instant'.
         // All currently implemented internal methods require
         // further action from the merchant, and thus are
         // considered to be 'deferred'.
         $_SESSION['shop']['isInstantPayment'] = true;
     }
     // Send the Customer login separately, as the password possibly
     // won't be available later
     if (!empty($_SESSION['shop']['password'])) {
         self::sendLogin(self::$objCustomer->email(), $_SESSION['shop']['password']);
     }
     // Show payment processing page.
     // Note that some internal payments are redirected away
     // from this page in checkOut():
     // 'internal', 'internal_lsv'
     self::$objTemplate->setVariable('SHOP_PAYMENT_PROCESSING', PaymentProcessing::checkOut());
     // Clear the order ID.
     // The order may be resubmitted and the payment retried.
     unset($_SESSION['shop']['order_id']);
     // Custom.
     // Enable if Discount class is customized and in use.
     //self::showCustomerDiscount(Cart::get_price());
     return true;
 }
예제 #3
0
 /**
  * Updates values in the session array with the current Cart contents,
  * and returns an array of Product data
  *
  * Called right after a Product has been added by {@see add_product()} or
  * quantities changed by {@see update_quantity()}.
  * Also computes the new count of items in the cart and calculates the
  * amount.
  * Stores details of the Products in the Cart in $products.
  * Note that the $objCustomer parameter is mandatory, but may be empty
  * in case it is a new Customer shopping.
  * @param   Customer    $objCustomer          The Customer
  * @global  ADONewConnection  $objDatabase    Database connection object
  * @return  boolean                           True on success,
  *                                            false otherwise
  * @static
  */
 static function update($objCustomer)
 {
     global $_ARRAYLANG;
     //DBG::log("Cart::update(): Cart: ".var_export($_SESSION['shop']['cart'], true));
     if (empty($_SESSION['shop']['cart'])) {
         self::init();
         return true;
         //self::get_products_array();
     }
     // No shipment by default.  Only if at least one Product with
     // type "delivery" is encountered, it is switched on.
     $_SESSION['shop']['cart']['shipment'] = false;
     $total_discount_amount = 0;
     $coupon_code = isset($_SESSION['shop']['coupon_code']) ? $_SESSION['shop']['coupon_code'] : '';
     $payment_id = isset($_SESSION['shop']['paymentId']) ? $_SESSION['shop']['paymentId'] : 0;
     $customer_id = $objCustomer ? $objCustomer->id() : 0;
     //DBG::log("Cart::update(): Coupon Code: $coupon_code");
     self::$products = array();
     $items = 0;
     $total_price = 0;
     $total_vat_amount = 0;
     $total_weight = 0;
     $total_discount_amount = 0;
     //DBG::log("Cart::update(): Products: ".var_export($products, true));
     // Loop 1: Collect necessary Product data
     $products = $_SESSION['shop']['cart']['items']->toArray();
     foreach ($products as $cart_id => &$product) {
         $objProduct = Product::getById($product['id']);
         if (!$objProduct) {
             unset($products[$cart_id]);
             continue;
         }
         // Check minimum order quantity, when set
         // Do not add error message if it's an AJAX request
         if ((empty($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest') && $product['quantity'] != 0 && $product['quantity'] < $objProduct->minimum_order_quantity()) {
             \Message::error($objProduct->name() . ': ' . $_ARRAYLANG['TXT_SHOP_MINIMUM_ORDER_QUANTITY_ERROR']);
         }
         // Limit Products in the cart to the stock available if the
         // stock_visibility is enabled.
         if ($objProduct->stock_visible() && $product['quantity'] > $objProduct->stock()) {
             $product['quantity'] = $objProduct->stock();
         }
         // Remove Products with quatities of zero or less
         if ($product['quantity'] <= 0) {
             unset($products[$cart_id]);
             continue;
         }
         $options_price = 0;
         // Array!
         $options_strings = Attributes::getAsStrings($product['options'], $options_price);
         //DBG::log("Cart::update(): options_price $options_price");
         /* Replaced by Attributes::getAsStrings()
                     foreach ($product['options'] as $attribute_id => $arrOptionIds) {
                         $objAttribute = Attribute::getById($attribute_id);
                         // Should be tested!
                         if (!$objAttribute) {
                             unset($product['options'][$attribute_id]);
                             continue;
                         }
                         $arrOptions = $objAttribute->getOptionArray();
                         foreach ($arrOptionIds as $option_id) {
                             $arrOption = null;
                             // Note that the options are indexed starting from 1!
                             // For types 4..7, the value entered in the text box is
                             // stored in $option_id.  Overwrite the value taken from
                             // the database.
                             if ($objAttribute->getType() >= Attribute::TYPE_TEXT_OPTIONAL) {
                                 $arrOption = current($arrOptions);
                                 $arrOption['value'] = $option_id;
                             } else {
                                 $arrOption = $arrOptions[$option_id];
                             }
                             if (!is_array($arrOption)) continue;
                             $option_value = ShopLibrary::stripUniqidFromFilename($arrOption['value']);
                             $path = Order::UPLOAD_FOLDER.$arrOption['value'];
                             if (   $option_value != $arrOption['value']
                                 && File::exists($path)) {
                                 $option_value =
                                     '<a href="$path" target="uploadimage">'.
                                     $option_value.'</a>';
                             }
                             $options .= " [$option_value]";
                             $options_price += $arrOption['price'];
                         }
                     }
                     if ($options_price != 0) {
                         $product['optionPrice'] = $options_price;
                     }
          */
         $quantity = $product['quantity'];
         $items += $quantity;
         $itemprice = $objProduct->get_custom_price($objCustomer, $options_price, $quantity);
         $price = $itemprice * $quantity;
         $handler = $objProduct->distribution();
         $itemweight = $handler == 'delivery' ? $objProduct->weight() : 0;
         // Requires shipment if the distribution type is 'delivery'
         if ($handler == 'delivery') {
             //DBG::log("Cart::update(): Product ID ".$objProduct->id()." needs delivery");
             $_SESSION['shop']['cart']['shipment'] = true;
         }
         $weight = $itemweight * $quantity;
         $vat_rate = Vat::getRate($objProduct->vat_id());
         $total_price += $price;
         $total_weight += $weight;
         self::$products[$cart_id] = array('id' => $objProduct->id(), 'product_id' => $objProduct->code(), 'cart_id' => $cart_id, 'title' => empty($_GET['remoteJs']) ? $objProduct->name() : htmlspecialchars(strtolower(CONTREXX_CHARSET) == 'utf-8' ? $objProduct->name() : utf8_encode($objProduct->name()), ENT_QUOTES, CONTREXX_CHARSET), 'options' => $product['options'], 'options_long' => $options_strings[0], 'options_cart' => $options_strings[1], 'price' => Currency::formatPrice($price), 'quantity' => $quantity, 'itemprice' => Currency::formatPrice($itemprice), 'vat_rate' => $vat_rate, 'itemweight' => $itemweight, 'weight' => $weight, 'group_id' => $objProduct->group_id(), 'article_id' => $objProduct->article_id(), 'product_images' => $objProduct->pictures(), 'minimum_order_quantity' => $objProduct->minimum_order_quantity());
         //DBG::log("Cart::update(): Loop 1: Product: ".var_export(self::$products[$cart_id], true));
     }
     $_SESSION['shop']['cart']['items'] = $products;
     // Loop 2: Calculate Coupon discounts and VAT
     $objCoupon = null;
     $hasCoupon = false;
     $discount_amount = 0;
     foreach (self::$products as $cart_id => &$product) {
         $discount_amount = 0;
         $product['discount_amount'] = 0;
         // Coupon:  Either the payment ID or the code are needed
         if ($payment_id || $coupon_code) {
             $objCoupon = Coupon::available($coupon_code, $total_price, $customer_id, $product['id'], $payment_id);
             if ($objCoupon) {
                 $hasCoupon = true;
                 //DBG::log("Cart::update(): PRODUCT; Coupon available: $coupon_code, Product ID {$product['id']}");
                 //DBG::log("Cart::update(): Loop 2: Product: ".var_export($product, true));
                 $discount_amount = $objCoupon->getDiscountAmount($product['price'], $customer_id);
                 if ($objCoupon->discount_amount() > 0 && $total_discount_amount + $discount_amount > $objCoupon->discount_amount()) {
                     //DBG::log("Cart::update(): COUPON prelimit: PRODUCT: price ".$product['price'].", coupon discount amount ".$objCoupon->discount_amount().", discount_amount $discount_amount, total discount amount $total_discount_amount");
                     $discount_amount = $objCoupon->discount_amount() - $total_discount_amount;
                     //DBG::log("Cart::update(): COUPON postlimit: PRODUCT: price ".$product['price'].", coupon discount amount ".$objCoupon->discount_amount().", discount_amount $discount_amount, total discount amount $total_discount_amount");
                 }
                 $total_discount_amount += $discount_amount;
                 //                    $product['price'] = Currency::formatPrice(
                 //                        $product['price'] - $discount_amount);
                 $product['discount_amount'] = $discount_amount;
                 // UNUSED
                 //                        $arrProduct['coupon_string'] =
                 //                            $objCoupon->getString($discount_amount);
                 //DBG::log("Cart::update(): PRODUCT: price ".$product['price'].", discount_amount $discount_amount, total discount $total_discount_amount");
             }
         }
         // Calculate the amount if it's excluded; we might add it later:
         // - If it's included, we don't care.
         // - If it's disabled, it's set to zero.
         $vat_amount = Vat::amount($product['vat_rate'], $product['price'] - $product['discount_amount']);
         if (!Vat::isIncluded()) {
             self::$products[$cart_id]['price'] += $vat_amount;
             self::$products[$cart_id]['price'] = Currency::formatPrice(self::$products[$cart_id]['price']);
         }
         $total_vat_amount += $vat_amount;
         self::$products[$cart_id]['vat_amount'] = Currency::formatPrice($vat_amount);
     }
     // Global Coupon:  Either the payment ID or the code are needed
     if (!$objCoupon && ($payment_id || $coupon_code)) {
         $discount_amount = 0;
         //DBG::log("Cart::update(): GLOBAL; Got Coupon code $coupon_code");
         $total_price_incl_vat = $total_price;
         if (!Vat::isIncluded()) {
             $total_price_incl_vat += $total_vat_amount;
         }
         $objCoupon = Coupon::available($coupon_code, $total_price_incl_vat, $customer_id, 0, $payment_id);
         if ($objCoupon) {
             $hasCoupon = true;
             $discount_amount = $objCoupon->getDiscountAmount($total_price_incl_vat, $customer_id);
             $total_discount_amount = $discount_amount;
             //DBG::log("Cart::update(): GLOBAL; Coupon available: $coupon_code");
             //DBG::log("Cart::update(): GLOBAL; total price $total_price, discount_amount $discount_amount, total discount $total_discount_amount");
         }
     }
     if ($objCoupon) {
         //DBG::log("Cart::update(): Got Coupon ".var_export($objCoupon, true));
         $total_price -= $discount_amount;
         //DBG::log("Cart::update(): COUPON; total price $total_price, discount_amount $discount_amount, total discount $total_discount_amount");
     }
     if ($hasCoupon) {
         \Message::clear();
     }
     $_SESSION['shop']['cart']['total_discount_amount'] = $total_discount_amount;
     $_SESSION['shop']['cart']['total_price'] = Currency::formatPrice($total_price);
     $_SESSION['shop']['cart']['total_vat_amount'] = Currency::formatPrice($total_vat_amount);
     //DBG::log("Cart::update(): Updated Cart (session): VAT amount: ".$_SESSION['shop']['cart']['total_vat_amount']);
     $_SESSION['shop']['cart']['total_items'] = $items;
     $_SESSION['shop']['cart']['total_weight'] = $total_weight;
     // In grams!
     //DBG::log("Cart::update(): Updated Cart (session): ".var_export($_SESSION['shop']['cart'], true));
     return true;
 }