diff --git a/admin-dev/themes/template/helper/list/list_action_supplier_order_receipt.tpl b/admin-dev/themes/template/helper/list/list_action_supplier_order_receipt.tpl new file mode 100644 index 000000000..4727b3407 --- /dev/null +++ b/admin-dev/themes/template/helper/list/list_action_supplier_order_receipt.tpl @@ -0,0 +1,27 @@ +{* +* 2007-2011 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2011 PrestaShop SA +* @version Release: $Revision$ +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*} + +{$action} diff --git a/admin-dev/themes/template/products/informations.tpl b/admin-dev/themes/template/products/informations.tpl index d2be92118..ed03b8f97 100644 --- a/admin-dev/themes/template/products/informations.tpl +++ b/admin-dev/themes/template/products/informations.tpl @@ -429,6 +429,20 @@ $(document).ready(function(){ + + + + + +
+ + + +
{* [end] of physical product *} {* [begin] virtual product *} diff --git a/classes/Carrier.php b/classes/Carrier.php index 50b55b354..188125a41 100644 --- a/classes/Carrier.php +++ b/classes/Carrier.php @@ -92,13 +92,29 @@ class CarrierCore extends ObjectModel /** @var int Position */ public $position; + /** @var int maximum package width managed by the transporter */ + public $max_width; + + /** @var int maximum package height managed by the transporter */ + public $max_height; + + /** @var int maximum package deep managed by the transporter */ + public $max_depth; + + /** @var int maximum package weight managed by the transporter */ + public $max_weight; + + /** @var int grade of the shipping delay (0 for longest, 9 for shortest) */ + public $grade; + protected $langMultiShop = true; protected $fieldsRequired = array('name', 'active'); - protected $fieldsSize = array('name' => 64); + protected $fieldsSize = array('name' => 64, 'grade' => 1); protected $fieldsValidate = array('id_tax_rules_group' => 'isInt', 'name' => 'isCarrierName', 'active' => 'isBool', 'is_free' => 'isBool', 'url' => 'isAbsoluteUrl', 'shipping_handling' => 'isBool', 'range_behavior' => 'isBool', - 'shipping_method' => 'isUnsignedInt'); + 'shipping_method' => 'isUnsignedInt', 'max_width' => 'isUnsignedInt', 'max_height' => 'isUnsignedInt', + 'max_deep' => 'isUnsignedInt', 'max_weight' => 'isUnsignedInt', 'grade' => 'isUnsignedInt'); protected $fieldsRequiredLang = array('delay'); protected $fieldsSizeLang = array('delay' => 128); protected $fieldsValidateLang = array('delay' => 'isGenericName'); @@ -138,6 +154,11 @@ class CarrierCore extends ObjectModel $fields['external_module_name'] = $this->external_module_name; $fields['need_range'] = $this->need_range; $fields['position'] = (int)$this->position; + $fields['max_width'] = (int)$this->max_width; + $fields['max_height'] = (int)$this->max_height; + $fields['max_depth'] = (int)$this->max_depth; + $fields['max_weight'] = (int)$this->max_weight; + $fields['grade'] = (int)$this->grade; return $fields; } @@ -553,6 +574,7 @@ class CarrierCore extends ObjectModel } // if we have to sort carriers by price + $prices = array(); if (Configuration::get('PS_CARRIER_DEFAULT_SORT') == Carrier::SORT_BY_PRICE) { foreach ($results_array as $r) @@ -993,5 +1015,76 @@ class CarrierCore extends ObjectModel $position = DB::getInstance()->getValue($sql); return ($position !== false) ? $position : -1; } + + /** + * For a given {product, warehouse}, gets the carrier available + * + * @param $product integer The id of the product, or an array with at least the package size and weight + */ + public static function getAvailableCarrierList($product, $id_warehouse, $id_shop = null) + { + if(is_numeric($product)) + $product = new Product((int)$product); + else if (is_array($product)) + { + $product['id'] = $product['id_product']; + $product = (object)$product; + } + + if (is_null($id_shop)) + $id_shop = Context::getContext()->shop->getID(true); + + // Does the product is linked with carriers? + $query = new DbQuery(); + $query->select('id_carrier'); + $query->from('product_carrier pc'); + $query->innerJoin('carrier c ON (c.id_reference = pc.id_carrier_reference AND c.deleted = 0)'); + $query->where('id_product = '.(int)($product->id)); + $query->where('id_shop = '.(int)$id_shop); + $carriers = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($query); + if (!empty($carriers)) + { + $carrier_list = array(); + foreach ($carriers as $carrier) + $carrier_list[] = $carrier['id_carrier']; + return $carrier_list; + } + + $carrier_list = array(); + + // The product is not dirrectly linked with a carrier + // Get all the carriers linked to a warehouse + if ($id_warehouse) + { + $warehouse = new Warehouse($id_warehouse); + $carrier_list = $warehouse->getCarriers(); + } + + if (empty($carrier_list)) // No carriers defined, get all available carriers + { + $carrier_list = array(); + $id_address = ((isset($product->id_address_delivery) && $product->id_address_delivery != 0) ? $product->id_address_delivery : Context::getContext()->cart->id_address_delivery); + $address = new Address($id_address); + $id_zone = Address::getZoneById($address->id); + $carriers = Carrier::getCarriersForOrder($id_zone, Context::getContext()->customer->getGroups()); + foreach ($carriers as $carrier) + $carrier_list[] = $carrier['id_carrier']; + } + + if ($product->width > 0 || $product->height > 0 || $product->depth > 0 || $product->weight > 0) + { + foreach ($carrier_list as $key => $id_carrier) + { + $carrier = new Carrier($id_carrier); + if (($carrier->max_width > 0 && $carrier->max_width < $product->width) + || ($carrier->max_height > 0 && $carrier->max_height > $product->height) + || ($carrier->max_depth > 0 && $carrier->max_depth > $product->depth) + || ($carrier->max_weight > 0 && $carrier->max_weight > $product->weight) + ) + unset($carrier_list[$key]); + } + } + return $carrier_list; + } } diff --git a/classes/Cart.php b/classes/Cart.php index 07146ebea..9ab545ab5 100644 --- a/classes/Cart.php +++ b/classes/Cart.php @@ -27,73 +27,79 @@ class CartCore extends ObjectModel { - public $id; + public $id; - public $id_group_shop; + public $id_group_shop; - public $id_shop; + public $id_shop; /** @var integer Customer delivery address ID */ - public $id_address_delivery; + public $id_address_delivery; /** @var integer Customer invoicing address ID */ - public $id_address_invoice; + public $id_address_invoice; /** @var integer Customer currency ID */ - public $id_currency; + public $id_currency; /** @var integer Customer ID */ - public $id_customer; + public $id_customer; /** @var integer Guest ID */ - public $id_guest; + public $id_guest; /** @var integer Language ID */ - public $id_lang; + public $id_lang; /** @var integer Carrier ID */ - public $id_carrier; + public $id_carrier; /** @var boolean True if the customer wants a recycled package */ - public $recyclable = 1; + public $recyclable = 1; /** @var boolean True if the customer wants a gift wrapping */ - public $gift = 0; + public $gift = 0; /** @var string Gift message if specified */ - public $gift_message; + public $gift_message; /** @var string Object creation date */ - public $date_add; + public $date_add; /** @var string secure_key */ - public $secure_key; + public $secure_key; /** @var string Object last modification date */ - public $date_upd; + public $date_upd; - public $checkedTos = false; - public $pictures; - public $textFields; + public $checkedTos = false; + public $pictures; + public $textFields; + + public $delivery_option; + + /** @var boolean Allow to seperate order in multiple package in order to recieve as soon as possible the available products */ + public $allow_seperated_package = false; protected static $_nbProducts = array(); protected static $_isVirtualCart = array(); - protected $fieldsRequired = array('id_currency', 'id_lang'); - protected $fieldsValidate = array('id_address_delivery' => 'isUnsignedId', 'id_address_invoice' => 'isUnsignedId', + protected $fieldsRequired = array('id_currency', 'id_lang'); + protected $fieldsValidate = array('id_address_delivery' => 'isUnsignedId', 'id_address_invoice' => 'isUnsignedId', 'id_currency' => 'isUnsignedId', 'id_customer' => 'isUnsignedId', 'id_guest' => 'isUnsignedId', 'id_lang' => 'isUnsignedId', - 'id_carrier' => 'isUnsignedId', 'recyclable' => 'isBool', 'gift' => 'isBool', 'gift_message' => 'isMessage'); + 'id_carrier' => 'isUnsignedId', 'recyclable' => 'isBool', 'gift' => 'isBool', 'gift_message' => 'isMessage', + 'allow_seperated_package' => 'isBool'); - protected $_products = NULL; - protected static $_totalWeight = array(); - protected $_taxCalculationMethod = PS_TAX_EXC; - protected static $_carriers = NULL; - protected static $_taxes_rate = NULL; - protected static $_attributesLists = array(); - protected $table = 'cart'; - protected $identifier = 'id_cart'; + protected $_products = null; + protected static $_totalWeight = array(); + protected $_taxCalculationMethod = PS_TAX_EXC; + protected static $_carriers = null; + protected static $_taxes_rate = null; + protected static $_attributesLists = array(); + protected $table = 'cart'; + protected $identifier = 'id_cart'; - protected $webserviceParameters = array( + protected $webserviceParameters = array( 'fields' => array( 'id_address_delivery' => array('xlink_resource' => 'addresses'), 'id_address_invoice' => array('xlink_resource' => 'addresses'), @@ -141,6 +147,8 @@ class CartCore extends ObjectModel $fields['gift_message'] = pSQL($this->gift_message); $fields['date_add'] = pSQL($this->date_add); $fields['date_upd'] = pSQL($this->date_upd); + $fields['allow_seperated_package'] = (boolean)$this->allow_seperated_package; + $fields['delivery_option'] = $this->delivery_option; return $fields; } @@ -311,7 +319,9 @@ class CartCore extends ObjectModel ORDER BY `date_add` DESC'; $result = Db::getInstance()->getRow($sql); if ($result AND isset($result['id_product']) AND $result['id_product']) - return $result; + foreach ($this->getProducts() as $product) + if ($result['id_product'] == $product['id_product'] && (!$result['id_product_attribute'] || $result['id_product_attribute'] == $product['id_product_attribute'])) + return $product; return false; } @@ -348,7 +358,7 @@ class CartCore extends ObjectModel pl.`description_short`, pl.`available_now`, pl.`available_later`, p.`id_product`, p.`id_category_default`, p.`id_supplier`, p.`id_manufacturer`, p.`on_sale`, p.`ecotax`, p.`additional_shipping_cost`, p.`available_for_order`, p.`price`, p.`weight`, p.`width`, p.`height`, p.`depth`, sa.`out_of_stock`, p.`active`, p.`date_add`, p.`date_upd`, t.`id_tax`, tl.`name` AS tax, t.`rate`, stock.quantity, pl.`link_rewrite`, cl.`link_rewrite` AS category, - CONCAT(cp.`id_product`, cp.`id_product_attribute`) AS unique_id'); + CONCAT(cp.`id_product`, cp.`id_product_attribute`, cp.`id_address_delivery`) AS unique_id, cp.id_address_delivery'); // Build FROM $sql->from('cart_product cp'); @@ -378,7 +388,7 @@ class CartCore extends ObjectModel $sql->groupBy('unique_id'); // Build ORDER BY - $sql->orderBy('cp.date_add ASC'); + $sql->orderBy('p.id_product, cp.id_product_attribute, cp.date_add ASC'); if (Customization::isFeatureActive()) { @@ -433,7 +443,7 @@ class CartCore extends ObjectModel { $row['price'] = Product::getPriceStatic((int)$row['id_product'], false, isset($row['id_product_attribute']) ? (int)($row['id_product_attribute']) : NULL, 2, NULL, false, true, (int)($row['cart_quantity']), false, ((int)($this->id_customer) ? (int)($this->id_customer) : NULL), (int)($this->id), ((int)($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) ? (int)($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) : NULL), $specificPriceOutput); // Here taxes are computed only once the quantity has been applied to the product price $row['price_wt'] = Product::getPriceStatic((int)$row['id_product'], true, isset($row['id_product_attribute']) ? (int)($row['id_product_attribute']) : NULL, 2, NULL, false, true, (int)($row['cart_quantity']), false, ((int)($this->id_customer) ? (int)($this->id_customer) : NULL), (int)($this->id), ((int)($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) ? (int)($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) : NULL)); - $tax_rate = Tax::getProductTaxRate((int)$row['id_product'], (int)($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')})); + $tax_rate = Tax::getProductTaxRate((int)$row['id_product'], (int)($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')})); $row['total_wt'] = Tools::ps_round($row['price'] * (float)$row['cart_quantity'] * (1 + (float)($tax_rate) / 100), 2); $row['total'] = $row['price'] * (int)($row['cart_quantity']); @@ -556,14 +566,14 @@ class CartCore extends ObjectModel return Db::getInstance()->AutoExecute(_DB_PREFIX_.'cart_cart_rule', array('id_cart_rule' => (int)$id_cart_rule, 'id_cart' => (int)$this->id), 'INSERT'); } - public function containsProduct($id_product, $id_product_attribute = 0, $id_customization = false) + public function containsProduct($id_product, $id_product_attribute = 0, $id_customization = false, $id_address_delivery = 0) { return Db::getInstance()->getRow(' SELECT cp.`quantity` FROM `'._DB_PREFIX_.'cart_product` cp '.($id_customization ? 'LEFT JOIN `'._DB_PREFIX_.'customization` c ON (c.`id_product` = cp.`id_product` AND c.`id_product_attribute` = cp.`id_product_attribute`)' : '').' WHERE cp.`id_product` = '.(int)$id_product.' AND cp.`id_product_attribute` = '.(int)$id_product_attribute.' AND cp.`id_cart` = '.(int)$this->id. - ($id_customization ? ' AND c.`id_customization` = '.(int)$id_customization : '')); + ($id_customization ? ' AND c.`id_customization` = '.(int)$id_customization : '').' AND cp.`id_address_delivery` = '.(int)$id_address_delivery); } /** @@ -574,7 +584,7 @@ class CartCore extends ObjectModel * @param integer $id_product_attribute Attribute ID if needed * @param string $operator Indicate if quantity must be increased or decreased */ - public function updateQty($quantity, $id_product, $id_product_attribute = null, $id_customization = false, $operator = 'up', Shop $shop = null) + public function updateQty($quantity, $id_product, $id_product_attribute = null, $id_customization = false, $id_address_delivery = 0, $operator = 'up', Shop $shop = null) { if (!$shop) $shop = Context::getContext()->shop; @@ -602,7 +612,7 @@ class CartCore extends ObjectModel else { /* Check if the product is already in the cart */ - $result = $this->containsProduct($id_product, $id_product_attribute, (int)$id_customization); + $result = $this->containsProduct($id_product, $id_product_attribute, (int)$id_customization, (int)$id_address_delivery); /* Update quantity if product already exist */ if ($result) @@ -643,7 +653,7 @@ class CartCore extends ObjectModel SET `quantity` = `quantity` '.$qty.', `date_add` = NOW() WHERE `id_product` = '.(int)$id_product. (!empty($id_product_attribute) ? ' AND `id_product_attribute` = '.(int)$id_product_attribute : '').' - AND `id_cart` = '.(int)$this->id.' + AND `id_cart` = '.(int)$this->id.' AND `id_address_delivery` = '.(int)$id_address_delivery.' LIMIT 1'); } @@ -666,6 +676,7 @@ class CartCore extends ObjectModel 'id_product' => (int)$id_product, 'id_product_attribute' => (int)$id_product_attribute, 'id_cart' => (int)$this->id, + 'id_address_delivery' => (int)$id_address_delivery, 'id_shop' => $shop->getID(true), 'quantity' => (int)$quantity, 'date_add' => date('Y-m-d H:i:s') @@ -680,7 +691,7 @@ class CartCore extends ObjectModel $context = Context::getContext()->cloneContext(); $context->cart = $this; CartRule::autoAddToCart($context); - + if ($product->customizable) return $this->_updateCustomizationQuantity((int)$quantity, (int)$id_customization, (int)$id_product, (int)$id_product_attribute, $operator); else @@ -794,9 +805,9 @@ class CartCore extends ObjectModel * * @return boolean result */ - public function OrderExists() + public function orderExists() { - return (bool)Db::getInstance()->getValue('SELECT `id_cart` FROM `'._DB_PREFIX_.'orders` WHERE `id_cart` = '.(int)$this->id); + return (bool)Db::getInstance()->getValue('SELECT count(*) FROM `'._DB_PREFIX_.'orders` WHERE `id_cart` = '.(int)$this->id); } /** @@ -821,7 +832,7 @@ class CartCore extends ObjectModel * @param integer $id_customization Customization id * @return boolean result */ - public function deleteProduct($id_product, $id_product_attribute = NULL, $id_customization = NULL) + public function deleteProduct($id_product, $id_product_attribute = NULL, $id_customization = NULL, $id_address_delivery = 0) { if (isset(self::$_nbProducts[$this->id])) unset(self::$_nbProducts[$this->id]); @@ -836,7 +847,8 @@ class CartCore extends ObjectModel FROM `'._DB_PREFIX_.'customization` WHERE `id_cart` = '.(int)$this->id.' AND `id_product` = '.(int)$id_product.' - AND `id_product_attribute` = '.(int)$id_product_attribute); + AND `id_product_attribute` = '.(int)$id_product_attribute.' + AND `id_address_delivery` = '.(int)$id_address_delivery); if (!$this->_deleteCustomization((int)$id_customization, (int)$id_product, (int)$id_product_attribute)) return false; // refresh cache of self::_products @@ -864,7 +876,9 @@ class CartCore extends ObjectModel ($id_product_attribute != NULL ? ' AND `id_product_attribute` = '.(int)($id_product_attribute) : '')); /* Product deletion */ - if (Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'cart_product` WHERE `id_product` = '.(int)($id_product).(!is_null($id_product_attribute) ? ' AND `id_product_attribute` = '.(int)($id_product_attribute) : '').' AND `id_cart` = '.(int)($this->id))) + if (Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'cart_product` WHERE `id_product` = '. + (int)($id_product).(!is_null($id_product_attribute) ? ' AND `id_product_attribute` = '.(int)($id_product_attribute) : ''). + ' AND `id_cart` = '.(int)($this->id).' AND `id_address_delivery` = '.(int)$id_address_delivery)) { // refresh cache of self::_products $this->_products = $this->getProducts(true); @@ -881,7 +895,7 @@ class CartCore extends ObjectModel * @param integer $id_customization * @return boolean result */ - protected function _deleteCustomization($id_customization, $id_product, $id_product_attribute) + protected function _deleteCustomization($id_customization, $id_product, $id_product_attribute, $id_address_delivery = 0) { $result = true; $customization = Db::getInstance()->getRow('SELECT * @@ -907,7 +921,8 @@ class CartCore extends ObjectModel SET `quantity` = `quantity` - '.(int)($customization['quantity']).' WHERE `id_cart` = '.(int)($this->id).' AND `id_product` = '.(int)($id_product).((int)($id_product_attribute) ? ' - AND `id_product_attribute` = '.(int)($id_product_attribute) : '')); + AND `id_product_attribute` = '.(int)($id_product_attribute) : '').' + AND `id_address_delivery` = '.(int)$id_address_delivery); if (!$result) return false; @@ -925,15 +940,15 @@ class CartCore extends ObjectModel $cart = new Cart($id_cart); if (!Validate::isLoadedObject($cart)) die(Tools::displayError()); - $with_taxes = $use_tax_display ? $cart->_taxCalculationMethod != PS_TAX_EXC : true; + $with_taxes = $use_tax_display ? $cart->_taxCalculationMethod != PS_TAX_EXC : true; return Tools::displayPrice($cart->getOrderTotal($with_taxes), Currency::getCurrencyInstance((int)($cart->id_currency)), false); } - public static function getOrderTotalUsingTaxCalculationMethod($id_cart) - { - return Cart::getTotalCart($id_cart, true); - } + public static function getOrderTotalUsingTaxCalculationMethod($id_cart) + { + return Cart::getTotalCart($id_cart, true); + } /** * This function returns the total cart amount @@ -951,7 +966,7 @@ class CartCore extends ObjectModel * @param integer $type Total type * @return float Order total */ - public function getOrderTotal($withTaxes = true, $type = Cart::BOTH) + public function getOrderTotal($withTaxes = true, $type = Cart::BOTH, $products = null, $id_carrier = null) { if (!$this->id) return 0; @@ -968,11 +983,23 @@ class CartCore extends ObjectModel return 0; if ($virtual AND $type == Cart::BOTH) $type = Cart::BOTH_WITHOUT_SHIPPING; - $shipping_fees = ($type != Cart::BOTH_WITHOUT_SHIPPING AND $type != Cart::ONLY_PRODUCTS_WITHOUT_SHIPPING) ? $this->getOrderShippingCost(NULL, (int)($withTaxes)) : 0; + + if ($type != Cart::BOTH_WITHOUT_SHIPPING AND $type != Cart::ONLY_PRODUCTS_WITHOUT_SHIPPING) + { + if (is_null($products) && is_null($id_carrier)) + $shipping_fees = $this->getTotalShippingCost(null, (boolean)$withTaxes); + else + $shipping_fees = $this->getPackageShippingCost($id_carrier, (int)($withTaxes), null, $products); + } + else + $shipping_fees = 0; + if ($type == Cart::ONLY_PRODUCTS_WITHOUT_SHIPPING) $type = Cart::ONLY_PRODUCTS; - $products = $this->getProducts(); + if (is_null($products)) + $products = $this->getProducts(); + $order_total = 0; if (Tax::excludeTaxeOption()) $withTaxes = false; @@ -983,13 +1010,13 @@ class CartCore extends ObjectModel // Here taxes are computed only once the quantity has been applied to the product price $price = Product::getPriceStatic((int)$product['id_product'], false, (int)$product['id_product_attribute'], 2, NULL, false, true, $product['cart_quantity'], false, (int)$this->id_customer ? (int)$this->id_customer : NULL, (int)$this->id, ($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')})); - $total_ecotax = $product['ecotax'] * (int)$product['cart_quantity']; + $total_ecotax = $product['ecotax'] * (int)$product['cart_quantity']; $total_price = $price * (int)$product['cart_quantity']; if ($withTaxes) { - $total_price = ($total_price - $total_ecotax) * (1 + (float)(Tax::getProductTaxRate((int)$product['id_product'], (int)$this->{Configuration::get('PS_TAX_ADDRESS_TYPE')})) / 100); - $total_ecotax = $total_ecotax * (1 + Tax::getProductEcotaxRate((int)$this->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) / 100); + $total_price = ($total_price - $total_ecotax) * (1 + (float)(Tax::getProductTaxRate((int)$product['id_product'], (int)$this->{Configuration::get('PS_TAX_ADDRESS_TYPE')})) / 100); + $total_ecotax = $total_ecotax * (1 + Tax::getProductEcotaxRate((int)$this->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) / 100); $total_price = Tools::ps_round($total_price + $total_ecotax, 2); } } @@ -1041,29 +1068,557 @@ class CartCore extends ObjectModel return $order_total_discount; return Tools::ps_round((float)$order_total, 2); } + + /** + * Get products grouped by package and by addresses to be sent individualy (one package = one shipping cost). + * + * @return array array( + * 0 => array( // First address + * 0 => array( // First package + * 'product_list' => array(...), + * 'carrier_list' => array(...), + * 'id_warehouse' => array(...), + * ), + * ), + * ); + * @todo Add avaibility check + */ + public function getPackageList() + { + $product_list = $this->getProducts(); + // Step 1 : Get product informations (warehouse_list and carrier_list), count warehouse + // Determine the best warehouse to determine the packages + // For that we count the number of time we can use a warehouse for a specific delivery address + $warehouse_count_by_address = array(); + $warehouse_carrier_list = array(); + foreach ($product_list as &$product) + { + if (!isset($warehouse_count_by_address[$product['id_address_delivery']])) + $warehouse_count_by_address[$product['id_address_delivery']] = array(); + + $product['warehouse_list'] = array(); + + $warehouse_list = Warehouse::getProductWarehouseList($product['id_product'], $product['id_product_attribute']); + // Does the product is in stock ? + // If yes, get only warehouse where the product is in stock + $warehouse_in_stock = array(); + $manager = StockManagerFactory::getManager(); + + foreach ($warehouse_list as $key => $warehouse) + { + if ($manager->getProductRealQuantities( + $product['id_product'], + $product['id_product_attribute'], + array($warehouse['id_warehouse']), + true) > 0 + ) + $warehouse_in_stock[] = $warehouse; + } + if (!empty($warehouse_in_stock)) + { + $warehouse = $warehouse_in_stock; + $product['in_stock'] = true; + } + else + $product['in_stock'] = false; + + foreach ($warehouse_list as $warehouse) + { + if (!isset($warehouse_carrier_list[$warehouse['id_warehouse']])) + { + $warehouse_object = new Warehouse($warehouse['id_warehouse']); + $warehouse_carrier_list[$warehouse['id_warehouse']] = $warehouse_object->getCarriers(); + } + + $product['warehouse_list'][] = $warehouse['id_warehouse']; + if (!isset($warehouse_count_by_address[$product['id_address_delivery']][$warehouse['id_warehouse']])) + $warehouse_count_by_address[$product['id_address_delivery']][$warehouse['id_warehouse']] = 0; + $warehouse_count_by_address[$product['id_address_delivery']][$warehouse['id_warehouse']]++; + } + } + + // If product from the cart are not in any warehouse, return false + //foreach ($warehouse_count_by_address as $warehouse_count) + // if (empty($warehouse_count)) + // return false; + + arsort($warehouse_count_by_address); + + // Step 2 : Group product by warehouse + $grouped_by_warehouse = array(); + foreach ($product_list as &$product) + { + if (!isset($grouped_by_warehouse[$product['id_address_delivery']])) + $grouped_by_warehouse[$product['id_address_delivery']] = array( + 'in_stock' => array(), + 'out_of_stock' => array(), + ); + + // Determine the warehouse to use for this product in order to reduce the number of package + $id_warehouse = 0; + foreach ($warehouse_count_by_address[$product['id_address_delivery']] as $id_warehouse) + if (in_array($id_warehouse, $product['warehouse_list'])) + break; + + if (!isset($grouped_by_warehouse[$product['id_address_delivery']]['in_stock'][$id_warehouse])) + { + $grouped_by_warehouse[$product['id_address_delivery']]['in_stock'][$id_warehouse] = array(); + $grouped_by_warehouse[$product['id_address_delivery']]['out_of_stock'][$id_warehouse] = array(); + } + + if (!$this->allow_seperated_package) + $key = 'in_stock'; + else + $key = ($product['in_stock']) ? 'in_stock' : 'out_of_stock'; + + $product['carrier_list'] = Carrier::getAvailableCarrierList($product, $id_warehouse); + + if (empty($product['carrier_list'])) + $product['carrier_list'] = array(0); + + $grouped_by_warehouse[$product['id_address_delivery']][$key][$id_warehouse][] = $product; + } + + // Step 3 : grouped product from grouped_by_warehouse by available carriers + $grouped_by_carriers = array(); + foreach ($grouped_by_warehouse as $id_address_delivery => $products_in_stock_list) + { + if (!isset($grouped_by_carriers[$id_address_delivery])) + $grouped_by_carriers[$id_address_delivery] = array( + 'in_stock' => array(), + 'out_of_stock' => array(), + ); + + foreach ($products_in_stock_list as $key => $warehouse_list) + { + if (!isset($grouped_by_carriers[$id_address_delivery][$key])) + $grouped_by_carriers[$id_address_delivery][$key] = array(); + foreach ($warehouse_list as $id_warehouse => $product_list) + { + if (!isset($grouped_by_carriers[$id_address_delivery][$key][$id_warehouse])) + $grouped_by_carriers[$id_address_delivery][$key][$id_warehouse] = array(); + + foreach ($product_list as $product) + { + $package_carriers_key = implode(',', $product['carrier_list']); + + if (!isset($grouped_by_carriers[$id_address_delivery][$key][$id_warehouse][$package_carriers_key])) + $grouped_by_carriers[$id_address_delivery][$key][$id_warehouse][$package_carriers_key] = array( + 'product_list' => array(), + 'carrier_list' => $product['carrier_list'] + ); + + $grouped_by_carriers[$id_address_delivery][$key][$id_warehouse][$package_carriers_key]['product_list'][] = $product; + } + } + } + } + + $package_list = array(); + // Step 4 : merge product from grouped_by_carriers into $package to minimize the number of package + foreach ($grouped_by_carriers as $id_address_delivery => $products_in_stock_list) + { + if (!isset($package_list[$id_address_delivery])) + $package_list[$id_address_delivery] = array( + 'in_stock' => array(), + 'out_of_stock' => array(), + ); + + + foreach ($products_in_stock_list as $key => $warehouse_list) + { + if (!isset($package_list[$id_address_delivery][$key])) + $package_list[$id_address_delivery][$key] = array(); + // Count occurance of each carriers to minimize the number of packages + $carrier_count = array(); + foreach ($warehouse_list as $id_warehouse => $products_grouped_by_carriers) + { + foreach ($products_grouped_by_carriers as $data) + { + foreach ($data['carrier_list'] as $id_carrier) + if (!isset($carrier_count[$id_carrier])) + $carrier_count[$id_carrier] = 0; + $carrier_count[$id_carrier]++; + } + } + arsort($carrier_count); + + foreach ($warehouse_list as $id_warehouse => $products_grouped_by_carriers) + { + if (!isset($package_list[$id_address_delivery][$key][$id_warehouse])) + $package_list[$id_address_delivery][$key][$id_warehouse] = array(); + foreach ($products_grouped_by_carriers as $data) + { + foreach ($carrier_count as $id_carrier => $rate) + { + if (in_array($id_carrier, $data['carrier_list'])) + { + if (!isset($package_list[$id_address_delivery][$key][$id_warehouse][$id_carrier])) + $package_list[$id_address_delivery][$key][$id_warehouse][$id_carrier] = array( + 'carrier_list' => $data['carrier_list'], + 'product_list' => array(), + ); + $package_list[$id_address_delivery][$key][$id_warehouse][$id_carrier]['carrier_list'] = + array_intersect($package_list[$id_address_delivery][$key][$id_warehouse][$id_carrier]['carrier_list'], $data['carrier_list']); + $package_list[$id_address_delivery][$key][$id_warehouse][$id_carrier]['product_list'] = + array_merge($package_list[$id_address_delivery][$key][$id_warehouse][$id_carrier]['product_list'], $data['product_list']); + break; + } + } + } + } + } + } + + // Step 5 : Reduce deep of $package_list + $final_package_list = array(); + foreach ($package_list as $id_address_delivery => $products_in_stock_list) + { + if (!isset($final_package_list[$id_address_delivery])) + $final_package_list[$id_address_delivery] = array(); + + foreach ($products_in_stock_list as $key => $warehouse_list) + foreach ($warehouse_list as $id_warehouse => $products_grouped_by_carriers) + foreach ($products_grouped_by_carriers as $data) + $final_package_list[$id_address_delivery][] = array( + 'product_list' => $data['product_list'], + 'carrier_list' => $data['carrier_list'], + 'id_warehouse' => $id_warehouse, + ); + } + return $final_package_list; + } + + /** + * Get all deliveries options available for the current cart + * @param Country $default_country + * + * @return array array( + * 0 => array( // First address + * 0 => array( // First delivery option available for this address + * carrier_list => array( + * 12 => array( // First carrier for this option + * 'instance' => Carrier Object, + * 'logo' => , + * 'price_with_tax' => 12.4, + * 'price_without_tax' => 12.4, + * 'package_list' => array( + * 1, + * 3, + * ), + * ), + * ), + * is_best_grade => true, // Does this option have the biggest grade (quick shipping) for this shipping address + * is_best_price => true, // Does this option have the lower price for this shipping address + * price_with_tax => 12.5, + * price_without_tax => 12.5, + * ), + * ), + * ); + */ + public function getDeliveryOptionList(Country $default_country = null) + { + $delivery_option_list = array(); + $carriers_price = array(); + $carrier_collection = array(); + + $package_list = $this->getPackageList(); + foreach ($package_list as $id_address => $packages) + { + $delivery_option_list[$id_address] = array(); + $carriers_price[$id_address] = array(); + + $common_carriers = array(); + $best_price_carriers = array(); + $best_grade_carriers = array(); + foreach ($packages as $id_package => $package) + { + // No carriers available + if (count($package['carrier_list']) == 1 && $package['carrier_list'][0] == 0) + return array(); + + $carriers_price[$id_address][$id_package] = array(); + $carriers_instance = array(); + + if (empty($common_carriers)) + $common_carriers = $package['carrier_list']; + else + array_intersect($common_carriers, $package['carrier_list']); + $best_price = null; + $best_price_carrier = null; + $best_grade = null; + $best_grade_carrier = null; + foreach ($package['carrier_list'] as $id_carrier) + { + if (!isset($carriers_instance[$id_carrier])) + $carriers_instance[$id_carrier] = new Carrier($id_carrier); + $price_with_tax = $this->getPackageShippingCost($id_carrier, true, $default_country, $package['product_list']); + $price_without_tax = $this->getPackageShippingCost($id_carrier, false, $default_country, $package['product_list']); + if (is_null($best_price) || $price_with_tax < $best_price) + { + $best_price = $price_with_tax; + $best_price_carrier = $id_carrier; + } + $carriers_price[$id_address][$id_package][$id_carrier] = array( + 'without_tax' => $price_without_tax, + 'with_tax' => $price_with_tax); + + $grade = $carriers_instance[$id_carrier]->grade; + if (is_null($best_grade) || $grade > $best_grade) + { + $best_grade = $grade; + $best_grade_carrier = $id_carrier; + } + + } + $best_price_carriers[$id_package] = $best_price_carrier; + $best_grade_carriers[$id_package] = $best_grade_carrier; + } + + $best_price_carrier = array(); + $key = ''; + foreach ($best_price_carriers as $id_package => $id_carrier) + { + $key .= $id_carrier.','; + if (!isset($best_price_carrier[$id_carrier])) + $best_price_carrier[$id_carrier] = array( + 'price_with_tax' => 0, + 'price_without_tax' => 0, + 'package_list' => array() + ); + $best_price_carrier[$id_carrier]['price_with_tax'] += $carriers_price[$id_address][$id_package][$id_carrier]['with_tax']; + $best_price_carrier[$id_carrier]['price_without_tax'] += $carriers_price[$id_address][$id_package][$id_carrier]['without_tax']; + $best_price_carrier[$id_carrier]['package_list'][] = $id_package; + $best_price_carrier[$id_carrier]['instance'] = $carriers_instance[$id_carrier]; + } + $delivery_option_list[$id_address][$key] = array( + 'carrier_list' => $best_price_carrier, + 'is_best_price' => true, + 'is_best_grade' => false, + 'unique_carrier' => false + ); + + $best_grade_carrier = array(); + $key = ''; + foreach ($best_grade_carriers as $id_package => $id_carrier) + { + $key .= $id_carrier.','; + if (!isset($best_grade_carrier[$id_carrier])) + $best_grade_carrier[$id_carrier] = array( + 'price_with_tax' => 0, + 'price_without_tax' => 0, + 'package_list' => array() + ); + $best_grade_carrier[$id_carrier]['price_with_tax'] += $carriers_price[$id_address][$id_package][$id_carrier]['with_tax']; + $best_grade_carrier[$id_carrier]['price_without_tax'] += $carriers_price[$id_address][$id_package][$id_carrier]['without_tax']; + $best_grade_carrier[$id_carrier]['package_list'][] = $id_package; + $best_grade_carrier[$id_carrier]['instance'] = $carriers_instance[$id_carrier]; + } + if (!isset($delivery_option_list[$id_address][$key])) + $delivery_option_list[$id_address][$key] = array( + 'carrier_list' => $best_grade_carrier, + 'is_best_price' => false, + 'unique_carrier' => false + ); + $delivery_option_list[$id_address][$key]['is_best_grade'] = true; + + foreach ($common_carriers as $id_carrier) + { + $price = 0; + $key = ''; + $package_list = array(); + $total_price_with_tax = 0; + $total_price_without_tax = 0; + $price_with_tax = 0; + $price_without_tax = 0; + foreach ($packages as $id_package => $package) + { + $key .= $id_carrier.','; + $price_with_tax += $carriers_price[$id_address][$id_package][$id_carrier]['with_tax']; + $price_without_tax += $carriers_price[$id_address][$id_package][$id_carrier]['without_tax']; + $package_list[] = $id_package; + } + if (!isset($delivery_option_list[$id_address][$key])) + $delivery_option_list[$id_address][$key] = array( + 'is_best_price' => false, + 'is_best_grade' => false, + 'unique_carrier' => false, + 'carrier_list' => array( + $id_carrier => array( + 'price_with_tax' => $price_with_tax, + 'price_without_tax' => $price_without_tax, + 'instance' => $carriers_instance[$id_carrier], + 'package_list' => $package_list + ) + ) + ); + else + $delivery_option_list[$id_address][$key]['unique_carrier'] = true; + } + foreach ($delivery_option_list as $id_address => $delivery_option) + foreach ($delivery_option as $key => $value) + { + $total_price_with_tax = 0; + $total_price_without_tax = 0; + foreach ($value['carrier_list'] as $id_carrier => $data) + { + $total_price_with_tax += $data['price_with_tax']; + $total_price_without_tax += $data['price_without_tax']; + + if (!isset($carrier_collection[$id_carrier])) + $carrier_collection[$id_carrier] = new Carrier($id_carrier); + $delivery_option_list[$id_address][$key]['carrier_list'][$id_carrier]['instance'] = $carrier_collection[$id_carrier]; + + if (file_exists(_PS_SHIP_IMG_DIR_.$id_carrier.'.jpg')) + $delivery_option_list[$id_address][$key]['carrier_list'][$id_carrier]['logo'] = _THEME_SHIP_DIR_.$id_carrier.'.jpg'; + else + $delivery_option_list[$id_address][$key]['carrier_list'][$id_carrier]['logo'] = false; + } + $delivery_option_list[$id_address][$key]['total_price_with_tax'] = $total_price_with_tax; + $delivery_option_list[$id_address][$key]['total_price_without_tax'] = $total_price_without_tax; + } + } + return $delivery_option_list; + } + + /** + * Get all delivery addresses object for the current cart + */ + public function getAddressCollection() + { + $collection = array(); + foreach (Db::getInstance()->executeS('SELECT DISTINCT `id_address_delivery` + FROM `'._DB_PREFIX_.'cart_product` + WHERE id_cart = '.(int)$this->id) + as $row + ) + $collection[$row['id_address_delivery']] = new Address($row['id_address_delivery']); + return $collection; + } + + /** + * Return the delivery option seleted, or if no delivery option was selected, the cheapest option for each address + * @return array delivery option + */ + public function getDeliveryOption($default_country = null) + { + // The delivery option was selected + if (isset($this->delivery_option) && $this->delivery_option != '') + return unserialize($this->delivery_option); + // The delivery option is not selected, get the better for all option + $delivery_option = array(); + foreach ($this->getDeliveryOptionList($default_country) as $id_address => $options) + foreach ($options as $key => $option) + if ($option['is_best_price']) + { + $delivery_option[$id_address] = $key; + break; + } + return $delivery_option; + } /** - * Return shipping total + * Return shipping total for the cart * - * @param integer $id_carrier Carrier ID (default : current carrier) + * @param array $delivery_option Array of the delivery option for each address + * @param booleal $useTax + * @param Country $default_country * @return float Shipping total */ - function getOrderShippingCost($id_carrier = NULL, $useTax = true, Country $default_country = null) - { + public function getTotalShippingCost($delivery_option = null, $useTax = true, Country $default_country = null) + { + if (is_null($delivery_option)) + $delivery_option = $this->getDeliveryOption($default_country); + + $total_shipping = 0; + $delivery_option_list = $this->getDeliveryOptionList(); + foreach ($delivery_option as $id_address => $key) + { + if ($useTax) + $total_shipping += $delivery_option_list[$id_address][$key]['total_price_with_tax']; + else + $total_shipping += $delivery_option_list[$id_address][$key]['total_price_without_tax']; + } + return $total_shipping; + } + + /** + * Return shipping total + * This function is dépreciate, use getTotalShippingCost or getPackageShippingCost + * + * @param integer $id_carrier Carrier ID (default : current carrier) + * @param booleal $useTax + * @param Country $default_country + * @param Array $product_list + * @param array $product_list List of product concerned by the shipping. If null, all the product of the cart are used to calculate the shipping cost + * @deprecated since 1.5.0 + * + * @return float Shipping total + */ + public function getOrderShippingCost($id_carrier = null, $useTax = true, Country $default_country = null, $product_list = null) + { + return $this->getPackageShippingCost($id_carrier, $useTax, $default_country, $product_list); + } + + /** + * Return package shipping cost + * + * @param integer $id_carrier Carrier ID (default : current carrier) + * @param booleal $useTax + * @param Country $default_country + * @param Array $product_list + * @param array $product_list List of product concerned by the shipping. If null, all the product of the cart are used to calculate the shipping cost + * + * @return float Shipping total + */ + public function getPackageShippingCost($id_carrier = null, $useTax = true, Country $default_country = null, $product_list = null) + { if ($this->isVirtualCart()) return 0; if (!$default_country) $default_country = Context::getContext()->country; + $complete_product_list = $this->getProducts(); + if (is_null($product_list)) + $products = $complete_product_list; + else + $products = $product_list; + + // Checking discounts in cart + if (Discount::isFeatureActive()) + $discounts = $this->getDiscounts(true); + else + $discounts = null; + if ($discounts) + foreach ($discounts AS $id_discount) + if ($id_discount['id_discount_type'] == Discount::FREE_SHIPPING) + { + if ($id_discount['minimal'] > 0) + { + $total_cart = 0; + + $categories = Discount::getCategories((int)($id_discount['id_discount'])); + if (sizeof($categories)) + foreach($complete_product_list AS $product) + if (Product::idIsOnCategoryId((int)($product['id_product']), $categories)) + $total_cart += $product['total_wt']; + + if ($total_cart >= $id_discount['minimal']) + return 0; + } + else + return 0; + } + // Order total in default currency without fees - $order_total = $this->getOrderTotal(true, Cart::ONLY_PRODUCTS_WITHOUT_SHIPPING); + $order_total = $this->getOrderTotal(true, Cart::ONLY_PRODUCTS_WITHOUT_SHIPPING, $product_list); // Start with shipping cost at 0 - $shipping_cost = 0; + $shipping_cost = 0; // If no product added, return 0 - if ($order_total <= 0 AND !(int)(self::getNbProducts($this->id))) + if ($order_total <= 0 && ((!(int)(self::getNbProducts($this->id) && is_null($product_list))) || (count($product_list) && !is_null($product_list)))) return $shipping_cost; // Get id zone @@ -1086,7 +1641,7 @@ class CartCore extends ObjectModel $id_carrier = ''; if (empty($id_carrier) && $this->isCarrierInRange(Configuration::get('PS_CARRIER_DEFAULT'), $id_zone)) - $id_carrier = (int)(Configuration::get('PS_CARRIER_DEFAULT')); + $id_carrier = (int)(Configuration::get('PS_CARRIER_DEFAULT')); if (empty($id_carrier)) { @@ -1110,8 +1665,8 @@ class CartCore extends ObjectModel $carrier = self::$_carriers[$row['id_carrier']]; // Get only carriers that are compliant with shipping method - if (($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT AND $carrier->getMaxDeliveryPriceByWeight($id_zone) === false) - OR ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_PRICE AND $carrier->getMaxDeliveryPriceByPrice($id_zone) === false)) + if (($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT && $carrier->getMaxDeliveryPriceByWeight($id_zone) === false) + || ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_PRICE && $carrier->getMaxDeliveryPriceByPrice($id_zone) === false)) { unset($result[$k]); continue ; @@ -1121,8 +1676,8 @@ class CartCore extends ObjectModel if ($row['range_behavior']) { // Get only carriers that have a range compatible with cart - if (($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT AND (!Carrier::checkDeliveryPriceByWeight($row['id_carrier'], $this->getTotalWeight(), $id_zone))) - OR ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_PRICE AND (!Carrier::checkDeliveryPriceByPrice($row['id_carrier'], $this->getOrderTotal(true, Cart::BOTH_WITHOUT_SHIPPING), $id_zone, (int)($this->id_currency))))) + if (($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT && (!Carrier::checkDeliveryPriceByWeight($row['id_carrier'], $this->getTotalWeight(), $id_zone))) + || ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_PRICE && (!Carrier::checkDeliveryPriceByPrice($row['id_carrier'], $this->getOrderTotal(true, Cart::BOTH_WITHOUT_SHIPPING), $id_zone, (int)($this->id_currency))))) { unset($result[$k]); continue ; @@ -1130,7 +1685,7 @@ class CartCore extends ObjectModel } if ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT) - $shipping = $carrier->getDeliveryPriceByWeight($this->getTotalWeight(), $id_zone); + $shipping = $carrier->getDeliveryPriceByWeight($this->getTotalWeight($product_list), $id_zone); else $shipping = $carrier->getDeliveryPriceByPrice($order_total, $id_zone, (int)($this->id_currency)); @@ -1153,7 +1708,7 @@ class CartCore extends ObjectModel $carrier = self::$_carriers[$id_carrier]; if (!Validate::isLoadedObject($carrier)) die(Tools::displayError('Fatal error: "no default carrier"')); - if (!$carrier->active) + if (!$carrier->active) return $shipping_cost; // Free fees if free carrier @@ -1172,39 +1727,42 @@ class CartCore extends ObjectModel // $orderTotalwithDiscounts = $this->getOrderTotal(true, Cart::BOTH_WITHOUT_SHIPPING); // if ($orderTotalwithDiscounts >= (float)($free_fees_price) AND (float)($free_fees_price) > 0) // return $shipping_cost; - if (isset($configuration['PS_SHIPPING_FREE_WEIGHT']) AND $this->getTotalWeight() >= (float)($configuration['PS_SHIPPING_FREE_WEIGHT']) AND (float)($configuration['PS_SHIPPING_FREE_WEIGHT']) > 0) + if (isset($configuration['PS_SHIPPING_FREE_WEIGHT']) && $this->getTotalWeight() >= (float)($configuration['PS_SHIPPING_FREE_WEIGHT']) && (float)($configuration['PS_SHIPPING_FREE_WEIGHT']) > 0) return $shipping_cost; - // Get shipping cost using correct method - if ($carrier->range_behavior) - { - // Get id zone - if ( - isset($this->id_address_delivery) - AND $this->id_address_delivery - AND Customer::customerHasAddress($this->id_customer, $this->id_address_delivery) - ) - $id_zone = Address::getZoneById((int)($this->id_address_delivery)); - else - $id_zone = (int)$default_country->id_zone; - if (($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT AND (!Carrier::checkDeliveryPriceByWeight($carrier->id, $this->getTotalWeight(), $id_zone))) - OR ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_PRICE AND (!Carrier::checkDeliveryPriceByPrice($carrier->id, $this->getOrderTotal(true, Cart::BOTH_WITHOUT_SHIPPING), $id_zone, (int)($this->id_currency))))) - $shipping_cost += 0; - else { - if ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT) - $shipping_cost += $carrier->getDeliveryPriceByWeight($this->getTotalWeight(), $id_zone); - else // by price - $shipping_cost += $carrier->getDeliveryPriceByPrice($order_total, $id_zone, (int)($this->id_currency)); - } - } + // Get shipping cost using correct method + if ($carrier->range_behavior) + { + // Get id zone + if ( + isset($this->id_address_delivery) + AND $this->id_address_delivery + AND Customer::customerHasAddress($this->id_customer, $this->id_address_delivery) + ) + $id_zone = Address::getZoneById((int)($this->id_address_delivery)); + else + $id_zone = (int)$default_country->id_zone; + if (($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT && (!Carrier::checkDeliveryPriceByWeight($carrier->id, $this->getTotalWeight(), $id_zone))) + || ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_PRICE + && (!Carrier::checkDeliveryPriceByPrice($carrier->id, $this->getOrderTotal(true, Cart::BOTH_WITHOUT_SHIPPING), $id_zone, (int)($this->id_currency)))) + ) + $shipping_cost += 0; else { if ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT) - $shipping_cost += $carrier->getDeliveryPriceByWeight($this->getTotalWeight(), $id_zone); - else + $shipping_cost += $carrier->getDeliveryPriceByWeight($this->getTotalWeight($product_list), $id_zone); + else // by price $shipping_cost += $carrier->getDeliveryPriceByPrice($order_total, $id_zone, (int)($this->id_currency)); - } + } + else + { + if ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT) + $shipping_cost += $carrier->getDeliveryPriceByWeight($this->getTotalWeight($product_list), $id_zone); + else + $shipping_cost += $carrier->getDeliveryPriceByPrice($order_total, $id_zone, (int)($this->id_currency)); + + } // Adding handling charges if (isset($configuration['PS_SHIPPING_HANDLING']) AND $carrier->shipping_handling) $shipping_cost += (float)($configuration['PS_SHIPPING_HANDLING']); @@ -1225,16 +1783,16 @@ class CartCore extends ObjectModel if (Validate::isLoadedObject($module)) { if (array_key_exists('id_carrier', $module)) - $module->id_carrier = $carrier->id; - if($carrier->need_range) - $shipping_cost = $module->getOrderShippingCost($this, $shipping_cost); - else - $shipping_cost = $module->getOrderShippingCostExternal($this); + $module->id_carrier = $carrier->id; + if ($carrier->need_range) + $shipping_cost = $module->getPackageShippingCost($this, $shipping_cost); + else + $shipping_cost = $module->getOrderShippingCostExternal($this); - // Check if carrier is available - if ($shipping_cost === false) - return false; - } + // Check if carrier is available + if ($shipping_cost === false) + return false; + } else return false; } @@ -1244,15 +1802,27 @@ class CartCore extends ObjectModel $shipping_cost *= 1 + ($carrierTax / 100); return (float)(Tools::ps_round((float)($shipping_cost), 2)); - } + } /** * Return cart weight - * * @return float Cart weight */ - public function getTotalWeight() + public function getTotalWeight($products = null) { + if(!is_null($products)) + { + $total_weight = 0; + foreach($products as $product) + { + if (is_null($product['weight_attribute'])) + $total_weight += $product['weight']; + else + $total_weight += $product['weight_attribute']; + } + return $total_weight; + } + if (!isset(self::$_totalWeight[$this->id])) { if (Combination::isFeatureActive()) @@ -1482,14 +2052,14 @@ class CartCore extends ObjectModel } public static function getCustomerCarts($id_customer) - { - $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' - SELECT * + { + $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT * FROM '._DB_PREFIX_.'cart c WHERE c.`id_customer` = '.(int)($id_customer).' ORDER BY c.`date_add` DESC'); - return $result; - } + return $result; + } public static function replaceZeroByShopName($echo, $tr) { @@ -1513,7 +2083,7 @@ class CartCore extends ObjectModel $products = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT * FROM `'._DB_PREFIX_.'cart_product` WHERE `id_cart` = '.(int)$this->id); foreach ($products AS $product) - $success &= $cart->updateQty($product['quantity'], (int)$product['id_product'], (int)$product['id_product_attribute'], NULL, 'up', new Shop($cart->id_shop)); + $success &= $cart->updateQty($product['quantity'], (int)$product['id_product'], (int)$product['id_product_attribute'], NULL, (int)$product['id_address_delivery'], 'up', new Shop($cart->id_shop)); // Customized products $customs = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' @@ -1580,6 +2150,251 @@ class CartCore extends ObjectModel } return true; } + + public function setProductAddressDelivery($id_product, $id_product_attribute, $old_id_address_delivery, $new_id_address_delivery) + { + // Check address is linked with the customer + if (!Customer::customerHasAddress(Context::getContext()->customer->id, $new_id_address_delivery)) + return false; + + if ($new_id_address_delivery == $old_id_address_delivery) + return false; + + // Checking if the product with the old address delivery exists + $sql = new DbQuery(); + $sql->select('count(*)'); + $sql->from('cart_product as cp'); + $sql->where('id_product = '.(int)$id_product); + $sql->where('id_product_attribute = '.(int)$id_product_attribute); + $sql->where('id_address_delivery = '.(int)$old_id_address_delivery); + $sql->where('id_cart = '.(int)$this->id); + $result = Db::getInstance()->getValue($sql); + if ($result == 0) + return false; + + // Checking if there is no others similar products with this new address delivery + $sql = new DbQuery(); + $sql->select('sum(quantity) as qty'); + $sql->from('cart_product as cp'); + $sql->where('id_product = '.(int)$id_product); + $sql->where('id_product_attribute = '.(int)$id_product_attribute); + $sql->where('id_address_delivery = '.(int)$new_id_address_delivery); + $sql->where('id_cart = '.(int)$this->id); + $result = Db::getInstance()->getValue($sql); + + // Removing similar products with this new address delivery + $sql = 'DELETE FROM '._DB_PREFIX_.'cart_product + WHERE id_product = '.(int)$id_product.' + AND id_product_attribute = '.(int)$id_product_attribute.' + AND id_address_delivery = '.(int)$new_id_address_delivery.' + AND id_cart = '.(int)$this->id.' + LIMIT 1'; + Db::getInstance()->execute($sql); + + // Changing the address + $sql = 'UPDATE '._DB_PREFIX_.'cart_product + SET `id_address_delivery` = '.(int)$new_id_address_delivery.', + `quantity` = `quantity` + '.(int)$result['sum'].' + WHERE id_product = '.(int)$id_product.' + AND id_product_attribute = '.(int)$id_product_attribute.' + AND id_address_delivery = '.(int)$old_id_address_delivery.' + AND id_cart = '.(int)$this->id.' + LIMIT 1'; + Db::getInstance()->execute($sql); + + // Changing the address of the customizations + $sql = 'UPDATE '._DB_PREFIX_.'customization + SET `id_address_delivery` = '.(int)$new_id_address_delivery.' + WHERE id_product = '.(int)$id_product.' + AND id_product_attribute = '.(int)$id_product_attribute.' + AND id_address_delivery = '.(int)$old_id_address_delivery.' + AND id_cart = '.(int)$this->id; + Db::getInstance()->execute($sql); + + return true; + } + + public function duplicateProduct($id_product, $id_product_attribute, $id_address_delivery, $new_id_address_delivery, $quantity = 1, $keep_quantity = false) + { + // Check address is linked with the customer + if (!Customer::customerHasAddress(Context::getContext()->customer->id, $new_id_address_delivery)) + return false; + + // Checking the product do not exist with the new address + $sql = new DbQuery(); + $sql->select('count(*)'); + $sql->from('cart_product as c'); + $sql->where('id_product = '.(int)$id_product); + $sql->where('id_product_attribute = '.(int)$id_product_attribute); + $sql->where('id_address_delivery = '.(int)$new_id_address_delivery); + $sql->where('id_cart = '.(int)$this->id); + $result = Db::getInstance()->getValue($sql); + if ($result > 0) + return false; + + // Duplicating cart_product line + $sql = 'INSERT INTO '._DB_PREFIX_.'cart_product values( + '.(int)$this->id.', + '.(int)$id_product.', + '.(int)$this->id_shop.', + '.(int)$id_product_attribute.', + '.(int)$quantity.', + NOW(), + '.(int)$new_id_address_delivery.')'; + Db::getInstance()->execute($sql); + + if (!$keep_quantity) + { + $sql = 'UPDATE '._DB_PREFIX_.'cart_product + SET `quantity` = `quantity` - '.(int)$quantity.' + WHERE id_cart = '.(int)$this->id.' + AND id_product = '.(int)$id_product.' + AND id_shop = '.(int)$this->id_shop.' + AND id_product_attribute = '.(int)$id_product_attribute.' + AND id_address_delivery = '.(int)$id_address_delivery; + Db::getInstance()->execute($sql); + } + + // Checking if there is customizations + $sql = new DbQuery(); + $sql->select('*'); + $sql->from('customization as c'); + $sql->where('id_product = '.(int)$id_product); + $sql->where('id_product_attribute = '.(int)$id_product_attribute); + $sql->where('id_address_delivery = '.(int)$id_address_delivery); + $sql->where('id_cart = '.(int)$this->id); + $results = Db::getInstance()->executeS($sql); + + foreach ($results as $customization) + { + + // Duplicate customization + $sql = 'INSERT INTO '._DB_PREFIX_.'customization(`id_product_attribute`, `id_address_delivery`, `id_cart`, `id_product`, `quantity`, `in_cart`) + VALUES ( + '.$customization['id_product_attribute'].', + '.$new_id_address_delivery.', + '.$customization['id_cart'].', + '.$customization['id_product'].', + '.$quantity.', + '.$customization['in_cart'].')'; + Db::getInstance()->execute($sql); + + $sql = 'INSERT INTO '._DB_PREFIX_.'customized_data(`id_customization`, `type`, `index`, `value`) + (SELECT '.(int)Db::getInstance()->Insert_ID().' `id_customization`, `type`, `index`, `value` FROM customized_data WHERE id_customization = '.$customization['id_customization'].')'; + Db::getInstance()->execute($sql); + } + + $customization_count = count($results); + if ($customization_count > 0) + { + $sql = 'UPDATE '._DB_PREFIX_.'cart_product + SET `quantity` = `quantity` = '.(int)($customization_count * $quantity).' + WHERE id_cart = '.(int)$this->id.' + AND id_product = '.(int)$id_product.' + AND id_shop = '.(int)$this->id_shop.' + AND id_product_attribute = '.(int)$id_product_attribute.' + AND id_address_delivery = '.(int)$new_id_address_delivery; + Db::getInstance()->execute($sql); + } + return true; + } + + /** + * Update products cart address delivery with the address delivery of the cart + */ + public function setNoMultishipping() + { + // Upgrading quantities + $sql = 'SELECT sum(`quantity`) as quantity, id_product, id_product_attribute, count(*) as count + FROM `'._DB_PREFIX_.'cart_product` + WHERE `id_cart` = '.(int)$this->id.' + AND `id_shop` = '.(int)$this->id_shop.' + GROUP BY id_product, id_product_attribute + HAVING count > 1'; + foreach (Db::getInstance()->executeS($sql) as $product) + { + $sql = 'UPDATE `'._DB_PREFIX_.'cart_product` + SET `quantity` = '.$product['quantity'].' + WHERE `id_cart` = '.(int)$this->id.' + AND `id_shop` = '.(int)$this->id_shop.' + AND id_product = '.$product['id_product'].' + AND id_product_attribute = '.$product['id_product_attribute']; + Db::getInstance()->execute($sql); + } + + // Merging multiple lines + $sql = 'DELETE cp1 + FROM `'._DB_PREFIX_.'cart_product` cp1 + INNER JOIN `'._DB_PREFIX_.'cart_product` cp2 + ON ( + (cp1.id_cart = cp2.id_cart) + AND (cp1.id_product = cp2.id_product) + AND (cp1.id_product_attribute = cp2.id_product_attribute) + AND (cp1.id_address_delivery <> cp2.id_address_delivery) + AND (cp1.date_add > cp2.date_add) + )'; + Db::getInstance()->execute($sql); + + + // upgradng address delivery + $sql = 'UPDATE `'._DB_PREFIX_.'cart_product` + SET `id_address_delivery` = + ( + SELECT `id_address_delivery` + FROM `'._DB_PREFIX_.'cart` + WHERE `id_cart` = '.(int)$this->id.' + AND `id_shop` = '.(int)$this->id_shop.' + ) + WHERE `id_cart` = '.(int)$this->id.' AND `id_shop` = '.(int)$this->id_shop; + Db::getInstance()->execute($sql); + $sql = 'UPDATE `'._DB_PREFIX_.'customization` + SET `id_address_delivery` = + ( + SELECT `id_address_delivery` + FROM `'._DB_PREFIX_.'cart` + WHERE `id_cart` = '.(int)$this->id.' + AND `id_shop` = '.(int)$this->id_shop.' + ) + WHERE `id_cart` = '.(int)$this->id; + Db::getInstance()->execute($sql); + } + + /** + * Set an address to all products on the cart without address delivery + */ + public function autosetProductAddress() + { + // Get the main address of the customer + if ((int)$this->id_address_delivery > 0) + $id_address_deivery = (int)$this->id_address_delivery; + else + { + if ((int)$cart->id_customer == 0) + return; + + $customer = new Customer((int)$cart->id_customer); + $addresses = $customer->getAddresses(Context::getContext()->language->id); + + if (count($addresses) == 0) + return; + + $id_address_delivery = $addresses[0]['id_address']; + } + + // Update + $sql = 'UPDATE `'._DB_PREFIX_.'cart_product` + SET `id_address_delivery` = + ( + SELECT `id_address_delivery` + FROM `'._DB_PREFIX_.'cart` + WHERE `id_cart` = '.(int)$this->id.' + AND `id_shop` = '.(int)$this->id_shop.' + ) + WHERE `id_cart` = '.(int)$this->id.' + AND (`id_address_delivery` = 0 OR `id_address_delivery` IS NULL) + AND `id_shop` = '.(int)$this->id_shop; + Db::getInstance()->execute($sql); + } public function deleteAssociations() { diff --git a/classes/Hook.php b/classes/Hook.php index 38facf452..f3a3cfde9 100644 --- a/classes/Hook.php +++ b/classes/Hook.php @@ -366,44 +366,46 @@ class HookCore extends ObjectModel static public function orderConfirmation($id_order) { - if (Validate::isUnsignedId($id_order)) - { + if (Validate::isUnsignedId($id_order)) + { $params = array(); $order = new Order((int)$id_order); $currency = new Currency((int)$order->id_currency); - if (Validate::isLoadedObject($order)) - { - $params['total_to_pay'] = $order->total_paid; + if (Validate::isLoadedObject($order)) + { + $cart = new Cart((int)$order->id_cart); + $params['total_to_pay'] = $cart->getOrderTotal(); $params['currency'] = $currency->sign; $params['objOrder'] = $order; $params['currencyObj'] = $currency; return Hook::exec('orderConfirmation', $params); + } } - } - return false; + return false; } static public function paymentReturn($id_order, $id_module) { - if (Validate::isUnsignedId($id_order) AND Validate::isUnsignedId($id_module)) - { + if (Validate::isUnsignedId($id_order) AND Validate::isUnsignedId($id_module)) + { $params = array(); $order = new Order((int)($id_order)); $currency = new Currency((int)($order->id_currency)); - if (Validate::isLoadedObject($order)) - { - $params['total_to_pay'] = $order->total_paid; + if (Validate::isLoadedObject($order)) + { + $cart = new Cart((int)$order->id_cart); + $params['total_to_pay'] = $cart->getOrderTotal(); $params['currency'] = $currency->sign; $params['objOrder'] = $order; $params['currencyObj'] = $currency; return Hook::exec('paymentReturn', $params, (int)($id_module)); } - } - return false; + } + return false; } static public function PDFInvoice($pdf, $id_order) diff --git a/classes/Order.php b/classes/Order.php index 803727708..4661b08de 100644 --- a/classes/Order.php +++ b/classes/Order.php @@ -21,69 +21,69 @@ * @author PrestaShop SA * @copyright 2007-2011 PrestaShop SA * @version Release: $Revision: 7331 $ -* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) +* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * International Registered Trademark & Property of PrestaShop SA */ class OrderCore extends ObjectModel { /** @var integer Delivery address id */ - public $id_address_delivery; + public $id_address_delivery; /** @var integer Invoice address id */ - public $id_address_invoice; + public $id_address_invoice; - public $id_group_shop; + public $id_group_shop; - public $id_shop; + public $id_shop; /** @var integer Cart id */ - public $id_cart; + public $id_cart; /** @var integer Currency id */ - public $id_currency; + public $id_currency; /** @var integer Language id */ - public $id_lang; + public $id_lang; /** @var integer Customer id */ - public $id_customer; + public $id_customer; /** @var integer Carrier id */ - public $id_carrier; + public $id_carrier; /** @var string Secure key */ - public $secure_key; + public $secure_key; /** @var string Payment method */ - public $payment; + public $payment; /** @var string Payment module */ - public $module; + public $module; /** @var float Currency conversion rate */ - public $conversion_rate; + public $conversion_rate; /** @var boolean Customer is ok for a recyclable package */ - public $recyclable = 1; + public $recyclable = 1; /** @var boolean True if the customer wants a gift wrapping */ - public $gift = 0; + public $gift = 0; /** @var string Gift message if specified */ - public $gift_message; + public $gift_message; /** @var string Shipping number */ - public $shipping_number; + public $shipping_number; /** @var float Discounts total */ - public $total_discounts; + public $total_discounts; public $total_discounts_tax_incl; public $total_discounts_tax_excl; /** @var float Total to pay */ - public $total_paid; + public $total_paid; /** @var float Total to pay tax included */ public $total_paid_tax_incl; @@ -92,16 +92,16 @@ class OrderCore extends ObjectModel public $total_paid_tax_excl; /** @var float Total really paid */ - public $total_paid_real; + public $total_paid_real; /** @var float Products total */ - public $total_products; + public $total_products; /** @var float Products total tax excluded */ - public $total_products_wt; + public $total_products_wt; /** @var float Shipping total */ - public $total_shipping; + public $total_shipping; /** @var float Shipping total tax included */ public $total_shipping_tax_incl; @@ -110,10 +110,10 @@ class OrderCore extends ObjectModel public $total_shipping_tax_excl; /** @var float Shipping tax rate */ - public $carrier_tax_rate; + public $carrier_tax_rate; /** @var float Wrapping total */ - public $total_wrapping; + public $total_wrapping; /** @var float Wrapping total tax included */ public $total_wrapping_tax_incl; @@ -122,30 +122,38 @@ class OrderCore extends ObjectModel public $total_wrapping_tax_excl; /** @var integer Invoice number */ - public $invoice_number; + public $invoice_number; /** @var integer Delivery number */ - public $delivery_number; + public $delivery_number; /** @var string Invoice creation date */ - public $invoice_date; + public $invoice_date; /** @var string Delivery creation date */ - public $delivery_date; + public $delivery_date; /** @var boolean Order validity (paid and not canceled) */ - public $valid; + public $valid; /** @var string Object creation date */ - public $date_add; + public $date_add; /** @var string Object last modification date */ - public $date_upd; + public $date_upd; + + /** @var string Order reference + * This reference is not unique, but unique for a payment + */ + public $reference; + + /** @var int Id warehouse */ + public $id_warehouse; protected $tables = array ('orders'); - protected $fieldsRequired = array('conversion_rate', 'id_address_delivery', 'id_address_invoice', 'id_cart', 'id_currency', 'id_lang', 'id_customer', 'id_carrier', 'payment', 'total_paid', 'total_paid_real', 'total_products', 'total_products_wt'); - protected $fieldsValidate = array( + protected $fieldsRequired = array('conversion_rate', 'id_address_delivery', 'id_address_invoice', 'id_cart', 'id_currency', 'id_lang', 'id_customer', 'id_carrier', 'payment', 'total_paid', 'total_paid_real', 'total_products', 'total_products_wt'); + protected $fieldsValidate = array( 'id_address_delivery' => 'isUnsignedId', 'id_address_invoice' => 'isUnsignedId', 'id_cart' => 'isUnsignedId', @@ -155,6 +163,7 @@ class OrderCore extends ObjectModel 'id_lang' => 'isUnsignedId', 'id_customer' => 'isUnsignedId', 'id_carrier' => 'isUnsignedId', + 'id_warehouse' => 'isUnsignedId', 'secure_key' => 'isMd5', 'payment' => 'isGenericName', 'recyclable' => 'isBool', @@ -172,7 +181,7 @@ class OrderCore extends ObjectModel 'conversion_rate' => 'isFloat' ); - protected $webserviceParameters = array( + protected $webserviceParameters = array( 'objectMethods' => array('add' => 'addWs'), 'objectNodeName' => 'order', 'objectsNodeName' => 'orders', @@ -209,9 +218,9 @@ class OrderCore extends ObjectModel ); /* MySQL does not allow 'order' for a table name */ - protected $table = 'orders'; - protected $identifier = 'id_order'; - protected $_taxCalculationMethod = PS_TAX_EXC; + protected $table = 'orders'; + protected $identifier = 'id_order'; + protected $_taxCalculationMethod = PS_TAX_EXC; protected static $_historyCache = array(); @@ -262,6 +271,8 @@ class OrderCore extends ObjectModel $fields['valid'] = (int)($this->valid) ? 1 : 0; $fields['date_add'] = pSQL($this->date_add); $fields['date_upd'] = pSQL($this->date_upd); + $fields['reference'] = pSQL($this->reference); + $fields['id_warehouse'] = pSQL($this->id_warehouse); return $fields; } @@ -321,7 +332,7 @@ class OrderCore extends ObjectModel $productPrice = number_format($quantity * $price, 2, '.', ''); /* Update cart */ $cart = new Cart($this->id_cart); - $cart->updateQty($quantity, $orderDetail->product_id, $orderDetail->product_attribute_id, false, 'down'); // customization are deleted in deleteCustomization + $cart->updateQty($quantity, $orderDetail->product_id, $orderDetail->product_attribute_id, false, 0, 'down'); // customization are deleted in deleteCustomization $cart->update(); /* Update order */ @@ -335,7 +346,7 @@ class OrderCore extends ObjectModel if ($this->total_products_wt != 0) $this->total_products_wt -= $productPrice; - $this->total_shipping = $cart->getOrderShippingCost(); + $this->total_shipping = $cart->getTotalShippingCost(); /* It's temporary fix for 1.3 version... */ if ($orderDetail->product_quantity_discount != '0.000000') @@ -731,16 +742,16 @@ class OrderCore extends ObjectModel * @return array Customer orders */ static public function getCustomerOrders($id_customer, $showHiddenStatus = false, Context $context = null) - { + { if (!$context) $context = Context::getContext(); - $res = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' - SELECT o.*, (SELECT SUM(od.`product_quantity`) FROM `'._DB_PREFIX_.'order_detail` od WHERE od.`id_order` = o.`id_order`) nb_products - FROM `'._DB_PREFIX_.'orders` o - WHERE o.`id_customer` = '.(int)$id_customer.' - GROUP BY o.`id_order` - ORDER BY o.`date_add` DESC'); + $res = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT o.*, (SELECT SUM(od.`product_quantity`) FROM `'._DB_PREFIX_.'order_detail` od WHERE od.`id_order` = o.`id_order`) nb_products + FROM `'._DB_PREFIX_.'orders` o + WHERE o.`id_customer` = '.(int)$id_customer.' + GROUP BY o.`id_order` + ORDER BY o.`date_add` DESC'); if (!$res) return array(); @@ -760,7 +771,7 @@ class OrderCore extends ObjectModel } return $res; - } + } public static function getOrdersIdByDate($date_from, $date_to, $id_customer = NULL, $type = NULL) { @@ -839,22 +850,22 @@ class OrderCore extends ObjectModel return $orders; } - /** - * Get product total without taxes - * - * @return Product total with taxes - */ - public function getTotalProductsWithoutTaxes($products = false) + /** + * Get product total without taxes + * + * @return Product total with taxes + */ + public function getTotalProductsWithoutTaxes($products = false) { return $this->total_products; } - /** - * Get product total with taxes - * - * @return Product total with taxes - */ - public function getTotalProductsWithTaxes($products = false) + /** + * Get product total with taxes + * + * @return Product total with taxes + */ + public function getTotalProductsWithTaxes($products = false) { if ($this->total_products_wt != '0.00' AND !$products) return $this->total_products_wt; @@ -895,15 +906,15 @@ class OrderCore extends ObjectModel * @return array Customer orders number */ public static function getCustomerNbOrders($id_customer) - { - $sql = 'SELECT COUNT(`id_order`) AS nb + { + $sql = 'SELECT COUNT(`id_order`) AS nb FROM `'._DB_PREFIX_.'orders` WHERE `id_customer` = '.(int)$id_customer - .Context::getContext()->shop->addSqlRestriction(); - $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql); + .Context::getContext()->shop->addSqlRestriction(); + $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql); return isset($result['nb']) ? $result['nb'] : 0; - } + } /** * Get an order by its cart id @@ -912,26 +923,26 @@ class OrderCore extends ObjectModel * @return array Order details */ public static function getOrderByCartId($id_cart) - { - $sql = 'SELECT `id_order` + { + $sql = 'SELECT `id_order` FROM `'._DB_PREFIX_.'orders` WHERE `id_cart` = '.(int)($id_cart) - .Context::getContext()->shop->addSqlRestriction(); - $result = Db::getInstance()->getRow($sql); + .Context::getContext()->shop->addSqlRestriction(); + $result = Db::getInstance()->getRow($sql); return isset($result['id_order']) ? $result['id_order'] : false; - } + } - /** + /** * @deprecated 1.5.0.1 */ - public function addDiscount($id_cart_rule, $name, $value) + public function addDiscount($id_cart_rule, $name, $value) { Tools::displayAsDeprecated(); return Order::addCartRule($id_cart_rule, $name, $value); } - public function addCartRule($id_cart_rule, $name, $value) + public function addCartRule($id_cart_rule, $name, $value) { return Db::getInstance()->AutoExecute(_DB_PREFIX_.'order_cart_rule', array('id_order' => (int)$this->id, 'id_cart_rule' => (int)$id_cart_rule, 'name' => pSQL($name), 'value' => (float)$value), 'INSERT'); } @@ -961,38 +972,38 @@ class OrderCore extends ObjectModel } - public static function getLastInvoiceNumber() - { - return (int)Db::getInstance()->getValue(' - SELECT MAX(`invoice_number`) AS `invoice_number` + public static function getLastInvoiceNumber() + { + return (int)Db::getInstance()->getValue(' + SELECT MAX(`invoice_number`) AS `invoice_number` FROM `'._DB_PREFIX_.'orders`'); - } + } public function setInvoice() { $number = (int)Configuration::get('PS_INVOICE_START_NUMBER'); if ($number) - Configuration::updateValue('PS_INVOICE_START_NUMBER', false); - else - $number = '(SELECT `invoice_number` - FROM ( - SELECT MAX(`invoice_number`) + 1 AS `invoice_number` - FROM `'._DB_PREFIX_.'orders`) - tmp )'; - // a way to avoid duplicate invoice number + Configuration::updateValue('PS_INVOICE_START_NUMBER', false); + else + $number = '(SELECT `invoice_number` + FROM ( + SELECT MAX(`invoice_number`) + 1 AS `invoice_number` + FROM `'._DB_PREFIX_.'orders`) + tmp )'; + // a way to avoid duplicate invoice number Db::getInstance()->execute(' UPDATE `'._DB_PREFIX_.'orders` SET `invoice_number` = '.$number.', `invoice_date` = \''.date('Y-m-d H:i:s').'\' WHERE `id_order` = '.(int)$this->id ); - $res = Db::getInstance()->getRow(' - SELECT `invoice_number`, `invoice_date` - FROM `'._DB_PREFIX_.'orders` + $res = Db::getInstance()->getRow(' + SELECT `invoice_number`, `invoice_date` + FROM `'._DB_PREFIX_.'orders` WHERE `id_order` = '.(int)$this->id - ); + ); - $this->invoice_date = $res['invoice_date']; - $this->invoice_number = $res['invoice_number']; + $this->invoice_date = $res['invoice_date']; + $this->invoice_number = $res['invoice_number']; } public function setDelivery() @@ -1034,10 +1045,10 @@ class OrderCore extends ObjectModel public static function getByDelivery($id_delivery) { $sql = 'SELECT id_order - FROM `'._DB_PREFIX_.'orders` - WHERE `delivery_number` = '.(int)($id_delivery).' - '.Context::getContext()->shop->addSqlRestriction(); - $res = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql); + FROM `'._DB_PREFIX_.'orders` + WHERE `delivery_number` = '.(int)($id_delivery).' + '.Context::getContext()->shop->addSqlRestriction(); + $res = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql); return new Order((int)($res['id_order'])); } @@ -1108,10 +1119,10 @@ class OrderCore extends ObjectModel SELECT `invoice_number`, `invoice_date`, `delivery_number`, `delivery_date` FROM `'._DB_PREFIX_.'orders` WHERE `id_order` = '.(int)$this->id); - $this->invoice_date = $res['invoice_date']; - $this->invoice_number = $res['invoice_number']; - $this->delivery_date = $res['delivery_date']; - $this->delivery_number = $res['delivery_number']; + $this->invoice_date = $res['invoice_date']; + $this->invoice_number = $res['invoice_number']; + $this->delivery_date = $res['delivery_date']; + $this->delivery_number = $res['delivery_number']; $history->addWithemail(); } @@ -1166,6 +1177,35 @@ class OrderCore extends ObjectModel return OrderDetail::getList($this->id); } + /** + * Gennerate a unique reference for orders generated with the same cart id + * This references, is usefull for check payment + * + * @return String + */ + public static function generateReference() + { + // To generate a random reference, we first generate a random number + // This number is a rand concated with the current timestamp + $rand = (int)(microtime(true) * 100 % 10000000000).rand(10, 1000); + $reference = ''; + + do { + $reference .= chr(65 + $rand % 26); + $rand = (int)($rand / 26); + } while ($rand > 26); + + // Check if semi-random string generated is not already used + // /!\ Here we CANNOT/MUSTN'T use _PS_USE_SQL_SLAVE_ + if (Db::getInstance()->getValue(' + SELECT count(*) + FROM '._DB_PREFIX_.'orders + WHERE reference = \''.$reference.'\'') > 0) + return self::generateReference(); // If the reference already exists, generate a new one + + return $reference; + } + public function orderContainProduct($id_product) { $product_list = $this->getOrderDetailList(); @@ -1174,7 +1214,6 @@ class OrderCore extends ObjectModel return true; return false; } - /** * This method returns true if at least one order details uses the * One After Another tax computation method. diff --git a/classes/OrderDetail.php b/classes/OrderDetail.php index c6bf35047..8e28911eb 100644 --- a/classes/OrderDetail.php +++ b/classes/OrderDetail.php @@ -522,13 +522,13 @@ class OrderDetailCore extends ObjectModel * @param object $cart * @param int $id_order_status */ - public function createList(Order $order, Cart $cart, $id_order_state) + public function createList(Order $order, Cart $cart, $id_order_state, $product_list) { $this->vat_address = new Address((int)($order->{Configuration::get('PS_TAX_ADDRESS_TYPE')})); $this->customer = new Customer((int)($order->id_customer)); $this->id_order = $order->id; - $products = $cart->getProducts(); + $products = $product_list; $this->outOfStock = false; foreach ($products as $product) diff --git a/classes/PaymentModule.php b/classes/PaymentModule.php index a537a7ed0..6dc7fb8e5 100644 --- a/classes/PaymentModule.php +++ b/classes/PaymentModule.php @@ -114,309 +114,351 @@ abstract class PaymentModuleCore extends Module { if ($secure_key !== false AND $secure_key != $cart->secure_key) die(Tools::displayError()); - - - // Be carefull, carrier may not exist - $carrier = new Carrier($cart->id_carrier, $cart->id_lang); - - // Copying data from cart - $order = new Order(); - $order->id_carrier = (int)$carrier->id; - $order->id_customer = (int)($cart->id_customer); - $order->id_address_invoice = (int)($cart->id_address_invoice); - $order->id_address_delivery = (int)($cart->id_address_delivery); - $order->id_currency = ($currency_special ? (int)($currency_special) : (int)($cart->id_currency)); - $order->id_lang = (int)($cart->id_lang); - $order->id_cart = (int)($cart->id); - - $order->id_shop = (int)($shop->getID() ? $shop->getID() : $cart->id_shop); - $order->id_group_shop = (int)($shop->getID() ? $shop->getGroupID() : $cart->id_group_shop); - - $customer = new Customer((int)($order->id_customer)); - $order->secure_key = ($secure_key ? pSQL($secure_key) : pSQL($customer->secure_key)); - $order->payment = $paymentMethod; - if (isset($this->name)) - $order->module = $this->name; - $order->recyclable = $cart->recyclable; - $order->gift = (int)($cart->gift); - $order->gift_message = $cart->gift_message; - $currency = new Currency($order->id_currency); - $order->conversion_rate = $currency->conversion_rate; - $amountPaid = !$dont_touch_amount ? Tools::ps_round((float)($amountPaid), 2) : $amountPaid; - $order->total_paid_real = $amountPaid; - $order->total_products = (float)$cart->getOrderTotal(false, Cart::ONLY_PRODUCTS); - $order->total_products_wt = (float)$cart->getOrderTotal(true, Cart::ONLY_PRODUCTS); - - $order->total_discounts = (float)abs($cart->getOrderTotal(true, Cart::ONLY_DISCOUNTS)); - $order->total_discounts_tax_excl = (float)abs($cart->getOrderTotal(false, Cart::ONLY_DISCOUNTS)); - $order->total_discounts_tax_incl = (float)abs($cart->getOrderTotal(true, Cart::ONLY_DISCOUNTS)); - - $order->total_shipping = (float)$cart->getOrderShippingCost(); - $order->total_shipping_tax_excl = (float)$cart->getOrderShippingCost(NULL, false); - $order->total_shipping_tax_incl = (float)$cart->getOrderShippingCost(); - - if (Validate::isLoadedObject($carrier)) - $order->carrier_tax_rate = $carrier->getTaxesRate(new Address($cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')})); - - $order->total_wrapping = (float)abs($cart->getOrderTotal(true, Cart::ONLY_WRAPPING)); - $order->total_wrapping_tax_excl = (float)abs($cart->getOrderTotal(false, Cart::ONLY_WRAPPING)); - $order->total_wrapping_tax_incl = (float)abs($cart->getOrderTotal(true, Cart::ONLY_WRAPPING)); - - $order->total_paid = (float)Tools::ps_round((float)($cart->getOrderTotal(true, Cart::BOTH)), 2); - $order->total_paid_tax_excl = (float)Tools::ps_round((float)($cart->getOrderTotal(false, Cart::BOTH)), 2); - $order->total_paid_tax_incl = (float)Tools::ps_round((float)($cart->getOrderTotal(true, Cart::BOTH)), 2); - - $order->invoice_date = '0000-00-00 00:00:00'; - $order->delivery_date = '0000-00-00 00:00:00'; - // Amount paid by customer is not the right one -> Status = payment error - // We don't use the following condition to avoid the float precision issues : http://www.php.net/manual/en/language.types.float.php - // if ($order->total_paid != $order->total_paid_real) - // We use number_format in order to compare two string - if (number_format($order->total_paid, 2) != number_format($order->total_paid_real, 2)) - $id_order_state = Configuration::get('PS_OS_ERROR'); - // Creating order - if ($cart->OrderExists() == false) - $result = $order->add(); - else + + // For each package, generate an order + $delivery_option_list = $cart->getDeliveryOptionList(); + $package_list = $cart->getPackageList(); + $cart_delivery_option = unserialize($cart->delivery_option); + foreach ($delivery_option_list as $id_address => $package) + { + if (!isset($cart_delivery_option[$id_address]) || !array_key_exists($cart_delivery_option[$id_address], $package)) + die('Error: delivery option for some addresses is not defined'); + } + + $order_list = array(); + $order_detail_list = array(); + $reference = Order::generateReference(); + $this->currentOrderReference = $reference; + + $id_currency = $currency_special ? (int)($currency_special) : (int)($cart->id_currency); + $currency = new Currency($id_currency); + + $this->context->cart->order_reference = $reference; + + $orderCreationFailed = false; + $cart_total_paid = (float)Tools::ps_round((float)($cart->getOrderTotal(true, Cart::BOTH)), 2); + + if ($cart->orderExists()) { $errorMessage = Tools::displayError('An order has already been placed using this cart.'); - Logger::addLog($errorMessage, 4, '0000001', 'Cart', intval($order->id_cart)); + Logger::addLog($errorMessage, 4, '0000001', 'Cart', intval($cart->id)); die($errorMessage); } + foreach ($cart_delivery_option as $id_address => $key_carriers) + foreach ($delivery_option_list[$id_address][$key_carriers]['carrier_list'] as $id_carrier => $data) + foreach ($data['package_list'] as $id_package) + { + $product_list = $package_list[$id_address][$id_package]['product_list']; + $carrier = new Carrier($id_carrier, $cart->id_lang); + $order = new Order(); + $order->id_carrier = (int)$carrier->id; + $order->id_customer = (int)($cart->id_customer); + $order->id_address_invoice = (int)($cart->id_address_invoice); + $order->id_address_delivery = (int)$id_address; + $order->id_currency = $id_currency; + $order->id_lang = (int)($cart->id_lang); + + $order->id_warehouse = $package_list[$id_address][$id_package]['id_warehouse']; + $order->id_cart = (int)($cart->id); + $order->reference = $reference; + + $order->id_shop = (int)($shop->getID() ? $shop->getID() : $cart->id_shop); + $order->id_group_shop = (int)($shop->getID() ? $shop->getGroupID() : $cart->id_group_shop); + + $customer = new Customer((int)($order->id_customer)); + $order->secure_key = ($secure_key ? pSQL($secure_key) : pSQL($customer->secure_key)); + $order->payment = $paymentMethod; + if (isset($this->name)) + $order->module = $this->name; + $order->recyclable = $cart->recyclable; + $order->gift = (int)($cart->gift); + $order->gift_message = $cart->gift_message; + $order->conversion_rate = $currency->conversion_rate; + $amountPaid = !$dont_touch_amount ? Tools::ps_round((float)($amountPaid), 2) : $amountPaid; + $order->total_paid_real = $amountPaid; + $order->total_products = (float)$cart->getOrderTotal(false, Cart::ONLY_PRODUCTS, $product_list, $id_carrier); + $order->total_products_wt = (float)$cart->getOrderTotal(true, Cart::ONLY_PRODUCTS, $product_list, $id_carrier); + + $order->total_discounts = (float)abs($cart->getOrderTotal(true, Cart::ONLY_DISCOUNTS, $product_list, $id_carrier)); + $order->total_discounts_tax_excl = (float)abs($cart->getOrderTotal(false, Cart::ONLY_DISCOUNTS, $product_list, $id_carrier)); + $order->total_discounts_tax_incl = (float)abs($cart->getOrderTotal(true, Cart::ONLY_DISCOUNTS, $product_list, $id_carrier)); + + $order->total_shipping = (float)$cart->getPackageShippingCost((int)$id_carrier, true, null, $product_list, $id_carrier); + $order->total_shipping_tax_excl = (float)$cart->getPackageShippingCost((int)$id_carrier, false, null, $product_list, $id_carrier); + $order->total_shipping_tax_incl = (float)$cart->getPackageShippingCost((int)$id_carrier, true, null, $product_list, $id_carrier); + + if (Validate::isLoadedObject($carrier)) + $order->carrier_tax_rate = $carrier->getTaxesRate(new Address($cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')})); + + $order->total_wrapping = (float)abs($cart->getOrderTotal(true, Cart::ONLY_WRAPPING, $product_list, $id_carrier)); + $order->total_wrapping_tax_excl = (float)abs($cart->getOrderTotal(false, Cart::ONLY_WRAPPING, $product_list, $id_carrier)); + $order->total_wrapping_tax_incl = (float)abs($cart->getOrderTotal(true, Cart::ONLY_WRAPPING, $product_list, $id_carrier)); + + $order->total_paid = (float)Tools::ps_round((float)($cart->getOrderTotal(true, Cart::BOTH, $product_list, $id_carrier)), 2); + $order->total_paid_tax_excl = (float)Tools::ps_round((float)($cart->getOrderTotal(false, Cart::BOTH, $product_list, $id_carrier)), 2); + $order->total_paid_tax_incl = (float)Tools::ps_round((float)($cart->getOrderTotal(true, Cart::BOTH, $product_list, $id_carrier)), 2); + + $order->invoice_date = '0000-00-00 00:00:00'; + $order->delivery_date = '0000-00-00 00:00:00'; + // Amount paid by customer is not the right one -> Status = payment error + // We don't use the following condition to avoid the float precision issues : http://www.php.net/manual/en/language.types.float.php + // if ($order->total_paid != $order->total_paid_real) + // We use number_format in order to compare two string + if (number_format($cart_total_paid, 2) != number_format($order->total_paid_real, 2)) + $id_order_state = Configuration::get('PS_OS_ERROR'); + + // Creating order + $result = $order->add(); + + $order_list[] = $order; + + // Insert new Order detail list using cart for the current order + $order_detail = new OrderDetail($this->context); + $order_detail->createList($order, $cart, $id_order_state, $product_list); + $order_detail_list[] = $order_detail; + } + $this->addPCC($reference, $id_currency, $amountPaid); + // Next ! - if ($result AND isset($order->id)) + foreach ($order_detail_list as $key => $order_detail) { - if (!$secure_key) - $message .= $this->l('Warning : the secure key is empty, check your payment account before validation'); - // Optional message to attach to this order - if (isset($message) AND !empty($message)) + $order = $order_list[$key]; + if (!$orderCreationFailed AND isset($order->id)) { - $msg = new Message(); - $message = strip_tags($message, '
'); - if (Validate::isCleanHtml($message)) + if (!$secure_key) + $message .= $this->l('Warning : the secure key is empty, check your payment account before validation'); + // Optional message to attach to this order + if (isset($message) AND !empty($message)) { - $msg->message = $message; - $msg->id_order = intval($order->id); - $msg->private = 1; - $msg->add(); - } - } - - // Insert new Order detail list using cart for the current order - $orderDetail = new OrderDetail($this->context); - $orderDetail->createList($order, $cart, $id_order_state); - - $this->addPCC($order->id, $order->id_currency, $amountPaid); - - // Insert products from cart into order_detail table - $productsList = ''; - $products = $cart->getProducts(); - foreach ($products AS $key => $product) - { - $price = Product::getPriceStatic((int)($product['id_product']), false, ($product['id_product_attribute'] ? (int)($product['id_product_attribute']) : NULL), 6, NULL, false, true, $product['cart_quantity'], false, (int)($order->id_customer), (int)($order->id_cart), (int)($order->{Configuration::get('PS_TAX_ADDRESS_TYPE')})); - $price_wt = Product::getPriceStatic((int)($product['id_product']), true, ($product['id_product_attribute'] ? (int)($product['id_product_attribute']) : NULL), 2, NULL, false, true, $product['cart_quantity'], false, (int)($order->id_customer), (int)($order->id_cart), (int)($order->{Configuration::get('PS_TAX_ADDRESS_TYPE')})); - - $customizationQuantity = 0; - if (isset($customizedDatas[$product['id_product']][$product['id_product_attribute']])) - { - $customizationText = ''; - foreach ($customizedDatas[$product['id_product']][$product['id_product_attribute']] AS $customization) + $msg = new Message(); + $message = strip_tags($message, '
'); + if (Validate::isCleanHtml($message)) { - if (isset($customization['datas'][Product::CUSTOMIZE_TEXTFIELD])) - foreach ($customization['datas'][Product::CUSTOMIZE_TEXTFIELD] AS $text) - $customizationText .= $text['name'].':'.' '.$text['value'].'
'; - - if (isset($customization['datas'][Product::CUSTOMIZE_FILE])) - $customizationText .= sizeof($customization['datas'][Product::CUSTOMIZE_FILE]) .' '. Tools::displayError('image(s)').'
'; - - $customizationText .= '---
'; + $msg->message = $message; + $msg->id_order = intval($order->id); + $msg->private = 1; + $msg->add(); } - - $customizationText = rtrim($customizationText, '---
'); - - $customizationQuantity = (int)($product['customizationQuantityTotal']); - $productsList .= - ' - '.$product['reference'].' - '.$product['name'].(isset($product['attributes']) ? ' - '.$product['attributes'] : '').' - '.$this->l('Customized').(!empty($customizationText) ? ' - '.$customizationText : '').' - '.Tools::displayPrice(Product::getTaxCalculationMethod() == PS_TAX_EXC ? $price : $price_wt, $currency, false).' - '.$customizationQuantity.' - '.Tools::displayPrice($customizationQuantity * (Product::getTaxCalculationMethod() == PS_TAX_EXC ? $price : $price_wt), $currency, false).' - '; } - if (!$customizationQuantity OR (int)$product['cart_quantity'] > $customizationQuantity) - $productsList .= - ' - '.$product['reference'].' - '.$product['name'].(isset($product['attributes']) ? ' - '.$product['attributes'] : '').' - '.Tools::displayPrice(Product::getTaxCalculationMethod() == PS_TAX_EXC ? $price : $price_wt, $currency, false).' - '.((int)($product['cart_quantity']) - $customizationQuantity).' - '.Tools::displayPrice(((int)($product['cart_quantity']) - $customizationQuantity) * (Product::getTaxCalculationMethod() == PS_TAX_EXC ? $price : $price_wt), $currency, false).' - '; - } // end foreach ($products) - - $cartRulesList = ''; - $result = $cart->getCartRules(); - $cartRules = ObjectModel::hydrateCollection('CartRule', $result, (int)$order->id_lang); - foreach ($cartRules AS $cartRule) - { - $value = $cartRule->getContextualValue(true); - // Todo: repair shrunk - // if ($shrunk AND ($total_discount_value + $value) > ($order->total_products_wt + $order->total_shipping + $order->total_wrapping)) - // { - // $amount_to_add = ($order->total_products_wt + $order->total_shipping + $order->total_wrapping) - $total_discount_value; - // if ($cartRule->id_discount_type == Discount::AMOUNT AND $cartRule->behavior_not_exhausted == 2) - // { - // $voucher = new Discount(); - // foreach ($cartRule AS $key => $discountValue) - // $voucher->$key = $discountValue; - // $voucher->name = 'VSRK'.(int)$order->id_customer.'O'.(int)$order->id; - // $voucher->value = (float)$value - $amount_to_add; - // $voucher->add(); - // $params['{voucher_amount}'] = Tools::displayPrice($voucher->value, $currency, false); - // $params['{voucher_num}'] = $voucher->name; - // $params['{firstname}'] = $customer->firstname; - // $params['{lastname}'] = $customer->lastname; - // $params['{id_order}'] = $order->id; - // @Mail::Send((int)$order->id_lang, 'voucher', Mail::l('New voucher regarding your order #').$order->id, $params, $customer->email, $customer->firstname.' '.$customer->lastname); - // } - // } - - $order->addCartRule($cartRule->id, $cartRule->name, $value); - if ($id_order_state != Configuration::get('PS_OS_ERROR') AND $id_order_state != Configuration::get('PS_OS_CANCELED')) - $cartRule->quantity = $cartRule->quantity - 1; - $cartRule->update(); - - $cartRulesList .= ' - - '.$this->l('Voucher name:').' '.$cartRule->name.' - '.($value != 0.00 ? '-' : '').Tools::displayPrice($value, $currency, false).' - '; - } - - // Specify order id for message - $oldMessage = Message::getMessageByCartId((int)($cart->id)); - if ($oldMessage) - { - $message = new Message((int)$oldMessage['id_message']); - $message->id_order = (int)$order->id; - $message->update(); - } - - // Hook validate order - $orderStatus = new OrderState((int)$id_order_state, (int)$order->id_lang); - if (Validate::isLoadedObject($orderStatus)) - { - Hook::exec('newOrder', array('cart' => $cart, 'order' => $order, 'customer' => $customer, 'currency' => $currency, 'orderStatus' => $orderStatus)); - foreach ($cart->getProducts() AS $product) - if ($orderStatus->logable) - ProductSale::addProductSale((int)$product['id_product'], (int)$product['cart_quantity']); - } - - if (Configuration::get('PS_STOCK_MANAGEMENT') && $orderDetail->getStockState()) - { - $history = new OrderHistory(); - $history->id_order = (int)$order->id; - $history->changeIdOrderState(Configuration::get('PS_OS_OUTOFSTOCK'), (int)$order->id); - $history->addWithemail(); - } - - // Set order state in order history ONLY even if the "out of stock" status has not been yet reached - // So you migth have two order states - $new_history = new OrderHistory(); - $new_history->id_order = (int)$order->id; - $new_history->changeIdOrderState((int)$id_order_state, (int)$order->id); - $new_history->addWithemail(true, $extraVars); - - unset($orderDetail, $pcc); - - // Order is reloaded because the status just changed - $order = new Order($order->id); - - // Send an e-mail to customer - if ($id_order_state != Configuration::get('PS_OS_ERROR') AND $id_order_state != Configuration::get('PS_OS_CANCELED') AND $customer->id) - { - $invoice = new Address((int)($order->id_address_invoice)); - $delivery = new Address((int)($order->id_address_delivery)); - $delivery_state = $delivery->id_state ? new State((int)($delivery->id_state)) : false; - $invoice_state = $invoice->id_state ? new State((int)($invoice->id_state)) : false; - - $data = array( - '{firstname}' => $customer->firstname, - '{lastname}' => $customer->lastname, - '{email}' => $customer->email, - '{delivery_block_txt}' => $this->_getFormatedAddress($delivery, "\n"), - '{invoice_block_txt}' => $this->_getFormatedAddress($invoice, "\n"), - '{delivery_block_html}' => $this->_getFormatedAddress($delivery, "
", - array( - 'firstname' => '%s', - 'lastname' => '%s')), - '{invoice_block_html}' => $this->_getFormatedAddress($invoice, "
", - array( - 'firstname' => '%s', - 'lastname' => '%s')), - '{delivery_company}' => $delivery->company, - '{delivery_firstname}' => $delivery->firstname, - '{delivery_lastname}' => $delivery->lastname, - '{delivery_address1}' => $delivery->address1, - '{delivery_address2}' => $delivery->address2, - '{delivery_city}' => $delivery->city, - '{delivery_postal_code}' => $delivery->postcode, - '{delivery_country}' => $delivery->country, - '{delivery_state}' => $delivery->id_state ? $delivery_state->name : '', - '{delivery_phone}' => ($delivery->phone) ? $delivery->phone : $delivery->phone_mobile, - '{delivery_other}' => $delivery->other, - '{invoice_company}' => $invoice->company, - '{invoice_vat_number}' => $invoice->vat_number, - '{invoice_firstname}' => $invoice->firstname, - '{invoice_lastname}' => $invoice->lastname, - '{invoice_address2}' => $invoice->address2, - '{invoice_address1}' => $invoice->address1, - '{invoice_city}' => $invoice->city, - '{invoice_postal_code}' => $invoice->postcode, - '{invoice_country}' => $invoice->country, - '{invoice_state}' => $invoice->id_state ? $invoice_state->name : '', - '{invoice_phone}' => ($invoice->phone) ? $invoice->phone : $invoice->phone_mobile, - '{invoice_other}' => $invoice->other, - '{order_name}' => sprintf("#%06d", (int)($order->id)), - '{date}' => Tools::displayDate(date('Y-m-d H:i:s'), (int)($order->id_lang), 1), - '{carrier}' => $carrier->name, - '{payment}' => Tools::substr($order->payment, 0, 32), - '{products}' => $productsList, - '{discounts}' => $cartRulesList, - '{total_paid}' => Tools::displayPrice($order->total_paid, $currency, false), - '{total_products}' => Tools::displayPrice($order->total_paid - $order->total_shipping - $order->total_wrapping + $order->total_discounts, $currency, false), - '{total_discounts}' => Tools::displayPrice($order->total_discounts, $currency, false), - '{total_shipping}' => Tools::displayPrice($order->total_shipping, $currency, false), - '{total_wrapping}' => Tools::displayPrice($order->total_wrapping, $currency, false)); - - if (is_array($extraVars)) - $data = array_merge($data, $extraVars); - - // Join PDF invoice - if ((int)(Configuration::get('PS_INVOICE')) AND Validate::isLoadedObject($orderStatus) AND $orderStatus->invoice AND $order->invoice_number) + // Insert new Order detail list using cart for the current order + //$orderDetail = new OrderDetail($this->context); + //$orderDetail->createList($order, $cart, $id_order_state); + + //$this->addPCC($order->id, $order->id_currency, $amountPaid); + + // Construct order detail table for the email + $productsList = ''; + $products = $cart->getProducts(); + foreach ($products AS $key => $product) { - $fileAttachment['content'] = PDF::invoice($order, 'S'); - $fileAttachment['name'] = Configuration::get('PS_INVOICE_PREFIX', (int)($order->id_lang)).sprintf('%06d', $order->invoice_number).'.pdf'; - $fileAttachment['mime'] = 'application/pdf'; + $price = Product::getPriceStatic((int)($product['id_product']), false, ($product['id_product_attribute'] ? (int)($product['id_product_attribute']) : NULL), 6, NULL, false, true, $product['cart_quantity'], false, (int)($order->id_customer), (int)($order->id_cart), (int)($order->{Configuration::get('PS_TAX_ADDRESS_TYPE')})); + $price_wt = Product::getPriceStatic((int)($product['id_product']), true, ($product['id_product_attribute'] ? (int)($product['id_product_attribute']) : NULL), 2, NULL, false, true, $product['cart_quantity'], false, (int)($order->id_customer), (int)($order->id_cart), (int)($order->{Configuration::get('PS_TAX_ADDRESS_TYPE')})); + + $customizationQuantity = 0; + if (isset($customizedDatas[$product['id_product']][$product['id_product_attribute']])) + { + $customizationText = ''; + foreach ($customizedDatas[$product['id_product']][$product['id_product_attribute']] AS $customization) + { + if (isset($customization['datas'][Product::CUSTOMIZE_TEXTFIELD])) + foreach ($customization['datas'][Product::CUSTOMIZE_TEXTFIELD] AS $text) + $customizationText .= $text['name'].':'.' '.$text['value'].'
'; + + if (isset($customization['datas'][Product::CUSTOMIZE_FILE])) + $customizationText .= sizeof($customization['datas'][Product::CUSTOMIZE_FILE]) .' '. Tools::displayError('image(s)').'
'; + + $customizationText .= '---
'; + } + + $customizationText = rtrim($customizationText, '---
'); + + $customizationQuantity = (int)($product['customizationQuantityTotal']); + $productsList .= + ' + '.$product['reference'].' + '.$product['name'].(isset($product['attributes']) ? ' - '.$product['attributes'] : '').' - '.$this->l('Customized').(!empty($customizationText) ? ' - '.$customizationText : '').' + '.Tools::displayPrice(Product::getTaxCalculationMethod() == PS_TAX_EXC ? $price : $price_wt, $currency, false).' + '.$customizationQuantity.' + '.Tools::displayPrice($customizationQuantity * (Product::getTaxCalculationMethod() == PS_TAX_EXC ? $price : $price_wt), $currency, false).' + '; + } + + if (!$customizationQuantity OR (int)$product['cart_quantity'] > $customizationQuantity) + $productsList .= + ' + '.$product['reference'].' + '.$product['name'].(isset($product['attributes']) ? ' - '.$product['attributes'] : '').' + '.Tools::displayPrice(Product::getTaxCalculationMethod() == PS_TAX_EXC ? $price : $price_wt, $currency, false).' + '.((int)($product['cart_quantity']) - $customizationQuantity).' + '.Tools::displayPrice(((int)($product['cart_quantity']) - $customizationQuantity) * (Product::getTaxCalculationMethod() == PS_TAX_EXC ? $price : $price_wt), $currency, false).' + '; + } // end foreach ($products) + + $cartRulesList = ''; + $result = $cart->getCartRules(); + $cartRules = ObjectModel::hydrateCollection('CartRule', $result, (int)$order->id_lang); +// @todo How to menage cart rules, with multiple shipping? + foreach ($cartRules AS $cartRule) + { + $value = $cartRule->getContextualValue(true); + // Todo: repair shrunk + // if ($shrunk AND ($total_discount_value + $value) > ($order->total_products_wt + $order->total_shipping + $order->total_wrapping)) + // { + // $amount_to_add = ($order->total_products_wt + $order->total_shipping + $order->total_wrapping) - $total_discount_value; + // if ($cartRule->id_discount_type == Discount::AMOUNT AND $cartRule->behavior_not_exhausted == 2) + // { + // $voucher = new Discount(); + // foreach ($cartRule AS $key => $discountValue) + // $voucher->$key = $discountValue; + // $voucher->name = 'VSRK'.(int)$order->id_customer.'O'.(int)$order->id; + // $voucher->value = (float)$value - $amount_to_add; + // $voucher->add(); + // $params['{voucher_amount}'] = Tools::displayPrice($voucher->value, $currency, false); + // $params['{voucher_num}'] = $voucher->name; + // $params['{firstname}'] = $customer->firstname; + // $params['{lastname}'] = $customer->lastname; + // $params['{id_order}'] = $order->id; + // @Mail::Send((int)$order->id_lang, 'voucher', Mail::l('New voucher regarding your order #').$order->id, $params, $customer->email, $customer->firstname.' '.$customer->lastname); + // } + // } + + $order->addCartRule($cartRule->id, $cartRule->name, $value); + if ($id_order_state != Configuration::get('PS_OS_ERROR') AND $id_order_state != Configuration::get('PS_OS_CANCELED')) + $cartRule->quantity = $cartRule->quantity - 1; + $cartRule->update(); + + $cartRulesList .= ' + + '.$this->l('Voucher name:').' '.$cartRule->name.' + '.($value != 0.00 ? '-' : '').Tools::displayPrice($value, $currency, false).' + '; + } + + // Specify order id for message + $oldMessage = Message::getMessageByCartId((int)($cart->id)); + if ($oldMessage) + { + $message = new Message((int)$oldMessage['id_message']); + $message->id_order = (int)$order->id; + $message->update(); + } + + // Hook validate order + $orderStatus = new OrderState((int)$id_order_state, (int)$order->id_lang); + if (Validate::isLoadedObject($orderStatus)) + { + Hook::exec('newOrder', array('cart' => $cart, 'order' => $order, 'customer' => $customer, 'currency' => $currency, 'orderStatus' => $orderStatus)); + foreach ($cart->getProducts() AS $product) + if ($orderStatus->logable) + ProductSale::addProductSale((int)$product['id_product'], (int)$product['cart_quantity']); + } + + if (Configuration::get('PS_STOCK_MANAGEMENT') && $order_detail->getStockState()) + { + $history = new OrderHistory(); + $history->id_order = (int)$order->id; + $history->changeIdOrderState(Configuration::get('PS_OS_OUTOFSTOCK'), (int)$order->id); + $history->addWithemail(); } - else - $fileAttachment = NULL; - if (Validate::isEmail($customer->email)) - Mail::Send((int)$order->id_lang, 'order_conf', Mail::l('Order confirmation', (int)$order->id_lang), $data, $customer->email, $customer->firstname.' '.$customer->lastname, NULL, NULL, $fileAttachment); + // Set order state in order history ONLY even if the "out of stock" status has not been yet reached + // So you migth have two order states + $new_history = new OrderHistory(); + $new_history->id_order = (int)$order->id; + $new_history->changeIdOrderState((int)$id_order_state, (int)$order->id); + $new_history->addWithemail(true, $extraVars); + + unset($order_detail, $pcc); + + // Order is reloaded because the status just changed + $order = new Order($order->id); + + // Send an e-mail to customer (one order = one email) + if ($id_order_state != Configuration::get('PS_OS_ERROR') AND $id_order_state != Configuration::get('PS_OS_CANCELED') AND $customer->id) + { + $invoice = new Address((int)($order->id_address_invoice)); + $delivery = new Address((int)($order->id_address_delivery)); + $delivery_state = $delivery->id_state ? new State((int)($delivery->id_state)) : false; + $invoice_state = $invoice->id_state ? new State((int)($invoice->id_state)) : false; + + $data = array( + '{firstname}' => $customer->firstname, + '{lastname}' => $customer->lastname, + '{email}' => $customer->email, + '{delivery_block_txt}' => $this->_getFormatedAddress($delivery, "\n"), + '{invoice_block_txt}' => $this->_getFormatedAddress($invoice, "\n"), + '{delivery_block_html}' => $this->_getFormatedAddress($delivery, "
", + array( + 'firstname' => '%s', + 'lastname' => '%s')), + '{invoice_block_html}' => $this->_getFormatedAddress($invoice, "
", + array( + 'firstname' => '%s', + 'lastname' => '%s')), + '{delivery_company}' => $delivery->company, + '{delivery_firstname}' => $delivery->firstname, + '{delivery_lastname}' => $delivery->lastname, + '{delivery_address1}' => $delivery->address1, + '{delivery_address2}' => $delivery->address2, + '{delivery_city}' => $delivery->city, + '{delivery_postal_code}' => $delivery->postcode, + '{delivery_country}' => $delivery->country, + '{delivery_state}' => $delivery->id_state ? $delivery_state->name : '', + '{delivery_phone}' => ($delivery->phone) ? $delivery->phone : $delivery->phone_mobile, + '{delivery_other}' => $delivery->other, + '{invoice_company}' => $invoice->company, + '{invoice_vat_number}' => $invoice->vat_number, + '{invoice_firstname}' => $invoice->firstname, + '{invoice_lastname}' => $invoice->lastname, + '{invoice_address2}' => $invoice->address2, + '{invoice_address1}' => $invoice->address1, + '{invoice_city}' => $invoice->city, + '{invoice_postal_code}' => $invoice->postcode, + '{invoice_country}' => $invoice->country, + '{invoice_state}' => $invoice->id_state ? $invoice_state->name : '', + '{invoice_phone}' => ($invoice->phone) ? $invoice->phone : $invoice->phone_mobile, + '{invoice_other}' => $invoice->other, + '{order_name}' => sprintf("#%06d", (int)($order->id)), + '{date}' => Tools::displayDate(date('Y-m-d H:i:s'), (int)($order->id_lang), 1), + '{carrier}' => $carrier->name, + '{payment}' => Tools::substr($order->payment, 0, 32), + '{products}' => $productsList, + '{discounts}' => $cartRulesList, + '{total_paid}' => Tools::displayPrice($order->total_paid, $currency, false), + '{total_products}' => Tools::displayPrice($order->total_paid - $order->total_shipping - $order->total_wrapping + $order->total_discounts, $currency, false), + '{total_discounts}' => Tools::displayPrice($order->total_discounts, $currency, false), + '{total_shipping}' => Tools::displayPrice($order->total_shipping, $currency, false), + '{total_wrapping}' => Tools::displayPrice($order->total_wrapping, $currency, false)); + + if (is_array($extraVars)) + $data = array_merge($data, $extraVars); + + // Join PDF invoice + if ((int)(Configuration::get('PS_INVOICE')) AND Validate::isLoadedObject($orderStatus) AND $orderStatus->invoice AND $order->invoice_number) + { + $fileAttachment['content'] = PDF::invoice($order, 'S'); + $fileAttachment['name'] = Configuration::get('PS_INVOICE_PREFIX', (int)($order->id_lang)).sprintf('%06d', $order->invoice_number).'.pdf'; + $fileAttachment['mime'] = 'application/pdf'; + } + else + $fileAttachment = NULL; + + if (Validate::isEmail($customer->email)) + Mail::Send((int)$order->id_lang, 'order_conf', Mail::l('Order confirmation', (int)$order->id_lang), $data, $customer->email, $customer->firstname.' '.$customer->lastname, NULL, NULL, $fileAttachment); + } + } + else + { + $errorMessage = Tools::displayError('Order creation failed'); + Logger::addLog($errorMessage, 4, '0000002', 'Cart', intval($order->id_cart)); + die($errorMessage); } - $this->currentOrder = (int)$order->id; - return true; - } - else - { - $errorMessage = Tools::displayError('Order creation failed'); - Logger::addLog($errorMessage, 4, '0000002', 'Cart', intval($order->id_cart)); - die($errorMessage); } + // Use the last order as currentOrder + $this->currentOrder = (int)$order->id; + return true; } else { @@ -432,11 +474,11 @@ abstract class PaymentModuleCore extends Module * @var int id_currency * @var float amount */ - private function addPCC($id_order, $id_currency, $amount) + private function addPCC($reference, $id_currency, $amount) { // Other information are set by the module - $this->pcc->id_order = (int)$id_order; + $this->pcc->order_reference = (int)$reference; $this->pcc->id_currency = (int)$id_currency; $this->pcc->amount = (float)$amount; $this->pcc->add(); diff --git a/classes/Product.php b/classes/Product.php index 2ff94b98f..4eac83d17 100644 --- a/classes/Product.php +++ b/classes/Product.php @@ -1696,6 +1696,38 @@ class ProductCore extends ObjectModel { return Product::getProductCategories($this->id); } + + /** + * Gets carriers assigned to the product + */ + public function getCarriers() + { + return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT c.* + FROM `'._DB_PREFIX_.'product_carrier` pc + INNER JOIN `'._DB_PREFIX_.'carrier` c + ON (c.`id_reference` = pc.`id_carrier_reference` AND c.`deleted` = 0) + WHERE pc.`id_product` = '.(int)$this->id.' + AND pc.`id_shop` = '.(int)$this->id_shop); + } + + /** + * Sets carriers assigned to the product + */ + public function setCarriers($carrier_list) + { + $data = array(); + foreach($carrier_list as $carrier) + { + $data[] = array( + 'id_product' => (int)$this->id, + 'id_carrier_reference' => (int)$carrier, + 'id_shop' => (int)$this->id_shop + ); + } + Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'product_carrier` WHERE id_product = '.(int)$this->id.' AND id_shop = '.(int)$this->id_shop); + Db::getInstance()->AutoExecute(_DB_PREFIX_.'product_carrier', $data, 'INSERT'); + } /** * Get product images and legends @@ -2994,7 +3026,7 @@ class ProductCore extends ObjectModel $id_lang = Context::getContext()->language->id; if (!$result = Db::getInstance()->executeS(' - SELECT cd.`id_customization`, c.`id_product`, cfl.`id_customization_field`, c.`id_product_attribute`, cd.`type`, cd.`index`, cd.`value`, cfl.`name` + SELECT cd.`id_customization`, c.`id_address_delivery`, c.`id_product`, cfl.`id_customization_field`, c.`id_product_attribute`, cd.`type`, cd.`index`, cd.`value`, cfl.`name` FROM `'._DB_PREFIX_.'customized_data` cd NATURAL JOIN `'._DB_PREFIX_.'customization` c LEFT JOIN `'._DB_PREFIX_.'customization_field_lang` cfl ON (cfl.id_customization_field = cd.`index` AND id_lang = '.(int)($id_lang).') @@ -3004,21 +3036,24 @@ class ProductCore extends ObjectModel return false; $customizedDatas = array(); foreach ($result as $row) - $customizedDatas[(int)($row['id_product'])][(int)($row['id_product_attribute'])][(int)($row['id_customization'])]['datas'][(int)($row['type'])][] = $row; - if (!$result = Db::getInstance()->executeS('SELECT `id_product`, `id_product_attribute`, `id_customization`, `quantity`, `quantity_refunded`, `quantity_returned` + $customizedDatas[(int)($row['id_product'])][(int)($row['id_product_attribute'])][(int)($row['id_address_delivery'])][(int)($row['id_customization'])]['datas'][(int)($row['type'])][] = $row; + if (!$result = Db::getInstance()->executeS('SELECT `id_product`, `id_product_attribute`, `id_customization`, `id_address_delivery`, `quantity`, `quantity_refunded`, `quantity_returned` FROM `'._DB_PREFIX_.'customization` WHERE `id_cart` = '.(int)($id_cart).($only_in_cart ? ' AND `in_cart` = 1' : ''))) return false; foreach ($result as $row) { - $customizedDatas[(int)($row['id_product'])][(int)($row['id_product_attribute'])][(int)($row['id_customization'])]['quantity'] = (int)($row['quantity']); - $customizedDatas[(int)($row['id_product'])][(int)($row['id_product_attribute'])][(int)($row['id_customization'])]['quantity_refunded'] = (int)($row['quantity_refunded']); - $customizedDatas[(int)($row['id_product'])][(int)($row['id_product_attribute'])][(int)($row['id_customization'])]['quantity_returned'] = (int)($row['quantity_returned']); + $customizedDatas[(int)($row['id_product'])][(int)($row['id_product_attribute'])][(int)($row['id_address_delivery'])][(int)($row['id_customization'])]['quantity'] = (int)($row['quantity']); + $customizedDatas[(int)($row['id_product'])][(int)($row['id_product_attribute'])][(int)($row['id_address_delivery'])][(int)($row['id_customization'])]['quantity_refunded'] = (int)($row['quantity_refunded']); + $customizedDatas[(int)($row['id_product'])][(int)($row['id_product_attribute'])][(int)($row['id_address_delivery'])][(int)($row['id_customization'])]['quantity_returned'] = (int)($row['quantity_returned']); } return $customizedDatas; } public static function addCustomizationPrice(&$products, &$customizedDatas) { + if(!$customizedDatas) + return; + foreach ($products as &$productUpdate) { if (!Customization::isFeatureActive()) @@ -3035,11 +3070,12 @@ class ProductCore extends ObjectModel /* Compatibility */ $productId = (int)(isset($productUpdate['id_product']) ? $productUpdate['id_product'] : $productUpdate['product_id']); $productAttributeId = (int)(isset($productUpdate['id_product_attribute']) ? $productUpdate['id_product_attribute'] : $productUpdate['product_attribute_id']); + $id_address_delivery = (int)$productUpdate['id_address_delivery']; $productQuantity = (int)(isset($productUpdate['cart_quantity']) ? $productUpdate['cart_quantity'] : $productUpdate['product_quantity']); $price = isset($productUpdate['price']) ? $productUpdate['price'] : $productUpdate['product_price']; $priceWt = $price * (1 + ((isset($productUpdate['tax_rate']) ? $productUpdate['tax_rate'] : $productUpdate['rate']) * 0.01)); if (isset($customizedDatas[$productId][$productAttributeId])) - foreach ($customizedDatas[$productId][$productAttributeId] as $customization) + foreach ($customizedDatas[$productId][$productAttributeId][$id_address_delivery] as $customization) { $customizationQuantity += (int)$customization['quantity']; $customizationQuantityRefunded += (int)$customization['quantity_refunded']; diff --git a/classes/Tools.php b/classes/Tools.php index 987fa01b9..cd47a332a 100644 --- a/classes/Tools.php +++ b/classes/Tools.php @@ -590,6 +590,20 @@ class ToolsCore return $object; } + /** + * Display a var dump in firebug console + * + * @param object $object Object to display + */ + public static function fd($object) + { + echo ' + + '; + } + /** * ALIAS OF dieObject() - Display an error with detailed object * diff --git a/classes/stock/StockAvailable.php b/classes/stock/StockAvailable.php index 1dc7cf650..0017e3d72 100644 --- a/classes/stock/StockAvailable.php +++ b/classes/stock/StockAvailable.php @@ -250,13 +250,33 @@ class StockAvailableCore extends ObjectModel /** * Upgrades total_quantity_available after having saved - * @see ObjectModel::save() + * @see ObjectModel::add() */ - public function save($null_values = false, $autodate = true) + public function add($autodate = true, $null_values = false) { - if (!parent::save($null_values, $autodate)) + if (!parent::add($autodate, $null_values)) return false; + $this->afterSave(); + } + /** + * Upgrades total_quantity_available after having update + * @see ObjectModel::update() + */ + public function update($null_values = false) + { + if (!parent::update($null_values)) + return false; + $this->afterSave(); + } + + /** + * Upgrades total_quantity_available after having saved + * @see StockAvailableCore::update() + * @see StockAvailableCore::add() + */ + public function afterSave() + { if ($this->id_product_attribute == 0) return true; diff --git a/classes/stock/Warehouse.php b/classes/stock/Warehouse.php index 1fcd02257..a9d0fd211 100644 --- a/classes/stock/Warehouse.php +++ b/classes/stock/Warehouse.php @@ -267,6 +267,30 @@ class WarehouseCore extends ObjectModel return (Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($query)); } + + /** + * For a given {product, product attribute} gets warehouse list + * + * @param int $id_product + * @param int $id_product_attribute + * @param int $id_shop + * @return string + */ + public static function getProductWarehouseList($id_product, $id_product_attribute, $id_shop = null) + { + if (is_null($id_shop)) + $id_shop = Context::getContext()->shop->getID(true); + + $query = new DbQuery(); + $query->select('wpl.id_warehouse'); + $query->from('warehouse_product_location wpl'); + $query->innerJoin('warehouse_shop ws ON (ws.id_warehouse = wpl.id_warehouse AND id_shop = '.(int)$id_shop.')'); + $query->where('id_product = '.(int)$id_product); + $query->where('id_product_attribute = '.(int)$id_product_attribute); + $query->groupBy('wpl.id_warehouse'); + + return (Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($query)); + } /** * Gets available warehouses diff --git a/config/alias.php b/config/alias.php index 2be292588..0051acb2d 100644 --- a/config/alias.php +++ b/config/alias.php @@ -25,6 +25,11 @@ * International Registered Trademark & Property of PrestaShop SA */ +function fd($var) +{ + return (Tools::fd($var)); +} + function p($var) { return (Tools::p($var)); diff --git a/controllers/admin/AdminCarriersController.php b/controllers/admin/AdminCarriersController.php index b8adf498f..bbfb7892a 100644 --- a/controllers/admin/AdminCarriersController.php +++ b/controllers/admin/AdminCarriersController.php @@ -214,6 +214,14 @@ class AdminCarriersControllerCore extends AdminController 'maxlength' => 128, 'p' => $this->l('Time taken for product delivery; displayed during checkout') ), + array( + 'type' => 'text', + 'label' => $this->l('Grade:'), + 'name' => 'grade', + 'required' => false, + 'size' => 1, + 'p' => $this->l('"0" for a longest shipping delay,"9" for the shortest shipping delay.') + ), array( 'type' => 'text', 'label' => $this->l('URL:'), @@ -360,6 +368,38 @@ class AdminCarriersControllerCore extends AdminController ), 'p' => $this->l('Out-of-range behavior when none is defined (e.g., when a customer\'s cart weight is greater than the highest range limit)') ), + array( + 'type' => 'text', + 'label' => $this->l('Maximium package height:'), + 'name' => 'max_height', + 'required' => false, + 'size' => 10, + 'p' => $this->l('Maximum height managed by this carrier. Set "0" or nothing, to ignore this field.') + ), + array( + 'type' => 'text', + 'label' => $this->l('Maximium package width:'), + 'name' => 'max_width', + 'required' => false, + 'size' => 10, + 'p' => $this->l('Maximum width managed by this carrier. Set "0" or nothing, to ignore this field.') + ), + array( + 'type' => 'text', + 'label' => $this->l('Maximium package deep:'), + 'name' => 'max_deep', + 'required' => false, + 'size' => 10, + 'p' => $this->l('Maximum deep managed by this carrier. Set "0" or nothing, to ignore this field.') + ), + array( + 'type' => 'text', + 'label' => $this->l('Maximium package weigth:'), + 'name' => 'max_weight', + 'required' => false, + 'size' => 10, + 'p' => $this->l('Maximum weight managed by this carrier. Set "0" or nothing, to ignore this field.') + ), array( 'type' => 'hidden', 'name' => 'is_module' @@ -375,7 +415,7 @@ class AdminCarriersControllerCore extends AdminController array( 'type' => 'hidden', 'name' => 'need_range' - ) + ), ) ); diff --git a/controllers/admin/AdminOrdersController.php b/controllers/admin/AdminOrdersController.php index b6cac4293..81730df4f 100755 --- a/controllers/admin/AdminOrdersController.php +++ b/controllers/admin/AdminOrdersController.php @@ -30,25 +30,25 @@ class AdminOrdersControllerCore extends AdminController public function __construct() { $this->table = 'order'; - $this->className = 'Order'; - $this->lang = false; + $this->className = 'Order'; + $this->lang = false; $this->edit = true; - $this->addRowAction('view'); + $this->addRowAction('view'); - $this->deleted = false; - $this->colorOnBackground = true; - $this->requiredDatabase = false; - $this->context = Context::getContext(); + $this->deleted = false; + $this->colorOnBackground = true; + $this->requiredDatabase = false; + $this->context = Context::getContext(); - $this->_select = ' + $this->_select = ' a.id_order AS id_pdf, CONCAT(LEFT(c.`firstname`, 1), \'. \', c.`lastname`) AS `customer`, osl.`name` AS `osname`, os.`color`, IF((SELECT COUNT(so.id_order) FROM `'._DB_PREFIX_.'orders` so WHERE so.id_customer = a.id_customer) > 1, 0, 1) as new, (SELECT COUNT(od.`id_order`) FROM `'._DB_PREFIX_.'order_detail` od WHERE od.`id_order` = a.`id_order` GROUP BY `id_order`) AS product_number'; - $this->_join = 'LEFT JOIN `'._DB_PREFIX_.'customer` c ON (c.`id_customer` = a.`id_customer`) - LEFT JOIN `'._DB_PREFIX_.'order_history` oh ON (oh.`id_order` = a.`id_order`) + $this->_join = 'LEFT JOIN `'._DB_PREFIX_.'customer` c ON (c.`id_customer` = a.`id_customer`) + LEFT JOIN `'._DB_PREFIX_.'order_history` oh ON (oh.`id_order` = a.`id_order`) LEFT JOIN `'._DB_PREFIX_.'order_state` os ON (os.`id_order_state` = oh.`id_order_state`) LEFT JOIN `'._DB_PREFIX_.'order_state_lang` osl ON (os.`id_order_state` = osl.`id_order_state` AND osl.`id_lang` = '.(int)$this->context->language->id.')'; $this->_where = 'AND oh.`id_order_history` = (SELECT MAX(`id_order_history`) FROM `'._DB_PREFIX_.'order_history` moh WHERE moh.`id_order` = a.`id_order` GROUP BY moh.`id_order`)'; @@ -62,6 +62,7 @@ class AdminOrdersControllerCore extends AdminController $this->fieldsDisplay = array( 'id_order' => array('title' => $this->l('ID'), 'align' => 'center', 'width' => 25), + 'reference' => array('title' => $this->l('Reference'), 'align' => 'center', 'width' => 65), 'new' => array('title' => $this->l('New'), 'width' => 25, 'align' => 'center', 'type' => 'bool', 'filter_key' => 'new', 'tmpTableFilter' => true, 'icon' => array(0 => 'blank.gif', 1 => 'news-new.gif'), 'orderby' => false), 'customer' => array('title' => $this->l('Customer'), 'filter_key' => 'customer', 'tmpTableFilter' => true), 'total_paid' => array('title' => $this->l('Total'), 'width' => 70, 'align' => 'right', 'prefix' => '', 'suffix' => '', 'type' => 'price', 'currency' => true), @@ -70,8 +71,8 @@ class AdminOrdersControllerCore extends AdminController 'date_add' => array('title' => $this->l('Date'), 'width' => 35, 'align' => 'right', 'type' => 'datetime', 'filter_key' => 'a!date_add'), 'id_pdf' => array('title' => $this->l('PDF'), 'width' => 35, 'align' => 'center', 'callback' => 'printPDFIcons', 'orderby' => false, 'search' => false)); - $this->shopLinkType = 'shop'; - $this->shopShareDatas = Shop::SHARE_ORDER; + $this->shopLinkType = 'shop'; + $this->shopShareDatas = Shop::SHARE_ORDER; parent::__construct(); } diff --git a/controllers/admin/AdminProductsController.php b/controllers/admin/AdminProductsController.php index 0179a97ae..986847dd2 100644 --- a/controllers/admin/AdminProductsController.php +++ b/controllers/admin/AdminProductsController.php @@ -87,7 +87,7 @@ class AdminProductsController extends AdminController LEFT JOIN `'._DB_PREFIX_.'category_product` cp ON (cp.`id_product` = a.`id_product`) LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr ON (a.`id_tax_rules_group` = tr.`id_tax_rules_group` AND tr.`id_country` = '.(int)$this->context->country->id.' AND tr.`id_state` = 0) - LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.`id_tax` = tr.`id_tax`)'; + LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.`id_tax` = tr.`id_tax`)'; $this->_filter = 'AND cp.`id_category` = '.(int)$this->_category->id; $this->_select = 'cl.name `name_category`, cp.`position`, i.`id_image`, (a.`price` * ((100 + (t.`rate`))/100)) AS price_final'; @@ -1195,6 +1195,7 @@ if (false) $this->copyFromPost($object, $this->table); if ($object->update()) { + $this->addCarriers(); if ($id_reason = (int)Tools::getValue('id_mvt_reason') && Tools::getValue('mvt_quantity') > 0 && $id_reason > 0) { if (!$object->addStockMvt(Tools::getValue('mvt_quantity'), $id_reason, null, null, $this->context->employee->id)) @@ -1258,6 +1259,7 @@ if (false) $this->copyFromPost($object, $this->table); if ($object->add()) { + $this->addCarriers(); $this->updateAssoShop((int)$object->id); $this->updateAccessories($object); if (!$this->updatePackItems($object)) @@ -1305,19 +1307,30 @@ if (false) $this->_errors[] = Tools::displayError('An error occurred while creating object.').' '.$this->table.''; } } - + } + + protected function addCarriers() + { + if (Tools::getValue('carriers')) + { + if (Validate::isLoadedObject($product = new Product((int)(Tools::getValue('id_product'))))) + { + if (Tools::getValue('carriers')) + $product->setCarriers(Tools::getValue('carriers')); + } + } } private function _removeTaxFromEcotax() { - $ecotaxTaxRate = Tax::getProductEcotaxRate(); + $ecotaxTaxRate = Tax::getProductEcotaxRate(); if ($ecotax = Tools::getValue('ecotax')) $_POST['ecotax'] = Tools::ps_round(Tools::getValue('ecotax') / (1 + $ecotaxTaxRate / 100), 6); } private function _applyTaxToEcotax($product) { - $ecotaxTaxRate = Tax::getProductEcotaxRate(); + $ecotaxTaxRate = Tax::getProductEcotaxRate(); if ($product->ecotax) $product->ecotax = Tools::ps_round($product->ecotax * (1 + $ecotaxTaxRate / 100), 2); } @@ -2353,6 +2366,8 @@ if (false) $cache_default_attribute = (int) $this->getFieldValue($product, 'cache_default_attribute'); $data->assign('feature_shop_active', Shop::isFeatureActive()); $data->assign('displayAssoShop', $this->displayAssoShop()); + $data->assign('carrier_list', $this->getCarrierList()); + $product_props = array(); // global informations @@ -3199,6 +3214,23 @@ if (false) $data->assign('default_form_language', $this->default_form_language); $this->tpl_form_vars['custom_form'] = $this->context->smarty->createTemplate($this->tpl_form, $data)->fetch(); } + + protected function getCarrierList() + { + $carrier_list = Carrier::getCarriers($this->context->language->id); + if ($product = $this->loadObject(true)) + { + $carrier_selected_list = $product->getCarriers(); + foreach ($carrier_list as &$carrier) + foreach ($carrier_selected_list as $carrier_selected) + if ($carrier_selected['id_reference'] == $carrier['id_reference']) + { + $carrier['selected'] = true; + continue; + } + } + return $carrier_list; + } public function ajaxProcessProductQuantity() { diff --git a/controllers/front/AddressController.php b/controllers/front/AddressController.php index e02cd47ab..75e2b96e7 100644 --- a/controllers/front/AddressController.php +++ b/controllers/front/AddressController.php @@ -246,7 +246,6 @@ class AddressControllerCore extends FrontController } else Tools::redirect('index.php?controller=addresses'); - Tools::redirect($mod ? $back.'&back='.$mod : $back); } $this->errors[] = Tools::displayError('An error occurred while updating your address.'); } diff --git a/controllers/front/CartController.php b/controllers/front/CartController.php index ba23ffae7..81de037eb 100644 --- a/controllers/front/CartController.php +++ b/controllers/front/CartController.php @@ -31,6 +31,7 @@ class CartControllerCore extends FrontController protected $id_product; protected $id_product_attribute; + protected $id_address_delivery; protected $customization_id; protected $qty; @@ -54,6 +55,7 @@ class CartControllerCore extends FrontController $this->id_product_attribute = (int)Tools::getValue('id_product_attribute', Tools::getValue('ipa')); $this->customization_id = (int)Tools::getValue('id_customization'); $this->qty = abs(Tools::getValue('qty', 1)); + $this->id_address_delivery = (int)Tools::getValue('id_address_delivery'); } public function postProcess() @@ -68,6 +70,10 @@ class CartControllerCore extends FrontController $this->processChangeProductInCart(); else if (Tools::getIsset('delete')) $this->processDeleteProductInCart(); + else if(Tools::getIsset('changeAddressDelivery')) + $this->processChangeProductAddressDelivery(); + else if(Tools::getIsset('duplicate')) + $this->processDuplicateProduct(); // Make redirection if (!$this->errors && !$this->ajax) @@ -94,7 +100,8 @@ class CartControllerCore extends FrontController */ protected function processDeleteProductInCart() { - if ($this->context->cart->deleteProduct($this->id_product, $this->id_product_attribute, $this->customization_id)) + if ($this->context->cart->deleteProduct($this->id_product, $this->id_product_attribute, $this->customization_id, $this->id_address_delivery)) + { if (!Cart::getNbProducts((int)($this->context->cart->id))) { $this->context->cart->id_carrier = 0; @@ -102,8 +109,40 @@ class CartControllerCore extends FrontController $this->context->cart->gift_message = ''; $this->context->cart->update(); } + } CartRule::autoRemoveFromCart(); } + + protected function processChangeProductAddressDelivery() + { + $old_id_address_delivery = (int)Tools::getValue('old_id_address_delivery'); + $new_id_address_delivery = (int)Tools::getValue('new_id_address_delivery'); + + $this->context->cart->setProductAddressDelivery( + $this->id_product, + $this->id_product_attribute, + $old_id_address_delivery, + $new_id_address_delivery); + } + + protected function processDuplicateProduct() + { + if ( + !$this->context->cart->duplicateProduct( + $this->id_product, + $this->id_product_attribute, + $this->id_address_delivery, + (int)Tools::getValue('new_id_address_delivery'), + 1, + true + ) + ) + { + //$error_message = $this->l('Error durring product duplication'); + // For the moment no translations + $error_message = 'Error durring product duplication'; + } + } /** * This process add or update a product in the cart @@ -160,7 +199,7 @@ class CartControllerCore extends FrontController if (!$this->errors) { - $updateQuantity = $this->context->cart->updateQty($this->qty, $this->id_product, $this->id_product_attribute, $this->customization_id, Tools::getValue('op', 'up')); + $updateQuantity = $this->context->cart->updateQty($this->qty, $this->id_product, $this->id_product_attribute, $this->customization_id, $this->id_address_delivery, Tools::getValue('op', 'up')); if ($updateQuantity < 0) { // If product has attribute, minimal quantity is set with minimal quantity of attribute diff --git a/controllers/front/OrderController.php b/controllers/front/OrderController.php index 9be768f82..745a2e674 100644 --- a/controllers/front/OrderController.php +++ b/controllers/front/OrderController.php @@ -64,6 +64,16 @@ class OrderControllerCore extends ParentOrderController if (!$this->context->customer->isLogged(true) && in_array($this->step, array(1, 2, 3))) Tools::redirect('index.php?controller=authentication&back='.urlencode('order.php&step='.$this->step)); + + if (Tools::getValue('multi-shipping') == 1) + $this->context->smarty->assign('multi_shipping', true); + else + $this->context->smarty->assign('multi_shipping', false); + + if ($this->context->customer->id) + $this->context->smarty->assign('address_list', $this->context->customer->getAddresses($this->context->language->id)); + else + $this->context->smarty->assign('address_list', array()); } /** @@ -84,11 +94,17 @@ class OrderControllerCore extends ParentOrderController $this->context->smarty->assign('empty', 1); $this->setTemplate(_PS_THEME_DIR_.'shopping-cart.tpl'); break; - + case 1: $this->_assignAddress(); $this->processAddressFormat(); - $this->setTemplate(_PS_THEME_DIR_.'order-address.tpl'); + if (Tools::getValue('multi-shipping') == 1) + { + $this->_assignSummaryInformations(); + $this->setTemplate(_PS_THEME_DIR_.'order-address-multishipping.tpl'); + } + else + $this->setTemplate(_PS_THEME_DIR_.'order-address.tpl'); break; case 2: @@ -183,6 +199,11 @@ class OrderControllerCore extends ParentOrderController */ public function processAddress() { + if (!Tools::getValue('multi-shipping')) + $this->context->cart->setNoMultishipping(); + + // Add checking for all addresses + if (!Tools::isSubmit('id_address_delivery') || !Address::isCountryActiveById((int)Tools::getValue('id_address_delivery'))) $this->errors[] = Tools::displayError('This address is not in a valid area.'); else @@ -213,7 +234,6 @@ class OrderControllerCore extends ParentOrderController protected function processCarrier() { global $orderTotal; - parent::_processCarrier(); if (count($this->errors)) @@ -227,7 +247,7 @@ class OrderControllerCore extends ParentOrderController } $orderTotal = $this->context->cart->getOrderTotal(); } - + /** * Address step */ @@ -235,6 +255,9 @@ class OrderControllerCore extends ParentOrderController { parent::_assignAddress(); + if (Tools::getValue('multi-shipping')) + $this->context->cart->autosetProductAddress(); + $this->context->smarty->assign('cart', $this->context->cart); if ($this->context->customer->is_guest) Tools::redirect('index.php?controller=order&step=2'); diff --git a/controllers/front/ParentOrderController.php b/controllers/front/ParentOrderController.php index 59468612d..2f55f5f2a 100644 --- a/controllers/front/ParentOrderController.php +++ b/controllers/front/ParentOrderController.php @@ -217,10 +217,8 @@ class ParentOrderControllerCore extends FrontController else $id_zone = Country::getIdZone((int)Configuration::get('PS_COUNTRY_DEFAULT')); - if (Validate::isInt(Tools::getValue('id_carrier')) && count(Carrier::checkCarrierZone((int)(Tools::getValue('id_carrier')), (int)($id_zone)))) - $this->context->cart->id_carrier = (int)(Tools::getValue('id_carrier')); - else if (!$this->context->cart->isVirtualCart() && (int)(Tools::getValue('id_carrier')) == 0) - $this->errors[] = Tools::displayError('Invalid carrier or no carrier selected'); + if (Tools::getIsset('delivery_option') && $this->validateDeliveryOption(Tools::getValue('delivery_option'))) + $this->context->cart->delivery_option = serialize(Tools::getValue('delivery_option')); Hook::exec('processCarrier', array('cart' => $this->context->cart)); @@ -230,6 +228,22 @@ class ParentOrderControllerCore extends FrontController // Carrier has changed, so we check if the cart rules still apply CartRule::autoRemoveFromCart(); } + + /** + * Validate get/post param delivery option + * @param array $delivery_option + */ + protected function validateDeliveryOption($delivery_option) + { + if (!is_array($delivery_option)) + return false; + + foreach ($delivery_option as $option) + if (!preg_match('/(\d+,)?\d+/', $option)) + return false; + + return true; + } protected function _assignSummaryInformations() { @@ -364,10 +378,14 @@ class ParentOrderControllerCore extends FrontController $address = new Address($this->context->cart->id_address_delivery); $id_zone = Address::getZoneById($address->id); $carriers = Carrier::getCarriersForOrder($id_zone, $this->context->customer->getGroups()); + $this->context->smarty->assign(array( 'checked' => $this->setDefaultCarrierSelection($carriers), 'carriers' => $carriers, + 'address_collection' => $this->context->cart->getAddressCollection(), + 'delivery_option_list' => $this->context->cart->getDeliveryOptionList(), + 'delivery_option' => $this->context->cart->getDeliveryOption(), 'default_carrier' => (int)(Configuration::get('PS_CARRIER_DEFAULT')) )); $this->context->smarty->assign(array( diff --git a/install-dev/sql/db.sql b/install-dev/sql/db.sql index abc30738a..2b3019fe6 100644 --- a/install-dev/sql/db.sql +++ b/install-dev/sql/db.sql @@ -139,6 +139,11 @@ CREATE TABLE `PREFIX_carrier` ( `external_module_name` varchar(64) DEFAULT NULL, `shipping_method` int(2) NOT NULL DEFAULT '0', `position` int(10) unsigned NOT NULL default '0', + `max_width` int(10) DEFAULT 0, + `max_height` int(10) DEFAULT 0, + `max_deep` int(10) DEFAULT 0, + `max_weight` int(10) DEFAULT 0, + `grade` int(10) DEFAULT 0, PRIMARY KEY (`id_carrier`), KEY `deleted` (`deleted`,`active`), KEY `id_tax_rules_group` (`id_tax_rules_group`) @@ -148,8 +153,8 @@ CREATE TABLE `PREFIX_carrier_lang` ( `id_carrier` int(10) unsigned NOT NULL, `id_shop` int(11) unsigned NOT NULL DEFAULT '1', `id_lang` int(10) unsigned NOT NULL, - `delay` varchar(128) default NULL, - UNIQUE KEY `shipper_lang_index` (`id_lang`,`id_shop`, `id_carrier`) + `delay` varchar(128) DEFAULT NULL, + PRIMARY KEY `shipper_lang_index` (`id_lang`,`id_shop`, `id_carrier`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8; CREATE TABLE `PREFIX_carrier_zone` ( @@ -163,6 +168,7 @@ CREATE TABLE `PREFIX_cart` ( `id_group_shop` INT(11) UNSIGNED NOT NULL DEFAULT '1', `id_shop` INT(11) UNSIGNED NOT NULL DEFAULT '1', `id_carrier` int(10) unsigned NOT NULL, + `delivery_option` varchar(100), `id_lang` int(10) unsigned NOT NULL, `id_address_delivery` int(10) unsigned NOT NULL, `id_address_invoice` int(10) unsigned NOT NULL, @@ -173,6 +179,7 @@ CREATE TABLE `PREFIX_cart` ( `recyclable` tinyint(1) unsigned NOT NULL default '1', `gift` tinyint(1) unsigned NOT NULL default '0', `gift_message` text, + `allow_seperated_package` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, `date_add` datetime NOT NULL, `date_upd` datetime NOT NULL, PRIMARY KEY (`id_cart`), @@ -274,6 +281,7 @@ CREATE TABLE `PREFIX_cart_cart_rule` ( CREATE TABLE `PREFIX_cart_product` ( `id_cart` int(10) unsigned NOT NULL, `id_product` int(10) unsigned NOT NULL, + `id_address_delivery` int(10) UNSIGNED DEFAULT 0, `id_shop` int(10) unsigned NOT NULL DEFAULT '1', `id_product_attribute` int(10) unsigned default NULL, `quantity` int(10) unsigned NOT NULL default '0', @@ -583,13 +591,14 @@ CREATE TABLE `PREFIX_customer_thread` ( CREATE TABLE `PREFIX_customization` ( `id_customization` int(10) unsigned NOT NULL auto_increment, `id_product_attribute` int(10) unsigned NOT NULL default '0', + `id_address_delivery` int(10) UNSIGNED NOT NULL DEFAULT 0, `id_cart` int(10) unsigned NOT NULL, `id_product` int(10) NOT NULL, `quantity` int(10) NOT NULL, `quantity_refunded` INT NOT NULL DEFAULT '0', `quantity_returned` INT NOT NULL DEFAULT '0', `in_cart` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0', - PRIMARY KEY (`id_customization`,`id_cart`,`id_product`), + PRIMARY KEY (`id_customization`,`id_cart`,`id_product`, `id_address_delivery`), KEY `id_product_attribute` (`id_product_attribute`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8; @@ -753,6 +762,13 @@ CREATE TABLE `PREFIX_product_group_reduction_cache` ( PRIMARY KEY(`id_product`, `id_group`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8; +CREATE TABLE `PREFIX_product_carrier` ( + `id_product` int(10) unsigned NOT NULL, + `id_carrier_reference` int(10) unsigned NOT NULL, + `id_shop` int(10) unsigned NOT NULL, + PRIMARY KEY (`id_product`, `id_carrier_reference`, `id_shop`) +) ENGINE = ENGINE_TYPE DEFAULT CHARSET=utf8; + CREATE TABLE `PREFIX_guest` ( `id_guest` int(10) unsigned NOT NULL auto_increment, `id_operating_system` int(10) unsigned default NULL, @@ -971,9 +987,11 @@ CREATE TABLE `PREFIX_operating_system` ( CREATE TABLE `PREFIX_orders` ( `id_order` int(10) unsigned NOT NULL auto_increment, + `reference` VARCHAR(9), `id_group_shop` INT(11) UNSIGNED NOT NULL DEFAULT '1', `id_shop` INT(11) UNSIGNED NOT NULL DEFAULT '1', `id_carrier` int(10) unsigned NOT NULL, + `id_warehouse` int(10) unsigned DEFAULT 0, `id_lang` int(10) unsigned NOT NULL, `id_customer` int(10) unsigned NOT NULL, `id_cart` int(10) unsigned NOT NULL, diff --git a/install-dev/sql/db_settings_extends.sql b/install-dev/sql/db_settings_extends.sql index 8edfd2af1..7024a714b 100644 --- a/install-dev/sql/db_settings_extends.sql +++ b/install-dev/sql/db_settings_extends.sql @@ -174,8 +174,8 @@ INSERT INTO `PREFIX_cart` (`id_cart`, `id_carrier`, `id_lang`, `id_address_deliv INSERT INTO `PREFIX_cart_product` (`id_cart`, `id_product`, `id_shop`, `id_product_attribute`, `quantity`, `date_add`) VALUES (1, 7, 1, 23, 1, NOW()); INSERT INTO `PREFIX_cart_product` (`id_cart`, `id_product`, `id_shop`, `id_product_attribute`, `quantity`, `date_add`) VALUES (1, 9, 1, 0, 1, NOW()); -INSERT INTO `PREFIX_orders` (`id_order`, `id_carrier`, `id_lang`, `id_customer`, `id_cart`, `id_currency`, `id_address_delivery`, `id_address_invoice`, `secure_key`, `payment`, `module`, `recyclable`, `gift`, `gift_message`, `shipping_number`, `total_discounts`, `total_paid`, `total_paid_real`, `total_products`, `total_products_wt`, `total_shipping`, `total_wrapping`, `invoice_number`, `delivery_number`, `invoice_date`, `delivery_date`, `date_add`, `date_upd`) - VALUES (1, 2, 2, 1, 1, 1, 2, 2, '47ce86627c1f3c792a80773c5d2deaf8', 'Chèque', 'cheque', 0, 0, '', '', '0.00', '625.98', '625.98', '516.72', '618.00', '7.98', '0.00', 0, 0, '0000-00-00 00:00:00', '0000-00-00 00:00:00', NOW(), NOW()); +INSERT INTO `PREFIX_orders` (`id_order`, `reference`, `id_carrier`, `id_lang`, `id_customer`, `id_cart`, `id_currency`, `id_address_delivery`, `id_address_invoice`, `secure_key`, `payment`, `module`, `recyclable`, `gift`, `gift_message`, `shipping_number`, `total_discounts`, `total_paid`, `total_paid_real`, `total_products`, `total_products_wt`, `total_shipping`, `total_wrapping`, `invoice_number`, `delivery_number`, `invoice_date`, `delivery_date`, `date_add`, `date_upd`) + VALUES (1, 'XKBKNABJ', 2, 2, 1, 1, 1, 2, 2, '47ce86627c1f3c792a80773c5d2deaf8', 'Chèque', 'cheque', 0, 0, '', '', '0.00', '625.98', '625.98', '516.72', '618.00', '7.98', '0.00', 0, 0, '0000-00-00 00:00:00', '0000-00-00 00:00:00', NOW(), NOW()); INSERT INTO `PREFIX_order_detail` (`id_order_detail`, `id_order`, `product_id`, `product_attribute_id`, `product_name`, `product_quantity`, `product_quantity_return`, `product_price`, `product_quantity_discount`, `product_ean13`, `product_reference`, `product_supplier_reference`, `product_weight`, `ecotax`, `download_hash`, `download_nb`, `download_deadline`, `tax_name`) VALUES (1, 1, 7, 23, 'iPod touch - Capacité: 32Go', 1, 0, '392.140500', '0.000000', NULL, NULL, NULL, 0, '0.00', '', 0, '0000-00-00 00:00:00', ''); INSERT INTO `PREFIX_order_detail` (`id_order_detail`, `id_order`, `product_id`, `product_attribute_id`, `product_name`, `product_quantity`, `product_quantity_return`, `product_price`, `product_quantity_discount`, `product_ean13`, `product_reference`, `product_supplier_reference`, `product_weight`, `ecotax`, `download_hash`, `download_nb`, `download_deadline`, `tax_name`) diff --git a/install-dev/sql/upgrade/1.5.0.1.sql b/install-dev/sql/upgrade/1.5.0.1.sql index c62ced319..7c038dc2e 100644 --- a/install-dev/sql/upgrade/1.5.0.1.sql +++ b/install-dev/sql/upgrade/1.5.0.1.sql @@ -189,7 +189,6 @@ CREATE TABLE IF NOT EXISTS `PREFIX_request_sql` ( /* PHP:add_new_tab(AdminRequestSql, fr:SQL Manager|es:SQL Manager|en:SQL Manager|de:Wunsh|it:SQL Manager, 9); */; - ALTER TABLE `PREFIX_carrier` ADD COLUMN `id_reference` int(10) NOT NULL AFTER `id_carrier`; UPDATE `PREFIX_carrier` SET id_reference = id_carrier; @@ -241,6 +240,31 @@ ALTER TABLE `PREFIX_order_state` ADD COLUMN `shipped` TINYINT(1) UNSIGNED NOT NU UPDATE `PREFIX_order_state` SET `shipped` = 1 WHERE id_order_states IN (4, 5); +ALTER TABLE `PREFIX_carrier` + ADD COLUMN `max_width` int(10) DEFAULT 0 AFTER `position`, + ADD COLUMN `max_height` int(10) DEFAULT 0 AFTER `max_width`, + ADD COLUMN `max_depth` int(10) DEFAULT 0 AFTER `max_height`, + ADD COLUMN `max_weight` int(10) DEFAULT 0 AFTER `max_deep`, + ADD COLUMN `grade` int(10) DEFAULT 0 AFTER `max_weight`; + +ALTER TABLE `PREFIX_cart_product` + ADD COLUMN `id_address_delivery` int(10) UNSIGNED DEFAULT 0 AFTER `date_add`; + +UPDATE `PREFIX_cart_product` SET id_address_delivery = 0; + +ALTER TABLE `PREFIX_cart` ADD COLUMN `allow_seperated_package` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0 AFTER `gift_message`; +CREATE TABLE `PREFIX_product_carrier` ( + `id_product` int(10) unsigned NOT NULL, + `id_carrier_reference` int(10) unsigned NOT NULL, + `id_shop` int(10) unsigned NOT NULL, + PRIMARY KEY (`id_product`, `id_carrier_reference`, `id_shop`) +) ENGINE = ENGINE_TYPE DEFAULT CHARSET=utf8; + +ALTER TABLE `PREFIX_customization` ADD COLUMN `id_address_delivery` int(10) UNSIGNED NOT NULL DEFAULT 0 AFTER `id_product_attribute`, + DROP PRIMARY KEY, + ADD PRIMARY KEY USING BTREE(`id_customization`, `id_cart`, `id_product`, `id_address_delivery`); + + CREATE TABLE `PREFIX_cart_rule` ( `id_cart_rule` int(10) unsigned NOT NULL auto_increment, `id_customer` int unsigned NOT NULL default 0, @@ -490,6 +514,13 @@ ALTER TABLE `PREFIX_specific_price` ADD `id_product_attribute` INT UNSIGNED NOT ALTER TABLE `PREFIX_specific_price` DROP INDEX `id_product`; ALTER TABLE `PREFIX_specific_price` ADD INDEX `id_product` (`id_product`, `id_product_attribute`, `id_shop`, `id_currency`, `id_country`, `id_group`, `from_quantity`, `from`, `to`); + +ALTER TABLE `PREFIX_orders` ADD COLUMN `reference` varchar(9) AFTER `id_order`; +ALTER TABLE `PREFIX_orders` ADD COLUMN `id_warehouse` int(10) unsigned DEFAULT 0 AFTER `id_carrier`; + +ALTER TABLE `PREFIX_cart` ADD COLUMN `order_reference` varchar(9) AFTER `id_cart`; +ALTER TABLE `PREFIX_cart` ADD COLUMN `delivery_option` varchar(100) AFTER `id_carrier`; + ALTER TABLE `PREFIX_hook` ADD `is_native` TINYINT( 1 ) NOT NULL DEFAULT '0'; ALTER TABLE `PREFIX_tax` ADD COLUMN `account_number` VARCHAR(64) NOT NULL; diff --git a/modules/blockcart/blockcart-json.tpl b/modules/blockcart/blockcart-json.tpl index 792642e42..e4e9ca535 100644 --- a/modules/blockcart/blockcart-json.tpl +++ b/modules/blockcart/blockcart-json.tpl @@ -48,7 +48,7 @@ "customizedDatas":[ {if isset($blockcart_customizedDatas.$productId.$productAttributeId)} - {foreach from=$blockcart_customizedDatas.$productId.$productAttributeId key='id_customization' item='customization' name='customizedDatas'}{ldelim} + {foreach from=$blockcart_customizedDatas.$productId.$productAttributeId[$product.id_address_delivery] key='id_customization' item='customization' name='customizedDatas'}{ldelim} {* This empty line was made in purpose (product addition debug), please leave it here *} "customizationId": {$id_customization}, diff --git a/modules/blockcart/blockcart.tpl b/modules/blockcart/blockcart.tpl index eb780f2b1..1eb137d92 100644 --- a/modules/blockcart/blockcart.tpl +++ b/modules/blockcart/blockcart.tpl @@ -76,7 +76,7 @@ var removingLinkText = '{l s='remove this product from my cart' mod='blockcart' {if isset($blockcart_customizedDatas.$productId.$productAttributeId)} {if !isset($product.attributes_small)}
{/if}
    - {foreach from=$blockcart_customizedDatas.$productId.$productAttributeId key='id_customization' item='customization' name='customizations'} + {foreach from=$blockcart_customizedDatas.$productId.$productAttributeId[$product.id_address_delivery] key='id_customization' item='customization' name='customizations'}
  • {$customization.quantity}x{if isset($customization.datas.$blockcart_CUSTOMIZE_TEXTFIELD.0)} @@ -142,7 +142,7 @@ var removingLinkText = '{l s='remove this product from my cart' mod='blockcart' {/if}

    {capture name=step_order_process} - {if $blockcart_order_process == 'order'}step=1{/if} + {if $blockcart_order_process == 'order'}step=1{/if} {/capture} {if $blockcart_order_process == 'order'}{l s='Cart' mod='blockcart'}{/if} {l s='Check out' mod='blockcart'} diff --git a/modules/blocklayered/blocklayered.js b/modules/blocklayered/blocklayered.js index 9202044ed..77cfcc26a 100644 --- a/modules/blocklayered/blocklayered.js +++ b/modules/blocklayered/blocklayered.js @@ -19,7 +19,7 @@ * * @author PrestaShop SA * @copyright 2007-2011 PrestaShop SA -* @version Release: $Revision: 7096 $ +* @version Release: $Revision: 9532 $ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registred Trademark & Property of PrestaShop SA */ @@ -52,16 +52,16 @@ $(document).ready(function() // Click on label $('label a').live({ - click: function() { - if($(this).parent().parent().find('input').attr('disabled') == '') + click: function() { + if ($(this).parent().parent().find('input').attr('disabled') == '') { - $(this).parent().parent().find('input').click(); - reloadContent(); + $(this).parent().parent().find('input').click(); + reloadContent(); } - return false; - } - }); + return false; + } + }); paginationButton(); initLayered(); }); @@ -94,14 +94,6 @@ function initLayered() var params = window.location.href.split('#')[1]; reloadContent('&selected_filters='+params); } - } - -function updatelink(link) -{ - baseUrl = link.split('#'); - linkFilterUpdate = baseUrl[0]+getValueSelected(); - linkFilterUpdate = friendlyUrl(linkFilterUpdate, 'short'); - return linkFilterUpdate; } function getValueSelected(){ @@ -125,37 +117,6 @@ function getValueSelected(){ return checkboxChecked; } -function friendlyUrl(link, encode) -{ - var friendlyTab = - { - 'layered_condition_' : 'cond_', - 'layered_id_attribute_group_' : 'g_', - 'id_category_layered=' : 'c=', - 'layered_manufacturer_' : 'm_', - 'layered_id_feature_' : 'f_', - 'layered_category_' : 'cat_', - }; - - if(encode == 'short') - { - $.each(friendlyTab, function(key, value) - { - Expression = new RegExp(key,'g'); - link = link.replace(Expression, value); - }); - }else - { - $.each(friendlyTab, function(key, value) - { - Expression = new RegExp(value,'g'); - link = link.replace(Expression, key); - }); - } - - return link; -} - function paginationButton() { $('#pagination li').not('.current, .disabled').each( function () { var nbPage = 0; @@ -181,7 +142,7 @@ function cancelFilter() { $('#enabled_filters a').live('click', function(e) { - if($(this).attr('rel').search(/_slider$/) > 0) + if ($(this).attr('rel').search(/_slider$/) > 0) { $('#'+$(this).attr('rel')).slider('values' , 0, $('#'+$(this).attr('rel')).slider('option' , 'min' )); $('#'+$(this).attr('rel')).slider('values' , 1, $('#'+$(this).attr('rel')).slider('option' , 'max' )); @@ -189,8 +150,8 @@ function cancelFilter() } else { - $('#'+$(this).attr('rel')).attr('checked', false); - $('#layered_form input[name='+$(this).attr('rel')+']:hidden').remove(); + $('#'+$(this).attr('rel')).attr('checked', false); + $('#layered_form input[name='+$(this).attr('rel')+']:hidden').remove(); } reloadContent(); e.preventDefault(); @@ -224,7 +185,7 @@ function reloadContent(params_plus) if (!ajaxLoaderOn) { - $('#product_list').prepend($('#layered_ajax_loader').html()); + $('#product_list').prepend($('#layered_ajax_loader').html()); $('#product_list').css('opacity', '0.7'); ajaxLoaderOn = 1; } @@ -233,7 +194,7 @@ function reloadContent(params_plus) $('.layered_slider').each( function () { var sliderStart = $(this).slider('values', 0); var sliderStop = $(this).slider('values', 1); - if(typeof(sliderStart) == 'number' && typeof(sliderStop) == 'number') + if (typeof(sliderStart) == 'number' && typeof(sliderStop) == 'number') data += '&'+$(this).attr('id')+'='+sliderStart+'_'+sliderStop; }); @@ -244,7 +205,7 @@ function reloadContent(params_plus) } var slideUp = true; - if(params_plus == undefined) + if (params_plus == undefined) { params_plus = ''; slideUp = false; @@ -267,7 +228,7 @@ function reloadContent(params_plus) { $('#layered_block_left').after('

    ').remove(); $('#tmp_layered_block_left').html(result.filtersBlock).attr('id', 'layered_block_left'); - + $('.category-product-count').html(result.categoryCount); $('#product_list').replaceWith(result.productList); @@ -290,7 +251,7 @@ function reloadContent(params_plus) return false; }); if (typeof(ajaxCart) != "undefined") - ajaxCart.overrideButtonsInThePage(); + ajaxCart.overrideButtonsInThePage(); if (typeof(reloadProductComparison) == 'function') reloadProductComparison(); @@ -314,7 +275,7 @@ function reloadContent(params_plus) }); if (current_friendly_url == '#') current_friendly_url = '#/'; - window.location = current_friendly_url; + window.location = current_friendly_url; lockLocationChecking = true; if(slideUp) @@ -339,7 +300,7 @@ function initLocationChange(func, time) lockLocationChecking = true; reloadContent('&selected_filters='+getUrlParams().replace(/^#/, '')); - } + } else { lockLocationChecking = false; current_friendly_url = getUrlParams(); diff --git a/modules/blocklayered/blocklayered.tpl b/modules/blocklayered/blocklayered.tpl index ea4a151c3..edec30ee5 100644 --- a/modules/blocklayered/blocklayered.tpl +++ b/modules/blocklayered/blocklayered.tpl @@ -19,7 +19,7 @@ * * @author PrestaShop SA * @copyright 2007-2011 PrestaShop SA -* @version Release: $Revision: 7310 $ +* @version Release: $Revision: 9528 $ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registred Trademark & Property of PrestaShop SA *} @@ -72,7 +72,7 @@ param_product_url = '#{$param_product_url}'; {if isset($filter.slider)}
  • {/foreach} {else}
    -
    +
    {/if} diff --git a/modules/cheque/payment_execution.tpl b/modules/cheque/payment_execution.tpl index 46409092f..fab36e35b 100644 --- a/modules/cheque/payment_execution.tpl +++ b/modules/cheque/payment_execution.tpl @@ -48,8 +48,8 @@ - {l s='The total amount of your order is' mod='cheque'} {displayPrice price=$total} {if $use_taxes == 1} - {l s='(tax incl.)' mod='cheque'} - {/if} + {l s='(tax incl.)' mod='cheque'} + {/if}

    - diff --git a/themes/prestashop/css/global.css b/themes/prestashop/css/global.css index 07e337606..5a5a307fe 100644 --- a/themes/prestashop/css/global.css +++ b/themes/prestashop/css/global.css @@ -361,6 +361,8 @@ table.std { .std tr.alternate_item td, .std tr.alternate_item th { background-color: #f1f2f4 } .std tbody td,.std tfoot td { border-top: 1px solid #bdc2c9 } +.std tbody tr.shipping-address td,.std tfoot tr.shipping-address td { border-top: none } +.std tbody tr.shipping-address td.label,.std tfoot tr.shipping-address td.label { text-align: right; } .std thead th { background-color: transparent; background-image: url('../img/table_header.gif'); @@ -1024,10 +1026,13 @@ p.cart_navigation .button, p.cart_navigation .exclusive, p.cart_navigation .exclusive_large, p.cart_navigation .exclusive_large_disabled { float: right } +p.cart_navigation .multishipping-button { margin-right: 10px } p.cart_navigation_extra { text-align: center; width: auto } +.address-form-multishipping { padding: 10px; } +.address-form-multishipping a { float: none; margin-left: 210px; } form.std p.cart_navigation span { float: none; text-align: left; @@ -1044,6 +1049,7 @@ form.std p.cart_navigation span { font-size: 1em; text-decoration: none } +tr.cart_item td .cart_address_delivery { width: 100px; margin-right: 10px; } tr.cart_item td { padding: 0.5em 0 } td.cart_product a { margin: 0 0.6em } td.cart_product a img { @@ -1099,23 +1105,13 @@ tr.cart_total_price { font-weight: bold } cursor:pointer; } #order form#voucher h4, - #order form#voucher p, - #order form#voucher .button { display: inline } +#order form#voucher p, +#order form#voucher .button { display: inline } #order form#voucher h4 { float: left; line-height: 1.5em; margin-right: 6em } -#order #order_carrier { - clear: both; - margin-top: 1em; - border: 1px solid #bdc2c9; - padding: 0.5em -} -#order #order_carrier h4 { - width: 50%; - display: inline -} #order #order_carrier img { vertical-align: middle } #order #order_carrier span { margin: 0 0.2em } #order #gift_div { display: none } @@ -1124,6 +1120,65 @@ tr.cart_total_price { font-weight: bold } width: 100%; margin: 1em 0 } +#order .delivery_options_address { + width: 541px; + margin-left: 7px; +} +#order .delivery_options_address h3 { + background: url("../img/table_header.gif") no-repeat; + background: url("http://prestashop15.localhost/themes/prestashop/css/../img/table_header.gif") no-repeat; + color: #374853; + font-weight: bold; + height: 14px; + padding: 5px 10px; + margin: 10px 0 0 0; +} +#order .delivery_option_radio { + float: left; + margin-top: 12px; +} +#order .delivery_option label { + display: block; + padding-bottom: 5px; +} +#order .delivery_option label > table.resume { + width: 522px; + height: 40px; +} +#order .delivery_option.item { + background: #FAFAFA; +} +#order .delivery_option.alternate_item { + background: #F1F2F4; + border-top: 1px solid #BDC2C9; +} +#order .delivery_option label > table.resume td { + padding: 0 8px; +} +#order .delivery_option label > table.resume td + td { + text-align: left; + width: 380px; +} +#order .delivery_option label > table.resume td + td + td { + text-align: right; + width: 100px; +} +#order .delivery_options_address .delivery_option_logo { + text-align: center; + width: 100px; +} +#order .delivery_option_icon { +} +#order .delivery_option_price { +} +#order .delivery_options_address .delivery_option_logo img { height: 40px; } +#order .delivery_option_carrier { + border: 1px solid #BDC2C9; + background: white; + margin: 5px 20px; + padding: 5px; + width: 500px; +} #order-opc tfoot p { margin: 0; diff --git a/themes/prestashop/history.tpl b/themes/prestashop/history.tpl index 025105ff4..e81fcec04 100644 --- a/themes/prestashop/history.tpl +++ b/themes/prestashop/history.tpl @@ -55,7 +55,7 @@ {l s='#'}{$order.id_order|string_format:"%06d"} {dateFormat date=$order.date_add full=0} - {displayPrice price=$order.total_paid_real currency=$order.id_currency no_utf8=false convert=false} + {displayPrice price=$order.total_paid currency=$order.id_currency no_utf8=false convert=false} {$order.payment|escape:'htmlall':'UTF-8'} {if isset($order.order_state)}{$order.order_state|escape:'htmlall':'UTF-8'}{/if} diff --git a/themes/prestashop/js/cart-summary.js b/themes/prestashop/js/cart-summary.js index 45f213e02..fdd94eed1 100644 --- a/themes/prestashop/js/cart-summary.js +++ b/themes/prestashop/js/cart-summary.js @@ -29,13 +29,186 @@ $(document).ready(function() // If block cart isn't used, we don't bind the handle actions if (window.ajaxCart !== undefined) { - $('.cart_quantity_up').unbind('click').click(function(){ upQuantity($(this).attr('id').replace('cart_quantity_up_', '')); return false; }); - $('.cart_quantity_down').unbind('click').click(function(){ downQuantity($(this).attr('id').replace('cart_quantity_down_', '')); return false; }); - $('.cart_quantity_delete' ).unbind('click').click(function(){ deletProductFromSummary($(this).attr('id')); return false; }); + $('.cart_quantity_up').unbind('click').live('click', function(){ upQuantity($(this).attr('id').replace('cart_quantity_up_', '')); return false; }); + $('.cart_quantity_down').unbind('click').live('click', function(){ downQuantity($(this).attr('id').replace('cart_quantity_down_', '')); return false; }); + $('.cart_quantity_delete' ).unbind('click').live('click', function(){ deleteProductFromSummary($(this).attr('id')); return false; }); $('.cart_quantity_input').typeWatch({ highlight: true, wait: 600, captureLength: 0, callback: updateQty }); + + $('.cart_address_delivery').live('change', function(){ changeAddressDelivery($(this)); }); } + else + { + $('.cart_address_delivery').change(function(){ submit(); }); + } + cleanSelectAddressDelivery(); }); +function cleanSelectAddressDelivery() +{ + if (window.ajaxCart !== undefined) + { + //Removing "Ship to an other address" from the address delivery select option if there is not enought address + $.each($('.cart_address_delivery'), function(it, item) + { + var options = $(item).find('option'); + var address_count = 0; + + var ids = $(item).attr('id').split('_'); + var id_product = ids[3]; + var id_product_attribute = ids[4]; + var id_address_delivery = ids[5]; + + $.each(options, function(i) { + if ($(options[i]).val() > 0 + && ($('#product_' + id_product + '_' + id_product_attribute + '_0_' + $(options[i]).val()).length == 0 // Check the address is not already used for a similare products + || id_address_delivery == $(options[i]).val() + ) + ) + address_count++; + }); + + if (address_count < 2) // Need at least two address to allow skipping products to multiple address + $($(item).find('option[value=-2]')).remove(); + else if($($(item).find('option[value=-2]')).length == 0) + $(item).append($('')); // @todo add translation + }); + } +} + +function changeAddressDelivery(obj) +{ + var ids = obj.attr('id').split('_'); + var id_product = ids[3]; + var id_product_attribute = ids[4]; + var old_id_address_delivery = ids[5]; + var new_id_address_delivery = obj.val(); + + if (new_id_address_delivery == old_id_address_delivery) + return; + + if (new_id_address_delivery > 0) // Change the delivery address + { + $.ajax({ + type: 'GET', + url: baseDir, + async: true, + cache: false, + dataType: 'json', + data: 'controller=cart&ajax=true&changeAddressDelivery&summary&id_product='+id_product + +'&id_product_attribute='+id_product_attribute + +'&old_id_address_delivery='+old_id_address_delivery + +'&new_id_address_delivery='+new_id_address_delivery + +'&token='+static_token, + success: function(jsonData) + { + // The product exist + if ($('#product_'+id_product+'_'+id_product_attribute+'_0_'+new_id_address_delivery).length) + { + updateCustomizedDatas(jsonData.customizedDatas); + updateCartSummary(jsonData.summary); + updateHookShoppingCart(jsonData.HOOK_SHOPPING_CART); + updateHookShoppingCartExtra(jsonData.HOOK_SHOPPING_CART_EXTRA); + if (jsonData.carriers != null) + updateCarrierList(jsonData); + + // @todo reverse the remove order + // This effect remove the current line, but it's better to remove the other one, and refresshing this one + $('#product_'+id_product+'_'+id_product_attribute+'_0_'+old_id_address_delivery).remove(); + + // @todo improve customization upgrading + $('.product_'+id_product+'_'+id_product_attribute+'_0_'+old_id_address_delivery).remove(); + } + + if (window.ajaxCart !== undefined) + ajaxCart.refresh(); + updateAddressId(id_product, id_product_attribute, old_id_address_delivery, new_id_address_delivery); + cleanSelectAddressDelivery(); + } + }); + } + else if (new_id_address_delivery == -1) // Adding a new address + window.location = $($('.address_add a')[0]).attr('href'); + else if (new_id_address_delivery == -2) // Add a new line for this product + { + // This test is will not usefull in the future + if (old_id_address_delivery == 0) + { + alert('Please select first an address'); // @todo translate + return false; + } + + // Get new address to deliver + var id_address_delivery = 0; + var options = $('#select_address_delivery_'+id_product+'_'+id_product_attribute+'_'+old_id_address_delivery+' option'); + $.each(options, function(i) { + if ($(options[i]).val() > 0 && $(options[i]).val() != old_id_address_delivery + && $('#product_' + id_product + '_' + id_product_attribute + '_0_' + $(options[i]).val()).length == 0 // Check the address is not already used for a similare products + ) + { + id_address_delivery = $(options[i]).val(); + return false; + } + }); + + $.ajax({ + type: 'GET', + url: baseDir, + async: true, + cache: false, + dataType: 'json', + context: obj, + data: 'controller=cart&ajax=true&duplicate&summary&id_product='+id_product+'&id_product_attribute='+id_product_attribute+'&id_address_delivery='+old_id_address_delivery+'&new_id_address_delivery='+id_address_delivery+'&token='+static_token , + success: function(jsonData) + { + if (jsonData.error) + { + alert(jsonData.reason); + return; + } + + var line = $('#product_' + id_product+'_' + id_product_attribute + '_0_' + old_id_address_delivery); + var new_line = line.clone(); + updateAddressId(id_product, id_product_attribute, old_id_address_delivery, id_address_delivery, new_line); + line.after(new_line); + new_line.find('input[name=quantity_' + id_product+'_' + id_product_attribute + '_0_' + old_id_address_delivery + '_hidden]') + .val(1); + new_line.find('.cart_quantity_input') + .val(1); + $('#select_address_delivery_' + id_product+'_' + id_product_attribute + '_' + old_id_address_delivery).val(old_id_address_delivery); + $('#select_address_delivery_' + id_product+'_' + id_product_attribute + '_' + id_address_delivery).val(id_address_delivery); + + + cleanSelectAddressDelivery(); + + updateCartSummary(jsonData.summary); + + if (window.ajaxCart !== undefined) + ajaxCart.refresh(); + } + }); + } +} + +function updateAddressId(id_product, id_product_attribute, old_id_address_delivery, id_address_delivery, line) +{ + if (typeof(line) == 'undefined') + var line = $('#product_' + id_product+'_' + id_product_attribute + '_0_' + old_id_address_delivery); + + line.attr('id', 'product_' + id_product+'_' + id_product_attribute + '_0_' + id_address_delivery); + line.find('.cart_quantity_input') + .attr('name', 'quantity_' + id_product+'_' + id_product_attribute + '_0_' + id_address_delivery); + line.find('input[name=quantity_' + id_product+'_' + id_product_attribute + '_0_' + old_id_address_delivery + '_hidden]') + .attr('name', 'quantity_' + id_product+'_' + id_product_attribute + '_0_' + id_address_delivery + '_hidden'); + line.find('#cart_quantity_down_' + id_product+'_' + id_product_attribute + '_0_' + old_id_address_delivery) + .attr('id', 'cart_quantity_down_' + id_product+'_' + id_product_attribute + '_0_' + id_address_delivery); + line.find('#cart_quantity_up_' + id_product+'_' + id_product_attribute + '_0_' + old_id_address_delivery) + .attr('id', 'cart_quantity_up_' + id_product+'_' + id_product_attribute + '_0_' + id_address_delivery); + line.find('#' + id_product+'_' + id_product_attribute + '_0_' + old_id_address_delivery) + .attr('id', id_product+'_' + id_product_attribute + '_0_' + id_address_delivery); + line.find('#select_address_delivery_' + id_product+'_' + id_product_attribute + '_' + old_id_address_delivery) + .attr('id', 'select_address_delivery_' + id_product+'_' + id_product_attribute + '_' + id_address_delivery); +} + function updateQty(val) { var id = $(this.el).attr('name'); @@ -48,19 +221,20 @@ function updateQty(val) var QtyToUp = parseInt(input) - parseInt(hidden); if (parseInt(QtyToUp) > 0) - upQuantity(id.replace('quantity_', ''),QtyToUp); + upQuantity(id.replace('quantity_', ''), QtyToUp); else if(parseInt(QtyToUp) < 0) - downQuantity(id.replace('quantity_', ''),QtyToUp); + downQuantity(id.replace('quantity_', ''), QtyToUp); } else $('input[name='+ id +']').val($('input[name='+ id +'_hidden]').val()); } -function deletProductFromSummary(id) +function deleteProductFromSummary(id) { var customizationId = 0; var productId = 0; var productAttributeId = 0; + var id_address_delivery = 0; var ids = 0; ids = id.split('_'); productId = parseInt(ids[0]); @@ -68,34 +242,36 @@ function deletProductFromSummary(id) productAttributeId = parseInt(ids[1]); if (typeof(ids[2]) != 'undefined') customizationId = parseInt(ids[2]); + if (typeof(ids[3]) != 'undefined') + id_address_delivery = parseInt(ids[3]); $.ajax({ - type: 'GET', - url: baseDir + 'cart.php', - async: true, - cache: false, - dataType: 'json', - data: 'ajax=true&delete&summary&id_product='+productId+'&ipa='+productAttributeId+ ( (customizationId != 0) ? '&id_customization='+customizationId : '') + '&token=' + static_token , - success: function(jsonData) - { - if (jsonData.hasError) - { - var errors = ''; - for(error in jsonData.errors) - //IE6 bug fix - if(error != 'indexOf') - errors += jsonData.errors[error] + "\n"; - } - else - { - if (parseInt(jsonData.summary.products.length) == 0) - { - $('#center_column').children().each(function() { - if ($(this).attr('id') != 'emptyCartWarning' && $(this).attr('class') != 'breadcrumb' && $(this).attr('id') != 'cart_title') - { - $(this).fadeOut('slow', function () { - $(this).remove(); - }); - } + type: 'GET', + url: baseDir, + async: true, + cache: false, + dataType: 'json', + data: 'controller=cart&ajax=true&delete&summary&id_product='+productId+'&ipa='+productAttributeId+'&id_address_delivery='+id_address_delivery+ ( (customizationId != 0) ? '&id_customization='+customizationId : '') + '&token=' + static_token , + success: function(jsonData) + { + if (jsonData.hasError) + { + var errors = ''; + for(error in jsonData.errors) + //IE6 bug fix + if(error != 'indexOf') + errors += jsonData.errors[error] + "\n"; + } + else + { + if (parseInt(jsonData.summary.products.length) == 0) + { + $('#center_column').children().each(function() { + if ($(this).attr('id') != 'emptyCartWarning' && $(this).attr('class') != 'breadcrumb' && $(this).attr('id') != 'cart_title') + { + $(this).fadeOut('slow', function () { + $(this).remove(); + }); + } }); $('#summary_products_label').remove(); $('#emptyCartWarning').fadeIn('slow'); @@ -104,6 +280,7 @@ function deletProductFromSummary(id) { $('#product_'+ id).fadeOut('slow', function() { $(this).remove(); + cleanSelectAddressDelivery(); }); var exist = false; @@ -123,11 +300,10 @@ function deletProductFromSummary(id) updateCustomizedDatas(jsonData.customizedDatas); if (jsonData.carriers != null) updateCarrierList(jsonData); - - } - }, - error: function(XMLHttpRequest, textStatus, errorThrown) {alert("TECHNICAL ERROR: unable to save update quantity \n\nDetails:\nError thrown: " + XMLHttpRequest + "\n" + 'Text status: ' + textStatus);} - }); + } + }, + error: function(XMLHttpRequest, textStatus, errorThrown) {alert("TECHNICAL ERROR: unable to save update quantity \n\nDetails:\nError thrown: " + XMLHttpRequest + "\n" + 'Text status: ' + textStatus);} + }); } function upQuantity(id, qty) @@ -137,6 +313,7 @@ function upQuantity(id, qty) var customizationId = 0; var productId = 0; var productAttributeId = 0; + var id_address_delivery = 0; var ids = 0; ids = id.split('_'); productId = parseInt(ids[0]); @@ -144,37 +321,39 @@ function upQuantity(id, qty) productAttributeId = parseInt(ids[1]); if (typeof(ids[2]) != 'undefined') customizationId = parseInt(ids[2]); + if (typeof(ids[3]) != 'undefined') + id_address_delivery = parseInt(ids[3]); $.ajax({ - type: 'GET', - url: baseDir + 'cart.php', - async: true, - cache: false, - dataType: 'json', - data: 'ajax=true&add&getproductprice&summary&id_product='+productId+'&ipa='+productAttributeId + ( (customizationId != 0) ? '&id_customization='+customizationId : '') + '&qty='+qty+'&token=' + static_token , - success: function(jsonData) - { - if (jsonData.hasError) - { - var errors = ''; - for(error in jsonData.errors) - //IE6 bug fix - if(error != 'indexOf') - errors += jsonData.errors[error] + "\n"; - alert(errors); - $('input[name=quantity_'+ id +']').val($('input[name=quantity_'+ id +'_hidden]').val()); - } - else - { - updateCustomizedDatas(jsonData.customizedDatas); - updateCartSummary(jsonData.summary); - updateHookShoppingCart(jsonData.HOOK_SHOPPING_CART); + type: 'GET', + url: baseDir, + async: true, + cache: false, + dataType: 'json', + data: 'controller=cart&ajax=true&add&getproductprice&summary&id_product='+productId+'&ipa='+productAttributeId+'&id_address_delivery='+id_address_delivery + ( (customizationId != 0) ? '&id_customization='+customizationId : '') + '&qty='+qty+'&token=' + static_token , + success: function(jsonData) + { + if (jsonData.hasError) + { + var errors = ''; + for(error in jsonData.errors) + //IE6 bug fix + if(error != 'indexOf') + errors += jsonData.errors[error] + "\n"; + alert(errors); + $('input[name=quantity_'+ id +']').val($('input[name=quantity_'+ id +'_hidden]').val()); + } + else + { + updateCustomizedDatas(jsonData.customizedDatas); + updateCartSummary(jsonData.summary); + updateHookShoppingCart(jsonData.HOOK_SHOPPING_CART); updateHookShoppingCartExtra(jsonData.HOOK_SHOPPING_CART_EXTRA); - if (jsonData.carriers != null) + if (jsonData.carriers != null) updateCarrierList(jsonData); - } - }, - error: function(XMLHttpRequest, textStatus, errorThrown) {alert("TECHNICAL ERROR: unable to save update quantity \n\nDetails:\nError thrown: " + XMLHttpRequest + "\n" + 'Text status: ' + textStatus);} - }); + } + }, + error: function(XMLHttpRequest, textStatus, errorThrown) {alert("TECHNICAL ERROR: unable to save update quantity \n\nDetails:\nError thrown: " + XMLHttpRequest + "\n" + 'Text status: ' + textStatus);} + }); } function downQuantity(id, qty) @@ -187,10 +366,11 @@ function downQuantity(id, qty) newVal = val - 1; } else if (qty < 0) - qty = -qty; + qty = -qty; var customizationId = 0; var productId = 0; var productAttributeId = 0; + var id_address_delivery = 0; var ids = 0; if (newVal > 0) { @@ -200,42 +380,44 @@ function downQuantity(id, qty) productAttributeId = parseInt(ids[1]); if (typeof(ids[2]) != 'undefined') customizationId = parseInt(ids[2]); + if (typeof(ids[3]) != 'undefined') + id_address_delivery = parseInt(ids[3]); $.ajax({ - type: 'GET', - url: baseDir + 'cart.php', - async: true, - cache: false, - dataType: 'json', - data: 'ajax=true&add&getproductprice&summary&id_product='+productId+'&ipa='+productAttributeId+'&op=down' + ( (customizationId != 0) ? '&id_customization='+customizationId : '') + '&qty='+qty+'&token=' + static_token , - success: function(jsonData) - { - if (jsonData.hasError) - { - var errors = ''; - for(error in jsonData.errors) - //IE6 bug fix - if(error != 'indexOf') - errors += jsonData.errors[error] + "\n"; - alert(errors); - $('input[name=quantity_'+ id +']').val($('input[name=quantity_'+ id +'_hidden]').val()); - } - else - { - updateCustomizedDatas(jsonData.customizedDatas); - updateCartSummary(jsonData.summary); - updateHookShoppingCart(jsonData.HOOK_SHOPPING_CART); + type: 'GET', + url: baseDir, + async: true, + cache: false, + dataType: 'json', + data: 'controller=cart&ajax=true&add&getproductprice&summary&id_product='+productId+'&ipa='+productAttributeId+'&id_address_delivery='+id_address_delivery+'&op=down' + ( (customizationId != 0) ? '&id_customization='+customizationId : '') + '&qty='+qty+'&token=' + static_token , + success: function(jsonData) + { + if (jsonData.hasError) + { + var errors = ''; + for(error in jsonData.errors) + //IE6 bug fix + if(error != 'indexOf') + errors += jsonData.errors[error] + "\n"; + alert(errors); + $('input[name=quantity_'+ id +']').val($('input[name=quantity_'+ id +'_hidden]').val()); + } + else + { + updateCustomizedDatas(jsonData.customizedDatas); + updateCartSummary(jsonData.summary); + updateHookShoppingCart(jsonData.HOOK_SHOPPING_CART); updateHookShoppingCartExtra(jsonData.HOOK_SHOPPING_CART_EXTRA); - if (jsonData.carriers != null) + if (jsonData.carriers != null) updateCarrierList(jsonData); - } - }, - error: function(XMLHttpRequest, textStatus, errorThrown) {alert("TECHNICAL ERROR: unable to save update quantity \n\nDetails:\nError thrown: " + XMLHttpRequest + "\n" + 'Text status: ' + textStatus);} - }); + } + }, + error: function(XMLHttpRequest, textStatus, errorThrown) {alert("TECHNICAL ERROR: unable to save update quantity \n\nDetails:\nError thrown: " + XMLHttpRequest + "\n" + 'Text status: ' + textStatus);} + }); } else { - deletProductFromSummary(id); + deleteProductFromSummary(id); } } @@ -253,7 +435,9 @@ function updateCartSummary(json) // if reduction, we need to show it in the cart by showing the initial price above the current one var reduction = json.products[i].reduction_applies; var initial_price_text = ''; - initial_price = formatCurrency(json.products[i].price_without_quantity_discount, currencyFormat, currencySign, currencyBlank); + initial_price = ''; + if (typeof(json.products[i].price_without_quantity_discount) != 'undefined') + initial_price = formatCurrency(json.products[i].price_without_quantity_discount, currencyFormat, currencySign, currencyBlank); var current_price = ''; if (priceDisplayMethod != 0) current_price = formatCurrency(json.products[i].price, currencyFormat, currencySign, currencyBlank); @@ -273,34 +457,37 @@ function updateCartSummary(json) if (priceDisplayMethod != 0) { - $('#cart_block_product_'+key_for_blockcart+' span.price').html(formatCurrency(json.products[i].total, currencyFormat, currencySign, currencyBlank)); - $('#product_price_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute).html(initial_price_text+current_price); - $('#total_product_price_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute).html(formatCurrency(json.products[i].total, currencyFormat, currencySign, currencyBlank)); + $('#cart_block_product_'+key_for_blockcart+' span.price').html(formatCurrency(json.products[i].total, currencyFormat, currencySign, currencyBlank)); + $('#product_price_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute+'_'+json.products[i].id_address_delivery).html(initial_price_text+current_price); + $('#total_product_price_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute+'_'+json.products[i].id_address_delivery).html(formatCurrency(json.products[i].total, currencyFormat, currencySign, currencyBlank)); } else { - $('#cart_block_product_'+key_for_blockcart+' span.price').html(formatCurrency(json.products[i].total_wt, currencyFormat, currencySign, currencyBlank)); - $('#product_price_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute).html(initial_price_text+current_price); - $('#total_product_price_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute).html(formatCurrency(json.products[i].total_wt, currencyFormat, currencySign, currencyBlank)); + $('#cart_block_product_'+key_for_blockcart+' span.price').html(formatCurrency(json.products[i].total_wt, currencyFormat, currencySign, currencyBlank)); + $('#product_price_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute+'_'+json.products[i].id_address_delivery).html(initial_price_text+current_price); + $('#total_product_price_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute+'_'+json.products[i].id_address_delivery).html(formatCurrency(json.products[i].total_wt, currencyFormat, currencySign, currencyBlank)); } nbrProducts += parseInt(json.products[i].cart_quantity); if(json.products[i].id_customization == null) { - $('input[name=quantity_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute+(json.products[i].id_customization != null ? '_'+json.products[i].id_customization : '')+']').val(json.products[i].cart_quantity); - $('input[name=quantity_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute+(json.products[i].id_customization != null ? '_'+json.products[i].id_customization : '')+'_hidden]').val(json.products[i].cart_quantity); + $('input[name=quantity_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute+'_0_'+json.products[i].id_address_delivery+']').val(json.products[i].cart_quantity); + $('input[name=quantity_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute+'_0_'+json.products[i].id_address_delivery+'_hidden]').val(json.products[i].cart_quantity); } else { - $('#cart_quantity_custom_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute).html(json.products[i].cart_quantity); + //$('input[name=quantity_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute+'_'+json.products[i].id_customization+'_'+json.products[i].id_address_delivery+']') + // .val(json.products[i].cart_quantity); + $('#cart_quantity_custom_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute+'_'+json.products[i].id_address_delivery) + .html(json.products[i].cart_quantity); } // Show / hide quantity button if minimal quantity if (parseInt(json.products[i].minimal_quantity) == parseInt(json.products[i].cart_quantity) && json.products[i].minimal_quantity != 1) - $('#cart_quantity_down_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute+(json.products[i].id_customization != null ? '_'+json.products[i].id_customization : '')).fadeTo('slow',0.3); + $('#cart_quantity_down_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute+Number(json.products[i].id_customization)+'_'+json.products[i].id_address_delivery).fadeTo('slow',0.3); else - $('#cart_quantity_down_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute+(json.products[i].id_customization != null ? '_'+json.products[i].id_customization : '')).fadeTo('slow',1); + $('#cart_quantity_down_'+json.products[i].id_product+'_'+json.products[i].id_product_attribute+Number(json.products[i].id_customization)+'_'+json.products[i].id_address_delivery).fadeTo('slow',1); } @@ -342,16 +529,16 @@ function updateCartSummary(json) } // Block cart - if (priceDisplayMethod != 0) + if (priceDisplayMethod != 0) { - $('#cart_block_shipping_cost').html(formatCurrency(json.total_shipping_tax_exc, currencyFormat, currencySign, currencyBlank)); - $('#cart_block_wrapping_cost').html(formatCurrency(json.total_wrapping_tax_exc, currencyFormat, currencySign, currencyBlank)); - $('#cart_block_total').html(formatCurrency(json.total_price_without_tax, currencyFormat, currencySign, currencyBlank)); - } else { - $('#cart_block_shipping_cost').html(formatCurrency(json.total_shipping, currencyFormat, currencySign, currencyBlank)); - $('#cart_block_wrapping_cost').html(formatCurrency(json.total_wrapping, currencyFormat, currencySign, currencyBlank)); - $('#cart_block_total').html(formatCurrency(json.total_price, currencyFormat, currencySign, currencyBlank)); - } + $('#cart_block_shipping_cost').html(formatCurrency(json.total_shipping_tax_exc, currencyFormat, currencySign, currencyBlank)); + $('#cart_block_wrapping_cost').html(formatCurrency(json.total_wrapping_tax_exc, currencyFormat, currencySign, currencyBlank)); + $('#cart_block_total').html(formatCurrency(json.total_price_without_tax, currencyFormat, currencySign, currencyBlank)); + } else { + $('#cart_block_shipping_cost').html(formatCurrency(json.total_shipping, currencyFormat, currencySign, currencyBlank)); + $('#cart_block_wrapping_cost').html(formatCurrency(json.total_wrapping, currencyFormat, currencySign, currencyBlank)); + $('#cart_block_total').html(formatCurrency(json.total_price, currencyFormat, currencySign, currencyBlank)); + } $('#cart_block_tax_cost').html(formatCurrency(json.total_tax, currencyFormat, currencySign, currencyBlank)); $('.ajax_cart_quantity').html(nbrProducts); @@ -359,9 +546,9 @@ function updateCartSummary(json) // Cart summary $('#summary_products_quantity').html(nbrProducts+' '+(nbrProducts > 1 ? txtProducts : txtProduct)); if (priceDisplayMethod != 0) - $('#total_product').html(formatCurrency(json.total_products, currencyFormat, currencySign, currencyBlank)); - else - $('#total_product').html(formatCurrency(json.total_products_wt, currencyFormat, currencySign, currencyBlank)); + $('#total_product').html(formatCurrency(json.total_products, currencyFormat, currencySign, currencyBlank)); + else + $('#total_product').html(formatCurrency(json.total_products_wt, currencyFormat, currencySign, currencyBlank)); $('#total_price').html(formatCurrency(json.total_price, currencyFormat, currencySign, currencyBlank)); $('#total_price_without_tax').html(formatCurrency(json.total_price_without_tax, currencyFormat, currencySign, currencyBlank)); $('#total_tax').html(formatCurrency(json.total_tax, currencyFormat, currencySign, currencyBlank)); @@ -372,12 +559,12 @@ function updateCartSummary(json) { $('.cart_total_delivery').fadeIn(); if (priceDisplayMethod != 0) - { - $('#total_shipping').html(formatCurrency(json.total_shipping_tax_exc, currencyFormat, currencySign, currencyBlank)); + { + $('#total_shipping').html(formatCurrency(json.total_shipping_tax_exc, currencyFormat, currencySign, currencyBlank)); } else { - $('#total_shipping').html(formatCurrency(json.total_shipping, currencyFormat, currencySign, currencyBlank)); + $('#total_shipping').html(formatCurrency(json.total_shipping, currencyFormat, currencySign, currencyBlank)); } } @@ -408,10 +595,11 @@ function updateCustomizedDatas(json) for(i in json) for(j in json[i]) for(k in json[i][j]) - { - $('input[name=quantity_'+i+'_'+j+'_'+k+'_hidden]').val(json[i][j][k]['quantity']); - $('input[name=quantity_'+i+'_'+j+'_'+k+']').val(json[i][j][k]['quantity']); - } + for(l in json[i][j][k]) + { + $('input[name=quantity_'+i+'_'+j+'_'+l+'_'+k+'_hidden]').val(json[i][j][k][l]['quantity']); + $('input[name=quantity_'+i+'_'+j+'_'+l+'_'+k+']').val(json[i][j][k][l]['quantity']); + } } function updateHookShoppingCart(html) @@ -422,4 +610,17 @@ function updateHookShoppingCart(html) function updateHookShoppingCartExtra(html) { $('#HOOK_SHOPPING_CART_EXTRA').html(html); -} \ No newline at end of file +} +$(document).ready(function() { + $.each($('.delivery_option_radio'), function() { + if ($(this).attr('checked')) + $(this).parent().find('.delivery_option_carrier').show(); + else + $(this).parent().find('.delivery_option_carrier').hide(); + + }); + $('.delivery_option_radio').change(function() { + $(this).parent().parent().find('.delivery_option_carrier').hide(); + $(this).parent().find('.delivery_option_carrier').show(); + }); +}); \ No newline at end of file diff --git a/themes/prestashop/js/order-address.js b/themes/prestashop/js/order-address.js index 7ee141e92..49441f2d3 100644 --- a/themes/prestashop/js/order-address.js +++ b/themes/prestashop/js/order-address.js @@ -60,7 +60,7 @@ function updateAddressesDisplay(first_view) else { $('#address_invoice_form:hidden').show('fast'); - if ($('select#id_address_invoice').val()) + if ($('#id_address_invoice').val()) updateAddressDisplay('invoice'); else { @@ -82,20 +82,23 @@ function updateAddressDisplay(addressType) if (formatedAddressFieldsValuesList.length <= 0) return false; - var idAddress = $('select#id_address_' + addressType + '').val(); + var idAddress = $('#id_address_' + addressType + '').val(); buildAddressBlock(idAddress, addressType, $('#address_'+ addressType)); // change update link var link = $('ul#address_' + addressType + ' li.address_update a').attr('href'); var expression = /id_address=\d+/; - link = link.replace(expression, 'id_address='+idAddress); - $('ul#address_' + addressType + ' li.address_update a').attr('href', link); + if (link) + { + link = link.replace(expression, 'id_address='+idAddress); + $('ul#address_' + addressType + ' li.address_update a').attr('href', link); + } } function updateAddresses() { - var idAddress_delivery = $('select#id_address_delivery').val(); - var idAddress_invoice = $('input[type=checkbox]#addressesAreEquals:checked').length == 1 ? idAddress_delivery : $('select#id_address_invoice').val(); + var idAddress_delivery = $('#id_address_delivery').val(); + var idAddress_invoice = $('input[type=checkbox]#addressesAreEquals:checked').length == 1 ? idAddress_delivery : $('#id_address_invoice').val(); $.ajax({ type: 'GET', diff --git a/themes/prestashop/js/order-opc.js b/themes/prestashop/js/order-opc.js index 12c301c67..eafd1cf79 100755 --- a/themes/prestashop/js/order-opc.js +++ b/themes/prestashop/js/order-opc.js @@ -112,8 +112,8 @@ function updatePaymentMethods(json) function updateAddressSelection() { - var idAddress_delivery = ($('input#opc_id_address_delivery').length == 1 ? $('input#opc_id_address_delivery').val() : $('select#id_address_delivery').val()); - var idAddress_invoice = ($('input#opc_id_address_invoice').length == 1 ? $('input#opc_id_address_invoice').val() : ($('input[type=checkbox]#addressesAreEquals:checked').length == 1 ? idAddress_delivery : ($('select#id_address_invoice').length == 1 ? $('select#id_address_invoice').val() : idAddress_delivery))); + var idAddress_delivery = ($('input#opc_id_address_delivery').length == 1 ? $('input#opc_id_address_delivery').val() : $('#id_address_delivery').val()); + var idAddress_invoice = ($('input#opc_id_address_invoice').length == 1 ? $('input#opc_id_address_invoice').val() : ($('input[type=checkbox]#addressesAreEquals:checked').length == 1 ? idAddress_delivery : ($('#id_address_invoice').length == 1 ? $('select#id_address_invoice').val() : idAddress_delivery))); $('#opc_account-overlay').fadeIn('slow'); $('#opc_delivery_methods-overlay').fadeIn('slow'); diff --git a/themes/prestashop/order-address-multishipping.tpl b/themes/prestashop/order-address-multishipping.tpl new file mode 100644 index 000000000..870bc07ee --- /dev/null +++ b/themes/prestashop/order-address-multishipping.tpl @@ -0,0 +1,289 @@ +{* +* 2007-2011 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2011 PrestaShop SA +* @version Release: $Revision$ +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*} +{* Will be deleted for 1.5 version and more *} +{if !isset($formatedAddressFieldsValuesList)} + {$ignoreList.0 = "id_address"} + {$ignoreList.1 = "id_country"} + {$ignoreList.2 = "id_state"} + {$ignoreList.3 = "id_customer"} + {$ignoreList.4 = "id_manufacturer"} + {$ignoreList.5 = "id_supplier"} + {$ignoreList.6 = "date_add"} + {$ignoreList.7 = "date_upd"} + {$ignoreList.8 = "active"} + {$ignoreList.9 = "deleted"} + + {* PrestaShop 1.4.0.17 compatibility *} + {if isset($addresses)} + {foreach from=$addresses key=k item=address} + {counter start=0 skip=1 assign=address_key_number} + {$id_address = $address.id_address} + {foreach from=$address key=address_key item=address_content} + {if !in_array($address_key, $ignoreList)} + {$formatedAddressFieldsValuesList.$id_address.ordered_fields.$address_key_number = $address_key} + {$formatedAddressFieldsValuesList.$id_address.formated_fields_values.$address_key = $address_content} + {counter} + {/if} + {/foreach} + {/foreach} + {/if} +{/if} + + + +{if !$opc} +{capture name=path}{l s='Addresses'}{/capture} +{include file="$tpl_dir./breadcrumb.tpl"} +{/if} + +{if !$opc}

    {l s='Addresses'}

    {else}

    1. {l s='Addresses'}

    {/if} + +{if !$opc} +{assign var='current_step' value='address'} +{include file="$tpl_dir./order-steps.tpl"} +{include file="$tpl_dir./errors.tpl"} + +

    {l s='Choose the delivery addresses:'}

    + +
    + + + + + + + + + + + + {foreach from=$products item=product name=productLoop} + {assign var='productId' value=$product.id_product} + {assign var='productAttributeId' value=$product.id_product_attribute} + {assign var='quantityDisplayed' value=0} + {* Display the product line *} + {include file="$tpl_dir./order-address-product-line.tpl" productLast=$smarty.foreach.productLoop.last productFirst=$smarty.foreach.productLoop.first} + {* Then the customized datas ones*} + {if isset($customizedDatas.$productId.$productAttributeId)} + {foreach from=$customizedDatas.$productId.$productAttributeId[$product.id_address_delivery] key='id_customization' item='customization'} + + + + + + {assign var='quantityDisplayed' value=$quantityDisplayed+$customization.quantity} + {/foreach} + {* If it exists also some uncustomized products *} + {if $product.quantity-$quantityDisplayed > 0} + {include file="$tpl_dir./order-address-product-line.tpl" productLast=$smarty.foreach.productLoop.last productFirst=$smarty.foreach.productLoop.first} + {/if} + + {/if} + {/foreach} + +
    {l s='Product'}{l s='Description'}{l s='Ref.'}{l s='Qty'}{l s='Shipping address'}
    + {foreach from=$customization.datas key='type' item='datas'} + {if $type == $CUSTOMIZE_FILE} +
    +
      + {foreach from=$datas item='picture'}
    • {/foreach} +
    +
    + {elseif $type == $CUSTOMIZE_TEXTFIELD} +
      + {foreach from=$datas item='textField' name='typedText'}
    • {if $textField.name}{$textField.name}{else}{l s='Text #'}{$smarty.foreach.typedText.index+1}{/if}{l s=':'} {$textField.value}
    • {/foreach} +
    + {/if} + {/foreach} +
    + {if isset($cannotModify) AND $cannotModify == 1} + {if $quantityDisplayed == 0 AND isset($customizedDatas.$productId.$productAttributeId)}{$customizedDatas.$productId.$productAttributeId|@count}{else}{$product.cart_quantity-$quantityDisplayed}{/if} + {else} +
    + {l s='Delete'} +
    +
    + {l s='Add'}
    + {if $product.minimal_quantity < ($customization.quantity -$quantityDisplayed) OR $product.minimal_quantity <= 1} + + {l s='Subtract'} + + {else} + + {l s='Subtract'} + + {/if} +
    + + + {/if} +
    +
    + +
    +{else} +
    + +{/if} + +
    + +

    id_address_invoice == $cart->id_address_delivery}style="display: none;"{/if}> + + {if $addresses|@count > 1} + + + {else} + {if $back} + {l s='Add a new address'} + {else} + {l s='Add a new address'} + {/if} + {/if} +

    +
    +
      +
    +
    +

    + {if $back} + {l s='Add a new address'} + {else} + {l s='Add a new address'} + {/if} +

    + {if !$opc} +
    +

    {l s='If you would like to add a comment about your order, please write it below.'}

    +

    +
    + {/if} +
    +{if !$opc} +

    + + + {if $back} + « {l s='Previous'} + {else} + « {l s='Previous'} + {/if} + +

    + +{else} +
    +{/if} diff --git a/themes/prestashop/order-address-product-line.tpl b/themes/prestashop/order-address-product-line.tpl new file mode 100644 index 000000000..1d52bd7d6 --- /dev/null +++ b/themes/prestashop/order-address-product-line.tpl @@ -0,0 +1,84 @@ +{* +* 2007-2011 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2011 PrestaShop SA +* @version Release: $Revision$ +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*} + + + {$product.name|escape:'htmlall':'UTF-8'} + + +
    {$product.name|escape:'htmlall':'UTF-8'}
    + {if isset($product.attributes) && $product.attributes}{$product.attributes|escape:'htmlall':'UTF-8'}{/if} + + {if $product.reference}{$product.reference|escape:'htmlall':'UTF-8'}{else}--{/if} + + {if isset($cannotModify) AND $cannotModify == 1} + {if $quantityDisplayed == 0 AND isset($customizedDatas.$productId.$productAttributeId)}{$customizedDatas.$productId.$productAttributeId|@count}{else}{$product.cart_quantity-$quantityDisplayed}{/if} + {else} + {if isset($customizedDatas.$productId.$productAttributeId) AND $quantityDisplayed == 0}{$product.customizationQuantityTotal}{/if} + {if !isset($customizedDatas.$productId.$productAttributeId) OR $quantityDisplayed > 0} +
    + {l s='Delete'} +
    +
    + {l s='Add'}
    + {if $product.minimal_quantity < ($product.cart_quantity-$quantityDisplayed) OR $product.minimal_quantity <= 1} + + {l s='Subtract'} + + {else} + + {l s='Subtract'} + + {/if} +
    + + + + {/if} + {/if} + + + + + + + + + \ No newline at end of file diff --git a/themes/prestashop/order-address.tpl b/themes/prestashop/order-address.tpl index 2c985d594..a6ecb0793 100644 --- a/themes/prestashop/order-address.tpl +++ b/themes/prestashop/order-address.tpl @@ -157,6 +157,12 @@ {assign var='current_step' value='address'} {include file="$tpl_dir./order-steps.tpl"} {include file="$tpl_dir./errors.tpl"} + +
    {else}
    diff --git a/themes/prestashop/order-carrier.tpl b/themes/prestashop/order-carrier.tpl index 5a9acf7f6..104c80b32 100644 --- a/themes/prestashop/order-carrier.tpl +++ b/themes/prestashop/order-carrier.tpl @@ -82,7 +82,7 @@ {include file="$tpl_dir./errors.tpl"} - + {else}
    @@ -112,45 +112,83 @@

    {/if} -

    {l s='There are no carriers available that deliver to this address.'}

    - - - - - - - - - - - {if isset($carriers)} - {foreach from=$carriers item=carrier name=myLoop} - - - - - - - {/foreach} - {$HOOK_EXTRACARRIER} - {/if} - -
    {l s='Carrier'}{l s='Information'}{l s='Price'}
    - - - - {$carrier.delay|escape:'htmlall':'UTF-8'} - {if $carrier.price} - - {if $priceDisplay == 1}{convertPrice price=$carrier.price_tax_exc}{else}{convertPrice price=$carrier.price}{/if} - - {if $use_taxes}{if $priceDisplay == 1} {l s='(tax excl.)'}{else} {l s='(tax incl.)'}{/if}{/if} - {else} - {l s='Free!'} - {/if} -
    +
    + {foreach $delivery_option_list as $id_address => $option_list} +

    {$address_collection[$id_address]->alias}

    +
    + {foreach $option_list as $key => $option} +
    + + +
    + {/foreach} +
    + {/foreach} +
    {if $giftAllowed} @@ -182,12 +220,12 @@ {if !$is_guest} {if $back} - « {l s='Previous'} + « {l s='Previous'} {else} - « {l s='Previous'} + « {l s='Previous'} {/if} {else} - « {l s='Previous'} + « {l s='Previous'} {/if}

    diff --git a/themes/prestashop/order-payment.tpl b/themes/prestashop/order-payment.tpl index 7df24f47c..f306eaa4f 100644 --- a/themes/prestashop/order-payment.tpl +++ b/themes/prestashop/order-payment.tpl @@ -207,7 +207,7 @@ {include file="$tpl_dir./shopping-cart-product-line.tpl"} {* Then the customized datas ones*} {if isset($customizedDatas.$productId.$productAttributeId)} - {foreach from=$customizedDatas.$productId.$productAttributeId key='id_customization' item='customization'} + {foreach from=$customizedDatas.$productId.$productAttributeId[$product.id_address_delivery] key='id_customization' item='customization'} {foreach from=$customization.datas key='type' item='datas'} diff --git a/themes/prestashop/order-steps.tpl b/themes/prestashop/order-steps.tpl index f53a301bd..23fee7406 100644 --- a/themes/prestashop/order-steps.tpl +++ b/themes/prestashop/order-steps.tpl @@ -33,7 +33,7 @@
    • {if $current_step=='payment' || $current_step=='shipping' || $current_step=='address' || $current_step=='login'} - + {l s='Summary'} {else} @@ -42,7 +42,7 @@
    • {if $current_step=='payment' || $current_step=='shipping' || $current_step=='address'} - + {l s='Login'} {else} @@ -51,7 +51,7 @@
    • {if $current_step=='payment' || $current_step=='shipping'} - + {l s='Address'} {else} @@ -60,7 +60,7 @@
    • {if $current_step=='payment'} - + {l s='Shipping'} {else} diff --git a/themes/prestashop/shopping-cart-product-line.tpl b/themes/prestashop/shopping-cart-product-line.tpl index 87ade0f29..004c07aca 100644 --- a/themes/prestashop/shopping-cart-product-line.tpl +++ b/themes/prestashop/shopping-cart-product-line.tpl @@ -24,7 +24,7 @@ * International Registered Trademark & Property of PrestaShop SA *} - + {$product.name|escape:'htmlall':'UTF-8'} @@ -41,7 +41,7 @@ {/if} - + {if isset($product.is_discounted) && $product.is_discounted} {convertPrice price=$product.price_without_specific_price}
      {/if} @@ -56,31 +56,31 @@ {if isset($cannotModify) AND $cannotModify == 1} {if $quantityDisplayed == 0 AND isset($customizedDatas.$productId.$productAttributeId)}{$customizedDatas.$productId.$productAttributeId|@count}{else}{$product.cart_quantity-$quantityDisplayed}{/if} {else} - {if isset($customizedDatas.$productId.$productAttributeId) AND $quantityDisplayed == 0}{$product.customizationQuantityTotal}{/if} + {if isset($customizedDatas.$productId.$productAttributeId) AND $quantityDisplayed == 0}{$product.customizationQuantityTotal}{/if} {if !isset($customizedDatas.$productId.$productAttributeId) OR $quantityDisplayed > 0}
      - {l s='Delete'} + {l s='Delete'}
      - {l s='Add'}
      + {l s='Add'}
      {if $product.minimal_quantity < ($product.cart_quantity-$quantityDisplayed) OR $product.minimal_quantity <= 1} - + {l s='Subtract'} {else} - + {l s='Subtract'} {/if}
      - - + + - {/if} + {/if} {/if} - + {if $quantityDisplayed == 0 AND isset($customizedDatas.$productId.$productAttributeId)} {if !$priceDisplay}{displayPrice price=$product.total_customization_wt}{else}{displayPrice price=$product.total_customization}{/if} {else} diff --git a/themes/prestashop/shopping-cart.tpl b/themes/prestashop/shopping-cart.tpl index ca16bd660..084f1f538 100644 --- a/themes/prestashop/shopping-cart.tpl +++ b/themes/prestashop/shopping-cart.tpl @@ -55,21 +55,17 @@ {if isset($lastProductAdded) AND $lastProductAdded} - {foreach from=$products item=product} - {if $product.id_product == $lastProductAdded.id_product AND (!$product.id_product_attribute OR ($product.id_product_attribute == $lastProductAdded.id_product_attribute))} -
      -
      -
      {l s='Last added product'}
      -
      - {$product.name|escape:'htmlall':'UTF-8'} -
      -
      {$product.name|escape:'htmlall':'UTF-8'}
      - {if isset($product.attributes) && $product.attributes}{$product.attributes|escape:'htmlall':'UTF-8'}{/if} -
      -
      -
      - {/if} - {/foreach} +
      +
      +
      {l s='Last added product'}
      +
      + {$lastProductAdded.name|escape:'htmlall':'UTF-8'} +
      +
      {$lastProductAdded.name|escape:'htmlall':'UTF-8'}
      + {if isset($lastProductAdded.attributes) && $lastProductAdded.attributes}{$lastProductAdded.attributes|escape:'htmlall':'UTF-8'}{/if} +
      +
      +
      {/if}

      {l s='Your shopping cart contains'} {$productNumber} {if $productNumber == 1}{l s='product'}{else}{l s='products'}{/if}

      @@ -219,24 +215,36 @@ {assign var='productAttributeId' value=$product.id_product_attribute} {assign var='quantityDisplayed' value=0} {* Display the product line *} - {include file="$tpl_dir./shopping-cart-product-line.tpl"} + {include file="$tpl_dir/shopping-cart-product-line.tpl" productLast=$smarty.foreach.productLoop.last productFirst=$smarty.foreach.productLoop.first} {* Then the customized datas ones*} {if isset($customizedDatas.$productId.$productAttributeId)} - {foreach from=$customizedDatas.$productId.$productAttributeId key='id_customization' item='customization'} - + {foreach from=$customizedDatas.$productId.$productAttributeId[$product.id_address_delivery] key=id_customization item=customization} + - {foreach from=$customization.datas key='type' item='datas'} + {foreach from=$customization.datas key=type item=custom_data} + {if $type == $CUSTOMIZE_FILE}
        - {foreach from=$datas item='picture'}
      • {/foreach} + {foreach from=$datas item=picture}
      • {/foreach}
      {elseif $type == $CUSTOMIZE_TEXTFIELD}
        - {foreach from=$datas item='textField' name='typedText'}
      • {if $textField.name}{$textField.name}{else}{l s='Text #'}{$smarty.foreach.typedText.index+1}{/if}{l s=':'} {$textField.value}
      • {/foreach} + {foreach from=$custom_data item=textField name=typedText} +
      • + {if $textField.name} + {$textField.name} + {else} + {l s='Text #'}{$smarty.foreach.typedText.index+1} + {/if} + {l s=':'} {$textField.value} +
      • + {/foreach} +
      {/if} + {/foreach} @@ -244,22 +252,22 @@ {if $quantityDisplayed == 0 AND isset($customizedDatas.$productId.$productAttributeId)}{$customizedDatas.$productId.$productAttributeId|@count}{else}{$product.cart_quantity-$quantityDisplayed}{/if} {else}
      - {l s='Delete'} + {l s='Delete'}
      - {l s='Add'}
      + {l s='Add'}
      {if $product.minimal_quantity < ($customization.quantity -$quantityDisplayed) OR $product.minimal_quantity <= 1} - + {l s='Subtract'} {else} - + {l s='Subtract'} {/if}
      - - + + {/if} @@ -267,7 +275,7 @@ {assign var='quantityDisplayed' value=$quantityDisplayed+$customization.quantity} {/foreach} {* If it exists also some uncustomized products *} - {if $product.quantity-$quantityDisplayed > 0}{include file="$tpl_dir./shopping-cart-product-line.tpl"}{/if} + {if $product.quantity-$quantityDisplayed > 0}{include file="$tpl_dir./shopping-cart-product-line.tpl" productLast=$smarty.foreach.productLoop.last productFirst=$smarty.foreach.productLoop.first}{/if} {/if} {/foreach} @@ -395,7 +403,10 @@
      {/if}

      - {if !$opc}{l s='Next'} »{/if} + {if !$opc} + {l s='Next'} » + {l s='Multi-shipping'} » + {/if} « {l s='Continue shopping'}