diff --git a/app/code/Magento/Customer/Model/ResourceModel/Group/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Group/Collection.php
index 48710c46a5f1d..6e93210d04c3c 100644
--- a/app/code/Magento/Customer/Model/ResourceModel/Group/Collection.php
+++ b/app/code/Magento/Customer/Model/ResourceModel/Group/Collection.php
@@ -12,6 +12,11 @@
*/
class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
+ /**
+ * @var string
+ */
+ protected $_idFieldName = 'customer_group_id';
+
/**
* Resource initialization
*
diff --git a/app/code/Magento/Customer/Model/ResourceModel/Group/Grid/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Group/Grid/Collection.php
index bf3400c3a2f68..f264245b30c4a 100644
--- a/app/code/Magento/Customer/Model/ResourceModel/Group/Grid/Collection.php
+++ b/app/code/Magento/Customer/Model/ResourceModel/Group/Grid/Collection.php
@@ -7,8 +7,62 @@
*/
namespace Magento\Customer\Model\ResourceModel\Group\Grid;
-class Collection extends \Magento\Customer\Model\ResourceModel\Group\Collection
+use Magento\Customer\Model\ResourceModel\Group\Collection as GroupCollection;
+use Magento\Framework\Api\Search\SearchResultInterface;
+use Magento\Framework\Search\AggregationInterface;
+
+/**
+ * Collection for displaying grid of customer groups
+ */
+class Collection extends GroupCollection implements SearchResultInterface
{
+ /**
+ * @var AggregationInterface
+ */
+ protected $aggregations;
+
+ /**
+ * @param \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory
+ * @param \Psr\Log\LoggerInterface $logger
+ * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy
+ * @param \Magento\Framework\Event\ManagerInterface $eventManager
+ * @param string $mainTable
+ * @param string $eventPrefix
+ * @param string $eventObject
+ * @param string $resourceModel
+ * @param string $model
+ * @param \Magento\Framework\DB\Adapter\AdapterInterface|string|null $connection
+ * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource
+ *
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+ */
+ public function __construct(
+ \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory,
+ \Psr\Log\LoggerInterface $logger,
+ \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
+ \Magento\Framework\Event\ManagerInterface $eventManager,
+ $mainTable,
+ $eventPrefix,
+ $eventObject,
+ $resourceModel,
+ $model = \Magento\Framework\View\Element\UiComponent\DataProvider\Document::class,
+ $connection = null,
+ \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null
+ ) {
+ parent::__construct(
+ $entityFactory,
+ $logger,
+ $fetchStrategy,
+ $eventManager,
+ $connection,
+ $resource
+ );
+ $this->_eventPrefix = $eventPrefix;
+ $this->_eventObject = $eventObject;
+ $this->_init($model, $resourceModel);
+ $this->setMainTable($mainTable);
+ }
+
/**
* Resource initialization
* @return $this
@@ -19,4 +73,78 @@ protected function _initSelect()
$this->addTaxClass();
return $this;
}
+
+ /**
+ * @return AggregationInterface
+ */
+ public function getAggregations()
+ {
+ return $this->aggregations;
+ }
+
+ /**
+ * @param AggregationInterface $aggregations
+ * @return $this
+ */
+ public function setAggregations($aggregations)
+ {
+ $this->aggregations = $aggregations;
+ return $this;
+ }
+
+ /**
+ * Get search criteria.
+ *
+ * @return \Magento\Framework\Api\SearchCriteriaInterface|null
+ */
+ public function getSearchCriteria()
+ {
+ return null;
+ }
+
+ /**
+ * Set search criteria.
+ *
+ * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
+ * @return $this
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function setSearchCriteria(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria = null)
+ {
+ return $this;
+ }
+
+ /**
+ * Get total count.
+ *
+ * @return int
+ */
+ public function getTotalCount()
+ {
+ return $this->getSize();
+ }
+
+ /**
+ * Set total count.
+ *
+ * @param int $totalCount
+ * @return $this
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function setTotalCount($totalCount)
+ {
+ return $this;
+ }
+
+ /**
+ * Set items list.
+ *
+ * @param \Magento\Framework\Api\ExtensibleDataInterface[] $items
+ * @return $this
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function setItems(array $items = null)
+ {
+ return $this;
+ }
}
diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/Group/Grid/CollectionTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/Group/Grid/CollectionTest.php
new file mode 100644
index 0000000000000..fc4f762afb0bb
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/Group/Grid/CollectionTest.php
@@ -0,0 +1,136 @@
+entityFactoryMock = $this->getMockBuilder(EntityFactoryInterface::class)
+ ->getMockForAbstractClass();
+ $this->loggerMock = $this->getMockBuilder(LoggerInterface::class)
+ ->getMockForAbstractClass();
+ $this->fetchStrategyMock = $this->getMockBuilder(FetchStrategyInterface::class)
+ ->getMockForAbstractClass();
+ $this->eventManagerMock = $this->getMockBuilder(ManagerInterface::class)
+ ->getMockForAbstractClass();
+ $this->resourceMock = $this->getMockBuilder(AbstractDb::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->aggregationsMock = $this->getMockBuilder(AggregationInterface::class)
+ ->getMockForAbstractClass();
+ $this->connectionMock = $this->getMockBuilder(AdapterInterface::class)
+ ->getMockForAbstractClass();
+ $this->selectMock = $this->getMockBuilder(Select::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->resourceMock->expects($this->any())
+ ->method('getConnection')
+ ->willReturn($this->connectionMock);
+ $this->connectionMock->expects($this->once())
+ ->method('select')
+ ->willReturn($this->selectMock);
+
+ $this->model = (new ObjectManager($this))->getObject(Collection::class, [
+ 'entityFactory' => $this->entityFactoryMock,
+ 'logger' => $this->loggerMock,
+ 'fetchStrategy' => $this->fetchStrategyMock,
+ 'eventManager' => $this->eventManagerMock,
+ 'mainTable' => null,
+ 'eventPrefix' => 'test_event_prefix',
+ 'eventObject' => 'test_event_object',
+ 'resourceModel' => null,
+ 'resource' => $this->resourceMock,
+ ]);
+ }
+
+ /**
+ * @covers \Magento\Customer\Model\ResourceModel\Group\Grid\Collection::setSearchCriteria
+ * @covers \Magento\Customer\Model\ResourceModel\Group\Grid\Collection::getAggregations
+ */
+ public function testSetGetAggregations()
+ {
+ $this->model->setAggregations($this->aggregationsMock);
+ $this->assertInstanceOf(AggregationInterface::class, $this->model->getAggregations());
+ }
+
+ /**
+ * @covers \Magento\Customer\Model\ResourceModel\Group\Grid\Collection::setSearchCriteria
+ */
+ public function testSetSearchCriteria()
+ {
+ $this->assertEquals($this->model, $this->model->setSearchCriteria());
+ }
+}
diff --git a/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/GroupActionsTest.php b/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/GroupActionsTest.php
new file mode 100644
index 0000000000000..fdd841ea88cf8
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/GroupActionsTest.php
@@ -0,0 +1,141 @@
+createMock(ContextInterface::class);
+
+ $processor = $this->getMockBuilder(Processor::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $context->expects(static::never())
+ ->method('getProcessor')
+ ->willReturn($processor);
+
+ $this->urlBuilder = $this->createMock(UrlInterface::class);
+
+ $this->escaper = $this->getMockBuilder(Escaper::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['escapeHtml'])
+ ->getMock();
+
+ $this->groupActions = $objectManager->getObject(GroupActions::class, [
+ 'context' => $context,
+ 'urlBuilder' => $this->urlBuilder,
+ 'escaper' => $this->escaper,
+ ]);
+ }
+
+ /**
+ * @covers \Magento\Customer\Ui\Component\Listing\Column\GroupActions::prepareDataSource
+ */
+ public function testPrepareDataSource()
+ {
+ $groupId = 1;
+ $groupCode = 'group code';
+ $items = [
+ 'data' => [
+ 'items' => [
+ [
+ 'customer_group_id' => $groupId,
+ 'customer_group_code' => $groupCode
+ ]
+ ]
+ ]
+ ];
+ $name = 'item_name';
+ $expectedItems = [
+ [
+ 'customer_group_id' => $groupId,
+ 'customer_group_code' => $groupCode,
+ $name => [
+ 'edit' => [
+ 'href' => 'test/url/edit',
+ 'label' => __('Edit'),
+ ],
+ 'delete' => [
+ 'href' => 'test/url/delete',
+ 'label' => __('Delete'),
+ 'confirm' => [
+ 'title' => __('Delete %1', $groupCode),
+ 'message' => __('Are you sure you want to delete a %1 record?', $groupCode)
+ ],
+ ]
+ ],
+ ]
+ ];
+
+ $this->escaper->expects(static::once())
+ ->method('escapeHtml')
+ ->with($groupCode)
+ ->willReturn($groupCode);
+
+ $this->urlBuilder->expects(static::exactly(2))
+ ->method('getUrl')
+ ->willReturnMap(
+ [
+ [
+ GroupActions::URL_PATH_EDIT,
+ [
+ 'id' => $groupId
+ ],
+ 'test/url/edit',
+ ],
+ [
+ GroupActions::URL_PATH_DELETE,
+ [
+ 'id' => $groupId
+ ],
+ 'test/url/delete',
+ ],
+ ]
+ );
+
+ $this->groupActions->setData('name', $name);
+
+ $actual = $this->groupActions->prepareDataSource($items);
+ static::assertEquals($expectedItems, $actual['data']['items']);
+ }
+}
diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php b/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php
new file mode 100644
index 0000000000000..4e6d8c70923ac
--- /dev/null
+++ b/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php
@@ -0,0 +1,109 @@
+urlBuilder = $urlBuilder;
+ $this->escaper = $escaper;
+ parent::__construct($context, $uiComponentFactory, $components, $data);
+ }
+
+ /**
+ * Prepare Data Source
+ *
+ * @param array $dataSource
+ * @return array
+ */
+ public function prepareDataSource(array $dataSource)
+ {
+ if (isset($dataSource['data']['items'])) {
+ foreach ($dataSource['data']['items'] as & $item) {
+ if (isset($item['customer_group_id'])) {
+ $title = $this->escaper->escapeHtml($item['customer_group_code']);
+ $item[$this->getData('name')] = [
+ 'edit' => [
+ 'href' => $this->urlBuilder->getUrl(
+ static::URL_PATH_EDIT,
+ [
+ 'id' => $item['customer_group_id']
+ ]
+ ),
+ 'label' => __('Edit'),
+ ],
+ ];
+
+ // hide delete action for 'NOT LOGGED IN' group
+ if ($item['customer_group_id'] == 0 && $item['customer_group_code']) {
+ continue;
+ }
+
+ $item[$this->getData('name')]['delete'] = [
+ 'href' => $this->urlBuilder->getUrl(
+ static::URL_PATH_DELETE,
+ [
+ 'id' => $item['customer_group_id']
+ ]
+ ),
+ 'label' => __('Delete'),
+ 'confirm' => [
+ 'title' => __('Delete %1', $title),
+ 'message' => __('Are you sure you want to delete a %1 record?', $title)
+ ]
+ ];
+ }
+ }
+ }
+
+ return $dataSource;
+ }
+}
diff --git a/app/code/Magento/Customer/etc/di.xml b/app/code/Magento/Customer/etc/di.xml
index 0d99c1145e81b..3169139ffd8bb 100644
--- a/app/code/Magento/Customer/etc/di.xml
+++ b/app/code/Magento/Customer/etc/di.xml
@@ -215,6 +215,7 @@