Question

I have added sku column on sales order grid using following steps,

Package\Company\view\adminhtml\ui_component\sales_order_grid.xml

<?xml version="1.0" encoding="UTF-8"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <columns name="sales_order_columns">
        <column name="sku" class="Krish\Customerabandoned\Ui\Component\Listing\Column\Sku">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="label" xsi:type="string" translate="true">Sku</item>

                </item>
            </argument>
        </column>
    </columns>
</listing>

Package\Company\Ui\Component\Listing\Column\Sku.php

public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
foreach ($dataSource['data']['items'] as & $item) {

    $order1  = $this->_orderRepository->get($item["entity_id"]);
    $items123 = $order1->getAllItems();
    $skuArray = [];

    foreach ($items123 as $key => $item1) {
        $skuArray[] = $item1->getSku();}
    $export_status = implode (", ", $skuArray);
$item[$this->getData('name')] = $export_status;
}
}

return $dataSource;
}
}

The sku field displaying in order grid but not showing when I export file to csv.

ScreenShot: enter image description here

Was it helpful?

Solution 2

I found solution for this,

First require to add sku column on sales grid as per describe in question, To display also in csv and xml file, we need to override following two files,

  1. /vendor/magento/module-ui/Model/Export/ConvertToCsv.php
  2. /vendor/magento/module-ui/Model/Export/ConvertToXml.php

Now, we need to add custom code on function getCsvFile() as per below,

public function getCsvFile()
{
    $component = $this->filter->getComponent();


    $name = md5(microtime());
    $file = 'export/'. $component->getName() . $name . '.csv';

    $this->filter->prepareComponent($component);
    $this->filter->applySelectionOnTargetProvider();
    $dataProvider = $component->getContext()->getDataProvider();
    //exit(get_class($dataProvider));
    $fields = $this->metadataProvider->getFields($component);

    $options = $this->metadataProvider->getOptions();

    $this->directory->create('export');
    $stream = $this->directory->openFile($file, 'w+');
    $stream->lock();
    $stream->writeCsv($this->metadataProvider->getHeaders($component));
    $i = 1;
    $searchCriteria = $dataProvider->getSearchCriteria()
        ->setCurrentPage($i)
        ->setPageSize($this->pageSize);
    $totalCount = (int) $dataProvider->getSearchResult()->getTotalCount();
    while ($totalCount > 0) {
        $items = $dataProvider->getSearchResult()->getItems();
        //  echo '<pre>'; print_r(get_class($dataProvider)); exit;
        foreach ($items as $item) {
            if($component->getName()=='sales_order_grid') {
                $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
                $order = $objectManager->create('Magento\Sales\Model\Order')->load($item->getEntityId());
                $items123 = $order->getAllItems();
                $skuArray = [];

                foreach ($items123 as $key => $item1) {
                    $skuArray[] = $item1->getSku();

                }
                $export_status = implode(", ", $skuArray);
                $item->setSku($export_status);
            }
            $this->metadataProvider->convertDate($item, $component->getName());
            $stream->writeCsv($this->metadataProvider->getRowData($item, $fields, $options));
        }
        $searchCriteria->setCurrentPage(++$i);
        $totalCount = $totalCount - $this->pageSize;
    }
    $stream->unlock();
    $stream->close();

    return [
        'type' => 'filename',
        'value' => $file,
        'rm' => true  // can delete file after use
    ];
}

OTHER TIPS

To add SKUs Column in order Ui grid. You need to modify below files.

TO add column, add below code in your module

[vendor]/[module]/view/adminhtml/ui_component/sales_order_grid.xml

<?xml version="1.0" encoding="UTF-8"?>
    <listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <columns name="sales_order_columns">
        <column name="skus">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="sortable" xsi:type="boolean">false</item>
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="label" xsi:type="string" translate="true">Skus</item>
                </item>
            </argument>
        </column>
    </columns>
</listing>

[vendor]/[module]/etc/adminhtml/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Sales\Model\ResourceModel\Order\Grid\Collection">
        <plugin name="ordercolumn_grid_load_before" type="[vendor]\[module]\Plugin\Admin\Order\Grid" sortOrder="10" disabled="false"/>
    </type>
</config>

and then Create a plugin file.

[vendor]/[module]/Plugin/Admin/Order/Grid.php

<?php
namespace [vendor]\[module]\Plugin\Admin\Order;

use Magento\Backend\Model\Auth\Session;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Data\Collection\EntityFactoryInterface;
use Magento\Sales\Model\ResourceModel\Order\Grid\Collection;
use Magento\User\Model\ResourceModel\User\Collection as UserCollection;

class Grid extends \Magento\Framework\Data\Collection
{
    protected $coreResource;

    protected $adminUsers;

    public function __construct(
        EntityFactoryInterface $entityFactory,
        ResourceConnection $coreResource,
        UserCollection $adminUsers
    ) {
        parent::__construct($entityFactory);
        $this->coreResource = $coreResource;
        $this->adminUsers = $adminUsers;
    }

    public function beforeLoad($printQuery = false, $logQuery = false)
    {
        if ($printQuery instanceof Collection) {
            $collection = $printQuery;

            $joined_tables = array_keys(
                $collection->getSelect()->getPart('from')
            );

                $collection->getSelect()
                    ->columns(
                        array(
                            'skus' => new \Zend_Db_Expr('(SELECT GROUP_CONCAT(`sku` SEPARATOR " & ") FROM `sales_order_item` WHERE `sales_order_item`.`order_id` = main_table.`entity_id` GROUP BY `sales_order_item`.`order_id`)')
                        )
                    );

        }
    }
}

I think the best solution is to make a preference for \Magento\Sales\Ui\Component\DataProvider\Document and override getCustomAttribute($attributeCode) method

Example file (\Magento\Sales\Ui\Component\DataProvider\Document):

    /**
     * @inheritdoc
     */
    public function getCustomAttribute($attributeCode)
    {
        switch ($attributeCode) {
            case self::$stateAttributeCode:
                $this->setStateValue();
                break;
            case self::$customerGroupAttributeCode:
                $this->setCustomerGroupValue();
                break;
        }

        return parent::getCustomAttribute($attributeCode);
    }

    /**
     * Update invoice state value
     * Method set text label instead id value
     * @return void
     */
    private function setStateValue()
    {
        $value = $this->getData(self::$stateAttributeCode);
        /** @var \Magento\Framework\Phrase $state */
        $state = Invoice::getStates()[$value];

        $this->setCustomAttribute(self::$stateAttributeCode, $state->getText());
    }

    /**
     * Update customer group value
     * Method set group code instead id value
     * @return void
     */
    private function setCustomerGroupValue()
    {
        $value = $this->getData(self::$customerGroupAttributeCode);
        try {
            $group = $this->groupRepository->getById($value);
            $this->setCustomAttribute(self::$customerGroupAttributeCode, $group->getCode());
        } catch (NoSuchEntityException $e) {
            $this->setCustomAttribute(self::$customerGroupAttributeCode, 'N/A');
        }
    }

The answer above from Nirav Patel is really useful, but the actual plugin code is unnecessarily complicated and just dumps in a SQL query directly, rather than using the the available parent methods to manipulate the query.

The idea is that before the order grid table is loaded, its possible to intercept the call with a plugin and modify the database query, joining other tables, and selecting the additional columns you need, which in this case is the SKU.

The class responsible for loading the data rendered in the sales order grid is \Magento\Sales\Model\ResourceModel\Order\Grid\Collection, so we need define our plugin in our modules /etc/di.xml to intercept this class:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Sales\Model\ResourceModel\Order\Grid\Collection">
        <plugin name="[vendor]_[module]_myplugin" type="[Vendor]\[Module]\Plugin\Admin\Order\Grid" sortOrder="10" disabled="false"/>
    </type>
</config>

Then create the actual plugin itself in the file referenced above [Vendor]\[Module]\Plugin\Admin\Order\Grid

<?php

namespace [Vendor]\[Module]\Plugin\Admin\Order;

class Grid
{
    public function beforeLoad(\Magento\Sales\Model\ResourceModel\Order\Grid\Collection $subject)
    {
        $subject->join(
            ['sales_order_item' => 'sales_order_item'],
            'sales_order_item.order_id = main_table.entity_id')
            ->addExpressionFieldToSelect('sku', "GROUP_CONCAT({{sku}} SEPARATOR '|')", ['sku' => 'sku'])
            ->getSelect()->group('main_table.entity_id');
    }
}

The $subject passed to the plugin's beforeLoad method is an instance of the resource model \Magento\Sales\Model\ResourceModel\Order\Grid\Collection, responsible for loading the data. The ancestors of this class include the load method, which is the one we're intercepting.

We're using the \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection::join method to join to the sales_order_item table to get the SKU data.

Then the \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection::addExpressionFieldToSelect allows us to add in a SQL agregating expression. Which in this case is the GROUP_CONCAT to create a string of all SKUs for the order. This new aggregate column will be aliased with the name sku.

Finally we get the select statement with \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection::getSelect, in order to add the group by clause.

At this point, all we've done is modify the query, so that when \Magento\Framework\Data\Collection\AbstractDb::load is called, the query is changed to return the extra SKU data in a new column. To actually show this data in the admin grid, we have to add the column to the UI component. This is done by adding a file to the module in the location view/adminhtml/ui_component/sales_order_grid.xml with the content

<?xml version="1.0" encoding="UTF-8"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <columns name="sales_order_columns">
        <column name="skus" class="[Vendor]\[Module]\Ui\Component\Listing\Column\Sku">
            <settings>
                <label>Skus</label>
                <sortable>false</sortable>
                <bodyTmpl>ui/grid/cells/html</bodyTmpl>
                <visible>true</visible>
            </settings>
        </column>
    </columns>
</listing>

The class attribute on the column node is unnessesary as is the bodyTmpl node, but together these can be used to preprocess the raw SQL result, formatting the SKU data as you want, eg a newline after each SKU. If you include the class attribute, then you need to define the class \[Vendor]\[Module]\Ui\Component\Listing\Column\Sku as follows:

<?php

namespace [Vendor]\[Module]\Ui\Component\Listing\Column;

use Magento\Ui\Component\Listing\Columns\Column;

class Sku extends Column
{

    public function prepareDataSource(array $dataSource)
    {
        if (isset($dataSource['data']['items'])) {
            foreach ($dataSource['data']['items'] as & $item) {
                $item[$this->getData('name')] = $this->prepareItem($item);
            }
        }

        return $dataSource;
    }

    protected function prepareItem(array $item)
    {
        //return a string based on the $item['sku'] value
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top