.. _stock-management:

Stock Management
================

All stock management must be done through the relevant services.

Reading Stock Information
-------------------------

Stock quantities for individual products can be queried using
``pickware.erp.article_detail_stock_info_provider_service``, which conforms to the
:php:interface:`erp:Shopware\\Plugins\\ViisonPickwareERP\\Components\\StockLedger\\ArticleDetailStockInfoProvider`
interface.

.. code-block:: php

    $articleDetail = $container->get('models')->getRepository(ArticleDetail::class)->findOneBy([
        'number' => 'SW10107',
    ]);
    $defaultWarehouse = $container->get('models')->getRepository(Warehouse::class)->getDefaultWarehouse();

    $stockInfoProvider = $container->get('pickware.erp.article_detail_stock_info_provider_service');
    $stockInWarehouse = $stockInfoProvider->getStockInWarehouse($articleDetail, $defaultWarehouse);
    $totalPhysicalStock = $stockInfoProvider->getTotalPhysicalStock($articleDetail);
    $totalPhysicalStockForSale = $stockInfoProvider->getTotalPhysicalStockForSale($articleDetail);

    echo 'Stock for SW10107 in default warehouse: ' . $stockInWarehouse . "\n";
    echo 'Total physical stock for SW10107: ' . $totalPhysicalStock . "\n";
    echo 'Total physical stock for sale for SW10107: ' . $totalPhysicalStockForSale . "\n";

In addition to the stock quantities for a product, the service can also be used to query whether a product is
stock-managed, whether it is initialized and to fetch the product's latest stock ledger entry.

Performing Stock Initialization for a Newly-Created Product
-----------------------------------------------------------

After creating a product, it needs to be stock-initialized, which entails writing the products initial stock into the
default warehouse. This can be achieved using ``pickware.erp.stock_initialization_service``, which conforms to the
:php:interface:`erp:Shopware\\Plugins\\ViisonPickwareERP\\Components\\StockLedger\\StockInitialization` interface.

.. code-block:: php

    // Create, persist and flush the Article\Detail and its Article first
    $articleDetail = ...

    // Perform stock initialization for the Article\Detail
    $stockInitialization = $container->get('pickware.erp.stock_initialization_service');
    $stockInitialization->initializeStocksOfArticleDetails([$articleDetail]);

Please note that the initialization call will throw if the product is already stock-initialized.

Controlling Whether a Product's Stock Should Be Managed by Shopware ERP
-----------------------------------------------------------------------

For some products, it might make sense to disable stock management. This includes virtual products for which the concept
of stock makes no sense, such as gift vouchers and digital downloads, products such as sets, for which stock is a
derived quantity, but also products whose stock is managed outside of Shopware ERP. A typical example of the latter are
drop-shipped products, for which the available stock is synchronized into Shopware via its REST API. In this scenario,
disabling stock management will prevent unnecessary stock ledger entries from being written during the synchronization
process. The stock management status of a product can be changed through ``pickware.erp.stock_ledger_service``
(:php:class:`erp:Shopware\\Plugins\\ViisonPickwareERP\\Components\\StockLedger\\StockLedgerService`).

.. code-block:: php

    $stockLedger = $container->get('pickware.erp.stock_ledger_service');

    // To disable stock management
    $stockLedger->stopRecordingStockChangesForArticleDetail($articleDetail);

    // To enable stock management
    $stockLedger->startRecordingStockChangesForArticleDetail($articleDetail);

Changing the Physical Stock of a Product
----------------------------------------

A product's physical stock can be changed by using ``pickware.erp.stock_change_list_factory_service`` (see
:php:interface:`erp:Shopware\\Plugins\\ViisonPickwareERP\\Components\\StockLedger\\StockChangeList\\StockChangeListFactory`)
to create a list of stock changes (see
:php:class:`erp:Shopware\\Plugins\\ViisonPickwareERP\\Components\\StockLedger\\StockChangeList\\AbstractStockChangeList`),
which can can then be passed into one of the stock recording methods of ``pickware.erp.stock_ledger_service`` (see
:php:class:`erp:Shopware\\Plugins\\ViisonPickwareERP\\Components\\StockLedger\\StockLedgerService`).

.. _picking-order:

Since stock for an individual product can reside on multiple bin locations within the same warehouse, removing a certain
amount of stock may mean that stock has to be reduced on multiple bin locations, too. So for example, given a product
which has 4 pieces on its default bin location A-3, 3 pieces on bin location F-11 and 24 pieces of stock on bin location
D-15, removing 10 pieces of stock might cause all stock to be removed from bin locations A-3 and F-11, with the stock
on D-15 being reduced by the remaining 3 pieces to 21. Which amount of stock should be removed from each bin location is
encoded in the stock change lists which can be created using the ``StockChangeListFactory``. The code for this use case
looks like this:

.. code-block:: php

    // Fetch the Article\Detail and the Warehouse
    $articleDetail = $container->get('models')->getRepository(ArticleDetail::class)->findOneBy([
        'number' => 'SW10107',
    ]);
    $secondWarehouse = $container->get('models')->getRepository(Warehouse::class)->findOneBy([
        'code' => 'L2',
    ]);

    // Create a stock change list for removing 10 pieces of stock of SW10107 from the L2 Warehouse
    $stockChangeListFactory = $container->get('pickware.erp.stock_change_list_factory_service');
    $stockChanges = $stockChangeListFactory->createStockChangeList($secondWarehouse, $articleDetail, -10);

    // Actually perform the stock changes by recording them in the stock ledger
    $stockLedgerService = $container->get('pickware.erp.stock_ledger_service');
    $stockLedgerService->recordOutgoingStock($articleDetail, $stockChanges, 'Custom PHP integration');

Recording stock changes like this makes sense when the bin locations from which stock was actually removed are unknown.
In other cases where the bin location is actually known, it is possible to create a stock change list which reflects
this:

.. code-block:: php

    // Fetch the Article\Detail and the BinLocation
    $articleDetail = $container->get('models')->getRepository(ArticleDetail::class)->findOneBy([
        'number' => 'SW10107',
    ]);
    $secondWarehouse = $container->get('models')->getRepository(Warehouse::class)->findOneBy([
        'code' => 'L2',
    ]);
    $binLocation = $container->get('models')->getRepository(BinLocation::class)->findOneBy([
        'warehouse' => $secondWarehouse,
        'code' => 'A2',
    ]);
    $stockChangeListFactory = $container->get('pickware.erp.stock_change_list_factory_service');
    $stockChanges = $stockChangeListFactory->createSingleBinLocationStockChangeList($binLocation, -10);

    $stockLedgerService = $container->get('pickware.erp.stock_ledger_service');
    $stockLedgerService->recordOutgoingStock($articleDetail, $stockChanges, 'Custom PHP integration');

Note that if there's not enough stock on the given bin location, the remaining stock will be removed from the null bin
location for that warehouse.
