diff --git a/admin-dev/themes/default/template/controllers/products/quantities.tpl b/admin-dev/themes/default/template/controllers/products/quantities.tpl index 0ad805a88..47431b65c 100644 --- a/admin-dev/themes/default/template/controllers/products/quantities.tpl +++ b/admin-dev/themes/default/template/controllers/products/quantities.tpl @@ -45,6 +45,8 @@

{l s='You can choose to use the advanced stock management system for this product or not.'}

{l s='You can manually specify the quantities for the product / each product combinations, or choose to automatically determine these quantities based on your stock (if advanced stock management is activated).'}

{l s='In this case, the quantities correspond to the quantitites of the real stock in the warehouses associated to the current shop or current group of shops.'}

+
+

{l s='For packs, if it has products that use the advanced stock management, you have to specify a common warehouse for these products and the pack.'}


{l s='Available quantities for sale'}

@@ -72,8 +74,10 @@ type="checkbox" name="advanced_stock_management" class="advanced_stock_management" id="advanced_stock_management" />

@@ -91,8 +95,10 @@ type="radio" name="depends_on_stock" class="depends_on_stock" id="depends_on_stock_1" value="1"/>

diff --git a/classes/Cart.php b/classes/Cart.php index c5c27bd62..550790196 100644 --- a/classes/Cart.php +++ b/classes/Cart.php @@ -739,7 +739,7 @@ class CartCore extends ObjectModel { if (!$shop) $shop = Context::getContext()->shop; - + if (Context::getContext()->customer->id) { if ($id_address_delivery == 0) // The $id_address_delivery is null, get the default customer address @@ -1388,13 +1388,15 @@ class CartCore extends ObjectModel $product['warehouse_list'] = array(); - if ($stock_management_active && (int)$product['advanced_stock_management'] == 1) + if ($stock_management_active && + ((int)$product['advanced_stock_management'] == 1 || Pack::usesAdvancedStockManagement((int)$product['id_product']))) { $warehouse_list = Warehouse::getProductWarehouseList($product['id_product'], $product['id_product_attribute'], $this->id_shop); if (count($warehouse_list) == 0) $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(); @@ -1407,7 +1409,7 @@ class CartCore extends ObjectModel true ); - if ($product_real_quantities > 0) + if ($product_real_quantities > 0 || Pack::isPack((int)$product['id_product'])) $warehouse_in_stock[] = $warehouse; } @@ -1459,9 +1461,9 @@ class CartCore extends ObjectModel foreach ($warehouse_count_by_address[$product['id_address_delivery']] as $id_warehouse => $val) if (in_array((int)$id_warehouse, $product['warehouse_list'])) break; - + $id_warehouse = (int)$id_warehouse; - + 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(); @@ -1575,7 +1577,7 @@ class CartCore extends ObjectModel } } - // Step 5 : Reduce deep of $package_list + // Step 5 : Reduce depth of $package_list $final_package_list = array(); foreach ($package_list as $id_address_delivery => $products_in_stock_list) { @@ -1585,11 +1587,13 @@ class CartCore extends ObjectModel 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, ); + } } $cache = $final_package_list; @@ -1951,7 +1955,7 @@ class CartCore extends ObjectModel } $delivery_option_list = $this->getDeliveryOptionList(null , true); - + foreach ($delivery_option_list as $id_address => $options) if (!isset($delivery_option[$id_address])) foreach ($options as $key => $option) @@ -2895,9 +2899,9 @@ class CartCore extends ObjectModel $sql->where('id_address_delivery = '.(int)$id_address_delivery); $sql->where('id_cart = '.(int)$this->id); $duplicatedQuantity = Db::getInstance()->getValue($sql); - + if ($duplicatedQuantity > $quantity) { - + $sql = 'UPDATE '._DB_PREFIX_.'cart_product SET `quantity` = `quantity` - '.(int)$quantity.' WHERE id_cart = '.(int)$this->id.' @@ -2955,7 +2959,7 @@ class CartCore extends ObjectModel AND id_address_delivery = '.(int)$new_id_address_delivery; Db::getInstance()->execute($sql); } - + return true; } @@ -3032,10 +3036,10 @@ class CartCore extends ObjectModel $id_address_delivery = (int)$this->id_address_delivery; else $id_address_delivery = (int)Address::getFirstCustomerAddressId(Context::getContext()->customer->id); - + if (!$id_address_delivery) return; - + // Update $sql = 'UPDATE `'._DB_PREFIX_.'cart_product` SET `id_address_delivery` = '.(int)$id_address_delivery.' @@ -3048,7 +3052,7 @@ class CartCore extends ObjectModel SET `id_address_delivery` = '.(int)$id_address_delivery.' WHERE `id_cart` = '.(int)$this->id.' AND (`id_address_delivery` = 0 OR `id_address_delivery` IS NULL)'; - + Db::getInstance()->execute($sql); } diff --git a/classes/Pack.php b/classes/Pack.php index 25989122a..43cc37165 100644 --- a/classes/Pack.php +++ b/classes/Pack.php @@ -228,5 +228,27 @@ class PackCore extends Product FROM `'._DB_PREFIX_.'pack` '); } + + /** + * For a given pack, tells if it has at least one product using the advanced stock management + * + * @param int $id_product id_pack + * @return bool + */ + public static function usesAdvancedStockManagement($id_product) + { + if (!Pack::isPack($id_product)) + return false; + + $products = self::getItems($id_product, Configuration::get('PS_LANG_DEFAULT')); + foreach ($products as $product) + { + // if one product uses the advanced stock management + if ($product->advanced_stock_management == 1) + return true; + } + // not used + return false; + } } diff --git a/classes/order/OrderHistory.php b/classes/order/OrderHistory.php index 656923132..a66ee1f35 100644 --- a/classes/order/OrderHistory.php +++ b/classes/order/OrderHistory.php @@ -95,8 +95,6 @@ class OrderHistoryCore extends ObjectModel && $oldOrderStatus->logable)) { ProductSale::addProductSale($product['product_id'], $product['product_quantity']); - if ($oldOrderStatus->id == Configuration::get('PS_OS_ERROR') || $oldOrderStatus->id == Configuration::get('PS_OS_CANCELED')) - StockAvailable::updateQuantity($product['product_id'], $product['product_attribute_id'], -(int)$product['product_quantity'], $order->id_shop); } /* If becoming unlogable => removing sale */ else if (!$newOS->logable @@ -124,7 +122,7 @@ class OrderHistoryCore extends ObjectModel && $oldOrderStatus instanceof OrderState && $oldOrderStatus->shipped == 0 && Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') - && (int)$product['advanced_stock_management'] == 1) + && ((int)$product['advanced_stock_management'] == 1 || Pack::usesAdvancedStockManagement($product['product_id']))) { $manager = StockManagerFactory::getManager(); $warehouse = new Warehouse($product['id_warehouse']); @@ -143,27 +141,54 @@ class OrderHistoryCore extends ObjectModel StockAvailable::synchronize($product['product_id']); else StockAvailable::updateQuantity($product['product_id'], $product['product_attribute_id'], -(int)$product['product_quantity'], $order->id_shop); + //@TODO Synchronize for products in pack if needed ?! } else if ($newOS->shipped == 0 && $oldOrderStatus instanceof OrderState && $oldOrderStatus->shipped == 1 && Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') - && (int)$product['advanced_stock_management'] == 1 - ) + && ((int)$product['advanced_stock_management'] == 1 || Pack::usesAdvancedStockManagement($product['product_id']))) { $manager = StockManagerFactory::getManager(); - $mvts = StockMvt::getNegativeStockMvts($order->id, $product['product_id'], $product['product_attribute_id'], $product['product_quantity']); - foreach ($mvts as $mvt) + if (Pack::isPack($product['product_id'])) { - $manager->addProduct( - $product['product_id'], - $product['product_attribute_id'], - new Warehouse($mvt['id_warehouse']), - $mvt['physical_quantity'], - null, - $mvt['price_te'], - true - ); + $pack_products = Pack::getItems($product['product_id'], Configuration::get('PS_LANG_DEFAULT')); + foreach ($pack_products as $pack_product) + { + if ($pack_product->advanced_stock_management == 1) + { + $mvts = StockMvt::getNegativeStockMvts($order->id, $pack_product->id, 0, $pack_product->pack_quantity * $product['product_quantity']); + foreach ($mvts as $mvt) + { + $manager->addProduct( + $pack_product->id, + 0, + new Warehouse($mvt['id_warehouse']), + $mvt['physical_quantity'], + null, + $mvt['price_te'], + true + ); + } + } + } + } + else + { + + $mvts = StockMvt::getNegativeStockMvts($order->id, $product['product_id'], $product['product_attribute_id'], $product['product_quantity']); + foreach ($mvts as $mvt) + { + $manager->addProduct( + $product['product_id'], + $product['product_attribute_id'], + new Warehouse($mvt['id_warehouse']), + $mvt['physical_quantity'], + null, + $mvt['price_te'], + true + ); + } } if (StockAvailable::dependsOnStock($product['product_id'], $order->id_shop)) diff --git a/classes/stock/StockManager.php b/classes/stock/StockManager.php index 3e1808884..484d03043 100644 --- a/classes/stock/StockManager.php +++ b/classes/stock/StockManager.php @@ -207,12 +207,14 @@ class StockManagerCore implements StockManagerInterface // Special case of a pack if (Pack::isPack($id_product)) { + // Gets items $products_pack = Pack::getItems($id_product, (int)Configuration::get('PS_LANG_DEFAULT')); + // Foreach item foreach ($products_pack as $product_pack) { - //@TODO is there a better way to retrieve the product attribute assciated to the pack ? - $pack_id_product_attribute = Product::getDefaultAttribute($id_product_attribute, 1); - $this->removeProduct($product_pack->id, $pack_id_product_attribute, $product_pack->pack_quantity * $quantity, $warehouse, $id_order); + //TODO $pack_id_product_attribute = Product::getDefaultAttribute($id_product_attribute, 1); + if ($product_pack->advanced_stock_management == 1) + $this->removeProduct($product_pack->id, 0, $warehouse, $product_pack->pack_quantity * $quantity, $id_stock_mvt_reason, $is_usable, $id_order); } } else @@ -633,4 +635,5 @@ class StockManagerCore implements StockManagerInterface return $stocks; } + } \ No newline at end of file diff --git a/classes/stock/Warehouse.php b/classes/stock/Warehouse.php index ad4ffee2a..e48c162e5 100644 --- a/classes/stock/Warehouse.php +++ b/classes/stock/Warehouse.php @@ -309,6 +309,16 @@ class WarehouseCore extends ObjectModel if (is_null($id_shop)) $id_shop = Context::getContext()->shop->getID(true); + // if it's a pack, returns warehouses if and only if some products use the advanced stock management + if (Pack::isPack($id_product)) + { + $warehouses = self::getPackWarehouses($id_product); + $res = array(); + foreach ($warehouses as $warehouse) + $res[]['id_warehouse'] = $warehouse; + return $res; + } + $query = new DbQuery(); $query->select('wpl.id_warehouse, CONCAT(w.reference, " - ", w.name) as name'); $query->from('warehouse_product_location', 'wpl'); @@ -475,6 +485,50 @@ class WarehouseCore extends ObjectModel return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($query); } + /** + * For a given pack, returns the warehouse it can be shipped from + * + * @param int $id_product + * @return int|bool id_warehouse or false + */ + public static function getPackWarehouses($id_product, $id_shop = null) + { + if (!Pack::isPack($id_product)) + return false; + + if (is_null($id_shop)) + $id_shop = Context::getContext()->shop->getID(true); + + // warehouses of the pack + $pack_warehouses = WarehouseProductLocation::getCollection((int)$id_product); + // products in the pack + $products = Pack::getItems((int)$id_product, Configuration::get('PS_LANG_DEFAULT')); + + // array with all warehouses id to check + $list = array(); + + // fills $list + foreach ($pack_warehouses as $pack_warehouse) + $list['pack_warehouses'][] = (int)$pack_warehouse->id_warehouse; + + // for each products in the pack + foreach ($products as $product) + { + if ($product->advanced_stock_management) + { + // gets the warehouses of one product + $product_warehouses = Warehouse::getProductWarehouseList((int)$product->id, 0, (int)$id_shop); + $list[(int)$product->id] = array(); + // fills array with warehouses for this product + foreach ($product_warehouses as $product_warehouse) + $list[(int)$product->id][] = $product_warehouse['id_warehouse']; + } + } + + // returns final list + return call_user_func_array('array_intersect', $list); + } + /*********************************\ * * Webservices Specific Methods diff --git a/classes/stock/WarehouseProductLocation.php b/classes/stock/WarehouseProductLocation.php index cebc739bf..1f8205138 100644 --- a/classes/stock/WarehouseProductLocation.php +++ b/classes/stock/WarehouseProductLocation.php @@ -129,7 +129,7 @@ class WarehouseProductLocationCore extends ObjectModel public static function getCollection($id_product) { $collection = new Collection('WarehouseProductLocation'); - $collection->where('id_product', '=', $id_product); + $collection->where('id_product', '=', (int)$id_product); return $collection; } } \ No newline at end of file diff --git a/controllers/admin/AdminProductsController.php b/controllers/admin/AdminProductsController.php index 0ed0a679f..2cb606fba 100644 --- a/controllers/admin/AdminProductsController.php +++ b/controllers/admin/AdminProductsController.php @@ -3499,7 +3499,8 @@ class AdminProductsControllerCore extends AdminController $show_quantities = false; } // if we are in shop context - else { + else + { // if quantities are shared between shops of the group, it's not possible to manage them for a given shop if ($group_shop->share_stock) $show_quantities = false; @@ -3549,8 +3550,10 @@ class AdminProductsControllerCore extends AdminController // gets an array of quantities (quantity for the product / quantity in pack) $pack_quantities = array(); foreach ($items as $item) + { if (!$item->isAvailableWhenOutOfStock((int)$item->out_of_stock)) $pack_quantities[] = Product::getQuantity($item->id) / ($item->pack_quantity !== 0 ? $item->pack_quantity : 1); + } // gets the minimum $pack_quantity = $pack_quantities[0]; @@ -3559,6 +3562,9 @@ class AdminProductsControllerCore extends AdminController if ($pack_quantity > $value) $pack_quantity = $value; } + + if (!Warehouse::getPackWarehouses((int)$obj->id)) + $this->displayWarning($this->l('You have to have a common warehouse between this pack and its products because .')); } $data->assign(array(