0

Currently in magento when you manually change the quantity of a stock managed product in the admin there is no reason assigned to this.

So, what I would like to do is add a dropdown field below the qty field in product edit. Then when quantity is changed you choose from here the reason.

I have made a custom module to track stock movement already. Stock Movements Grid

But cannot figure out out how I would go about adding a custom field to the inventory tab and store the reasoning in the message field of my table.

How would I go about amending my existing code to incorporate this into it?

My config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Vish_StockMovements>
            <version>1.1.0</version>
        </Vish_StockMovements>
    </modules>
    <global>
        <resources>
            <vish_stockmovements_setup>
                <setup>
                    <module>Vish_StockMovements</module>
                </setup>
            </vish_stockmovements_setup>
        </resources>
        <blocks>
            <vish_stockmovements>
                <class>Vish_StockMovements_Block</class>
            </vish_stockmovements>
        </blocks>
        <helpers>
            <vish_stockmovements>
                <class>Vish_StockMovements_Helper</class>
            </vish_stockmovements>
        </helpers>
        <models>
            <vish_stockmovements>
                <class>Vish_StockMovements_Model</class>
                <resourceModel>vish_stockmovements_resource</resourceModel>
            </vish_stockmovements>
            <vish_stockmovements_resource>
                <class>Vish_StockMovements_Model_Resource</class>
                <entities>
                    <stock_movement>
                        <table>vish_stock_movement</table>
                    </stock_movement>
                </entities>
            </vish_stockmovements_resource>
            <cataloginventory>
                <rewrite>
                    <stock>Vish_StockMovements_Model_CatalogInventory_Stock</stock>
                </rewrite>
            </cataloginventory>
        </models>
        <events>
            <checkout_submit_all_after>
                <observers>
                    <vish_stockmovements>
                        <class>vish_stockmovements/stock_observer</class>
                        <method>checkoutAllSubmitAfter</method>
                    </vish_stockmovements>
                </observers>
            </checkout_submit_all_after>
            <cataloginventory_stock_item_save_after>
                <observers>
                    <vish_stockmovements>
                        <type>singleton</type>
                        <class>vish_stockmovements/stock_observer</class>
                        <method>saveStockItemAfter</method>
                    </vish_stockmovements>
                </observers>
            </cataloginventory_stock_item_save_after>
            <cataloginventory_stock_revert_products_sale>
                <observers>
                    <vish_stockmovements>
                        <class>vish_stockmovements/stock_observer</class>
                        <method>stockRevertProductsSale</method>
                    </vish_stockmovements>
                </observers>
            </cataloginventory_stock_revert_products_sale>
            <catalog_product_import_finish_before>
                <observers>
                    <vish_stockmovements>
                        <class>vish_stockmovements/stock_observer</class>
                        <method>catalogProductImportFinishBefore</method>
                    </vish_stockmovements>
                </observers>
            </catalog_product_import_finish_before>
            <controller_action_layout_render_before_adminhtml_catalog_product_edit>
                <observers>
                    <vish_stockmovements>
                        <class>vish_stockmovements/stock_observer</class>
                        <method>addStockMovementsTab</method>
                    </vish_stockmovements>
                </observers>
            </controller_action_layout_render_before_adminhtml_catalog_product_edit>
            <sales_order_item_cancel>
                <observers>
                    <inventory><type>disabled</type></inventory>
                    <vish_stockmovements>
                        <class>vish_stockmovements/stock_observer</class>
                        <method>cancelOrderItem</method>
                    </vish_stockmovements>
                </observers>
            </sales_order_item_cancel>
        </events>
    </global>
    <adminhtml>
        <translate>
            <modules>
                <Vish_StockMovements>
                    <files>
                        <default>Vish_StockMovements.csv</default>
                    </files>
                </Vish_StockMovements>
            </modules>
        </translate>
    </adminhtml>
    <admin>
        <routers>
            <adminhtml>
                <args>
                    <modules>
                        <Vish_StockMovements before="Mage_Adminhtml">Vish_StockMovements_Adminhtml</Vish_StockMovements>
                    </modules>
                </args>
            </adminhtml>
        </routers>
    </admin>
</config>

My observer.php

<?php
/**
 * @category    Vish
 * @package     Vish_StockMovements
 * @version     1.0.0
 */
class Vish_StockMovements_Model_Stock_Observer
{
    public function addStockMovementsTab()
    {


        $layout = Mage::getSingleton('core/layout');
        /** @var Mage_Adminhtml_Block_Catalog_Product_Edit_Tabs $block */
        $block = $layout->getBlock('product_tabs');
        $manageStock = $block->getProduct()->getStockItem()->getManageStock();
        if ($block && $block->getProduct() && $block->getProduct()->getTypeId() == 'simple') {
            $block->addTab('stock_movements', array(
                'after' => 'inventory',
                'label' => Mage::helper('vish_stockmovements')->__('Stock Movements'),
                'content' => $layout->createBlock('vish_stockmovements/adminhtml_stock_movement_grid')->toHtml(),
            ));
        }
    }

    public function cancelOrderItem($observer)
    {
        $item = $observer->getEvent()->getItem();

        $children = $item->getChildrenItems();
        $qty = $item->getQtyOrdered() - max($item->getQtyShipped(), $item->getQtyInvoiced()) - $item->getQtyCanceled();
        $manageStock = $item->getProduct()->getStockItem()->getManageStock();
        if ($manageStock && $item->getId() && ($productId = $item->getProductId()) && empty($children) && $qty) {
            Mage::getSingleton('cataloginventory/stock')->backItemQty($productId, $qty);
            $stockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($item->getProductId());
            $this->insertStockMovement($stockItem, sprintf(
                'Product restocked after order cancellation (order: %s)',
                $item->getOrder()->getIncrementId())
            );
        }

        return $this;
    }

    public function catalogProductImportFinishBefore($observer)
    {
        $productIds = array();
        $adapter = $observer->getEvent()->getAdapter();
        $resource = Mage::getResourceModel('vish_stockmovements/stock_movement');

        if ($adapter instanceof Mage_Catalog_Model_Convert_Adapter_Product) {
            $productIds = $adapter->getAffectedEntityIds();
        } else {
            Mage_ImportExport_Model_Import::getDataSourceModel()->getIterator()->rewind();
            $skus = array();
            while ($bunch = $adapter->getNextBunch()) {
                foreach ($bunch as $rowData) {
                    if (null !== $rowData['sku']) {
                        $skus[] = $rowData['sku'];
                    }
                }
            }
            if (!empty($skus)) {
                $productIds = $resource->getProductsIdBySku($skus);
            }
        }

        if (!empty($productIds)) {
            $stock = Mage::getSingleton('cataloginventory/stock');
            $stocks = Mage::getResourceModel('cataloginventory/stock')->getProductsStock($stock, $productIds);
            $stocksMovements = array();
            $datetime = Varien_Date::formatDate(time());
            foreach ($stocks as $stockData) {
                $manageStock = ($stockData['manage_stock'] == 1 ||  $stockData['use_config_manage_stock'] == 1 ? true : false);
                if($manageStock === true){
                $stocksMovements[] = array(
                    'item_id'     => $stockData['item_id'],
                    'user'        => $this->_getUsername(),
                    'user_id'     => $this->_getUserId(),
                    'qty'         => $stockData['qty'],
                    'is_in_stock' => (int) $stockData['is_in_stock'],
                    'message'     => 'Product import',
                    'created_at'  => $datetime,
                );
                }
            }

            if (!empty($stocksMovements)) {
                $resource->insertStocksMovements($stocksMovements);
            }
        }
    }

    public function checkoutAllSubmitAfter($observer)
    {
        if ($observer->getEvent()->hasOrders()) {
            $orders = $observer->getEvent()->getOrders();
        } else {
            $orders = array($observer->getEvent()->getOrder());
        }
        $stockItems = array();
        foreach ($orders as $order) {
            if ($order) {
                foreach ($order->getAllItems() as $orderItem) {
                    /** @var Mage_Sales_Model_Order_Item $orderItem */
                    if ($orderItem->getQtyOrdered() && $orderItem->getProductType() == 'simple') {
                        $stockItem = Mage::getModel('cataloginventory/stock_item')
                            ->loadByProduct($orderItem->getProductId());
                        if($stockItem->getManageStock()){
                            if (!isset($stockItems[$stockItem->getId()])) {
                                $stockItems[$stockItem->getId()] = array(
                                    'item' => $stockItem,
                                    'orders' => array($order->getIncrementId()),
                                );
                            } else {
                                $stockItems[$stockItem->getId()]['orders'][] = $order->getIncrementId();
                            }
                       } 
                    }

                }
            }
        }

        if (!empty($stockItems)) {
            foreach ($stockItems as $data) {
                $this->insertStockMovement($data['item'], sprintf(
                    'Product ordered (order%s: %s)',
                    count($data['orders']) > 1 ? 's' : '',
                    implode(', ', $data['orders'])
                ));
            }
        }
    }

    public function insertStockMovement(Mage_CatalogInventory_Model_Stock_Item $stockItem, $message = '')
    {
        if ($stockItem->getId()) {
            Mage::getModel('vish_stockmovements/stock_movement')
                ->setItemId($stockItem->getId())
                ->setUser($this->_getUsername())
                ->setUserId($this->_getUserId())
                ->setIsAdmin((int) Mage::getSingleton('admin/session')->isLoggedIn())
                ->setQty($stockItem->getQty())
                ->setIsInStock((int) $stockItem->getIsInStock())
                ->setMessage($message)
                ->save();
            Mage::getModel('catalog/product')->load($stockItem->getProductId())->cleanCache();
        }
    }

    public function saveStockItemAfter($observer)
    {

        if ($actionInstance = Mage::app()->getFrontController()->getAction()->getFullActionName() == 'adminhtml_catalog_product_save') {    
               $stockItem = $observer->getEvent()->getItem();
            if (!$stockItem->getStockStatusChangedAutomaticallyFlag() && $stockItem->getOriginalInventoryQty() != $stockItem->getQty()) {
                if (!$message = $stockItem->getSaveMovementMessage()) {
                        if (Mage::getSingleton('api/session')->getSessionId()) {
                            $message = 'Stock saved from Magento API';
                        } else {
                            $message = 'Stock saved manually';
                        }
                    }
                    $this->insertStockMovement($stockItem, $message);
                }           
        }
    }

    public function stockRevertProductsSale($observer)
    {
        $items = $observer->getEvent()->getItems();
        foreach ($items as $productId => $item) {
            $stockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($productId);
            if ($stockItem->getManageStock() && $stockItem->getId()) {
                $message = 'Product restocked';
                if ($creditMemo = Mage::registry('current_creditmemo')) {
                    $message = sprintf(
                        'Product restocked after credit memo creation (credit memo: %s)',
                        $creditMemo->getIncrementId()
                    );
                }
                $this->insertStockMovement($stockItem, $message);
            }
        }
    }

    protected function _getUserId()
    {
        $userId = null;
        if (Mage::getSingleton('customer/session')->isLoggedIn()) {
            $userId = Mage::getSingleton('customer/session')->getCustomerId();
        } elseif (Mage::getSingleton('admin/session')->isLoggedIn()) {
            $userId = Mage::getSingleton('admin/session')->getUser()->getId();
        }

        return $userId;
    }

    protected function _getUsername()
    {
        $username = '-';
        if (Mage::getSingleton('api/session')->isLoggedIn()) {
            $username = Mage::getSingleton('api/session')->getUser()->getUsername();
        } elseif (Mage::getSingleton('customer/session')->isLoggedIn()) {
            $username = Mage::getSingleton('customer/session')->getCustomer()->getName();
        } elseif (Mage::getSingleton('admin/session')->isLoggedIn()) {
            $username = Mage::getSingleton('admin/session')->getUser()->getUsername();
        }

        return $username;
    }
}

I have programmatically added a custom attribute. But, it is being added to a new tab also called 'inventory'. How can I add to the actual inventory tab?

My install/upgrade script:

$installer  = new Mage_Eav_Model_Entity_Setup('core_setup'); 
$installer->startSetup();
$installer->addAttribute('catalog_product', 'stock_reason', array(
            'group'           => 'Inventory',
            'label'           => 'Change Reason',
            'input'           => 'select',
            'type'            => 'int',
            'required'        => 0,
            'visible_on_front'=> 0,
            'filterable'      => 0,
            'searchable'      => 0,
            'comparable'      => 0,
            'user_defined'    => 1,
            'is_configurable' => 0,
            'global'          => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL,
            'note'            => '',
            'option' => 
  array (
    'values' => 
    array (
      0 => 'Incorrect Stock Level',
      1 => 'Received Stock',
      2 => 'Returned Stock',
      3 => 'Customer Cancelled',
      4 => 'Internal Usage',
    ),
  ),
));
$installer->endSetup();

Inventory tab duplication

The inventory tab is not dynamically editable. Thus, I believe correct way is to leave unassinged when adding, then maybe somehow before render add it the tab. This should be attainable via an observer method. Which I have done as follows:

public function addStockChangeReasonSelect()
{


    $layout = Mage::getSingleton('core/layout');
    /** @var Mage_Adminhtml_Block_Catalog_Product_Edit_Tabs $block */
    $block = $layout->getBlock('product_tabs');
    $manageStock = $block->getProduct()->getStockItem()->getManageStock();
    if ($manageStock && $block && $block->getProduct() && $block->getProduct()->getTypeId() == 'simple') {
        //Get Inventory tab and add my input here
    }
}

However, I am not sure of how to target a specific tab.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.