Esempio n. 1
0
 public static function getPacksTable($id_product, $id_lang, $full = false, $limit = NULL)
 {
     $price_sql = Product::getProductPriceSql('p.id_product', 'pp');
     $sql = "\n\t\t SELECT\n                  p.*, pl.*, i.`id_image`, il.`legend`, t.`rate`\n\t\t FROM\n                  `PREFIX_product` p\n\t\t  NATURAL LEFT JOIN `PREFIX_product_lang` pl\n\t\t  LEFT JOIN `PREFIX_image` i ON\n                   i.`id_product` = p.`id_product` AND i.`cover` = 1\n\t\t  LEFT JOIN `PREFIX_image_lang` il ON\n                   i.`id_image` = il.`id_image` AND il.`id_lang` = {$id_lang}\n                  {$price_sql}\n\t\t  LEFT JOIN `PREFIX_tax` t ON\n                   t.`id_tax` = pp.`id_tax`\n\t\t WHERE pl.`id_lang` = {$id_lang}\n\t\t AND p.`id_product` IN (\n\t\t\t SELECT a.`id_product_pack`\n\t\t\t FROM `PREFIX_pack` a\n\t\t\t WHERE a.`id_product_item` = {$id_product})\n\t\t";
     $sql = str_replace('PREFIX_', _DB_PREFIX_, $sql);
     $result = Db::getInstance()->ExecuteS($sql);
     if ($limit) {
         $sql .= ' LIMIT ' . intval($limit);
     }
     $result = Db::getInstance()->ExecuteS($sql);
     if (!$full) {
         return $result;
     }
     $arrayResult = array();
     foreach ($result as $row) {
         if (!Pack::isPacked($row['id_product'])) {
             $arrayResult[] = Product::getProductProperties($id_lang, $row);
         }
     }
     return $arrayResult;
 }
Esempio n. 2
0
    public static function getPacksTable($id_product, $id_lang, $full = false, $limit = null)
    {
        if (!Pack::isFeatureActive()) {
            return array();
        }
        $packs = Db::getInstance()->getValue('
		SELECT GROUP_CONCAT(a.`id_product_pack`)
		FROM `' . _DB_PREFIX_ . 'pack` a
		WHERE a.`id_product_item` = ' . (int) $id_product);
        if (!(int) $packs) {
            return array();
        }
        $context = Context::getContext();
        $sql = '
		SELECT p.*, product_shop.*, pl.*, image_shop.`id_image` id_image, il.`legend`, IFNULL(product_attribute_shop.id_product_attribute, 0) id_product_attribute
		FROM `' . _DB_PREFIX_ . 'product` p
		NATURAL LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl
		' . Shop::addSqlAssociation('product', 'p') . '
		LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_shop` product_attribute_shop
	   		ON (p.`id_product` = product_attribute_shop.`id_product` AND product_attribute_shop.`default_on` = 1 AND product_attribute_shop.id_shop=' . (int) $context->shop->id . ')
		LEFT JOIN `' . _DB_PREFIX_ . 'image_shop` image_shop
			ON (image_shop.`id_product` = p.`id_product` AND image_shop.cover=1 AND image_shop.id_shop=' . (int) $context->shop->id . ')
		LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = ' . (int) $id_lang . ')
		WHERE pl.`id_lang` = ' . (int) $id_lang . '
			' . Shop::addSqlRestrictionOnLang('pl') . '
			AND p.`id_product` IN (' . $packs . ')
		GROUP BY p.id_product';
        if ($limit) {
            $sql .= ' LIMIT ' . (int) $limit;
        }
        $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
        if (!$full) {
            return $result;
        }
        $array_result = array();
        foreach ($result as $row) {
            if (!Pack::isPacked($row['id_product'])) {
                $array_result[] = Product::getProductProperties($id_lang, $row);
            }
        }
        return $array_result;
    }
Esempio n. 3
0
    public static function getPacksTable($id_product, $id_lang, $full = false, $limit = null)
    {
        if (!Pack::isFeatureActive()) {
            return array();
        }
        $packs = Db::getInstance()->getValue('
		SELECT GROUP_CONCAT(a.`id_product_pack`)
		FROM `' . _DB_PREFIX_ . 'pack` a
		WHERE a.`id_product_item` = ' . (int) $id_product);
        if (!(int) $packs) {
            return array();
        }
        $sql = '
		SELECT p.*, product_shop.*, pl.*, image_shop.`id_image`, il.`legend`
		FROM `' . _DB_PREFIX_ . 'product` p
		NATURAL LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl
		' . Shop::addSqlAssociation('product', 'p') . '
		LEFT JOIN `' . _DB_PREFIX_ . 'image` i ON (i.`id_product` = p.`id_product`)' . Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1') . '
		LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = ' . (int) $id_lang . ')
		WHERE pl.`id_lang` = ' . (int) $id_lang . '
			' . Shop::addSqlRestrictionOnLang('pl') . '
			AND p.`id_product` IN (' . $packs . ')
			AND ((image_shop.id_image IS NOT NULL OR i.id_image IS NULL) OR (image_shop.id_image IS NULL AND i.cover=1))';
        if ($limit) {
            $sql .= ' LIMIT ' . (int) $limit;
        }
        $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
        if (!$full) {
            return $result;
        }
        $array_result = array();
        foreach ($result as $row) {
            if (!Pack::isPacked($row['id_product'])) {
                $array_result[] = Product::getProductProperties($id_lang, $row);
            }
        }
        return $array_result;
    }
 /**
  * Is this product in a pack?
  * If $id_product_attribute specified, then will restrict search on the given combination,
  * else this method will match a product if at least one of all its combination is in a pack.
  *
  * @param Product $product
  * @param integer $id_product_attribute Optional combination of the product
  * @return boolean
  */
 public function isPacked($product, $id_product_attribute = false)
 {
     return Pack::isPacked($product->id, $id_product_attribute);
 }
Esempio n. 5
0
 public function initFormInformations($product)
 {
     if (!$this->default_form_language) {
         $this->getLanguages();
     }
     $data = $this->createTemplate($this->tpl_form);
     $currency = $this->context->currency;
     $data->assign('languages', $this->_languages);
     $data->assign('default_form_language', $this->default_form_language);
     $data->assign('currency', $currency);
     $this->object = $product;
     //$this->display = 'edit';
     $data->assign('product_name_redirected', Product::getProductName((int) $product->id_product_redirected, null, (int) $this->context->language->id));
     /*
      * Form for adding a virtual product like software, mp3, etc...
      */
     $product_download = new ProductDownload();
     if ($id_product_download = $product_download->getIdFromIdProduct($this->getFieldValue($product, 'id'))) {
         $product_download = new ProductDownload($id_product_download);
     }
     $product->{'productDownload'} = $product_download;
     $cache_default_attribute = (int) $this->getFieldValue($product, 'cache_default_attribute');
     $product_props = array();
     // global informations
     array_push($product_props, 'reference', 'ean13', 'upc', 'available_for_order', 'show_price', 'online_only', 'id_manufacturer');
     // specific / detailled information
     array_push($product_props, 'width', 'height', 'weight', 'active', 'is_virtual', 'cache_default_attribute', 'uploadable_files', 'text_fields');
     // prices
     array_push($product_props, 'price', 'wholesale_price', 'id_tax_rules_group', 'unit_price_ratio', 'on_sale', 'unity', 'minimum_quantity', 'additional_shipping_cost', 'available_now', 'available_later', 'available_date');
     if (Configuration::get('PS_USE_ECOTAX')) {
         array_push($product_props, 'ecotax');
     }
     foreach ($product_props as $prop) {
         $product->{$prop} = $this->getFieldValue($product, $prop);
     }
     $product->name['class'] = 'updateCurrentText';
     if (!$product->id || Configuration::get('PS_FORCE_FRIENDLY_PRODUCT')) {
         $product->name['class'] .= ' copy2friendlyUrl';
     }
     $images = Image::getImages($this->context->language->id, $product->id);
     if (is_array($images)) {
         foreach ($images as $k => $image) {
             $images[$k]['src'] = $this->context->link->getImageLink($product->link_rewrite[$this->context->language->id], $product->id . '-' . $image['id_image'], 'small_default');
         }
         $data->assign('images', $images);
     }
     $data->assign('imagesTypes', ImageType::getImagesTypes('products'));
     $product->tags = Tag::getProductTags($product->id);
     $data->assign('product_type', (int) Tools::getValue('type_product', $product->getType()));
     $data->assign('is_in_pack', (int) Pack::isPacked($product->id));
     $check_product_association_ajax = false;
     if (Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_ALL) {
         $check_product_association_ajax = true;
     }
     // TinyMCE
     $iso_tiny_mce = $this->context->language->iso_code;
     $iso_tiny_mce = file_exists(_PS_ROOT_DIR_ . '/js/tiny_mce/langs/' . $iso_tiny_mce . '.js') ? $iso_tiny_mce : 'en';
     $data->assign('ad', dirname($_SERVER['PHP_SELF']));
     $data->assign('iso_tiny_mce', $iso_tiny_mce);
     $data->assign('check_product_association_ajax', $check_product_association_ajax);
     $data->assign('id_lang', $this->context->language->id);
     $data->assign('product', $product);
     $data->assign('token', $this->token);
     $data->assign('currency', $currency);
     $data->assign($this->tpl_form_vars);
     $data->assign('link', $this->context->link);
     $data->assign('PS_PRODUCT_SHORT_DESC_LIMIT', Configuration::get('PS_PRODUCT_SHORT_DESC_LIMIT') ? Configuration::get('PS_PRODUCT_SHORT_DESC_LIMIT') : 400);
     $this->tpl_form_vars['product'] = $product;
     $this->tpl_form_vars['custom_form'] = $data->fetch();
 }
Esempio n. 6
0
    public static function getPacksTable($id_product, $id_lang, $full = false, $limit = NULL)
    {
        $packs = Db::getInstance()->getValue('
		SELECT GROUP_CONCAT(a.`id_product_pack`)
		FROM `' . _DB_PREFIX_ . 'pack` a
		WHERE a.`id_product_item` = ' . (int) $id_product);
        if (!(int) $packs) {
            return array();
        }
        $sql = '
		SELECT p.*, pl.*, i.`id_image`, il.`legend`, t.`rate`
		FROM `' . _DB_PREFIX_ . 'product` p
		NATURAL LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl
		LEFT JOIN `' . _DB_PREFIX_ . 'image` i ON (i.`id_product` = p.`id_product` AND i.`cover` = 1)
		LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = ' . (int) $id_lang . ')
		LEFT JOIN `' . _DB_PREFIX_ . 'tax_rule` tr ON (p.`id_tax_rules_group` = tr.`id_tax_rules_group`
		                                           AND tr.`id_country` = ' . (int) Country::getDefaultCountryId() . '
	                                           	   AND tr.`id_state` = 0)
	    LEFT JOIN `' . _DB_PREFIX_ . 'tax` t ON (t.`id_tax` = tr.`id_tax`)
		LEFT JOIN `' . _DB_PREFIX_ . 'tax_lang` tl ON (t.`id_tax` = tl.`id_tax` AND tl.`id_lang` = ' . (int) $id_lang . ')
		WHERE pl.`id_lang` = ' . (int) $id_lang . '
		AND p.`id_product` IN (' . $packs . ')';
        if ($limit) {
            $sql .= ' LIMIT ' . (int) $limit;
        }
        $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS($sql);
        if (!$full) {
            return $result;
        }
        $arrayResult = array();
        foreach ($result as $row) {
            if (!Pack::isPacked($row['id_product'])) {
                $arrayResult[] = Product::getProductProperties($id_lang, $row);
            }
        }
        return $arrayResult;
    }
Esempio n. 7
0
    public static function getPacksTable($id_product, $id_lang, $full = false, $limit = NULL)
    {
        $sql = '
		SELECT p.*, pl.*, i.`id_image`, il.`legend`, t.`rate`
		FROM `' . _DB_PREFIX_ . 'product` p
		NATURAL LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl
		LEFT JOIN `' . _DB_PREFIX_ . 'image` i ON (i.`id_product` = p.`id_product` AND i.`cover` = 1)
		LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = ' . intval($id_lang) . ')
		LEFT JOIN `' . _DB_PREFIX_ . 'tax` t ON (t.`id_tax` = p.`id_tax`)
		WHERE pl.`id_lang` = ' . intval($id_lang) . '
		AND p.`id_product` IN (
			SELECT a.`id_product_pack`
			FROM `' . _DB_PREFIX_ . 'pack` a
			WHERE a.`id_product_item` = ' . intval($id_product) . ')
		';
        if ($limit) {
            $sql .= ' LIMIT ' . intval($limit);
        }
        $result = Db::getInstance()->ExecuteS($sql);
        if (!$full) {
            return $result;
        }
        $arrayResult = array();
        foreach ($result as $row) {
            if (!Pack::isPacked($row['id_product'])) {
                $arrayResult[] = Product::getProductProperties($id_lang, $row);
            }
        }
        return $arrayResult;
    }
Esempio n. 8
0
    /**
     * @see StockManagerInterface::removeProduct()
     *
     * @param int           $id_product
     * @param int|null      $id_product_attribute
     * @param Warehouse     $warehouse
     * @param int           $quantity
     * @param int           $id_stock_mvt_reason
     * @param bool          $is_usable
     * @param int|null      $id_order
     * @param int           $ignore_pack
     * @param Employee|null $employee
     *
     * @return array
     * @throws PrestaShopException
     */
    public function removeProduct($id_product, $id_product_attribute = null, Warehouse $warehouse, $quantity, $id_stock_mvt_reason, $is_usable = true, $id_order = null, $ignore_pack = 0, $employee = null)
    {
        $return = array();
        if (!Validate::isLoadedObject($warehouse) || !$quantity || !$id_product) {
            return $return;
        }
        if (!StockMvtReason::exists($id_stock_mvt_reason)) {
            $id_stock_mvt_reason = Configuration::get('PS_STOCK_MVT_DEC_REASON_DEFAULT');
        }
        $context = Context::getContext();
        // Special case of a pack
        if (Pack::isPack((int) $id_product) && !$ignore_pack) {
            if (Validate::isLoadedObject($product = new Product((int) $id_product))) {
                // Gets items
                if ($product->pack_stock_type == 1 || $product->pack_stock_type == 2 || $product->pack_stock_type == 3 && Configuration::get('PS_PACK_STOCK_TYPE') > 0) {
                    $products_pack = Pack::getItems((int) $id_product, (int) Configuration::get('PS_LANG_DEFAULT'));
                    // Foreach item
                    foreach ($products_pack as $product_pack) {
                        if ($product_pack->advanced_stock_management == 1) {
                            $product_warehouses = Warehouse::getProductWarehouseList($product_pack->id, $product_pack->id_pack_product_attribute);
                            $warehouse_stock_found = false;
                            foreach ($product_warehouses as $product_warehouse) {
                                if (!$warehouse_stock_found) {
                                    if (Warehouse::exists($product_warehouse['id_warehouse'])) {
                                        $current_warehouse = new Warehouse($product_warehouse['id_warehouse']);
                                        $return[] = $this->removeProduct($product_pack->id, $product_pack->id_pack_product_attribute, $current_warehouse, $product_pack->pack_quantity * $quantity, $id_stock_mvt_reason, $is_usable, $id_order);
                                        // The product was found on this warehouse. Stop the stock searching.
                                        $warehouse_stock_found = !empty($return[count($return) - 1]);
                                    }
                                }
                            }
                        }
                    }
                }
                if ($product->pack_stock_type == 0 || $product->pack_stock_type == 2 || $product->pack_stock_type == 3 && (Configuration::get('PS_PACK_STOCK_TYPE') == 0 || Configuration::get('PS_PACK_STOCK_TYPE') == 2)) {
                    $return = array_merge($return, $this->removeProduct($id_product, $id_product_attribute, $warehouse, $quantity, $id_stock_mvt_reason, $is_usable, $id_order, 1));
                }
            } else {
                return false;
            }
        } else {
            // gets total quantities in stock for the current product
            $physical_quantity_in_stock = (int) $this->getProductPhysicalQuantities($id_product, $id_product_attribute, array($warehouse->id), false);
            $usable_quantity_in_stock = (int) $this->getProductPhysicalQuantities($id_product, $id_product_attribute, array($warehouse->id), true);
            // check quantity if we want to decrement unusable quantity
            if (!$is_usable) {
                $quantity_in_stock = $physical_quantity_in_stock - $usable_quantity_in_stock;
            } else {
                $quantity_in_stock = $usable_quantity_in_stock;
            }
            // checks if it's possible to remove the given quantity
            if ($quantity_in_stock < $quantity) {
                return $return;
            }
            $stock_collection = $this->getStockCollection($id_product, $id_product_attribute, $warehouse->id);
            $stock_collection->getAll();
            // check if the collection is loaded
            if (count($stock_collection) <= 0) {
                return $return;
            }
            $stock_history_qty_available = array();
            $mvt_params = array();
            $stock_params = array();
            $quantity_to_decrement_by_stock = array();
            $global_quantity_to_decrement = $quantity;
            // switch on MANAGEMENT_TYPE
            switch ($warehouse->management_type) {
                // case CUMP mode
                case 'WA':
                    /** @var Stock $stock */
                    // There is one and only one stock for a given product in a warehouse in this mode
                    $stock = $stock_collection->current();
                    $mvt_params = array('id_stock' => $stock->id, 'physical_quantity' => $quantity, 'id_stock_mvt_reason' => $id_stock_mvt_reason, 'id_order' => $id_order, 'price_te' => $stock->price_te, 'last_wa' => $stock->price_te, 'current_wa' => $stock->price_te, 'id_employee' => (int) $context->employee->id ? (int) $context->employee->id : $employee->id, 'employee_firstname' => $context->employee->firstname ? $context->employee->firstname : $employee->firstname, 'employee_lastname' => $context->employee->lastname ? $context->employee->lastname : $employee->lastname, 'sign' => -1);
                    $stock_params = array('physical_quantity' => $stock->physical_quantity - $quantity, 'usable_quantity' => $is_usable ? $stock->usable_quantity - $quantity : $stock->usable_quantity);
                    // saves stock in warehouse
                    $stock->hydrate($stock_params);
                    $stock->update();
                    // saves stock mvt
                    $stock_mvt = new StockMvt();
                    $stock_mvt->hydrate($mvt_params);
                    $stock_mvt->save();
                    $return[$stock->id]['quantity'] = $quantity;
                    $return[$stock->id]['price_te'] = $stock->price_te;
                    break;
                case 'LIFO':
                case 'FIFO':
                    // for each stock, parse its mvts history to calculate the quantities left for each positive mvt,
                    // according to the instant available quantities for this stock
                    foreach ($stock_collection as $stock) {
                        /** @var Stock $stock */
                        $left_quantity_to_check = $stock->physical_quantity;
                        if ($left_quantity_to_check <= 0) {
                            continue;
                        }
                        $resource = Db::getInstance(_PS_USE_SQL_SLAVE_)->query('
							SELECT sm.`id_stock_mvt`, sm.`date_add`, sm.`physical_quantity`,
								IF ((sm2.`physical_quantity` is null), sm.`physical_quantity`, (sm.`physical_quantity` - SUM(sm2.`physical_quantity`))) as qty
							FROM `' . _DB_PREFIX_ . 'stock_mvt` sm
							LEFT JOIN `' . _DB_PREFIX_ . 'stock_mvt` sm2 ON sm2.`referer` = sm.`id_stock_mvt`
							WHERE sm.`sign` = 1
							AND sm.`id_stock` = ' . (int) $stock->id . '
							GROUP BY sm.`id_stock_mvt`
							ORDER BY sm.`date_add` DESC');
                        while ($row = Db::getInstance()->nextRow($resource)) {
                            // break - in FIFO mode, we have to retreive the oldest positive mvts for which there are left quantities
                            if ($warehouse->management_type == 'FIFO') {
                                if ($row['qty'] == 0) {
                                    break;
                                }
                            }
                            // converts date to timestamp
                            $date = new DateTime($row['date_add']);
                            $timestamp = $date->format('U');
                            // history of the mvt
                            $stock_history_qty_available[$timestamp] = array('id_stock' => $stock->id, 'id_stock_mvt' => (int) $row['id_stock_mvt'], 'qty' => (int) $row['qty']);
                            // break - in LIFO mode, checks only the necessary history to handle the global quantity for the current stock
                            if ($warehouse->management_type == 'LIFO') {
                                $left_quantity_to_check -= (int) $row['qty'];
                                if ($left_quantity_to_check <= 0) {
                                    break;
                                }
                            }
                        }
                    }
                    if ($warehouse->management_type == 'LIFO') {
                        // orders stock history by timestamp to get newest history first
                        krsort($stock_history_qty_available);
                    } else {
                        // orders stock history by timestamp to get oldest history first
                        ksort($stock_history_qty_available);
                    }
                    // checks each stock to manage the real quantity to decrement for each of them
                    foreach ($stock_history_qty_available as $entry) {
                        if ($entry['qty'] >= $global_quantity_to_decrement) {
                            $quantity_to_decrement_by_stock[$entry['id_stock']][$entry['id_stock_mvt']] = $global_quantity_to_decrement;
                            $global_quantity_to_decrement = 0;
                        } else {
                            $quantity_to_decrement_by_stock[$entry['id_stock']][$entry['id_stock_mvt']] = $entry['qty'];
                            $global_quantity_to_decrement -= $entry['qty'];
                        }
                        if ($global_quantity_to_decrement <= 0) {
                            break;
                        }
                    }
                    // for each stock, decrements it and logs the mvts
                    foreach ($stock_collection as $stock) {
                        if (array_key_exists($stock->id, $quantity_to_decrement_by_stock) && is_array($quantity_to_decrement_by_stock[$stock->id])) {
                            $total_quantity_for_current_stock = 0;
                            foreach ($quantity_to_decrement_by_stock[$stock->id] as $id_mvt_referrer => $qte) {
                                $mvt_params = array('id_stock' => $stock->id, 'physical_quantity' => $qte, 'id_stock_mvt_reason' => $id_stock_mvt_reason, 'id_order' => $id_order, 'price_te' => $stock->price_te, 'sign' => -1, 'referer' => $id_mvt_referrer, 'id_employee' => (int) $context->employee->id ? (int) $context->employee->id : $employee->id);
                                // saves stock mvt
                                $stock_mvt = new StockMvt();
                                $stock_mvt->hydrate($mvt_params);
                                $stock_mvt->save();
                                $total_quantity_for_current_stock += $qte;
                            }
                            $stock_params = array('physical_quantity' => $stock->physical_quantity - $total_quantity_for_current_stock, 'usable_quantity' => $is_usable ? $stock->usable_quantity - $total_quantity_for_current_stock : $stock->usable_quantity);
                            $return[$stock->id]['quantity'] = $total_quantity_for_current_stock;
                            $return[$stock->id]['price_te'] = $stock->price_te;
                            // saves stock in warehouse
                            $stock->hydrate($stock_params);
                            $stock->update();
                        }
                    }
                    break;
            }
            if (Pack::isPacked($id_product, $id_product_attribute)) {
                $packs = Pack::getPacksContainingItem($id_product, $id_product_attribute, (int) Configuration::get('PS_LANG_DEFAULT'));
                foreach ($packs as $pack) {
                    // Decrease stocks of the pack only if pack is in linked stock mode (option called 'Decrement both')
                    if (!((int) $pack->pack_stock_type == 2) && !((int) $pack->pack_stock_type == 3 && (int) Configuration::get('PS_PACK_STOCK_TYPE') == 2)) {
                        continue;
                    }
                    // Decrease stocks of the pack only if there is not enough items to constituate the actual pack stocks.
                    // How many packs can be constituated with the remaining product stocks
                    $quantity_by_pack = $pack->pack_item_quantity;
                    $stock_available_quantity = $quantity_in_stock - $quantity;
                    $max_pack_quantity = max(array(0, floor($stock_available_quantity / $quantity_by_pack)));
                    $quantity_delta = Pack::getQuantity($pack->id) - $max_pack_quantity;
                    if ($pack->advanced_stock_management == 1 && $quantity_delta > 0) {
                        $product_warehouses = Warehouse::getPackWarehouses($pack->id);
                        $warehouse_stock_found = false;
                        foreach ($product_warehouses as $product_warehouse) {
                            if (!$warehouse_stock_found) {
                                if (Warehouse::exists($product_warehouse)) {
                                    $current_warehouse = new Warehouse($product_warehouse);
                                    $return[] = $this->removeProduct($pack->id, null, $current_warehouse, $quantity_delta, $id_stock_mvt_reason, $is_usable, $id_order, 1);
                                    // The product was found on this warehouse. Stop the stock searching.
                                    $warehouse_stock_found = !empty($return[count($return) - 1]);
                                }
                            }
                        }
                    }
                }
            }
        }
        // if we remove a usable quantity, exec hook
        if ($is_usable) {
            Hook::exec('actionProductCoverage', array('id_product' => $id_product, 'id_product_attribute' => $id_product_attribute, 'warehouse' => $warehouse));
        }
        return $return;
    }