/** * 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; }
/** * Generates an overview of the Order for the Customer to confirm * * Forward her to the processing of the Order after the button has been * clicked. * @return boolean True on success, false otherwise */ static function confirm() { global $_ARRAYLANG; // If the cart or address is missing, return to the shop if (!self::verifySessionAddress()) { \Cx\Core\Csrf\Controller\Csrf::redirect(\Cx\Core\Routing\Url::fromModuleAndCmd('Shop', '')); } self::$show_currency_navbar = false; // The Customer clicked the confirm button; this must not be the case // the first time this method is called. if (isset($_POST['process'])) { return self::process(); } // Show confirmation page. self::$objTemplate->hideBlock('shopProcess'); self::$objTemplate->setGlobalVariable($_ARRAYLANG); // It may be necessary to refresh the cart here, as the customer // may return to the cart, then press "Back". self::_initPaymentDetails(); foreach (Cart::get_products_array() as $arrProduct) { $objProduct = Product::getById($arrProduct['id']); if (!$objProduct) { // TODO: Implement a proper method // unset(Cart::get_product_id($cart_id]); continue; } $price_options = 0; $attributes = Attributes::getAsStrings($arrProduct['options'], $price_options); $attributes = $attributes[0]; // Note: The Attribute options' price is added // to the price here! $price = $objProduct->get_custom_price(self::$objCustomer, $price_options, $arrProduct['quantity']); // Test the distribution method for delivery $productDistribution = $objProduct->distribution(); $weight = $productDistribution == 'delivery' ? Weight::getWeightString($objProduct->weight()) : '-'; $vatId = $objProduct->vat_id(); $vatRate = Vat::getRate($vatId); $vatPercent = Vat::getShort($vatId); $vatAmount = Vat::amount($vatRate, $price * $arrProduct['quantity']); self::$objTemplate->setVariable(array('SHOP_PRODUCT_ID' => $arrProduct['id'], 'SHOP_PRODUCT_CUSTOM_ID' => $objProduct->code(), 'SHOP_PRODUCT_TITLE' => contrexx_raw2xhtml($objProduct->name()), 'SHOP_PRODUCT_PRICE' => Currency::formatPrice($price * $arrProduct['quantity']), 'SHOP_PRODUCT_QUANTITY' => $arrProduct['quantity'], 'SHOP_PRODUCT_ITEMPRICE' => Currency::formatPrice($price), 'SHOP_UNIT' => Currency::getActiveCurrencySymbol())); if ($attributes && self::$objTemplate->blockExists('attributes')) { self::$objTemplate->setVariable('SHOP_PRODUCT_OPTIONS', $attributes); } if (\Cx\Core\Setting\Controller\Setting::getValue('weight_enable', 'Shop')) { self::$objTemplate->setVariable(array('SHOP_PRODUCT_WEIGHT' => $weight, 'TXT_WEIGHT' => $_ARRAYLANG['TXT_WEIGHT'])); } if (Vat::isEnabled()) { self::$objTemplate->setVariable(array('SHOP_PRODUCT_TAX_RATE' => $vatPercent, 'SHOP_PRODUCT_TAX_AMOUNT' => Currency::formatPrice($vatAmount) . ' ' . Currency::getActiveCurrencySymbol())); } self::$objTemplate->parse("shopCartRow"); } $total_discount_amount = 0; if (Cart::get_discount_amount()) { $total_discount_amount = Cart::get_discount_amount(); self::$objTemplate->setVariable(array('SHOP_DISCOUNT_COUPON_TOTAL' => $_ARRAYLANG['TXT_SHOP_DISCOUNT_COUPON_AMOUNT_TOTAL'], 'SHOP_DISCOUNT_COUPON_TOTAL_AMOUNT' => Currency::formatPrice(-$total_discount_amount))); } self::$objTemplate->setVariable(array('SHOP_UNIT' => Currency::getActiveCurrencySymbol(), 'SHOP_TOTALITEM' => Cart::get_item_count(), 'SHOP_PAYMENT_PRICE' => Currency::formatPrice($_SESSION['shop']['payment_price']), 'SHOP_TOTALPRICE' => Currency::formatPrice(Cart::get_price()), 'SHOP_PAYMENT' => Payment::getProperty($_SESSION['shop']['paymentId'], 'name'), 'SHOP_GRAND_TOTAL' => Currency::formatPrice($_SESSION['shop']['grand_total_price']), 'SHOP_COMPANY' => stripslashes($_SESSION['shop']['company']), 'SHOP_TITLE' => stripslashes($_SESSION['shop']['gender']), 'SHOP_GENDER' => stripslashes($_SESSION['shop']['gender']), 'SHOP_LASTNAME' => stripslashes($_SESSION['shop']['lastname']), 'SHOP_FIRSTNAME' => stripslashes($_SESSION['shop']['firstname']), 'SHOP_ADDRESS' => stripslashes($_SESSION['shop']['address']), 'SHOP_ZIP' => stripslashes($_SESSION['shop']['zip']), 'SHOP_CITY' => stripslashes($_SESSION['shop']['city']), 'SHOP_COUNTRY' => \Cx\Core\Country\Controller\Country::getNameById($_SESSION['shop']['countryId']), 'SHOP_EMAIL' => stripslashes($_SESSION['shop']['email']), 'SHOP_PHONE' => stripslashes($_SESSION['shop']['phone']), 'SHOP_FAX' => stripslashes($_SESSION['shop']['fax']))); if (!empty($_SESSION['shop']['lastname2'])) { self::$objTemplate->setVariable(array('SHOP_COMPANY2' => stripslashes($_SESSION['shop']['company2']), 'SHOP_TITLE2' => stripslashes($_SESSION['shop']['gender2']), 'SHOP_LASTNAME2' => stripslashes($_SESSION['shop']['lastname2']), 'SHOP_FIRSTNAME2' => stripslashes($_SESSION['shop']['firstname2']), 'SHOP_ADDRESS2' => stripslashes($_SESSION['shop']['address2']), 'SHOP_ZIP2' => stripslashes($_SESSION['shop']['zip2']), 'SHOP_CITY2' => stripslashes($_SESSION['shop']['city2']), 'SHOP_COUNTRY2' => \Cx\Core\Country\Controller\Country::getNameById($_SESSION['shop']['countryId2']), 'SHOP_PHONE2' => stripslashes($_SESSION['shop']['phone2']))); } if (!empty($_SESSION['shop']['note'])) { self::$objTemplate->setVariable(array('SHOP_CUSTOMERNOTE' => $_SESSION['shop']['note'])); } if (Vat::isEnabled()) { self::$objTemplate->setVariable(array('TXT_TAX_RATE' => $_ARRAYLANG['TXT_SHOP_VAT_RATE'], 'SHOP_TAX_PRICE' => Currency::formatPrice($_SESSION['shop']['vat_price']), 'SHOP_TAX_PRODUCTS_TXT' => $_SESSION['shop']['vat_products_txt'], 'SHOP_TAX_GRAND_TXT' => $_SESSION['shop']['vat_grand_txt'], 'TXT_TAX_PREFIX' => Vat::isIncluded() ? $_ARRAYLANG['TXT_SHOP_VAT_PREFIX_INCL'] : $_ARRAYLANG['TXT_SHOP_VAT_PREFIX_EXCL'])); if (Vat::isIncluded()) { self::$objTemplate->setVariable(array('SHOP_GRAND_TOTAL_EXCL_TAX' => Currency::formatPrice($_SESSION['shop']['grand_total_price'] - $_SESSION['shop']['vat_price']))); } } // TODO: Make sure in payment() that those two are either both empty or // both non-empty! if (!Cart::needs_shipment() && empty($_SESSION['shop']['shipperId'])) { if (self::$objTemplate->blockExists('shipping_address')) { self::$objTemplate->hideBlock('shipping_address'); } } else { // Shipment is required, so if (empty($_SESSION['shop']['shipperId'])) { \Cx\Core\Csrf\Controller\Csrf::redirect(\Cx\Core\Routing\Url::fromModuleAndCmd('Shop', 'payment')); } self::$objTemplate->setVariable(array('SHOP_SHIPMENT_PRICE' => Currency::formatPrice($_SESSION['shop']['shipment_price']), 'SHOP_SHIPMENT' => Shipment::getShipperName($_SESSION['shop']['shipperId']))); } // Custom. // Enable if Discount class is customized and in use. //self::showCustomerDiscount(Cart::get_price()); return true; }
/** * View of this Orders' items * @global ADONewConnection $objDatabase * @global array $_ARRAYLANG * @param HTML_Template_Sigma $objTemplate The template * @param type $edit If true, items are editable * @param type $total_weight Initial value for the * total item weight, by * reference. * Usually empty or zero * @param type $i Initial value for the row * count, by reference. * Usually empty or zero. * @return float The net item sum on success, * false otherwise */ function view_items($objTemplate, $edit, &$total_weight = 0, $i = 0) { global $objDatabase, $_ARRAYLANG; // Order items // c_sp // Mind the custom price calculation $objCustomer = Customer::getById($this->customer_id); if (!$objCustomer) { \Message::error(sprintf($_ARRAYLANG['TXT_SHOP_ORDER_ERROR_MISSING_CUSTOMER'], $this->customer_id)); $objCustomer = new Customer(); } $query = "\n SELECT `id`, `product_id`, `product_name`,\n `price`, `quantity`, `vat_rate`, `weight`\n FROM `" . DBPREFIX . "module_shop" . MODULE_INDEX . "_order_items`\n WHERE `order_id`=?"; $objResult = $objDatabase->Execute($query, array($this->id)); if (!$objResult) { return self::errorHandler(); } $arrProductOptions = $this->getOptionArray(); $total_vat_amount = 0; $total_net_price = 0; // Orders with Attributes cannot currently be edited // (this would spoil all the options!) // $have_option = false; while (!$objResult->EOF) { $item_id = $objResult->fields['id']; $name = $objResult->fields['product_name']; $price = $objResult->fields['price']; $quantity = $objResult->fields['quantity']; $vat_rate = $objResult->fields['vat_rate']; $product_id = $objResult->fields['product_id']; // Get missing product details $objProduct = Product::getById($product_id); if (!$objProduct) { \Message::warning(sprintf($_ARRAYLANG['TXT_SHOP_PRODUCT_NOT_FOUND'], $product_id)); $objProduct = new Product('', 0, $name, '', $price, 0, 0, 0, $product_id); } $code = $objProduct->code(); $distribution = $objProduct->distribution(); if (isset($arrProductOptions[$item_id])) { if ($edit) { // Edit options } else { //DBG::log("Order::view_items(): Item ID $item_id, Attributes: ".var_export($arrProductOptions[$item_id], true)); // Verify that options are properly shown foreach ($arrProductOptions[$item_id] as $attribute_id => $attribute) { //DBG::log("Order::view_items(): Added option, price: $options_price"); foreach ($attribute as $a) { $name .= '<i><br />- ' . $attribute_id . ': ' . $a['name'] . ' (' . $a['price'] . ')</i>'; $price += $a['price']; } } } } // c_sp $row_net_price = $price * $quantity; $row_price = $row_net_price; // VAT added later, if applicable $total_net_price += $row_net_price; // Here, the VAT has to be recalculated before setting up the // fields. If the VAT is excluded, it must be added here. // Note: the old Order.vat_amount field is no longer valid, // individual shop_order_items *MUST* have been UPDATEd by the // time PHP parses this line. // Also note that this implies that the vat_id and // country_id can be ignored, as they are considered when the // order is placed and the VAT is applied to the order // accordingly. // Calculate the VAT amount per row, included or excluded $row_vat_amount = Vat::amount($vat_rate, $row_net_price); //\DBG::log("$row_vat_amount = Vat::amount($vat_rate, $row_net_price)"); // and add it to the total VAT amount $total_vat_amount += $row_vat_amount; if (!Vat::isIncluded()) { // Add tax to price $row_price += $row_vat_amount; } //else { // VAT is disabled. // There shouldn't be any non-zero percentages in the order_items! // but if there are, there probably has been a change and we *SHOULD* // still treat them as if VAT had been enabled at the time the order // was placed! // That's why the else {} block is commented out. //} $weight = '-'; if ($distribution != 'download') { $weight = $objResult->fields['weight']; if (intval($weight) > 0) { $total_weight += $weight * $quantity; } } $itemHasOptions = !empty($arrProductOptions[$item_id]); $objTemplate->setVariable(array('SHOP_PRODUCT_ID' => $product_id, 'SHOP_ROWCLASS' => 'row' . (++$i % 2 + 1), 'SHOP_QUANTITY' => $quantity, 'SHOP_PRODUCT_NAME' => $name, 'SHOP_PRODUCT_PRICE' => Currency::formatPrice($price), 'SHOP_PRODUCT_SUM' => Currency::formatPrice($row_net_price), 'SHOP_P_ID' => $edit ? $item_id : $objResult->fields['product_id'], 'SHOP_PRODUCT_CODE' => $code, 'SHOP_PRODUCT_TAX_RATE' => $edit ? $vat_rate : Vat::format($vat_rate), 'SHOP_PRODUCT_TAX_AMOUNT' => Currency::formatPrice($row_vat_amount), 'SHOP_PRODUCT_WEIGHT' => Weight::getWeightString($weight), 'SHOP_ACCOUNT_VALIDITY' => \FWUser::getValidityString($weight))); // Get a product menu for each Product if $edit-ing. // Preselect the current Product ID. if ($edit) { if ($itemHasOptions && $objTemplate->blockExists('order_item_product_options_tooltip')) { $objTemplate->touchBlock('order_item_product_options_tooltip'); } $objTemplate->setVariable('SHOP_PRODUCT_IDS_MENU', Products::getMenuoptions($product_id, null, +$_ARRAYLANG['TXT_SHOP_PRODUCT_MENU_FORMAT'], false)); } $objTemplate->parse('order_item'); $objResult->MoveNext(); } return $total_net_price; }