Szybki import stanów magazynowych w Magento

Często zachodzi potrzeba zaimportowania stanów magazynowych z innego systemu. Najczęściej w tym celu wykorzystywane jest API, jednak ma ono jedną podstawową wadę, jest bardzo powolne. Kiedy potrzebujemy w krótkim czasie dokonać zmiany kilku tysięcy produktów, to wykorzystanie API może być nie najlepszym rozwiązaniem z powodu jego bardzo dużej pracochłonności i narzutu na zasoby.

Innym popularnym rozwiązaniem jest napisanie własnego skryptu mającego realizować to zadanie w oparciu o modele Magento.
Poniżej prezentujemy przykładowy skrypt importujący stany magazynowe na podstawie pliku CSV, oczywiście jako źródło wejściowe posłużyć inna baza danych lub API programu magazynowego.

ini_set("display_errors", 1);
ini_set("memory_limit","1024M");
 
require_once 'app/Mage.php';
umask(0);
 
$currentStore = Mage::app()->getStore()->getId();
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
 
$path = getcwd().'/WebStockInput.csv';
$readfile = file ($path);
for ($i=1; $i < count($readfile); $i++ ) {
    $fields = split("\t",$readfile[$i]);
     
    $product = Mage::getModel('catalog/product');
    $product->load($product->getIdBySku($fields[0]));
    // get product's stock data such quantity, in_stock etc
    $stockData = $product->getStockData();
 
    // update stock data using new data
    $stockData['qty'] = $fields[2];
    $stockData['is_in_stock'] = 1;
 
    // then set product's stock data to update
    $product->setStockData($stockData);
    try {
        $product->save();
    }
    catch (Exception $ex) {
        echo $ex->getMessage();
    }
 
}
 
Mage::app()->setCurrentStore($currentStore);
?>

Niestety i to rozwiązanie nie należy do zbyt szybkich. Dlatego poniżej prezentujemy jak my to robimy. Nasz skrypt pozwala na zmianę stanów kilku tysięcy produktów w mniej niż 120 sekund. Został przetestowany w wielu rozwiązaniach i jak na razie sprawdza się bardzo dobrze. Jeżeli zajdzie potrzeba dostosowania go do współpracy z innymi programami magazynowymi lub źródłami danych zachęcamy do kontaktu z nami.

Konfiguracja
Aby skrypt działał poprawnie należy ustawić zmienną $workdir na folder z plikiem wejściowym o nazwie magento2.csv.
W tym też folderze skrypt zapisze raport z wynikiem swojej pracy.

//Centerkom.pl - najlepsze rozwiązania Magento

$workdir - "/var/www/import/"

function getmicrotime(){list($usec, $sec) = explode(" ",microtime());return ((float)$usec + (float)$sec);}
$time_start = getmicrotime();


$mageFilename = "../app/Mage.php";
require_once $mageFilename;
Mage::setIsDeveloperMode(true);
ini_set("display_errors", 1);
umask(0);
Mage::app("admin");
Mage::register("isSecureArea", 1);
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
 
set_time_limit(0);
ini_set("memory_limit","1024M");
 
/***************** UTILITY FUNCTIONS ********************/
function _getConnection($type = "core_read"){
    return Mage::getSingleton("core/resource")->getConnection($type);
}
 
function _getTableName($tableName){
    return Mage::getSingleton("core/resource")->getTableName($tableName);
}
 
function _getAttributeId($attribute_code = "price"){
    $connection = _getConnection("core_read");
    $sql = "SELECT attribute_id
                FROM " . _getTableName("eav_attribute") . "
            WHERE
                entity_type_id = ?
                AND attribute_code = ?";
    $entity_type_id = _getEntityTypeId();
    return $connection->fetchOne($sql, array($entity_type_id, $attribute_code));
}
 
function _getEntityTypeId($entity_type_code = "catalog_product"){
    $connection = _getConnection("core_read");
    $sql        = "SELECT entity_type_id FROM " . _getTableName("eav_entity_type") . " WHERE entity_type_code = ?";
    return $connection->fetchOne($sql, array($entity_type_code));
}
 
function _checkIfSkuExists($sku){
    $connection = _getConnection("core_read");
    $sql        = "SELECT COUNT(*) AS count_no FROM " . _getTableName("catalog_product_entity") . " WHERE sku = ?";
    $count      = $connection->fetchOne($sql, array($sku));
    if($count > 0){
        return true;
    }else{
        return false;
    }
}
 
function _getIdFromSku($sku){
    $connection = _getConnection("core_read");
    $sql        = "SELECT entity_id FROM " . _getTableName("catalog_product_entity") . " WHERE sku = ?";
    return $connection->fetchOne($sql, array($sku));
}
 
function _updateStocks($data){
    $connection     = _getConnection("core_write");
    $sku            = $data[0];
    $newQty         = $data[1];
    $productId      = _getIdFromSku($sku);
    $attributeId    = _getAttributeId();
 
    $sql            = "UPDATE " . _getTableName("cataloginventory_stock_item") . " csi,
                       " . _getTableName("cataloginventory_stock_status") . " css
                       SET
                       csi.qty = ?,
                       csi.is_in_stock = ?,
                       css.qty = ?,
                       css.stock_status = ?
                       WHERE
                       csi.product_id = ?
                       AND csi.product_id = css.product_id";
    $isInStock      = $newQty > 0 ? 1 : 0;
    $stockStatus    = $newQty > 0 ? 1 : 0;
    $connection->query($sql, array($newQty, $isInStock, $newQty, $stockStatus, $productId));
}
/***************** UTILITY FUNCTIONS ********************/
 
$csv                = new Varien_File_Csv();
$data               = $csv->getData($workdir/"magento2.csv"); //path to csv
array_shift($data);
 
$message = "";

$count   = 1;
$count_success = 0;
foreach($data as $_data){
    if(_checkIfSkuExists($_data[0])){
        try{
            _updateStocks($_data);
            $message .= $count . "> Success:: Qty (" . $_data[1] . ") of Sku (" . $_data[0] . ") has been updated. \n";
            //echo $count . "> Success:: Qty (" . $_data[1] . ") of Sku (" . $_data[0] . ") has been updated. \n";
            $count_success++;
 
        }catch(Exception $e){
            $message .=  $count ."> Error:: while Upating  Qty (" . $_data[1] . ") of Sku (" . $_data[0] . ") => ".$e->getMessage()."\n";
            echo $count ."> Error:: while Upating  Qty (" . $_data[1] . ") of Sku (" . $_data[0] . ") => ".$e->getMessage()."\n";
        }
    }else{
        $message .=  $count ."> Error:: Product with Sku (" . $_data[0] . ") does't exist.\n";
        //echo  $count ."> Error:: Product with Sku (" . $_data[0] . ") does't exist.\n";
    }
    $count++;
}


$fp = fopen($workdir.date("Ymd"), 'w');
$time_end = getmicrotime();
$time = $time_end - $time_start;

fwrite($fp, $message);
fwrite($fp, "\nZmieniono :".$count_success);
fwrite($fp, "\nCzas wygenerowania strony $time s"); 
fwrite($fp, "\nmemory_get_usage ".((memory_get_usage()/1024)/1024));
fwrite($fp, "\nreal_memory_get_usage ".((memory_get_usage(true)/1024)/1024));