Cursos Magento

Integração/atualização de estoque no Magento 2

,

Publicado em 25 de setembro de 2018

A mais comum de todas as integrações pedidas por nossos clientes é a integração de estoque. Por isso, neste rápido tutorial mostrarei como fazer uma integração ativa de estoque, ou seja, onde o Magento fará a consulta a um webservice de terceiros e atualizará o estoque dos produtos da sua loja periodicamente usando tarefas agendadas (cron).

Conhecendo a Interface de estoque

Integração Estoque

Integração e atualização de estoque no Magento 2

A classe ou interface responsável por lidar com o inventário de nossa loja é a \Magento\CatalogInventory\Api\StockRegistryInterface. Sendo assim, é ela que vamos usar para obter e alterar informações de estoque dos produtos de nossa loja.

Criando nosso módulo de integração

1. Módulo base

Crie um módulo padrão Magento 2. Se não sabe como fazê-lo, veja este artigo, ou se inscreva no curso de Magento 2. ♥

2. Criando nossa classe para atualização de estoque

Nossa classe será colocada dentro do nosso módulo, em Cron/InventoryUpdate.php e ela será responsável por atualizar o estoque de todos os nossos produtos da nossa loja Magento.

No nosso exemplo, ela conterá apenas 3 métodos, e já está preparada para resolver um problema comum de consumo de memória que veremos na sequência.

public function __construct(
    StockRegistryInterface $stockRegistry,
    Iterator $iterator,
    ProductCollectionFactory $productCollection,
    LoggerInterface $logger
)
{
    $this->stockRegistry = $stockRegistry;
    $this->iterator = $iterator;
    $this->productCollection = $productCollection;
    $this->logger = $logger;
}

public function execute()
{
    $productCollection = $this->productCollection->create();
    $this->iterator ->walk($productCollection->getSelect(), [[$this,'updateItems']]);
}

public function updateItems($args)
{
    $sku = $args['row']['sku'];
    $currentQty = $this->stockRegistry->getStockItemBySku($sku); //qtd atual no estoque (você pode pular a atualização caso a quantidade não tenha sido alterada)
    $stockItem = $this->stockRegistry->getStockItemBySku($sku);

    //@TODO Buscar quantidade na fonte da integração.
    $newQty = 123;

    $stockItem->setQty($newQty); //Nova quantidade
    $stockItem->setIsInStock(($newQty > 0)); //Em estoque? true ou false

    $this->logger->debug('Atualizando inventário...', ['sku' => $sku, 'qtd anterior' => $currentQty, 'novaQtd'=> $newQty]);
    $this->stockRegistry->updateStockItemBySku($sku, $stockItem);
}

O método updateItems é responsável por atualizar cada produto de nossa loja e gravar no var/log/debug.log qual a quantidade disponível antes e depois da atualização. Idealmente, podemos fazer a integração ficar ainda mais rápida se não atualizarmos os itens que não sofreram atualização. Para isto basta verificar se $currentQty é igual a $newQty e devolver void (return;).

Para o log funcionar corretamente, nossa loja deve estar com os logs habilitados. Você pode habilitá-los com bin/magento config:set dev/debug/debug_logging 1.

Importante notar que não devemos fazer a consulta ao sistema externo (erp, etc) a cada iteração no nosso script. Isso deve ser carregado antes, em outro lugar antes mesmo de entrarmos no nosso método updateItems.

O grande problema: consumo de memória em grandes loops

Um dos maiores problemas que vejo em módulos de integração acontece ao tentar fazer um loop em todos os produtos do estoque do Magento. Seria algo como…

/** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection */
foreach ($productCollection->toArray() as $item) {
  ...
}

Para uma loja com poucos produtos, não teremos muitos problemas. No entanto se a loja tiver algumas dezenas de milhares de produtos, isso logo causará alguma dor de cabeça no consumo de memória.

Esse problema pode ser facilmente contornado com o Iterator nativo do Magento 2, o Magento\Framework\Model\ResourceModel\Iterator. Com ele, podemos alterar a forma como o PHP fará o loop em nossos produtos. O código fica um pouco mais chato e complexo (por isso quase ninguém usa), mas o resultado é ótimo para esse tipo de situação.

Com ele, nós chamamos um outro método para interagir com cada linha do nosso loop. Ficaria assim:

$this->iterator
    ->walk($productCollection->getSelect(), 
    [[$this,'updateItems']]);

E na mesma classe, criamos um método chamado updateItems.
public function updateItems($args)
{
    $sku = $args['row']['sku'];
    $currentQty = $this->stockRegistry->getStockItemBySku($sku); //qtd atual no estoque (você pode pular a atualização caso a quantidade não tenha sido alterada)
    $stockItem = $this->stockRegistry->getStockItemBySku($sku);
    $stockItem->setQty(123); //Nova quantidade
    $stockItem->setIsInStock(true); //Em estoque?
    $this->stockRegistry->updateStockItemBySku($sku, $stockItem);
}

Resultado: em um Magento 2 simples, com sample data (12 mil produtos), ao criar um console command que simplesmente exibe os sku’s de todos os produtos, o Iterator se mostra cerca de 16% mais eficiente no consumo de memória. Em operações mais complexas, envolvendo atualização de estoque e outras inteligências, usar o Iterator pode ser o que vai dizer se sua integração vai falhar por exceder o limite de memória, ou não.

Você poderá encontrar o comparativo de performance com o comando bin/magento magenteiro:inventoryTest e bin/magento magenteiro:inventoryTestBetter no módulo desta aula.

3. Criando a tarefa agendada no Magento 2

Como queremos que nosso cron seja disparado de tempos em tempos, vamos criar o agendador de tarefas no Magento 2. Para isso, crie um arquivo em etc/crontab.xml do nosso módulo.

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
    <group id="default">
        <job name="magenteiro_inventory_integration" instance="Magenteiro\InventoryIntegration\Cron\InventoryUpdate"
        method="execute">
            <!-- Altere isso a gosto. http://crontab-generator.org/ -->
            <schedule>* * * * *</schedule>

            <!--
            exemplos:

            Todo minuto
            <schedule>* * * * *</schedule>

            Todo dia ao meio dia e a meia noite
            <schedule>0 0,12 * * *</schedule>

            De 6 em 6 horas
            <schedule>0 */6 * * *</schedule>

            -->

        </job>
    </group>
</config>

Com isso, nosso cron de estoque será executado a todo minuto, atualizando nosso estoque.

4. Testando nosso script do cron (sem um cron)

Para testar se nosso cron está funcionando, usarei o n98-magerun2 e o comando abaixo:

n98-magerun2 sys:cron:list | grep magenteiro

Com este comando poderemos listar todos os crons do magento e ver se o nosso magenteiro_inventory_integration está presente.

Para executar o nosso cron manualmente no Magento, usamos: n98-magerun2 sys:cron:run magenteiro_inventory_integration.

Note que o caminho do seu n98 e o nome dele em seu sistema pode variar.

Cron de estoque executado manualmente com sucesso

Cron de estoque executado manualmente com sucesso

Conclusão: no ambiente local, levei cerca de 2 minutos para atualizar meus 12 mil produtos. 🙂

 

Download do módulo de integração e aula

A aula  em vídeo sobre este assunto e o módulo inteiro para download está disponível no curso de Magento 2 do Magenteiro. Esta é uma aula bônus adicionada após o lançamento do curso.

Últimos posts por Ricardo Martins (exibir todos)
Comentários

Deixe seu comentário

[fbcomments url="https://www.magenteiro.com/blog/magento-2/integracao-atualizacao-de-estoque-no-magento-2/"]