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 @@ Magento\Customer\Model\ResourceModel\Grid\Collection Magento\Customer\Model\ResourceModel\Online\Grid\Collection + Magento\Customer\Model\ResourceModel\Group\Grid\Collection @@ -433,6 +434,14 @@ + + + customer_group + customer_group_grid_collection + customer_group_collection + Magento\Customer\Model\ResourceModel\Group + + diff --git a/app/code/Magento/Customer/view/adminhtml/layout/customer_group_index.xml b/app/code/Magento/Customer/view/adminhtml/layout/customer_group_index.xml index ecb64711bdb94..a65ef3cd395d0 100644 --- a/app/code/Magento/Customer/view/adminhtml/layout/customer_group_index.xml +++ b/app/code/Magento/Customer/view/adminhtml/layout/customer_group_index.xml @@ -8,48 +8,7 @@ - - - - customerGroupGrid - Magento\Customer\Model\ResourceModel\Group\Grid\ServiceCollection - type - asc - 1 - - - - - customer/*/edit - - getId - - - - - - ID - id - id - col-id - col-id - - - - - Group - code - - - - - Tax Class - tax_class_name - - - - - + diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_group_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_group_listing.xml new file mode 100644 index 0000000000000..0787e0713aa9f --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_group_listing.xml @@ -0,0 +1,76 @@ + + ++ + + customer_group_listing.customer_group_listing_data_source + + + + + + + customer_group_columns + + customer_group_listing.customer_group_listing_data_source + + + + + + customer_group_id + + + + Magento_Customer::group + + + id + customer_group_id + + + + + + + + + + + + + textRange + + asc + + + + + text + + + + + + + select + select + + + + + + customer_group_id + + + + diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Group/CustomerGroupGrid.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Group/CustomerGroupGrid.php index dcdf041761559..298d5bbf5c6bf 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Group/CustomerGroupGrid.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Group/CustomerGroupGrid.php @@ -6,14 +6,22 @@ namespace Magento\Customer\Test\Block\Adminhtml\Group; -use Magento\Backend\Test\Block\Widget\Grid; +use \Magento\Ui\Test\Block\Adminhtml\DataGrid; +use Magento\Mtf\Client\Element\SimpleElement; /** * Class CustomerGroupGrid * Adminhtml customer group grid */ -class CustomerGroupGrid extends Grid +class CustomerGroupGrid extends DataGrid { + /** + * Select action toggle. + * + * @var string + */ + protected $selectAction = '.action-select'; + /** * Initialize block elements * @@ -21,14 +29,21 @@ class CustomerGroupGrid extends Grid */ protected $filters = [ 'code' => [ - 'selector' => '#customerGroupGrid_filter_type', + 'selector' => '.admin__data-grid-filters input[name*=customer_group_code]', ], ]; /** - * Locator value for grid to click + * Click on "Edit" link. * - * @var string + * @param SimpleElement $rowItem + * @return void */ - protected $editLink = 'td[data-column="time"]'; + protected function clickEditLink(SimpleElement $rowItem) + { + if ($rowItem->find($this->selectAction)->isVisible()) { + $rowItem->find($this->selectAction)->click(); + } + $rowItem->find($this->editLink)->click(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Handler/CustomerGroup/Curl.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Handler/CustomerGroup/Curl.php index 0c1b4b7600605..e7a66139df02e 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Handler/CustomerGroup/Curl.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Handler/CustomerGroup/Curl.php @@ -9,6 +9,7 @@ use Magento\Backend\Test\Handler\Extractor; use Magento\Mtf\Fixture\FixtureInterface; use Magento\Mtf\Handler\Curl as AbstractCurl; +use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; use Magento\Mtf\Util\Protocol\CurlTransport\BackendDecorator; @@ -59,11 +60,22 @@ public function persist(FixtureInterface $fixture = null) */ public function getCustomerGroupId(array $data) { - $url = 'customer/group/index/sort/time/dir/desc'; - $regExp = '/.*id\/(\d+)\/.*' . $data['code'] . '/siu'; - $extractor = new Extractor($url, $regExp); - $match = $extractor->getData(); + $url = $_ENV['app_backend_url'] . 'mui/index/render/'; + $data = [ + 'namespace' => 'customer_group_listing', + 'filters' => [ + 'placeholder' => true, + 'customer_group_code' => $data['code'] + ], + 'isAjax' => true + ]; + $curl = new BackendDecorator(new CurlTransport(), $this->_configuration); + + $curl->write($url, $data, CurlInterface::POST); + $response = $curl->read(); + $curl->close(); + preg_match('/customer_group_listing_data_source.+items.+"customer_group_id":"(\d+)"/', $response, $match); return empty($match[1]) ? null : $match[1]; } } diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Page/Adminhtml/CustomerGroupIndex.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/Page/Adminhtml/CustomerGroupIndex.xml index 42ae87766c81f..7f5de5a2f6ddf 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Page/Adminhtml/CustomerGroupIndex.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Page/Adminhtml/CustomerGroupIndex.xml @@ -9,6 +9,6 @@ - +