From b25c01f70567e86ab3d2a6ed63faa37362b8ab00 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Fri, 26 Oct 2018 16:12:59 +0300 Subject: [PATCH 01/77] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page --- .../Adminhtml/Edit/Address/CancelButton.php | 41 + .../Adminhtml/Edit/Address/DeleteButton.php | 52 + .../Adminhtml/Edit/Address/GenericButton.php | 110 ++ .../Adminhtml/Edit/Address/SaveButton.php | 33 + .../Address/AbstractDefaultAddress.php | 99 ++ .../Address/DefaultBillingAddress.php | 39 + .../Address/DefaultShippingAddress.php | 39 + .../Controller/Adminhtml/Address/Delete.php | 65 + .../Adminhtml/Address/MassDelete.php | 87 ++ .../Controller/Adminhtml/Address/Save.php | 149 +++ .../Controller/Adminhtml/Address/Validate.php | 97 ++ .../Adminhtml/File/Address/Upload.php | 28 +- .../Controller/Adminhtml/Index/Save.php | 21 +- .../Controller/Adminhtml/Index/Validate.php | 39 +- .../Customer/Model/Address/DataProvider.php | 574 +++++++++ .../DataProviderWithDefaultAddresses.php | 585 +++++++++ .../Customer/Model/Metadata/Form/File.php | 2 +- .../ResourceModel/Address/Grid/Collection.php | 171 +++ .../Model/ResourceModel/Address/Relation.php | 15 +- .../ResourceModel/CustomerRepository.php | 54 +- .../Test/Unit/Controller/Address/SaveTest.php | 205 ++++ .../Unit/Controller/Address/ValidateTest.php | 118 ++ .../Controller/Adminhtml/Index/SaveTest.php | 226 +--- .../Adminhtml/Index/ValidateTest.php | 28 - .../Unit/Model/Address/DataProviderTest.php | 233 ++++ .../DataProviderWithDefaultAddressesTest.php | 1065 +++++++++++++++++ .../ResourceModel/CustomerRepositoryTest.php | 126 -- .../Ui/Component/Form/AddressFieldsetTest.php | 69 ++ .../Ui/Component/Form/AddressFieldset.php | 45 + .../Listing/Address/Column/Actions.php | 134 +++ .../Listing/Address/Column/Countries.php | 37 + app/code/Magento/Customer/etc/db_schema.xml | 9 + app/code/Magento/Customer/etc/di.xml | 9 + .../layout/customer_address_edit.xml | 15 + .../ui_component/customer_address_form.xml | 230 ++++ .../ui_component/customer_address_listing.xml | 171 +++ .../web/js/address/default-address-block.js | 17 + .../web/js/address/default-address.js | 44 + .../view/adminhtml/web/js/address/modal.js | 203 ++++ .../web/js/form/components/insert-form.js | 164 +++ .../web/template/default-address.html | 42 + .../view/base/ui_component/customer_form.xml | 357 +++--- .../Component/Form/Element/DataType/Media.php | 1 + .../Magento/Ui/Component/Form/Fieldset.php | 14 +- app/code/Magento/Ui/Component/Layout/Tabs.php | 4 + .../Test/Unit/Component/Form/FieldsetTest.php | 69 ++ .../base/web/js/form/components/collection.js | 7 +- .../web/js/form/components/collection/item.js | 5 +- .../view/base/web/templates/form/insert.html | 3 - .../web/css/source/_module.less | 72 +- .../Controller/Adminhtml/Address/SaveTest.php | 222 ++++ .../Test/Php/_files/blacklist/strict_type.txt | 2 +- .../config/customerConfig.xml | 2 +- 53 files changed, 5529 insertions(+), 719 deletions(-) create mode 100644 app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php create mode 100644 app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php create mode 100644 app/code/Magento/Customer/Block/Adminhtml/Edit/Address/GenericButton.php create mode 100644 app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php create mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php create mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php create mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php create mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php create mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php create mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php create mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php create mode 100644 app/code/Magento/Customer/Model/Address/DataProvider.php create mode 100644 app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php create mode 100644 app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php create mode 100644 app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php create mode 100644 app/code/Magento/Customer/Test/Unit/Controller/Address/ValidateTest.php create mode 100644 app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php create mode 100644 app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php create mode 100644 app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php create mode 100644 app/code/Magento/Customer/Ui/Component/Form/AddressFieldset.php create mode 100644 app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php create mode 100644 app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php create mode 100644 app/code/Magento/Customer/view/adminhtml/layout/customer_address_edit.xml create mode 100644 app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml create mode 100644 app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/address/default-address-block.js create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/address/modal.js create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js create mode 100644 app/code/Magento/Customer/view/adminhtml/web/template/default-address.html create mode 100644 app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php new file mode 100644 index 0000000000000..80d9780f819d0 --- /dev/null +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php @@ -0,0 +1,41 @@ + __('Cancel'), + 'on_click' => '', + 'data_attribute' => [ + 'mage-init' => [ + 'Magento_Ui/js/form/button-adapter' => [ + 'actions' => [ + [ + 'targetName' => 'customer_form.areas.address.address.customer_address_update_modal', + 'actionName' => 'closeModal' + ], + ], + ], + ], + ], + 'sort_order' => 20 + ]; + } +} diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php new file mode 100644 index 0000000000000..8375aa13fdeff --- /dev/null +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php @@ -0,0 +1,52 @@ +getAddressId()) { + $data = [ + 'label' => __('Delete'), + 'class' => 'delete', + 'on_click' => 'deleteConfirm(\'' . __( + 'Are you sure you want to delete this address?' + ) . '\', \'' . $this->getDeleteUrl() . '\')', + 'sort_order' => 15, + ]; + } + return $data; + } + + /** + * Get delete button url. + * + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function getDeleteUrl(): string + { + return $this->getUrl( + Actions::CUSTOMER_ADDRESS_PATH_DELETE, + ['parent_id' => $this->getCustomerId(), 'id' => $this->getAddressId()] + ); + } +} diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/GenericButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/GenericButton.php new file mode 100644 index 0000000000000..ae09ee6896891 --- /dev/null +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/GenericButton.php @@ -0,0 +1,110 @@ +addressFactory = $addressFactory; + $this->urlBuilder = $urlBuilder; + $this->addressResourceModel = $addressResourceModel; + $this->request = $request; + $this->addressRepository = $addressRepository; + } + + /** + * Return address Id. + * + * @return int|null + */ + public function getAddressId() + { + $address = $this->addressFactory->create(); + + $entityId = $this->request->getParam('entity_id'); + $this->addressResourceModel->load( + $address, + $entityId + ); + + return $address->getEntityId() ?: null; + } + + /** + * Get customer id. + * + * @return int|null + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function getCustomerId() + { + $addressId = $this->request->getParam('entity_id'); + + $address = $this->addressRepository->getById($addressId); + + return $address->getCustomerId() ?: null; + } + + /** + * Generate url by route and parameters + * + * @param string $route + * @param array $params + * @return string + */ + public function getUrl($route = '', array $params = []): string + { + return $this->urlBuilder->getUrl($route, $params); + } +} diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php new file mode 100644 index 0000000000000..706ef32c9e5a5 --- /dev/null +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php @@ -0,0 +1,33 @@ + __('Save'), + 'class' => 'save primary', + 'data_attribute' => [ + 'mage-init' => ['button' => ['event' => 'save']], + 'form-role' => 'save', + ], + 'sort_order' => 10 + ]; + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php new file mode 100644 index 0000000000000..a2f9d12282188 --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php @@ -0,0 +1,99 @@ +addressRepository = $addressRepository; + $this->logger = $logger; + } + + /** + * Execute action to change customer default address + * + * @return \Magento\Framework\Controller\Result\Redirect + */ + public function execute(): Redirect + { + $customerId = $this->getRequest()->getParam('parent_id', false); + $addressId = $this->getRequest()->getParam('id', false); + if ($addressId) { + try { + $address = $this->addressRepository->getById($addressId)->setCustomerId($customerId); + $this->setAddressAsDefault($address); + $this->addressRepository->save($address); + + $this->messageManager->addSuccessMessage($this->getSuccessMessage()); + } catch (\Exception $other) { + $this->logger->critical($other); + $this->messageManager->addExceptionMessage($other, $this->getExceptionMessage()); + } + } + /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + $resultRedirect = $this->resultRedirectFactory->create(); + + return $resultRedirect->setPath('customer/index/edit/id', ['id' => $customerId]); + } + + /** + * Set passed address as customer's default address + * + * @param \Magento\Customer\Api\Data\AddressInterface $address + * @return $this + */ + abstract protected function setAddressAsDefault($address); + + /** + * Get success message about default address changed + * + * @return \Magento\Framework\Phrase + */ + abstract protected function getSuccessMessage(): Phrase; + + /** + * Get error message about unsuccessful attempt to change default address + * + * @return \Magento\Framework\Phrase + */ + abstract protected function getExceptionMessage(): Phrase; +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php new file mode 100644 index 0000000000000..bd921a5a6642e --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php @@ -0,0 +1,39 @@ +setIsDefaultBilling(true); + } + + /** + * @inheritdoc + */ + protected function getSuccessMessage(): Phrase + { + return __('Default billing address has been changed.'); + } + + /** + * @inheritdoc + */ + protected function getExceptionMessage(): Phrase + { + return __('We can\'t change default billing address right now.'); + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php new file mode 100644 index 0000000000000..7d5bef9ab5be9 --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php @@ -0,0 +1,39 @@ +setIsDefaultShipping(true); + } + + /** + * @inheritdoc + */ + protected function getSuccessMessage(): Phrase + { + return __('Default shipping address has been changed.'); + } + + /** + * @inheritdoc + */ + protected function getExceptionMessage(): Phrase + { + return __('We can\'t change default shipping address right now.'); + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php new file mode 100644 index 0000000000000..8443c777546f6 --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php @@ -0,0 +1,65 @@ +addressRepository = $addressRepository; + } + + /** + * Delete action + * + * @return \Magento\Framework\Controller\Result\Redirect + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function execute(): Redirect + { + $customerId = $this->getRequest()->getParam('parent_id', false); + $addressId = $this->getRequest()->getParam('id', false); + if ($addressId && $this->addressRepository->getById($addressId)->getCustomerId() === $customerId) { + try { + $this->addressRepository->deleteById($addressId); + $this->messageManager->addSuccessMessage(__('You deleted the address.')); + } catch (\Exception $other) { + $this->messageManager->addExceptionMessage($other, __('We can\'t delete the address right now.')); + } + } + /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + $resultRedirect = $this->resultRedirectFactory->create(); + + return $resultRedirect->setPath('customer/index/edit/id', ['id' => $customerId]); + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php new file mode 100644 index 0000000000000..f022ea36f420d --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php @@ -0,0 +1,87 @@ +filter = $filter; + $this->collectionFactory = $collectionFactory; + $this->addressRepository = $addressRepository; + parent::__construct($context); + } + + /** + * Execute action + * + * @return \Magento\Backend\Model\View\Result\Redirect + * @throws \Magento\Framework\Exception\LocalizedException|\Exception + */ + public function execute() + { + /** @var \Magento\Customer\Model\ResourceModel\Address\Collection $collection */ + $collection = $this->filter->getCollection($this->collectionFactory->create()); + $collectionSize = $collection->getSize(); + + // Get id of the first item from addresses collection for providing it to the ResultRedirect and build a + // proper redirect URL + $customerId = $collection->getFirstItem()->getParentId(); + + /** @var \Magento\Customer\Model\Address $address */ + foreach ($collection as $address) { + $this->addressRepository->deleteById($address->getId()); + } + $this->messageManager->addSuccessMessage(__('A total of %1 record(s) have been deleted.', $collectionSize)); + + /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); + + return $resultRedirect->setPath('customer/index/edit/id', ['id' => $customerId]); + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php new file mode 100644 index 0000000000000..df041ac4e1202 --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php @@ -0,0 +1,149 @@ +addressRepository = $addressRepository; + $this->formFactory = $formFactory; + $this->customerRepository = $customerRepository; + $this->dataObjectHelper = $dataObjectHelper; + $this->addressDataFactory = $addressDataFactory; + $this->logger = $logger; + } + + /** + * Execute action to save customer address + * + * @return \Magento\Framework\Controller\Result\Redirect + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function execute(): Redirect + { + $customerId = $this->getRequest()->getParam('parent_id', false); + $addressId = $this->getRequest()->getParam('entity_id', false); + /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ + $customer = $this->customerRepository->getById($customerId); + + try { + $addressForm = $this->formFactory->create( + 'customer_address', + 'adminhtml_customer_address', + [], + false, + false + ); + $addressData = $addressForm->extractData($this->getRequest()); + $addressData = $addressForm->compactData($addressData); + + $addressData['region'] = [ + 'region' => $addressData['region'] ?? null, + 'region_id' => $addressData['region_id'] ?? null, + ]; + $addressToSave = $this->addressDataFactory->create(); + $this->dataObjectHelper->populateWithArray( + $addressToSave, + $addressData, + \Magento\Customer\Api\Data\AddressInterface::class + ); + $addressToSave->setCustomerId($customer->getId()); + $addressToSave->setIsDefaultBilling( + (bool)$this->getRequest()->getParam('default_billing', false) + ); + $addressToSave->setIsDefaultShipping( + (bool)$this->getRequest()->getParam('default_shipping', false) + ); + if ($addressId) { + $addressToSave->setId($addressId); + $saveMessage = __('Customer address has been updated.'); + } else { + $addressToSave->setId(null); + $saveMessage = __('New customer address has been added.'); + } + + $this->addressRepository->save($addressToSave); + $this->messageManager->addSuccessMessage($saveMessage); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->messageManager->addErrorMessage($e->getMessage()); + $this->logger->critical($e); + } catch (\Exception $e) { + $this->messageManager->addExceptionMessage( + $e, __('We can\'t change customer address right now.') + ); + } + + $resultRedirect = $this->resultRedirectFactory->create(); + $resultRedirect->setPath( + 'customer/index/edit', + ['id' => $customerId, '_current' => true] + ); + return $resultRedirect; + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php new file mode 100644 index 0000000000000..01ce720a20e63 --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php @@ -0,0 +1,97 @@ +resultJsonFactory = $resultJsonFactory; + $this->formFactory = $formFactory; + } + + /** + * AJAX customer validation action + * + * @return \Magento\Framework\Controller\Result\Json + */ + public function execute() + { + /** @var \Magento\Framework\DataObject $response */ + $response = new \Magento\Framework\DataObject(); + $response->setError(0); + + /** @var \Magento\Framework\DataObject $validatedResponse */ + $validatedResponse = $this->validateCustomerAddress($response); + $resultJson = $this->resultJsonFactory->create(); + if ($validatedResponse->getError()) { + $validatedResponse->setError(true); + $validatedResponse->setMessages($response->getMessages()); + } + + $resultJson->setData($validatedResponse); + + return $resultJson; + } + + /** + * Customer address validation. + * + * @param \Magento\Framework\DataObject $response + * @return \Magento\Framework\DataObject + */ + private function validateCustomerAddress(\Magento\Framework\DataObject $response) + { + $addressForm = $this->formFactory->create('customer_address', 'adminhtml_customer_address'); + $formData = $addressForm->extractData($this->getRequest()); + + $errors = $addressForm->validateData($formData); + if ($errors !== true) { + $messages = $response->hasMessages() ? $response->getMessages() : []; + foreach ($errors as $error) { + $messages[] = $error; + } + $response->setMessages($messages); + $response->setError(1); + } + + return $response; + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php b/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php index 506eac3230200..be1b1aec7b3a3 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php @@ -14,6 +14,9 @@ use Magento\Framework\Exception\LocalizedException; use Psr\Log\LoggerInterface; +/** + * Uploads files for customer address + */ class Upload extends Action { /** @@ -38,21 +41,29 @@ class Upload extends Action */ private $logger; + /** + * @var string + */ + private $scope; + /** * @param Context $context * @param FileUploaderFactory $fileUploaderFactory * @param AddressMetadataInterface $addressMetadataService * @param LoggerInterface $logger + * @param string $scope */ public function __construct( Context $context, FileUploaderFactory $fileUploaderFactory, AddressMetadataInterface $addressMetadataService, - LoggerInterface $logger + LoggerInterface $logger, + string $scope = 'address' ) { $this->fileUploaderFactory = $fileUploaderFactory; $this->addressMetadataService = $addressMetadataService; $this->logger = $logger; + $this->scope = $scope; parent::__construct($context); } @@ -69,14 +80,14 @@ public function execute() // Must be executed before any operations with $_FILES! $this->convertFilesArray(); - $attributeCode = key($_FILES['address']['name']); + $attributeCode = key($_FILES[$this->scope]['name']); $attributeMetadata = $this->addressMetadataService->getAttributeMetadata($attributeCode); /** @var FileUploader $fileUploader */ $fileUploader = $this->fileUploaderFactory->create([ 'attributeMetadata' => $attributeMetadata, 'entityTypeCode' => AddressMetadataInterface::ENTITY_TYPE_ADDRESS, - 'scope' => 'address', + 'scope' => $this->scope, ]); $errors = $fileUploader->validate(); @@ -114,14 +125,11 @@ public function execute() */ private function convertFilesArray() { - foreach ($_FILES['address'] as $itemKey => $item) { - foreach ($item as $value) { - if (is_array($value)) { - $_FILES['address'][$itemKey] = [ - key($value) => current($value), - ]; - } + foreach ($_FILES as $itemKey => $item) { + foreach ($item as $fieldName => $value) { + $_FILES[$this->scope][$fieldName] = [$itemKey => $value]; } + unset($_FILES[$itemKey]); } } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index 45a7c0182d41c..aed7908337ee1 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -108,6 +108,7 @@ protected function _extractData( /** * Saves default_billing and default_shipping flags for customer address * + * @deprecated must be removed because addresses are save separately for now * @param array $addressIdList * @param array $extractedCustomerData * @return array @@ -150,6 +151,7 @@ protected function saveDefaultFlags(array $addressIdList, array & $extractedCust /** * Reformat customer addresses data to be compatible with customer service interface * + * @deprecated must be removed because addresses are save separately for now * @param array $extractedCustomerData * @return array */ @@ -188,7 +190,6 @@ public function execute() try { // optional fields might be set in request for future processing by observers in other modules $customerData = $this->_extractCustomerData(); - $addressesData = $this->_extractCustomerAddressData($customerData); if ($customerId) { $currentCustomer = $this->_customerRepository->getById($customerId); @@ -206,28 +207,12 @@ public function execute() $customerData, \Magento\Customer\Api\Data\CustomerInterface::class ); - $addresses = []; - foreach ($addressesData as $addressData) { - $region = isset($addressData['region']) ? $addressData['region'] : null; - $regionId = isset($addressData['region_id']) ? $addressData['region_id'] : null; - $addressData['region'] = [ - 'region' => $region, - 'region_id' => $regionId, - ]; - $addressDataObject = $this->addressDataFactory->create(); - $this->dataObjectHelper->populateWithArray( - $addressDataObject, - $addressData, - \Magento\Customer\Api\Data\AddressInterface::class - ); - $addresses[] = $addressDataObject; - } $this->_eventManager->dispatch( 'adminhtml_customer_prepare_save', ['customer' => $customer, 'request' => $this->getRequest()] ); - $customer->setAddresses($addresses); + if (isset($customerData['sendemail_store_id'])) { $customer->setStoreId($customerData['sendemail_store_id']); } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php index be09eb7daff76..67adf98d6c718 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php @@ -72,40 +72,6 @@ protected function _validateCustomer($response) return $customer; } - /** - * Customer address validation. - * - * @param \Magento\Framework\DataObject $response - * @return void - */ - protected function _validateCustomerAddress($response) - { - $addresses = $this->getRequest()->getPost('address'); - if (!is_array($addresses)) { - return; - } - foreach (array_keys($addresses) as $index) { - if ($index == '_template_') { - continue; - } - - $addressForm = $this->_formFactory->create('customer_address', 'adminhtml_customer_address'); - - $requestScope = sprintf('address/%s', $index); - $formData = $addressForm->extractData($this->getRequest(), $requestScope); - - $errors = $addressForm->validateData($formData); - if ($errors !== true) { - $messages = $response->hasMessages() ? $response->getMessages() : []; - foreach ($errors as $error) { - $messages[] = $error; - } - $response->setMessages($messages); - $response->setError(1); - } - } - } - /** * AJAX customer validation action * @@ -116,10 +82,7 @@ public function execute() $response = new \Magento\Framework\DataObject(); $response->setError(0); - $customer = $this->_validateCustomer($response); - if ($customer) { - $this->_validateCustomerAddress($response); - } + $this->_validateCustomer($response); $resultJson = $this->resultJsonFactory->create(); if ($response->getError()) { $response->setError(true); diff --git a/app/code/Magento/Customer/Model/Address/DataProvider.php b/app/code/Magento/Customer/Model/Address/DataProvider.php new file mode 100644 index 0000000000000..34f4b8b4eca89 --- /dev/null +++ b/app/code/Magento/Customer/Model/Address/DataProvider.php @@ -0,0 +1,574 @@ + 'frontend_input', + 'visible' => 'is_visible', + 'required' => 'is_required', + 'label' => 'frontend_label', + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + ]; + + /** + * Form element mapping + * + * @var array + */ + private $formElement = [ + 'text' => 'input', + 'hidden' => 'input', + 'boolean' => 'checkbox', + ]; + + /** + * @var EavValidationRules + */ + private $eavValidationRules; + + /** + * @var CountryWithWebsites + */ + private $countryWithWebsiteSource; + + /** + * Allow to manage attributes, even they are hidden on storefront + * + * @var bool + */ + private $allowToShowHiddenAttributes; + + /* + * @var ContextInterface + */ + private $context; + + /** + * File types allowed for file_uploader UI component + * + * @var array + */ + private $fileUploaderTypes = [ + 'image', + 'file', + ]; + + /** + * @var \Magento\Customer\Model\Config\Share + */ + private $shareConfig; + + /** + * @var FileProcessorFactory + */ + private $fileProcessorFactory; + + /** + * @var array + */ + private $bannedInputTypes = ['media_image']; + + /** + * @var array + */ + private $attributesToEliminate = [ + 'region', + 'vat_is_valid', + 'vat_request_date', + 'vat_request_id', + 'vat_request_success' + ]; + + /** + * DataProvider constructor. + * @param string $name + * @param string $primaryFieldName + * @param string $requestFieldName + * @param CollectionFactory $addressCollectionFactory + * @param CustomerRepositoryInterface $customerRepository + * @param Config $eavConfig + * @param EavValidationRules $eavValidationRules + * @param ContextInterface $context + * @param FileProcessorFactory $fileProcessorFactory + * @param \Magento\Customer\Model\Config\Share $shareConfig + * @param array $meta + * @param array $data + * @param bool $allowToShowHiddenAttributes + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function __construct( + $name, + $primaryFieldName, + $requestFieldName, + CollectionFactory $addressCollectionFactory, + CustomerRepositoryInterface $customerRepository, + Config $eavConfig, + EavValidationRules $eavValidationRules, + ContextInterface $context, + FileProcessorFactory $fileProcessorFactory, + \Magento\Customer\Model\Config\Share $shareConfig, + array $meta = [], + array $data = [], + $allowToShowHiddenAttributes = true + ) { + parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); + $this->collection = $addressCollectionFactory->create(); + $this->collection->addAttributeToSelect('*'); + $this->customerRepository = $customerRepository; + $this->eavValidationRules = $eavValidationRules; + $this->allowToShowHiddenAttributes = $allowToShowHiddenAttributes; + $this->context = $context; + $this->fileProcessorFactory = $fileProcessorFactory; + $this->shareConfig = $shareConfig; + $this->meta['general']['children'] = $this->getAttributesMeta( + $eavConfig->getEntityType('customer_address') + ); + } + + /** + * Get Addresses data and process customer default billing & shipping addresses + * + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function getData() + { + if (null !== $this->loadedData) { + return $this->loadedData; + } + $items = $this->collection->getItems(); + /** @var Address $item */ + foreach ($items as $item) { + $addressId = $item->getEntityId(); + $item->load($addressId); + $this->loadedData[$addressId] = $item->getData(); + $customerId = $this->loadedData[$addressId]['parent_id']; + /** @var \Magento\Customer\Model\Customer $customer */ + $customer = $this->customerRepository->getById($customerId); + $defaultBilling = $customer->getDefaultBilling(); + $defaultShipping = $customer->getDefaultShipping(); + $this->prepareAddressData($addressId, $this->loadedData, $defaultBilling, $defaultShipping); + $this->overrideFileUploaderData($item, $this->loadedData[$addressId]); + } + + if (null === $this->loadedData) { + $this->loadedData[''] = $this->getDefaultData(); + } + + return $this->loadedData; + } + + /** + * Prepare address data + * + * @param int $addressId + * @param array $addresses + * @param string|null $defaultBilling + * @param string|null $defaultShipping + * @return void + */ + private function prepareAddressData($addressId, array &$addresses, $defaultBilling, $defaultShipping) + { + if (null !== $defaultBilling && $addressId == $defaultBilling) { + $addresses[$addressId]['default_billing'] = '1'; + } + if (null !== $defaultShipping && $addressId == $defaultShipping) { + $addresses[$addressId]['default_shipping'] = '1'; + } + if (null !== $addresses[$addressId]['street'] && !is_array($addresses[$addressId]['street'])) { + $addresses[$addressId]['street'] = explode("\n", $addresses[$addressId]['street']); + } + } + + /** + * Get default customer data for adding new address + * + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @return array + */ + private function getDefaultData() + { + $parentId = $this->context->getRequestParam('parent_id'); + $customer = $this->customerRepository->getById($parentId); + $data = [ + 'parent_id' => $parentId, + 'firstname' => $customer->getFirstname(), + 'lastname' => $customer->getLastname() + ]; + + return $data; + } + + /** + * Override file uploader UI component data + * + * Overrides data for attributes with frontend_input equal to 'image' or 'file'. + * + * @param Address $entity + * @param array $entityData + * @return void + */ + private function overrideFileUploaderData($entity, array &$entityData) + { + $attributes = $entity->getAttributes(); + foreach ($attributes as $attribute) { + /** @var Attribute $attribute */ + if (in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { + $entityData[$attribute->getAttributeCode()] = $this->getFileUploaderData( + $entity->getEntityType(), + $attribute, + $entityData + ); + } + } + } + + /** + * Get attributes meta + * + * @param Type $entityType + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ + protected function getAttributesMeta(Type $entityType): array + { + $meta = []; + $attributes = $entityType->getAttributeCollection(); + /* @var AbstractAttribute $attribute */ + foreach ($attributes as $attribute) { + $this->processFrontendInput($attribute, $meta); + + $code = $attribute->getAttributeCode(); + + if (in_array($attribute->getFrontendInput(), $this->bannedInputTypes)) { + continue; + } + if (in_array($attribute->getAttributeCode(), $this->attributesToEliminate)) { + continue; + } + + // use getDataUsingMethod, since some getters are defined and apply additional processing of returning value + foreach ($this->metaProperties as $metaName => $origName) { + $value = $attribute->getDataUsingMethod($origName); + $meta[$code]['arguments']['data']['config'][$metaName] = ($metaName === 'label') ? __($value) : $value; + if ('frontend_input' === $origName) { + $meta[$code]['arguments']['data']['config']['formElement'] = $this->formElement[$value] ?? $value; + } + } + + if ($attribute->usesSource()) { + if ($code == AddressInterface::COUNTRY_ID) { + $meta[$code]['arguments']['data']['config']['options'] = $this->getCountryWithWebsiteSource() + ->getAllOptions(); + } else { + $meta[$code]['arguments']['data']['config']['options'] = $attribute->getSource()->getAllOptions(); + } + } + + $rules = $this->eavValidationRules->build($attribute, $meta[$code]['arguments']['data']['config']); + if (!empty($rules)) { + $meta[$code]['arguments']['data']['config']['validation'] = $rules; + } + + $meta[$code]['arguments']['data']['config']['componentType'] = Field::NAME; + $meta[$code]['arguments']['data']['config']['visible'] = $this->canShowAttribute($attribute); + + $this->overrideFileUploaderMetadata($entityType, $attribute, $meta[$code]['arguments']['data']['config']); + } + + $this->processWebsiteMeta($meta); + return $meta; + } + + /** + * Process attributes by frontend input type + * + * @param AttributeInterface $attribute + * @param array $meta + * @return void + */ + private function processFrontendInput(AttributeInterface $attribute, array &$meta) + { + $code = $attribute->getAttributeCode(); + if ($attribute->getFrontendInput() === 'boolean') { + $meta[$code]['arguments']['data']['config']['prefer'] = 'toggle'; + $meta[$code]['arguments']['data']['config']['valueMap'] = [ + 'true' => '1', + 'false' => '0', + ]; + } + } + + /** + * Retrieve Country With Websites Source + * + * @return CountryWithWebsites + * @deprecated 100.2.0 + */ + private function getCountryWithWebsiteSource(): CountryWithWebsites + { + if (!$this->countryWithWebsiteSource) { + $this->countryWithWebsiteSource = ObjectManager::getInstance()->get(CountryWithWebsites::class); + } + + return $this->countryWithWebsiteSource; + } + + /** + * Detect can we show attribute on specific form or not + * + * @param Attribute $customerAttribute + * @return bool + */ + private function canShowAttribute(AbstractAttribute $customerAttribute): bool + { + $userDefined = (bool) $customerAttribute->getIsUserDefined(); + if (!$userDefined) { + return $customerAttribute->getIsVisible(); + } + + $canShowOnForm = $this->canShowAttributeInForm($customerAttribute); + + return ($this->allowToShowHiddenAttributes && $canShowOnForm) || + (!$this->allowToShowHiddenAttributes && $canShowOnForm && $customerAttribute->getIsVisible()); + } + + /** + * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... + * + * @param Attribute $customerAttribute + * @return bool + */ + private function canShowAttributeInForm(AbstractAttribute $customerAttribute): bool + { + $isRegistration = $this->context->getRequestParam($this->getRequestFieldName()) === null; + + if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { + return is_array($customerAttribute->getUsedInForms()) && + ( + (in_array('customer_account_create', $customerAttribute->getUsedInForms()) && $isRegistration) || + (in_array('customer_account_edit', $customerAttribute->getUsedInForms()) && !$isRegistration) + ); + } + return is_array($customerAttribute->getUsedInForms()) && + in_array('customer_address_edit', $customerAttribute->getUsedInForms()); + } + + /** + * Override file uploader UI component metadata + * + * Overrides metadata for attributes with frontend_input equal to 'image' or 'file'. + * + * @param Type $entityType + * @param AbstractAttribute $attribute + * @param array $config + * @return void + */ + private function overrideFileUploaderMetadata( + Type $entityType, + AbstractAttribute $attribute, + array &$config + ) { + if (in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { + $maxFileSize = self::MAX_FILE_SIZE; + + if (isset($config['validation']['max_file_size'])) { + $maxFileSize = (int)$config['validation']['max_file_size']; + } + + $allowedExtensions = []; + + if (isset($config['validation']['file_extensions'])) { + $allowedExtensions = explode(',', $config['validation']['file_extensions']); + array_walk($allowedExtensions, function (&$value) { + $value = strtolower(trim($value)); + }); + } + + $allowedExtensions = implode(' ', $allowedExtensions); + + $entityTypeCode = $entityType->getEntityTypeCode(); + $url = $this->getFileUploadUrl($entityTypeCode); + + $config = [ + 'formElement' => 'fileUploader', + 'componentType' => 'fileUploader', + 'maxFileSize' => $maxFileSize, + 'allowedExtensions' => $allowedExtensions, + 'uploaderConfig' => [ + 'url' => $url, + ], + 'label' => $this->getMetadataValue($config, 'label'), + 'sortOrder' => $this->getMetadataValue($config, 'sortOrder'), + 'required' => $this->getMetadataValue($config, 'required'), + 'visible' => $this->getMetadataValue($config, 'visible'), + 'validation' => $this->getMetadataValue($config, 'validation'), + ]; + } + } + + /** + * Add global scope parameter and filter options to website meta + * + * @param array $meta + * @return void + */ + private function processWebsiteMeta(&$meta) + { + if (isset($meta[CustomerInterface::WEBSITE_ID]) && $this->shareConfig->isGlobalScope()) { + $meta[CustomerInterface::WEBSITE_ID]['arguments']['data']['config']['isGlobalScope'] = 1; + } + + if (isset($meta[AddressInterface::COUNTRY_ID]) && !$this->shareConfig->isGlobalScope()) { + $meta[AddressInterface::COUNTRY_ID]['arguments']['data']['config']['filterBy'] = [ + 'target' => 'customer_form.customer_form_data_source:data.customer.website_id', + 'field' => 'website_ids' + ]; + } + } + + /** + * Retrieve metadata value + * + * @param array $config + * @param string $name + * @param mixed $default + * @return mixed + */ + private function getMetadataValue($config, $name, $default = null) + { + return $config[$name] ?? $default; + } + + /** + * Retrieve URL to file upload + * + * @param string $entityTypeCode + * @return string + */ + private function getFileUploadUrl($entityTypeCode): string + { + switch ($entityTypeCode) { + case CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER: + $url = 'customer/file/customer_upload'; + break; + + case AddressMetadataInterface::ENTITY_TYPE_ADDRESS: + $url = 'customer/file/address_upload'; + break; + + default: + $url = ''; + break; + } + return $url; + } + + /** + * Retrieve array of values required by file uploader UI component + * + * @param Type $entityType + * @param Attribute $attribute + * @param array $customerData + * @return array + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + private function getFileUploaderData( + Type $entityType, + Attribute $attribute, + array $customerData + ): array { + $attributeCode = $attribute->getAttributeCode(); + + $file = $customerData[$attributeCode] ?? ''; + + /** @var FileProcessor $fileProcessor */ + $fileProcessor = $this->fileProcessorFactory->create([ + 'entityTypeCode' => $entityType->getEntityTypeCode(), + ]); + + if (!empty($file) + && $fileProcessor->isExist($file) + ) { + $stat = $fileProcessor->getStat($file); + $viewUrl = $fileProcessor->getViewUrl($file, $attribute->getFrontendInput()); + + return [ + [ + 'file' => $file, + 'size' => null !== $stat ? $stat['size'] : 0, + 'url' => $viewUrl ?? '', + 'name' => basename($file), + 'type' => $fileProcessor->getMimeType($file), + ], + ]; + } + + return []; + } +} diff --git a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php new file mode 100644 index 0000000000000..d52c94fb034c6 --- /dev/null +++ b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php @@ -0,0 +1,585 @@ + 'frontend_input', + 'visible' => 'is_visible', + 'required' => 'is_required', + 'label' => 'frontend_label', + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + ]; + + /** + * Form element mapping + * + * @var array + */ + private $formElement = [ + 'text' => 'input', + 'hidden' => 'input', + 'boolean' => 'checkbox', + ]; + + /** + * @var EavValidationRules + */ + private $eavValidationRules; + + /** + * @var SessionManagerInterface + * @since 100.1.0 + */ + private $session; + + /** + * @var FileProcessorFactory + */ + private $fileProcessorFactory; + + /** + * File types allowed for file_uploader UI component + * + * @var array + */ + private $fileUploaderTypes = [ + 'image', + 'file', + ]; + + /** + * Customer fields that must be removed + * + * @var array + */ + private $forbiddenCustomerFields = [ + 'password_hash', + 'rp_token', + 'confirmation', + ]; + + /* + * @var ContextInterface + */ + private $context; + + /** + * Allow to manage attributes, even they are hidden on storefront + * + * @var bool + */ + private $allowToShowHiddenAttributes; + + /** + * @var \Magento\Directory\Model\CountryFactory + */ + private $countryFactory; + + /** + * DataProviderWithDefaultAddresses constructor. + * + * @param string $name + * @param string $primaryFieldName + * @param string $requestFieldName + * @param EavValidationRules $eavValidationRules + * @param CustomerCollectionFactory $customerCollectionFactory + * @param Config $eavConfig + * @param FileProcessorFactory $fileProcessorFactory + * @param ContextInterface $context + * @param \Magento\Directory\Model\CountryFactory $countryFactory + * @param SessionManagerInterface $session + * @param \Magento\Customer\Model\Config\Share $shareConfig + * @param CountryWithWebsites $countryWithWebsites + * @param bool $allowToShowHiddenAttributes + * @param array $meta + * @param array $data + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function __construct( + string $name, + string $primaryFieldName, + string $requestFieldName, + EavValidationRules $eavValidationRules, + CustomerCollectionFactory $customerCollectionFactory, + Config $eavConfig, + FileProcessorFactory $fileProcessorFactory, + ContextInterface $context, + \Magento\Directory\Model\CountryFactory $countryFactory, + \Magento\Framework\Session\SessionManagerInterface $session, + \Magento\Customer\Model\Config\Share $shareConfig, + CountryWithWebsites $countryWithWebsites, + $allowToShowHiddenAttributes = true, + array $meta = [], + array $data = [] + ) { + parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); + $this->eavValidationRules = $eavValidationRules; + $this->collection = $customerCollectionFactory->create(); + $this->collection->addAttributeToSelect('*'); + $this->fileProcessorFactory = $fileProcessorFactory; + $this->context = $context ?: ObjectManager::getInstance()->get(ContextInterface::class); + $this->allowToShowHiddenAttributes = $allowToShowHiddenAttributes; + $this->session = $session; + $this->countryWithWebsiteSource = $countryWithWebsites; + $this->shareConfig = $shareConfig; + $this->countryFactory = $countryFactory; + $this->meta['customer']['children'] = $this->getAttributesMeta( + $eavConfig->getEntityType('customer') + ); +// $this->meta['address']['children'] = $this->getAttributesMeta( +// $eavConfig->getEntityType('customer_address') +// ); + } + + /** + * Get data + * + * @return array + */ + public function getData() + { + if (null !== $this->loadedData) { + return $this->loadedData; + } + $items = $this->collection->getItems(); + /** @var Customer $customer */ + foreach ($items as $customer) { + $result['customer'] = $customer->getData(); + + $this->overrideFileUploaderData($customer, $result['customer']); + + $result['customer'] = array_diff_key( + $result['customer'], + array_flip($this->forbiddenCustomerFields) + ); + unset($result['address']); + + $result['default_billing_address'] = $this->prepareDefaultAddress( + $customer->getDefaultBillingAddress() + ); + $result['default_shipping_address'] = $this->prepareDefaultAddress( + $customer->getDefaultShippingAddress() + ); + $result['customer_id'] = $customer->getId(); + + $this->loadedData[$customer->getId()] = $result; + } + + $data = $this->session->getCustomerFormData(); + if (!empty($data)) { + $customerId = $data['customer']['entity_id'] ?? null; + $this->loadedData[$customerId] = $data; + $this->session->unsCustomerFormData(); + } + + return $this->loadedData; + } + + /** + * Prepare default address data. + * + * @param Address|false $address + * @return array + */ + private function prepareDefaultAddress($address): array + { + $addressData = []; + + if (!empty($address)) { + $addressData = $address->getData(); + if (isset($addressData['street']) && !\is_array($address['street'])) { + $addressData['street'] = explode("\n", $addressData['street']); + } + $addressData['country'] = $this->countryFactory->create() + ->loadByCode($addressData['country_id'])->getName(); + } + + return $addressData; + } + + /** + * Override file uploader UI component data + * + * Overrides data for attributes with frontend_input equal to 'image' or 'file'. + * + * @param Customer|Address $entity + * @param array $entityData + * @return void + */ + private function overrideFileUploaderData($entity, array &$entityData) + { + $attributes = $entity->getAttributes(); + foreach ($attributes as $attribute) { + /** @var Attribute $attribute */ + if (\in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { + $entityData[$attribute->getAttributeCode()] = $this->getFileUploaderData( + $entity->getEntityType(), + $attribute, + $entityData + ); + } + } + } + + /** + * Retrieve array of values required by file uploader UI component + * + * @param Type $entityType + * @param Attribute $attribute + * @param array $customerData + * @return array + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + private function getFileUploaderData( + Type $entityType, + Attribute $attribute, + array $customerData + ): array { + $attributeCode = $attribute->getAttributeCode(); + + $file = $customerData[$attributeCode] ?? ''; + + /** @var FileProcessor $fileProcessor */ + $fileProcessor = $this->getFileProcessorFactory()->create([ + 'entityTypeCode' => $entityType->getEntityTypeCode(), + ]); + + if (!empty($file) + && $fileProcessor->isExist($file) + ) { + $stat = $fileProcessor->getStat($file); + $viewUrl = $fileProcessor->getViewUrl($file, $attribute->getFrontendInput()); + + return [ + [ + 'file' => $file, + 'size' => null !== $stat ? $stat['size'] : 0, + 'url' => $viewUrl ?? '', + 'name' => basename($file), + 'type' => $fileProcessor->getMimeType($file), + ], + ]; + } + + return []; + } + + /** + * Get attributes meta + * + * @param Type $entityType + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ + protected function getAttributesMeta(Type $entityType): array + { + $meta = []; + $attributes = $entityType->getAttributeCollection(); + /* @var AbstractAttribute $attribute */ + foreach ($attributes as $attribute) { + $this->processFrontendInput($attribute, $meta); + + $code = $attribute->getAttributeCode(); + + // use getDataUsingMethod, since some getters are defined and apply additional processing of returning value + foreach ($this->metaProperties as $metaName => $origName) { + $value = $attribute->getDataUsingMethod($origName); + $meta[$code]['arguments']['data']['config'][$metaName] = ($metaName === 'label') ? __($value) : $value; + if ('frontend_input' === $origName) { + $meta[$code]['arguments']['data']['config']['formElement'] = $this->formElement[$value] ?? $value; + } + } + + if ($attribute->usesSource()) { + if ($code == AddressInterface::COUNTRY_ID) { + $meta[$code]['arguments']['data']['config']['options'] = $this->countryWithWebsiteSource + ->getAllOptions(); + } else { + $meta[$code]['arguments']['data']['config']['options'] = $attribute->getSource()->getAllOptions(); + } + } + + $rules = $this->eavValidationRules->build($attribute, $meta[$code]['arguments']['data']['config']); + if (!empty($rules)) { + $meta[$code]['arguments']['data']['config']['validation'] = $rules; + } + + $meta[$code]['arguments']['data']['config']['componentType'] = Field::NAME; + $meta[$code]['arguments']['data']['config']['visible'] = $this->canShowAttribute($attribute); + + $this->overrideFileUploaderMetadata($entityType, $attribute, $meta[$code]['arguments']['data']['config']); + } + + $this->processWebsiteMeta($meta); + return $meta; + } + + /** + * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... + * + * @param AbstractAttribute $customerAttribute + * @return bool + */ + private function canShowAttributeInForm(AbstractAttribute $customerAttribute) + { + $isRegistration = $this->context->getRequestParam($this->getRequestFieldName()) === null; + + if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { + return \is_array($customerAttribute->getUsedInForms()) && + ( + (\in_array('customer_account_create', $customerAttribute->getUsedInForms()) && $isRegistration) || + (\in_array('customer_account_edit', $customerAttribute->getUsedInForms()) && !$isRegistration) + ); + } + return \is_array($customerAttribute->getUsedInForms()) && + \in_array('customer_address_edit', $customerAttribute->getUsedInForms()); + } + + /** + * Detect can we show attribute on specific form or not + * + * @param AbstractAttribute $customerAttribute + * @return bool + */ + private function canShowAttribute(AbstractAttribute $customerAttribute) + { + $userDefined = (bool) $customerAttribute->getIsUserDefined(); + if (!$userDefined) { + return $customerAttribute->getIsVisible(); + } + + $canShowOnForm = $this->canShowAttributeInForm($customerAttribute); + + return ($this->allowToShowHiddenAttributes && $canShowOnForm) || + (!$this->allowToShowHiddenAttributes && $canShowOnForm && $customerAttribute->getIsVisible()); + } + + /** + * Add global scope parameter and filter options to website meta + * + * @param array $meta + * @return void + */ + private function processWebsiteMeta(&$meta) + { + if (isset($meta[CustomerInterface::WEBSITE_ID]) && $this->shareConfig->isGlobalScope()) { + $meta[CustomerInterface::WEBSITE_ID]['arguments']['data']['config']['isGlobalScope'] = 1; + } + + if (isset($meta[AddressInterface::COUNTRY_ID]) && !$this->shareConfig->isGlobalScope()) { + $meta[AddressInterface::COUNTRY_ID]['arguments']['data']['config']['filterBy'] = [ + 'target' => '${ $.provider }:data.customer.website_id', + 'field' => 'website_ids' + ]; + } + } + + /** + * Override file uploader UI component metadata + * + * Overrides metadata for attributes with frontend_input equal to 'image' or 'file'. + * + * @param Type $entityType + * @param AbstractAttribute $attribute + * @param array $config + * @return void + */ + private function overrideFileUploaderMetadata( + Type $entityType, + AbstractAttribute $attribute, + array &$config + ) { + if (\in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { + $maxFileSize = self::MAX_FILE_SIZE; + + if (isset($config['validation']['max_file_size'])) { + $maxFileSize = (int)$config['validation']['max_file_size']; + } + + $allowedExtensions = []; + + if (isset($config['validation']['file_extensions'])) { + $allowedExtensions = explode(',', $config['validation']['file_extensions']); + array_walk($allowedExtensions, function (&$value) { + $value = strtolower(trim($value)); + }); + } + + $allowedExtensions = implode(' ', $allowedExtensions); + + $entityTypeCode = $entityType->getEntityTypeCode(); + $url = $this->getFileUploadUrl($entityTypeCode); + + $config = [ + 'formElement' => 'fileUploader', + 'componentType' => 'fileUploader', + 'maxFileSize' => $maxFileSize, + 'allowedExtensions' => $allowedExtensions, + 'uploaderConfig' => [ + 'url' => $url, + ], + 'label' => $this->getMetadataValue($config, 'label'), + 'sortOrder' => $this->getMetadataValue($config, 'sortOrder'), + 'required' => $this->getMetadataValue($config, 'required'), + 'visible' => $this->getMetadataValue($config, 'visible'), + 'validation' => $this->getMetadataValue($config, 'validation'), + ]; + } + } + + /** + * Retrieve metadata value + * + * @param array $config + * @param string $name + * @param mixed $default + * @return mixed + */ + private function getMetadataValue($config, $name, $default = null) + { + return $config[$name] ?? $default; + } + + /** + * Retrieve URL to file upload + * + * @param string $entityTypeCode + * @return string + */ + private function getFileUploadUrl($entityTypeCode): string + { + switch ($entityTypeCode) { + case CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER: + $url = 'customer/file/customer_upload'; + break; + + case AddressMetadataInterface::ENTITY_TYPE_ADDRESS: + $url = 'customer/file/address_upload'; + break; + + default: + $url = ''; + break; + } + return $url; + } + + /** + * Process attributes by frontend input type + * + * @param AttributeInterface $attribute + * @param array $meta + * @return void + */ + private function processFrontendInput(AttributeInterface $attribute, array &$meta) + { + $code = $attribute->getAttributeCode(); + if ($attribute->getFrontendInput() === 'boolean') { + $meta[$code]['arguments']['data']['config']['prefer'] = 'toggle'; + $meta[$code]['arguments']['data']['config']['valueMap'] = [ + 'true' => '1', + 'false' => '0', + ]; + } + } + + /** + * Prepare address data + * + * @param int $addressId + * @param array $addresses + * @param array $customer + * @return void + */ + protected function prepareAddressData($addressId, array &$addresses, array $customer) + { + if (isset($customer['default_billing']) + && $addressId == $customer['default_billing'] + ) { + $addresses[$addressId]['default_billing'] = $customer['default_billing']; + } + if (isset($customer['default_shipping']) + && $addressId == $customer['default_shipping'] + ) { + $addresses[$addressId]['default_shipping'] = $customer['default_shipping']; + } + if (isset($addresses[$addressId]['street']) && !\is_array($addresses[$addressId]['street'])) { + $addresses[$addressId]['street'] = explode("\n", $addresses[$addressId]['street']); + } + } + + /** + * Get FileProcessorFactory instance + * + * @return FileProcessorFactory + * @deprecated 100.1.3 + */ + private function getFileProcessorFactory(): FileProcessorFactory + { + if ($this->fileProcessorFactory === null) { + $this->fileProcessorFactory = ObjectManager::getInstance() + ->get(\Magento\Customer\Model\FileProcessorFactory::class); + } + return $this->fileProcessorFactory; + } +} diff --git a/app/code/Magento/Customer/Model/Metadata/Form/File.php b/app/code/Magento/Customer/Model/Metadata/Form/File.php index e6e9c2b50c068..aca5b277186ca 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/File.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/File.php @@ -109,7 +109,7 @@ public function extractValue(\Magento\Framework\App\RequestInterface $request) $extend = $this->_getRequestValue($request); $attrCode = $this->getAttribute()->getAttributeCode(); - if ($this->_requestScope) { + if ($this->_requestScope || !isset($_FILES[$attrCode])) { $value = []; if (strpos($this->_requestScope, '/') !== false) { $scopes = explode('/', $this->_requestScope); diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php new file mode 100644 index 0000000000000..83129fee9b59b --- /dev/null +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php @@ -0,0 +1,171 @@ +context = $context; + $this->_eventPrefix = $eventPrefix; + $this->_eventObject = $eventObject; + $this->_init($model, $resourceModel); + $this->setMainTable($mainTable); + parent::__construct( + $entityFactory, + $logger, + $fetchStrategy, + $eventManager, + $connection, + $resource + ); + } + + /** + * Resource initialization + * + * @return $this + */ + protected function _initSelect() + { + parent::_initSelect(); + $parentId = $this->context->getRequestParam('parent_id'); + if ($parentId !== null) { + $this->getSelect()->where('parent_id=?', $parentId); + } + + return $this; + } + + /** + * {@inheritdoc} + * + * @return AggregationInterface + */ + public function getAggregations() + { + return $this->aggregations; + } + + /** + * {@inheritdoc} + * + * @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/Model/ResourceModel/Address/Relation.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php index d473a4dc01891..cbfebe87812bc 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php @@ -38,16 +38,26 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) /** * @var $object \Magento\Customer\Model\Address */ - if (!$object->getIsCustomerSaveTransaction() && $this->isAddressDefault($object)) { + if (!$object->getIsCustomerSaveTransaction() && $object->getId()) { $customer = $this->customerFactory->create()->load($object->getCustomerId()); $changedAddresses = []; if ($object->getIsDefaultBilling()) { $changedAddresses['default_billing'] = $object->getId(); + } elseif ($customer->getDefaultBillingAddress() + && (int)$customer->getDefaultBillingAddress()->getId() === (int)$object->getId() + && !$object->getIsDefaultBilling() + ) { + $changedAddresses['default_billing'] = null; } if ($object->getIsDefaultShipping()) { $changedAddresses['default_shipping'] = $object->getId(); + } elseif ($customer->getDefaultShippingAddress() + && (int)$customer->getDefaultShippingAddress()->getId() === (int)$object->getId() + && !$object->getIsDefaultShipping() + ) { + $changedAddresses['default_shipping'] = null; } if ($changedAddresses) { @@ -63,6 +73,9 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) /** * Checks if address has chosen as default and has had an id * + * @deprecated Is not used anymore due to changes in logic of save of address. + * If address was default and becomes not default than default address id for customer must be + * set to null * @param \Magento\Framework\Model\AbstractModel $object * @return bool */ diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php index 43ae2db0c2163..5d88cd92c1730 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php @@ -170,22 +170,18 @@ public function save(CustomerInterface $customer, $passwordHash = null) /** @var NewOperation|null $delegatedNewOperation */ $delegatedNewOperation = !$customer->getId() ? $this->delegatedStorage->consumeNewOperation() : null; $prevCustomerData = null; - $prevCustomerDataArr = null; if ($customer->getId()) { $prevCustomerData = $this->getById($customer->getId()); - $prevCustomerDataArr = $prevCustomerData->__toArray(); } - /** @var $customer \Magento\Customer\Model\Data\Customer */ - $customerArr = $customer->__toArray(); + $customer = $this->imageProcessor->save( $customer, CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, $prevCustomerData ); - $origAddresses = $customer->getAddresses(); - $customer->setAddresses([]); + + /** @var array $customerData */ $customerData = $this->extensibleDataObjectConverter->toNestedArray($customer, [], CustomerInterface::class); - $customer->setAddresses($origAddresses); /** @var Customer $customerModel */ $customerModel = $this->customerFactory->create(['data' => $customerData]); //Model's actual ID field maybe different than "id" so "id" field from $customerData may be ignored. @@ -200,51 +196,10 @@ public function save(CustomerInterface $customer, $passwordHash = null) $customerModel->setRpToken(null); $customerModel->setRpTokenCreatedAt(null); } - if (!array_key_exists('default_billing', $customerArr) - && null !== $prevCustomerDataArr - && array_key_exists('default_billing', $prevCustomerDataArr) - ) { - $customerModel->setDefaultBilling($prevCustomerDataArr['default_billing']); - } - if (!array_key_exists('default_shipping', $customerArr) - && null !== $prevCustomerDataArr - && array_key_exists('default_shipping', $prevCustomerDataArr) - ) { - $customerModel->setDefaultShipping($prevCustomerDataArr['default_shipping']); - } $customerModel->save(); $this->customerRegistry->push($customerModel); $customerId = $customerModel->getId(); - if (!$customer->getAddresses() - && $delegatedNewOperation - && $delegatedNewOperation->getCustomer()->getAddresses() - ) { - $customer->setAddresses($delegatedNewOperation->getCustomer()->getAddresses()); - } - if ($customer->getAddresses() !== null) { - if ($customer->getId()) { - $existingAddresses = $this->getById($customer->getId())->getAddresses(); - $getIdFunc = function ($address) { - return $address->getId(); - }; - $existingAddressIds = array_map($getIdFunc, $existingAddresses); - } else { - $existingAddressIds = []; - } - $savedAddressIds = []; - foreach ($customer->getAddresses() as $address) { - $address->setCustomerId($customerId) - ->setRegion($address->getRegion()); - $this->addressRepository->save($address); - if ($address->getId()) { - $savedAddressIds[] = $address->getId(); - } - } - $addressIdsToDelete = array_diff($existingAddressIds, $savedAddressIds); - foreach ($addressIdsToDelete as $addressId) { - $this->addressRepository->deleteById($addressId); - } - } + $this->customerRegistry->remove($customerId); $savedCustomer = $this->get($customer->getEmail(), $customer->getWebsiteId()); $this->eventManager->dispatch( @@ -374,7 +329,6 @@ public function deleteById($customerId) * @param \Magento\Framework\Api\Search\FilterGroup $filterGroup * @param \Magento\Customer\Model\ResourceModel\Customer\Collection $collection * @return void - * @throws \Magento\Framework\Exception\InputException */ protected function addFilterGroupToCollection( \Magento\Framework\Api\Search\FilterGroup $filterGroup, diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php new file mode 100644 index 0000000000000..47088eece23ed --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php @@ -0,0 +1,205 @@ +addressRepositoryMock = $this->createMock(\Magento\Customer\Api\AddressRepositoryInterface::class); + $this->formFactoryMock = $this->createMock(\Magento\Customer\Model\Metadata\FormFactory::class); + $this->customerRepositoryMock = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); + $this->dataObjectHelperMock = $this->createMock(\Magento\Framework\Api\DataObjectHelper ::class); + $this->addressDataFactoryMock = $this->createMock(\Magento\Customer\Api\Data\AddressInterfaceFactory::class); + $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); + $this->requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); + $this->resultRedirectFactoryMock = $this->createMock(\Magento\Backend\Model\View\Result\RedirectFactory::class); + $this->messageManagerMock = $this->createMock(\Magento\Framework\Message\ManagerInterface::class); + + $objectManager = new ObjectManagerHelper($this); + + $this->model = $objectManager->getObject( + \Magento\Customer\Controller\Adminhtml\Address\Save::class, + [ + 'addressRepository' => $this->addressRepositoryMock, + 'formFactory' => $this->formFactoryMock, + 'customerRepository' => $this->customerRepositoryMock, + 'dataObjectHelper' => $this->dataObjectHelperMock, + 'addressDataFactory' => $this->addressDataFactoryMock, + 'loggerMock' => $this->loggerMock, + 'request' => $this->requestMock, + 'resultRedirectFactory' => $this->resultRedirectFactoryMock, + 'messageManager' => $this->messageManagerMock, + ] + ); + } + + /** + * Test method \Magento\Customer\Controller\Adminhtml\Address\Save::execute + * + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testExecute() + { + $addressId = 11; + $customerId = 22; + + $addressExtractedData = [ + 'entity_id' => $addressId, + 'code' => 'value', + 'coolness' => false, + 'region' => 'region', + 'region_id' => 'region_id', + ]; + + $addressCompactedData = [ + 'entity_id' => $addressId, + 'default_billing' => 'true', + 'default_shipping' => 'true', + 'code' => 'value', + 'coolness' => false, + 'region' => 'region', + 'region_id' => 'region_id', + ]; + + $mergedAddressData = [ + 'entity_id' => $addressId, + 'default_billing' => true, + 'default_shipping' => true, + 'code' => 'value', + 'region' => [ + 'region' => 'region', + 'region_id' => 'region_id', + ], + 'region_id' => 'region_id', + 'id' => $addressId, + ]; + + $this->requestMock->method('getParam') + ->withConsecutive(['parent_id'], ['entity_id']) + ->willReturnOnConsecutiveCalls(22, 1); + + $customerMock = $this->getMockBuilder( + \Magento\Customer\Api\Data\CustomerInterface::class + )->disableOriginalConstructor()->getMock(); + + $this->customerRepositoryMock->expects($this->atLeastOnce()) + ->method('getById') + ->with($customerId) + ->willReturn($customerMock); + + $customerAddressFormMock = $this->createMock(\Magento\Customer\Model\Metadata\Form::class); + + $customerAddressFormMock->expects($this->atLeastOnce()) + ->method('extractData') + ->with($this->requestMock) + ->willReturn($addressExtractedData); + $customerAddressFormMock->expects($this->once()) + ->method('compactData') + ->with($addressExtractedData) + ->willReturn($addressCompactedData); + + $this->formFactoryMock->expects($this->exactly(1)) + ->method('create') + ->willReturn($customerAddressFormMock); + + $addressMock = $this->getMockBuilder(\Magento\Customer\Api\Data\AddressInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->addressDataFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($addressMock); + + $this->dataObjectHelperMock->expects($this->atLeastOnce()) + ->method('populateWithArray') + ->willReturn( + [ + $addressMock, + $mergedAddressData, \Magento\Customer\Api\Data\AddressInterface::class, + $this->dataObjectHelperMock, + ] + ); + + $this->messageManagerMock->expects($this->once()) + ->method('addSuccessMessage') + ->with(__('Customer address has been updated.')) + ->willReturnSelf(); + + $resultRedirect = $this->createMock(\Magento\Framework\Controller\Result\Redirect::class); + $resultRedirect->expects($this->atLeastOnce()) + ->method('setPath') + ->with('customer/index/edit', ['id' => $customerId, '_current' => true]) + ->willReturnSelf(); + $this->resultRedirectFactoryMock->method('create') + ->willReturn($resultRedirect); + + $this->assertEquals($resultRedirect, $this->model->execute()); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/ValidateTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/ValidateTest.php new file mode 100644 index 0000000000000..a724bdd24959b --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/ValidateTest.php @@ -0,0 +1,118 @@ +formFactoryMock = $this->createMock(\Magento\Customer\Model\Metadata\FormFactory::class); + $this->requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); + $this->resultJsonFactoryMock = $this->createMock(\Magento\Framework\Controller\Result\JsonFactory::class); + $this->resultRedirectFactoryMock = $this->createMock(\Magento\Backend\Model\View\Result\RedirectFactory::class); + + $objectManager = new ObjectManagerHelper($this); + + $this->model = $objectManager->getObject( + \Magento\Customer\Controller\Adminhtml\Address\Validate::class, + [ + 'formFactory' => $this->formFactoryMock, + 'request' => $this->requestMock, + 'resultRedirectFactory' => $this->resultRedirectFactoryMock, + 'resultJsonFactory' => $this->resultJsonFactoryMock, + ] + ); + } + + /** + * Test method \Magento\Customer\Controller\Adminhtml\Address\Save::execute + * + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testExecute() + { + $addressId = 11; + $errors = ['Error Message 1', 'Error Message 2']; + + $addressExtractedData = [ + 'entity_id' => $addressId, + 'default_billing' => true, + 'default_shipping' => true, + 'code' => 'value', + 'region' => [ + 'region' => 'region', + 'region_id' => 'region_id', + ], + 'region_id' => 'region_id', + 'id' => $addressId, + ]; + + $customerAddressFormMock = $this->createMock(\Magento\Customer\Model\Metadata\Form::class); + + $customerAddressFormMock->expects($this->atLeastOnce()) + ->method('extractData') + ->with($this->requestMock) + ->willReturn($addressExtractedData); + $customerAddressFormMock->expects($this->once()) + ->method('validateData') + ->with($addressExtractedData) + ->willReturn($errors); + + $this->formFactoryMock->expects($this->exactly(1)) + ->method('create') + ->willReturn($customerAddressFormMock); + + $resultJson = $this->createMock(\Magento\Framework\Controller\Result\Json::class); + $this->resultJsonFactoryMock->method('create') + ->willReturn($resultJson); + + $validateResponseMock = $this->createPartialMock( + \Magento\Framework\DataObject::class, + ['getError', 'setMessages'] + ); + $validateResponseMock->method('setMessages')->willReturnSelf(); + $validateResponseMock->method('getError')->willReturn(1); + + $resultJson->method('setData')->willReturnSelf(); + + $this->assertEquals($resultJson, $this->model->execute()); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php index 5372bb11a89b5..c52d5b2fb370f 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php @@ -5,7 +5,6 @@ */ namespace Magento\Customer\Test\Unit\Controller\Adminhtml\Index; -use Magento\Customer\Api\AddressMetadataInterface; use Magento\Customer\Api\CustomerMetadataInterface; use Magento\Customer\Api\Data\AttributeMetadataInterface; use Magento\Customer\Api\Data\CustomerInterface; @@ -281,7 +280,6 @@ protected function setUp() public function testExecuteWithExistentCustomer() { $customerId = 22; - $addressId = 11; $subscription = 'true'; $postValue = [ 'customer' => [ @@ -290,18 +288,6 @@ public function testExecuteWithExistentCustomer() 'coolness' => false, 'disable_auto_group_change' => 'false', ], - 'address' => [ - '_template_' => '_template_', - $addressId => [ - 'entity_id' => $addressId, - 'default_billing' => 'true', - 'default_shipping' => 'true', - 'code' => 'value', - 'coolness' => false, - 'region' => 'region', - 'region_id' => 'region_id', - ], - ], 'subscription' => $subscription, ]; $extractedData = [ @@ -318,22 +304,6 @@ public function testExecuteWithExistentCustomer() CustomerInterface::DEFAULT_BILLING => 2, CustomerInterface::DEFAULT_SHIPPING => 2 ]; - $addressExtractedData = [ - 'entity_id' => $addressId, - 'code' => 'value', - 'coolness' => false, - 'region' => 'region', - 'region_id' => 'region_id', - ]; - $addressCompactedData = [ - 'entity_id' => $addressId, - 'default_billing' => 'true', - 'default_shipping' => 'true', - 'code' => 'value', - 'coolness' => false, - 'region' => 'region', - 'region_id' => 'region_id', - ]; $savedData = [ 'entity_id' => $customerId, 'darkness' => true, @@ -341,61 +311,40 @@ public function testExecuteWithExistentCustomer() CustomerInterface::DEFAULT_BILLING => false, CustomerInterface::DEFAULT_SHIPPING => false, ]; - $savedAddressData = [ - 'entity_id' => $addressId, - 'default_billing' => true, - 'default_shipping' => true, - ]; $mergedData = [ 'entity_id' => $customerId, 'darkness' => true, 'name' => 'Name', 'code' => 'value', 'disable_auto_group_change' => 0, - CustomerInterface::DEFAULT_BILLING => $addressId, - CustomerInterface::DEFAULT_SHIPPING => $addressId, 'confirmation' => false, 'sendemail_store_id' => '1', 'id' => $customerId, ]; - $mergedAddressData = [ - 'entity_id' => $addressId, - 'default_billing' => true, - 'default_shipping' => true, - 'code' => 'value', - 'region' => [ - 'region' => 'region', - 'region_id' => 'region_id', - ], - 'region_id' => 'region_id', - 'id' => $addressId, - ]; /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( \Magento\Customer\Api\Data\AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); - $attributeMock->expects($this->exactly(2)) + $attributeMock->expects($this->atLeastOnce()) ->method('getAttributeCode') ->willReturn('coolness'); - $attributeMock->expects($this->exactly(2)) + $attributeMock->expects($this->atLeastOnce()) ->method('getFrontendInput') ->willReturn('int'); $attributes = [$attributeMock]; - $this->requestMock->expects($this->any()) + $this->requestMock->expects($this->atLeastOnce()) ->method('getPostValue') ->willReturnMap([ [null, null, $postValue], [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], - ['address/' . $addressId, null, $postValue['address'][$addressId]], ]); - $this->requestMock->expects($this->exactly(3)) + $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( [ ['customer', null, $postValue['customer']], - ['address', null, $postValue['address']], ['subscription', null, $subscription], ] ); @@ -404,16 +353,15 @@ public function testExecuteWithExistentCustomer() $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() ->getMock(); - $objectMock->expects($this->exactly(2)) + $objectMock->expects($this->atLeastOnce()) ->method('getData') ->willReturnMap( [ ['customer', null, $postValue['customer']], - ['address/' . $addressId, null, $postValue['address'][$addressId]], ] ); - $this->objectFactoryMock->expects($this->exactly(2)) + $this->objectFactoryMock->expects($this->exactly(1)) ->method('create') ->with(['data' => $postValue]) ->willReturn($objectMock); @@ -432,23 +380,7 @@ public function testExecuteWithExistentCustomer() $customerFormMock->expects($this->once()) ->method('getAttributes') ->willReturn($attributes); - - $customerAddressFormMock = $this->getMockBuilder( - \Magento\Customer\Model\Metadata\Form::class - )->disableOriginalConstructor()->getMock(); - $customerAddressFormMock->expects($this->once()) - ->method('extractData') - ->with($this->requestMock, 'address/' . $addressId) - ->willReturn($addressExtractedData); - $customerAddressFormMock->expects($this->once()) - ->method('compactData') - ->with($addressExtractedData) - ->willReturn($addressCompactedData); - $customerAddressFormMock->expects($this->once()) - ->method('getAttributes') - ->willReturn($attributes); - - $this->formFactoryMock->expects($this->exactly(2)) + $this->formFactoryMock->expects($this->exactly(1)) ->method('create') ->willReturnMap( [ @@ -461,15 +393,6 @@ public function testExecuteWithExistentCustomer() [], $customerFormMock ], - [ - AddressMetadataInterface::ENTITY_TYPE_ADDRESS, - 'adminhtml_customer_address', - $savedAddressData, - false, - Form::DONT_IGNORE_INVISIBLE, - [], - $customerAddressFormMock - ], ] ); @@ -492,25 +415,7 @@ public function testExecuteWithExistentCustomer() ->with($customerMock) ->willReturn($savedData); - $addressMock = $this->getMockBuilder(\Magento\Customer\Api\Data\AddressInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->customerAddressRepositoryMock->expects($this->once()) - ->method('getById') - ->with($addressId) - ->willReturn($addressMock); - - $this->customerAddressMapperMock->expects($this->once()) - ->method('toFlatArray') - ->with($addressMock) - ->willReturn($savedAddressData); - - $this->addressDataFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($addressMock); - - $this->dataHelperMock->expects($this->exactly(2)) + $this->dataHelperMock->expects($this->atLeastOnce()) ->method('populateWithArray') ->willReturnMap( [ @@ -519,19 +424,9 @@ public function testExecuteWithExistentCustomer() $mergedData, \Magento\Customer\Api\Data\CustomerInterface::class, $this->dataHelperMock ], - [ - $addressMock, - $mergedAddressData, \Magento\Customer\Api\Data\AddressInterface::class, - $this->dataHelperMock - ], ] ); - $customerMock->expects($this->once()) - ->method('setAddresses') - ->with([$addressMock]) - ->willReturnSelf(); - $this->customerRepositoryMock->expects($this->once()) ->method('save') ->with($customerMock) @@ -608,63 +503,33 @@ public function testExecuteWithExistentCustomer() public function testExecuteWithNewCustomer() { $customerId = 22; - $addressId = 11; + $subscription = '0'; $postValue = [ 'customer' => [ 'coolness' => false, 'disable_auto_group_change' => 'false', ], - 'address' => [ - '_template_' => '_template_', - $addressId => [ - 'entity_id' => $addressId, - 'code' => 'value', - 'coolness' => false, - 'region' => 'region', - 'region_id' => 'region_id', - ], - ], 'subscription' => $subscription, ]; $extractedData = [ 'coolness' => false, 'disable_auto_group_change' => 'false', ]; - $addressExtractedData = [ - 'entity_id' => $addressId, - 'code' => 'value', - 'coolness' => false, - 'region' => 'region', - 'region_id' => 'region_id', - ]; $mergedData = [ 'disable_auto_group_change' => 0, CustomerInterface::DEFAULT_BILLING => null, CustomerInterface::DEFAULT_SHIPPING => null, 'confirmation' => false, ]; - $mergedAddressData = [ - 'entity_id' => $addressId, - 'default_billing' => false, - 'default_shipping' => false, - 'code' => 'value', - 'region' => [ - 'region' => 'region', - 'region_id' => 'region_id', - ], - 'region_id' => 'region_id', - 'id' => $addressId, - ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( \Magento\Customer\Api\Data\AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); - $attributeMock->expects($this->exactly(2)) + $attributeMock->expects($this->atLeastOnce()) ->method('getAttributeCode') ->willReturn('coolness'); - $attributeMock->expects($this->exactly(2)) + $attributeMock->expects($this->atLeastOnce()) ->method('getFrontendInput') ->willReturn('int'); $attributes = [$attributeMock]; @@ -674,14 +539,12 @@ public function testExecuteWithNewCustomer() ->willReturnMap([ [null, null, $postValue], [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], - ['address/' . $addressId, null, $postValue['address'][$addressId]], ]); - $this->requestMock->expects($this->exactly(3)) + $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( [ ['customer', null, $postValue['customer']], - ['address', null, $postValue['address']], ['subscription', null, $subscription], ] ); @@ -690,16 +553,15 @@ public function testExecuteWithNewCustomer() $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() ->getMock(); - $objectMock->expects($this->exactly(2)) + $objectMock->expects($this->atLeastOnce()) ->method('getData') ->willReturnMap( [ ['customer', null, $postValue['customer']], - ['address/' . $addressId, null, $postValue['address'][$addressId]], ] ); - $this->objectFactoryMock->expects($this->exactly(2)) + $this->objectFactoryMock->expects($this->atLeastOnce()) ->method('create') ->with(['data' => $postValue]) ->willReturn($objectMock); @@ -719,22 +581,7 @@ public function testExecuteWithNewCustomer() ->method('getAttributes') ->willReturn($attributes); - $customerAddressFormMock = $this->getMockBuilder( - \Magento\Customer\Model\Metadata\Form::class - )->disableOriginalConstructor()->getMock(); - $customerAddressFormMock->expects($this->once()) - ->method('extractData') - ->with($this->requestMock, 'address/' . $addressId) - ->willReturn($addressExtractedData); - $customerAddressFormMock->expects($this->once()) - ->method('compactData') - ->with($addressExtractedData) - ->willReturn($addressExtractedData); - $customerAddressFormMock->expects($this->once()) - ->method('getAttributes') - ->willReturn($attributes); - - $this->formFactoryMock->expects($this->exactly(2)) + $this->formFactoryMock->expects($this->exactly(1)) ->method('create') ->willReturnMap( [ @@ -747,15 +594,6 @@ public function testExecuteWithNewCustomer() [], $customerFormMock ], - [ - AddressMetadataInterface::ENTITY_TYPE_ADDRESS, - 'adminhtml_customer_address', - [], - false, - Form::DONT_IGNORE_INVISIBLE, - [], - $customerAddressFormMock - ], ] ); @@ -768,25 +606,7 @@ public function testExecuteWithNewCustomer() ->method('create') ->willReturn($customerMock); - $addressMock = $this->getMockBuilder(\Magento\Customer\Api\Data\AddressInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->addressDataFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($addressMock); - - $this->customerAddressRepositoryMock->expects($this->once()) - ->method('getById') - ->with($addressId) - ->willReturn($addressMock); - - $this->customerAddressMapperMock->expects($this->once()) - ->method('toFlatArray') - ->with($addressMock) - ->willReturn([]); - - $this->dataHelperMock->expects($this->exactly(2)) + $this->dataHelperMock->expects($this->atLeastOnce()) ->method('populateWithArray') ->willReturnMap( [ @@ -795,11 +615,6 @@ public function testExecuteWithNewCustomer() $mergedData, \Magento\Customer\Api\Data\CustomerInterface::class, $this->dataHelperMock ], - [ - $addressMock, - $mergedAddressData, \Magento\Customer\Api\Data\AddressInterface::class, - $this->dataHelperMock - ], ] ); @@ -904,12 +719,11 @@ public function testExecuteWithNewCustomerAndValidationException() [null, null, $postValue], [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], ]); - $this->requestMock->expects($this->exactly(2)) + $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( [ ['customer', null, $postValue['customer']], - ['address', null, null], ] ); @@ -1047,12 +861,11 @@ public function testExecuteWithNewCustomerAndLocalizedException() [null, null, $postValue], [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], ]); - $this->requestMock->expects($this->exactly(2)) + $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( [ ['customer', null, $postValue['customer']], - ['address', null, null], ] ); @@ -1190,12 +1003,11 @@ public function testExecuteWithNewCustomerAndException() [null, null, $postValue], [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], ]); - $this->requestMock->expects($this->exactly(2)) + $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( [ ['customer', null, $postValue['customer']], - ['address', null, null], ] ); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ValidateTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ValidateTest.php index 7209ac9fd24b0..5adb902601630 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ValidateTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ValidateTest.php @@ -141,12 +141,6 @@ protected function setUp() public function testExecute() { - $this->request->expects($this->once()) - ->method('getPost') - ->willReturn([ - '_template_' => null, - 'address_index' => null - ]); $customerEntityId = 2; $this->request->expects($this->once()) ->method('getParam') @@ -162,11 +156,6 @@ public function testExecute() $this->form->expects($this->once())->method('setInvisibleIgnored'); $this->form->expects($this->atLeastOnce())->method('extractData')->willReturn([]); - $error = $this->createMock(\Magento\Framework\Message\Error::class); - $this->form->expects($this->once()) - ->method('validateData') - ->willReturn([$error]); - $validationResult = $this->getMockForAbstractClass( \Magento\Customer\Api\Data\ValidationResultsInterface::class, [], @@ -188,9 +177,6 @@ public function testExecute() public function testExecuteWithoutAddresses() { - $this->request->expects($this->once()) - ->method('getPost') - ->willReturn(null); $this->form->expects($this->once()) ->method('setInvisibleIgnored'); $this->form->expects($this->atLeastOnce()) @@ -223,9 +209,6 @@ public function testExecuteWithoutAddresses() public function testExecuteWithException() { - $this->request->expects($this->once()) - ->method('getPost') - ->willReturn(null); $this->form->expects($this->once()) ->method('setInvisibleIgnored'); $this->form->expects($this->atLeastOnce()) @@ -265,12 +248,6 @@ public function testExecuteWithException() public function testExecuteWithNewCustomerAndNoEntityId() { - $this->request->expects($this->once()) - ->method('getPost') - ->willReturn([ - '_template_' => null, - 'address_index' => null - ]); $this->request->expects($this->once()) ->method('getParam') ->with('customer') @@ -282,11 +259,6 @@ public function testExecuteWithNewCustomerAndNoEntityId() $this->form->expects($this->once())->method('setInvisibleIgnored'); $this->form->expects($this->atLeastOnce())->method('extractData')->willReturn([]); - $error = $this->createMock(\Magento\Framework\Message\Error::class); - $this->form->expects($this->once()) - ->method('validateData') - ->willReturn([$error]); - $validationResult = $this->getMockForAbstractClass( \Magento\Customer\Api\Data\ValidationResultsInterface::class, [], diff --git a/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php new file mode 100644 index 0000000000000..815a47288e370 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php @@ -0,0 +1,233 @@ +addressCollectionFactory = $this->getMockBuilder(CollectionFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->collection = $this->getMockBuilder(AddressCollection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->customerRepository = $this->getMockForAbstractClass(CustomerRepositoryInterface::class); + $this->eavValidationRules = $this->createMock(EavValidationRules::class); + $this->context = $this->getMockForAbstractClass(ContextInterface::class); + $this->fileProcessorFactory = $this->getMockBuilder(FileProcessorFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->shareConfig = $this->createMock(\Magento\Customer\Model\Config\Share::class); + $this->addressCollectionFactory->expects($this->once()) + ->method('create') + ->willReturn($this->collection); + $this->eavConfig = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->entityType = $this->getMockBuilder(Type::class) + ->disableOriginalConstructor() + ->getMock(); + $this->entityType->expects($this->once()) + ->method('getAttributeCollection') + ->willReturn([]); + $this->eavConfig->expects($this->once()) + ->method('getEntityType') + ->willReturn($this->entityType); + $this->customer = $this->getMockForAbstractClass(CustomerInterface::class); + $this->address = $this->getMockBuilder(AddressModel::class) + ->disableOriginalConstructor() + ->getMock(); + $this->attribute = $this->getMockBuilder(AttributeModel::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->model = $objectManagerHelper->getObject( + \Magento\Customer\Model\Address\DataProvider::class, + [ + '', + '', + '', + 'addressCollectionFactory' => $this->addressCollectionFactory, + 'customerRepository' => $this->customerRepository, + 'eavConfig' => $this->eavConfig, + 'eavValidationRules' => $this->eavValidationRules, + 'context' => $this->context, + 'fileProcessorFactory' => $this->fileProcessorFactory, + 'shareConfig' => $this->shareConfig, + [], + [], + true + ] + ); + } + + public function testGetDefaultData() + { + $expectedData = [ + '' => [ + 'parent_id' => 1, + 'firstname' => 'John', + 'lastname' => 'Doe' + ] + ]; + + $this->collection->expects($this->once()) + ->method('getItems') + ->willReturn([]); + + $this->context->expects($this->once()) + ->method('getRequestParam') + ->willReturn(1); + $this->customerRepository->expects($this->once()) + ->method('getById') + ->willReturn($this->customer); + $this->customer->expects($this->once()) + ->method('getFirstname') + ->willReturn('John'); + $this->customer->expects($this->once()) + ->method('getLastname') + ->willReturn('Doe'); + + $this->assertEquals($expectedData, $this->model->getData()); + } + + public function testGetData() + { + $expectedData = [ + '3' => [ + 'parent_id' => "1", + 'firstname' => 'John', + 'lastname' => 'Doe', + 'street' => [ + '42000 Ave W 55 Cedar City', + 'Apt. 33' + ] + ] + ]; + + $this->collection->expects($this->once()) + ->method('getItems') + ->willReturn([ + $this->address + ]); + + $this->customerRepository->expects($this->once()) + ->method('getById') + ->willReturn($this->customer); + $this->customer->expects($this->once()) + ->method('getDefaultBilling') + ->willReturn('1'); + $this->customer->expects($this->once()) + ->method('getDefaultShipping') + ->willReturn('1'); + + $this->address->expects($this->once()) + ->method('getEntityId') + ->willReturn('3'); + $this->address->expects($this->once()) + ->method('load') + ->with("3") + ->willReturnSelf(); + $this->address->expects($this->once()) + ->method('getData') + ->willReturn([ + 'parent_id' => "1", + 'firstname' => "John", + 'lastname' => 'Doe', + 'street' => "42000 Ave W 55 Cedar City\nApt. 33" + ]); + $this->address->expects($this->once()) + ->method('getAttributes') + ->willReturn([$this->attribute]); + $this->attribute->expects($this->once()) + ->method('getFrontendInput') + ->willReturn(null); + + $this->assertEquals($expectedData, $this->model->getData()); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php new file mode 100644 index 0000000000000..eb1241de3e32c --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php @@ -0,0 +1,1065 @@ +eavConfigMock = $this->getMockBuilder(\Magento\Eav\Model\Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->customerCollectionFactoryMock = $this->createPartialMock( + \Magento\Customer\Model\ResourceModel\Customer\CollectionFactory::class, + ['create'] + ); + $this->eavValidationRulesMock = $this + ->getMockBuilder(\Magento\Ui\DataProvider\EavValidationRules::class) + ->disableOriginalConstructor() + ->getMock(); + $this->sessionMock = $this + ->getMockBuilder(\Magento\Framework\Session\SessionManagerInterface::class) + ->setMethods(['getCustomerFormData', 'unsCustomerFormData']) + ->getMockForAbstractClass(); + + $this->fileProcessor = $this->getMockBuilder(\Magento\Customer\Model\FileProcessor::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->fileProcessorFactory = $this->getMockBuilder(\Magento\Customer\Model\FileProcessorFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->countryFactoryMock = $this->getMockBuilder(\Magento\Directory\Model\CountryFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create', 'loadByCode', 'getName']) + ->getMock(); + + $this->customerMock = $this->getMockBuilder(\Magento\Customer\Model\Customer::class) + ->disableOriginalConstructor() + ->getMock(); + $this->customerCollectionMock = $this->getMockBuilder( + \Magento\Customer\Model\ResourceModel\Customer\Collection::class + ) + ->disableOriginalConstructor() + ->getMock(); + $this->customerCollectionMock->expects($this->any())->method('addAttributeToSelect')->with('*'); + $this->customerCollectionFactoryMock + ->expects($this->any()) + ->method('create') + ->willReturn($this->customerCollectionMock); + + $this->eavConfigMock->expects($this->at(0)) + ->method('getEntityType') + ->with('customer') + ->willReturn($this->getTypeCustomerMock([])); + $this->eavConfigMock->expects($this->at(1)) + ->method('getEntityType') + ->with('customer_address') + ->willReturn($this->getTypeAddressMock()); + + $this->shareConfigMock = $this->getMockBuilder(\Magento\Customer\Model\Config\Share::class) + ->disableOriginalConstructor() + ->getMock(); + $this->countryWithWebsitesMock = $this->getMockBuilder(CountryWithWebsites::class) + ->disableOriginalConstructor() + ->setMethods(['getAllOptions']) + ->getMock(); + $this->countryWithWebsitesMock->expects($this->any())->method('getAllOptions')->willReturn('test-options'); + + $helper = new ObjectManager($this); + $this->dataProvider = $helper->getObject( + \Magento\Customer\Model\Customer\DataProviderWithDefaultAddresses::class, + [ + 'name' => 'test-name', + 'primaryFieldName' => 'primary-field-name', + 'requestFieldName' => 'request-field-name', + 'eavValidationRules' => $this->eavValidationRulesMock, + 'customerCollectionFactory' => $this->customerCollectionFactoryMock, + 'eavConfig' => $this->eavConfigMock, + 'countryFactory' => $this->countryFactoryMock, + 'session' => $this->sessionMock, + 'fileProcessorFactory' => $this->fileProcessorFactory, + 'shareConfig' => $this->shareConfigMock, + 'countryWithWebsites' => $this->countryWithWebsitesMock, + ] + ); + } + + /** + * Run test getAttributesMeta method + * + * @param array $expected + * @return void + * + * @dataProvider getAttributesMetaDataProvider + */ + public function testGetAttributesMetaWithOptions(array $expected) + { + $meta = $this->dataProvider->getMeta(); + $this->assertNotEmpty($meta); + $this->assertEquals($expected, $meta); + } + + /** + * Data provider for testGetAttributesMeta + * + * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function getAttributesMetaDataProvider() + { + return [ + [ + 'expected' => [ + 'customer' => [ + 'children' => [ + self::ATTRIBUTE_CODE => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'options' => 'test-options', + 'visible' => null, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + ], + ], + ], + ], + 'test-code-boolean' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'visible' => null, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + 'prefer' => 'toggle', + 'valueMap' => [ + 'true' => 1, + 'false' => 0, + ], + ], + ], + ], + ], + ], + ], + 'address' => [ + 'children' => [ + self::ATTRIBUTE_CODE => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'options' => 'test-options', + 'visible' => null, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + ], + ], + ], + ], + 'test-code-boolean' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'visible' => null, + 'required' => 'is_required', + 'label' => 'frontend_label', + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + 'prefer' => 'toggle', + 'valueMap' => [ + 'true' => 1, + 'false' => 0, + ], + ], + ], + ], + ], + 'country_id' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'options' => 'test-options', + 'visible' => null, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + 'filterBy' => [ + 'target' => '${ $.provider }:data.customer.website_id', + 'field' => 'website_ids' + ] + ], + ], + ], + ] + ], + ], + ] + ] + ]; + } + + /** + * @return CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected function getCustomerCollectionFactoryMock() + { + $collectionMock = $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Customer\Collection::class) + ->disableOriginalConstructor() + ->getMock(); + + $collectionMock->expects($this->any()) + ->method('addAttributeToSelect') + ->with('*'); + + $this->customerCollectionFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($collectionMock); + + return $this->customerCollectionFactoryMock; + } + + /** + * @return Config|\PHPUnit_Framework_MockObject_MockObject + */ + protected function getEavConfigMock($customerAttributes = []) + { + $this->eavConfigMock->expects($this->at(0)) + ->method('getEntityType') + ->with('customer') + ->willReturn($this->getTypeCustomerMock($customerAttributes)); + $this->eavConfigMock->expects($this->at(1)) + ->method('getEntityType') + ->with('customer_address') + ->willReturn($this->getTypeAddressMock()); + + return $this->eavConfigMock; + } + + /** + * @return Type|\PHPUnit_Framework_MockObject_MockObject + */ + protected function getTypeCustomerMock($customerAttributes = []) + { + $typeCustomerMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Type::class) + ->disableOriginalConstructor() + ->getMock(); + $attributesCollection = !empty($customerAttributes) ? $customerAttributes : $this->getAttributeMock(); + $typeCustomerMock->expects($this->any()) + ->method('getEntityTypeCode') + ->willReturn('customer'); + foreach ($attributesCollection as $attribute) { + $attribute->expects($this->any()) + ->method('getEntityType') + ->willReturn($typeCustomerMock); + } + + $typeCustomerMock->expects($this->once()) + ->method('getAttributeCollection') + ->willReturn($attributesCollection); + + return $typeCustomerMock; + } + + /** + * @return Type|\PHPUnit_Framework_MockObject_MockObject + */ + protected function getTypeAddressMock() + { + $typeAddressMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Type::class) + ->disableOriginalConstructor() + ->getMock(); + + $typeAddressMock->expects($this->once()) + ->method('getAttributeCollection') + ->willReturn($this->getAttributeMock('address')); + + return $typeAddressMock; + } + + /** + * @param \PHPUnit_Framework_MockObject_MockObject $attributeMock + * @param \PHPUnit_Framework_MockObject_MockObject $attributeBooleanMock + * @param array $options + */ + private function injectVisibilityProps( + \PHPUnit_Framework_MockObject_MockObject $attributeMock, + \PHPUnit_Framework_MockObject_MockObject $attributeBooleanMock, + array $options = [] + ) { + if (isset($options[self::ATTRIBUTE_CODE]['visible'])) { + $attributeMock->expects($this->any()) + ->method('getIsVisible') + ->willReturn($options[self::ATTRIBUTE_CODE]['visible']); + } + + if (isset($options[self::ATTRIBUTE_CODE]['user_defined'])) { + $attributeMock->expects($this->any()) + ->method('getIsUserDefined') + ->willReturn($options[self::ATTRIBUTE_CODE]['user_defined']); + } + + if (isset($options[self::ATTRIBUTE_CODE]['is_used_in_forms'])) { + $attributeMock->expects($this->any()) + ->method('getUsedInForms') + ->willReturn($options[self::ATTRIBUTE_CODE]['is_used_in_forms']); + } + + if (isset($options['test-code-boolean']['visible'])) { + $attributeBooleanMock->expects($this->any()) + ->method('getIsVisible') + ->willReturn($options['test-code-boolean']['visible']); + } + + if (isset($options['test-code-boolean']['user_defined'])) { + $attributeBooleanMock->expects($this->any()) + ->method('getIsUserDefined') + ->willReturn($options['test-code-boolean']['user_defined']); + } + + if (isset($options['test-code-boolean']['is_used_in_forms'])) { + $attributeBooleanMock->expects($this->any()) + ->method('getUsedInForms') + ->willReturn($options['test-code-boolean']['is_used_in_forms']); + } + } + + /** + * @return AbstractAttribute[]|\PHPUnit_Framework_MockObject_MockObject[] + */ + protected function getAttributeMock($type = 'customer', $options = []) + { + $attributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) + ->setMethods( + [ + 'getAttributeCode', + 'getDataUsingMethod', + 'usesSource', + 'getFrontendInput', + 'getIsVisible', + 'getSource', + 'getIsUserDefined', + 'getUsedInForms', + 'getEntityType', + ] + ) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $sourceMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Source\AbstractSource::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $attributeCode = self::ATTRIBUTE_CODE; + if (isset($options[self::ATTRIBUTE_CODE]['specific_code_prefix'])) { + $attributeCode .= $options[self::ATTRIBUTE_CODE]['specific_code_prefix']; + } + + $attributeMock->expects($this->exactly(2)) + ->method('getAttributeCode') + ->willReturn($attributeCode); + + $sourceMock->expects($this->any()) + ->method('getAllOptions') + ->willReturn(self::OPTIONS_RESULT); + + $attributeMock->expects($this->any()) + ->method('getDataUsingMethod') + ->willReturnCallback($this->attributeGetUsingMethodCallback()); + + $attributeMock->expects($this->any()) + ->method('usesSource') + ->willReturn(true); + $attributeMock->expects($this->any()) + ->method('getSource') + ->willReturn($sourceMock); + + $attributeBooleanMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) + ->setMethods( + [ + 'getAttributeCode', + 'getDataUsingMethod', + 'usesSource', + 'getFrontendInput', + 'getIsVisible', + 'getIsUserDefined', + 'getUsedInForms', + 'getSource', + 'getEntityType', + ] + ) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $attributeBooleanMock->expects($this->any()) + ->method('getFrontendInput') + ->willReturn('boolean'); + $attributeBooleanMock->expects($this->any()) + ->method('getDataUsingMethod') + ->willReturnCallback($this->attributeGetUsingMethodCallback()); + + $attributeBooleanMock->expects($this->once()) + ->method('usesSource') + ->willReturn(false); + $booleanAttributeCode = 'test-code-boolean'; + if (isset($options['test-code-boolean']['specific_code_prefix'])) { + $booleanAttributeCode .= $options['test-code-boolean']['specific_code_prefix']; + } + + $attributeBooleanMock->expects($this->exactly(2)) + ->method('getAttributeCode') + ->willReturn($booleanAttributeCode); + + $this->eavValidationRulesMock->expects($this->any()) + ->method('build') + ->willReturnMap([ + [$attributeMock, $this->logicalNot($this->isEmpty()), []], + [$attributeBooleanMock, $this->logicalNot($this->isEmpty()), []], + ]); + $mocks = [$attributeMock, $attributeBooleanMock]; + $this->injectVisibilityProps($attributeMock, $attributeBooleanMock, $options); + if ($type == "address") { + $mocks[] = $this->getCountryAttrMock(); + } + return $mocks; + } + + /** + * Callback for ::getDataUsingMethod + * + * @return \Closure + */ + private function attributeGetUsingMethodCallback() + { + return function ($origName) { + return $origName; + }; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getCountryAttrMock() + { + $objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); + $objectManagerMock->expects($this->any()) + ->method('get') + ->willReturnMap([ + [CountryWithWebsites::class, $this->countryWithWebsitesMock], + [Share::class, $this->shareConfigMock], + ]); + \Magento\Framework\App\ObjectManager::setInstance($objectManagerMock); + $countryAttrMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) + ->setMethods(['getAttributeCode', 'getDataUsingMethod', 'usesSource', 'getSource', 'getLabel']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $countryAttrMock->expects($this->exactly(2)) + ->method('getAttributeCode') + ->willReturn('country_id'); + + $countryAttrMock->expects($this->any()) + ->method('getDataUsingMethod') + ->willReturnCallback( + function ($origName) { + return $origName; + } + ); + $countryAttrMock->expects($this->any()) + ->method('getLabel') + ->willReturn(__('frontend_label')); + $countryAttrMock->expects($this->any()) + ->method('usesSource') + ->willReturn(true); + $countryAttrMock->expects($this->any()) + ->method('getSource') + ->willReturn(null); + + return $countryAttrMock; + } + + /** + * @return void + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testGetData() + { + $customerData = [ + 'email' => 'test@test.ua', + 'default_billing' => 2, + 'default_shipping' => 2, + 'password_hash' => 'password_hash', + 'rp_token' => 'rp_token', + 'confirmation' => 'confirmation', + ]; + + $address = $this->getMockBuilder(\Magento\Customer\Model\Address::class)->disableOriginalConstructor() + ->getMock(); + $this->customerCollectionMock->expects($this->once())->method('getItems')->willReturn([$this->customerMock]); + $this->customerMock->expects($this->once())->method('getData')->willReturn($customerData); + $this->customerMock->expects($this->once())->method('getAttributes')->willReturn([]); + + $this->customerMock->expects($this->once())->method('getDefaultBillingAddress')->willReturn($address); + $this->countryFactoryMock->expects($this->once())->method('create')->willReturnSelf(); + $this->countryFactoryMock->expects($this->once())->method('loadByCode')->willReturnSelf(); + $this->countryFactoryMock->expects($this->once())->method('getName')->willReturn('Ukraine'); + + $this->sessionMock->expects($this->once()) + ->method('getCustomerFormData') + ->willReturn(null); + + $this->assertEquals( + [ + '' => [ + 'customer' => [ + 'email' => 'test@test.ua', + 'default_billing' => 2, + 'default_shipping' => 2, + ], + 'default_billing_address' => [ + 'country' => 'Ukraine', + ], + 'default_shipping_address' => [] + ] + ], + $this->dataProvider->getData() + ); + } + + /** + * @return void + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testGetDataWithCustomerFormData() + { + $customerId = 11; + $customerFormData = [ + 'customer' => [ + 'email' => 'test1@test1.ua', + 'default_billing' => 3, + 'default_shipping' => 3, + 'entity_id' => $customerId, + ], + 'address' => [ + 3 => [ + 'firstname' => 'firstname1', + 'lastname' => 'lastname1', + 'street' => [ + 'street1', + 'street2', + ], + 'default_billing' => 3, + 'default_shipping' => 3, + ], + ], + ]; + + $this->customerCollectionMock->expects($this->once())->method('getItems')->willReturn([$this->customerMock]); + $this->customerMock->expects($this->once()) + ->method('getData') + ->willReturn([ + 'email' => 'test@test.ua', + 'default_billing' => 2, + 'default_shipping' => 2, + ]); + $this->customerMock->expects($this->once())->method('getId')->willReturn($customerId); + $this->customerMock->expects($this->once())->method('getAttributes')->willReturn([]); + + $this->sessionMock->expects($this->once())->method('getCustomerFormData')->willReturn($customerFormData); + $this->sessionMock->expects($this->once())->method('unsCustomerFormData'); + + $this->assertEquals([$customerId => $customerFormData], $this->dataProvider->getData()); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return void + */ + public function testGetDataWithCustomAttributeImage() + { + $customerId = 1; + $customerEmail = 'user1@example.com'; + + $filename = '/filename.ext1'; + $viewUrl = 'viewUrl'; + $mime = 'image/png'; + + $expectedData = [ + $customerId => [ + 'customer' => [ + 'email' => $customerEmail, + 'img1' => [ + [ + 'file' => $filename, + 'size' => 1, + 'url' => $viewUrl, + 'name' => 'filename.ext1', + 'type' => $mime, + ], + ], + ], + 'default_billing_address' => [], + 'default_shipping_address' => [], + ], + ]; + + $attributeMock = $this->getMockBuilder(\Magento\Customer\Model\Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + $attributeMock->expects($this->exactly(2)) + ->method('getFrontendInput') + ->willReturn('image'); + $attributeMock->expects($this->exactly(2)) + ->method('getAttributeCode') + ->willReturn('img1'); + + $entityTypeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Type::class) + ->disableOriginalConstructor() + ->getMock(); + $entityTypeMock->expects($this->once()) + ->method('getEntityTypeCode') + ->willReturn(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + + $this->customerMock->expects($this->once()) + ->method('getData') + ->willReturn([ + 'email' => $customerEmail, + 'img1' => $filename, + ]); + $this->customerMock->expects($this->once())->method('getId')->willReturn($customerId); + $this->customerMock->expects($this->once())->method('getAttributes')->willReturn([$attributeMock]); + $this->customerMock->expects($this->once())->method('getEntityType')->willReturn($entityTypeMock); + $this->customerCollectionMock->expects($this->any())->method('getItems')->willReturn([$this->customerMock]); + $this->sessionMock->expects($this->once())->method('getCustomerFormData')->willReturn([]); + $this->fileProcessorFactory->expects($this->any()) + ->method('create') + ->with([ + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + ]) + ->willReturn($this->fileProcessor); + $this->fileProcessor->expects($this->once())->method('isExist')->with($filename)->willReturn(true); + $this->fileProcessor->expects($this->once())->method('getStat')->with($filename)->willReturn(['size' => 1]); + $this->fileProcessor->expects($this->once())->method('getViewUrl') + ->with('/filename.ext1', 'image') + ->willReturn($viewUrl); + $this->fileProcessor->expects($this->once())->method('getMimeType')->with($filename)->willReturn($mime); + + $objectManager = new ObjectManager($this); + + $objectManager->setBackwardCompatibleProperty( + $this->dataProvider, + 'fileProcessorFactory', + $this->fileProcessorFactory + ); + + $this->assertEquals($expectedData, $this->dataProvider->getData()); + } + + public function testGetDataWithCustomAttributeImageNoData() + { + $customerId = 1; + $customerEmail = 'user1@example.com'; + + $expectedData = [ + $customerId => [ + 'customer' => [ + 'email' => $customerEmail, + 'img1' => [], + ], + 'default_billing_address' => [], + 'default_shipping_address' => [], + ], + ]; + + $attributeMock = $this->getMockBuilder(\Magento\Customer\Model\Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + $attributeMock->expects($this->once()) + ->method('getFrontendInput') + ->willReturn('image'); + $attributeMock->expects($this->exactly(2))->method('getAttributeCode') + ->willReturn('img1'); + + $entityTypeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Type::class) + ->disableOriginalConstructor() + ->getMock(); + $entityTypeMock->expects($this->once()) + ->method('getEntityTypeCode') + ->willReturn(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + + $this->customerMock->expects($this->once()) + ->method('getData') + ->willReturn([ + 'email' => $customerEmail, + ]); + $this->customerMock->expects($this->once())->method('getId')->willReturn($customerId); + $this->customerMock->expects($this->once())->method('getAttributes')->willReturn([$attributeMock]); + $this->customerMock->expects($this->once())->method('getEntityType')->willReturn($entityTypeMock); + $this->customerCollectionMock->expects($this->any())->method('getItems')->willReturn([$this->customerMock]); + $this->sessionMock->expects($this->once())->method('getCustomerFormData')->willReturn([]); + + $this->assertEquals($expectedData, $this->dataProvider->getData()); + } + + /** + * @return void + */ + public function testGetDataWithVisibleAttributes() + { + $firstAttributesBundle = $this->getAttributeMock( + 'customer', + [ + self::ATTRIBUTE_CODE => [ + 'visible' => true, + 'is_used_in_forms' => ['customer_account_edit'], + 'user_defined' => true, + 'specific_code_prefix' => "_1" + ], + 'test-code-boolean' => [ + 'visible' => true, + 'is_used_in_forms' => ['customer_account_create'], + 'user_defined' => true, + 'specific_code_prefix' => "_1" + ] + ] + ); + $secondAttributesBundle = $this->getAttributeMock( + 'customer', + [ + self::ATTRIBUTE_CODE => [ + 'visible' => true, + 'is_used_in_forms' => ['customer_account_create'], + 'user_defined' => false, + 'specific_code_prefix' => "_2" + ], + 'test-code-boolean' => [ + 'visible' => true, + 'is_used_in_forms' => ['customer_account_create'], + 'user_defined' => true, + 'specific_code_prefix' => "_2" + ] + ] + ); + + $helper = new ObjectManager($this); + /** @var \Magento\Customer\Model\Customer\DataProviderWithDefaultAddresses $dataProvider */ + $dataProvider = $helper->getObject( + \Magento\Customer\Model\Customer\DataProviderWithDefaultAddresses::class, + [ + 'name' => 'test-name', + 'primaryFieldName' => 'primary-field-name', + 'requestFieldName' => 'request-field-name', + 'eavValidationRules' => $this->eavValidationRulesMock, + 'customerCollectionFactory' => $this->getCustomerCollectionFactoryMock(), + 'eavConfig' => $this->getEavConfigMock(array_merge($firstAttributesBundle, $secondAttributesBundle)) + ] + ); + + $helper->setBackwardCompatibleProperty( + $dataProvider, + 'fileProcessorFactory', + $this->fileProcessorFactory + ); + + $meta = $dataProvider->getMeta(); + $this->assertNotEmpty($meta); + $this->assertEquals($this->getExpectationForVisibleAttributes(), $meta); + } + + /** + * Retrieve all customer variations of attributes with all variations of visibility + * + * @param bool $isRegistration + * @return array + */ + private function getCustomerAttributeExpectations($isRegistration) + { + return [ + self::ATTRIBUTE_CODE . "_1" => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'options' => 'test-options', + 'visible' => !$isRegistration, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + ], + ], + ], + ], + self::ATTRIBUTE_CODE . "_2" => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'options' => 'test-options', + 'visible' => true, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + ], + ], + ], + ], + 'test-code-boolean_1' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'visible' => $isRegistration, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + 'prefer' => 'toggle', + 'valueMap' => [ + 'true' => 1, + 'false' => 0, + ], + ], + ], + ], + ], + 'test-code-boolean_2' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'visible' => $isRegistration, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + 'prefer' => 'toggle', + 'valueMap' => [ + 'true' => 1, + 'false' => 0, + ], + ], + ], + ], + ], + ]; + } + + /** + * Retrieve all variations of attributes with all variations of visibility + * + * @param bool $isRegistration + * @return array + */ + private function getExpectationForVisibleAttributes($isRegistration = true) + { + return [ + 'customer' => [ + 'children' => $this->getCustomerAttributeExpectations($isRegistration), + ], + 'address' => [ + 'children' => [ + self::ATTRIBUTE_CODE => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'options' => 'test-options', + 'visible' => null, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + ], + ], + ], + ], + 'test-code-boolean' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'visible' => null, + 'required' => 'is_required', + 'label' => 'frontend_label', + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + 'prefer' => 'toggle', + 'valueMap' => [ + 'true' => 1, + 'false' => 0, + ], + ], + ], + ], + ], + 'country_id' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'options' => null, + 'visible' => null, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + 'filterBy' => [ + 'target' => '${ $.provider }:data.customer.website_id', + 'field' => 'website_ids' + ] + ], + ], + ], + ] + ], + ], + ]; + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php index a142f87dcf6c4..1d262e7549873 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php @@ -195,35 +195,6 @@ public function testSave() $customerId = 1; $storeId = 2; - $region = $this->getMockForAbstractClass(\Magento\Customer\Api\Data\RegionInterface::class, [], '', false); - $address = $this->getMockForAbstractClass( - \Magento\Customer\Api\Data\AddressInterface::class, - [], - '', - false, - false, - true, - [ - 'setCustomerId', - 'setRegion', - 'getRegion', - 'getId' - ] - ); - $address2 = $this->getMockForAbstractClass( - \Magento\Customer\Api\Data\AddressInterface::class, - [], - '', - false, - false, - true, - [ - 'setCustomerId', - 'setRegion', - 'getRegion', - 'getId' - ] - ); $customerModel = $this->createPartialMock(\Magento\Customer\Model\Customer::class, [ 'getId', 'setId', @@ -243,10 +214,6 @@ public function testSave() $origCustomer = $this->customer; - $this->customer->expects($this->atLeastOnce()) - ->method('__toArray') - ->willReturn(['default_billing', 'default_shipping']); - $customerAttributesMetaData = $this->getMockForAbstractClass( \Magento\Framework\Api\CustomAttributesDataInterface::class, [], @@ -258,8 +225,6 @@ public function testSave() 'getId', 'getEmail', 'getWebsiteId', - 'getAddresses', - 'setAddresses' ] ); $customerSecureData = $this->createPartialMock(\Magento\Customer\Model\Data\CustomerSecure::class, [ @@ -287,28 +252,6 @@ public function testSave() $this->customerRegistry->expects($this->atLeastOnce()) ->method("remove") ->with($customerId); - $address->expects($this->once()) - ->method('setCustomerId') - ->with($customerId) - ->willReturnSelf(); - $address->expects($this->once()) - ->method('getRegion') - ->willReturn($region); - $address->expects($this->atLeastOnce()) - ->method('getId') - ->willReturn(7); - $address->expects($this->once()) - ->method('setRegion') - ->with($region); - $customerAttributesMetaData->expects($this->atLeastOnce()) - ->method('getAddresses') - ->willReturn([$address]); - $customerAttributesMetaData->expects($this->at(1)) - ->method('setAddresses') - ->with([]); - $customerAttributesMetaData->expects($this->at(2)) - ->method('setAddresses') - ->with([$address]); $this->extensibleDataObjectConverter->expects($this->once()) ->method('toNestedArray') ->with($customerAttributesMetaData, [], \Magento\Customer\Api\Data\CustomerInterface::class) @@ -393,12 +336,6 @@ public function testSave() $this->customerRegistry->expects($this->once()) ->method('push') ->with($customerModel); - $this->customer->expects($this->once()) - ->method('getAddresses') - ->willReturn([$address, $address2]); - $this->addressRepository->expects($this->once()) - ->method('save') - ->with($address); $customerAttributesMetaData->expects($this->once()) ->method('getEmail') ->willReturn('example@example.com'); @@ -446,41 +383,8 @@ public function testSaveWithPasswordHash() '', false ); - $address = $this->getMockForAbstractClass( - \Magento\Customer\Api\Data\AddressInterface::class, - [], - '', - false, - false, - true, - [ - 'setCustomerId', - 'setRegion', - 'getRegion', - 'getId' - ] - ); - $address2 = $this->getMockForAbstractClass( - \Magento\Customer\Api\Data\AddressInterface::class, - [], - '', - false, - false, - true, - [ - 'setCustomerId', - 'setRegion', - 'getRegion', - 'getId' - ] - ); - $origCustomer = $this->customer; - $this->customer->expects($this->atLeastOnce()) - ->method('__toArray') - ->willReturn(['default_billing', 'default_shipping']); - $customerModel = $this->createPartialMock(\Magento\Customer\Model\Customer::class, [ 'getId', 'setId', @@ -505,8 +409,6 @@ public function testSaveWithPasswordHash() 'getId', 'getEmail', 'getWebsiteId', - 'getAddresses', - 'setAddresses' ] ); $customerModel->expects($this->atLeastOnce()) @@ -559,28 +461,6 @@ public function testSaveWithPasswordHash() ->method('save') ->with($this->customer, CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, $this->customer) ->willReturn($customerAttributesMetaData); - $address->expects($this->once()) - ->method('setCustomerId') - ->with($customerId) - ->willReturnSelf(); - $address->expects($this->once()) - ->method('getRegion') - ->willReturn($region); - $address->expects($this->atLeastOnce()) - ->method('getId') - ->willReturn(7); - $address->expects($this->once()) - ->method('setRegion') - ->with($region); - $customerAttributesMetaData->expects($this->any()) - ->method('getAddresses') - ->willReturn([$address]); - $customerAttributesMetaData->expects($this->at(1)) - ->method('setAddresses') - ->with([]); - $customerAttributesMetaData->expects($this->at(2)) - ->method('setAddresses') - ->with([$address]); $customerAttributesMetaData ->expects($this->atLeastOnce()) ->method('getId') @@ -618,12 +498,6 @@ public function testSaveWithPasswordHash() $this->customerRegistry->expects($this->once()) ->method('push') ->with($customerModel); - $this->customer->expects($this->any()) - ->method('getAddresses') - ->willReturn([$address, $address2]); - $this->addressRepository->expects($this->once()) - ->method('save') - ->with($address); $customerAttributesMetaData->expects($this->once()) ->method('getEmail') ->willReturn('example@example.com'); diff --git a/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php b/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php new file mode 100644 index 0000000000000..82ce4249bcd85 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php @@ -0,0 +1,69 @@ +context = $this->getMockForAbstractClass( + \Magento\Framework\View\Element\UiComponent\ContextInterface::class + ); + $this->fieldset = new AddressFieldset( + $this->context, + [], + [] + ); + } + + /** + * Run test for canShow() method + * + * @return void + * + */ + public function testCanShow() + { + $this->context->expects($this->atLeastOnce())->method('getRequestParam')->with('id') + ->willReturn(1); + $this->assertEquals(true, $this->fieldset->canShow()); + } + + /** + * Run test for canShow() method without customer id in context + * + * @return void + * + */ + public function testCanShowWithoutId() + { + $this->context->expects($this->atLeastOnce())->method('getRequestParam')->with('id') + ->willReturn(null); + $this->assertEquals(false, $this->fieldset->canShow()); + } +} diff --git a/app/code/Magento/Customer/Ui/Component/Form/AddressFieldset.php b/app/code/Magento/Customer/Ui/Component/Form/AddressFieldset.php new file mode 100644 index 0000000000000..4d7464b321e85 --- /dev/null +++ b/app/code/Magento/Customer/Ui/Component/Form/AddressFieldset.php @@ -0,0 +1,45 @@ +context = $context; + + parent::__construct($context, $components, $data); + } + + /** + * Can show customer addresses tab in tabs or not + * + * Will return false for not registered customer in a case when admin user created new customer account. + * Needed to hide addresses tab from create new customer page + * + * @return boolean + */ + public function canShow(): bool + { + $customerId = $this->context->getRequestParam('id'); + return (bool)$customerId; + } +} diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php new file mode 100644 index 0000000000000..75c02974ded8f --- /dev/null +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php @@ -0,0 +1,134 @@ +urlBuilder = $urlBuilder; + parent::__construct($context, $uiComponentFactory, $components, $data); + } + + /** + * Prepare Data Source + * + * @param array $dataSource + * @return array + */ + public function prepareDataSource(array $dataSource): array + { + if (isset($dataSource['data']['items'])) { + foreach ($dataSource['data']['items'] as &$item) { + $name = $this->getData('name'); + if (isset($item['entity_id'])) { + $item[$name]['edit'] = [ + 'callback' => [ + [ + 'provider' => 'customer_form.areas.address.address' + . '.customer_address_update_modal.update_customer_address_form_loader', + 'target' => 'destroyInserted', + ], + [ + 'provider' => 'customer_form.areas.address.address' + . '.customer_address_update_modal', + 'target' => 'openModal', + ], + [ + 'provider' => 'customer_form.areas.address.address' + . '.customer_address_update_modal.update_customer_address_form_loader', + 'target' => 'render', + 'params' => [ + 'entity_id' => $item['entity_id'], + ], + ] + ], + 'href' => '#', + 'label' => __('Edit'), + 'hidden' => false, + ]; + + $item[$name]['set_default_shipping'] = [ + 'href' => $this->urlBuilder->getUrl( + self::CUSTOMER_ADDRESS_PATH_DEFAULT_SHIPPING, + ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] + ), + 'label' => __('Set as default shipping'), + 'confirm' => [ + 'title' => __('Set address as default shipping'), + 'message' => __( + 'Are you sure you want to set the address with ID: %1 as default shipping address?', + $item['entity_id'] + ) + ] + ]; + + $item[$name]['set_default_billing'] = [ + 'href' => $this->urlBuilder->getUrl( + self::CUSTOMER_ADDRESS_PATH_DEFAULT_BILLING, + ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] + ), + 'label' => __('Set as default billing'), + 'confirm' => [ + 'title' => __('Set address as default billing'), + 'message' => __( + 'Are you sure you want to set the address with ID: %1 as default billing address?', + $item['entity_id'] + ) + ] + ]; + + $item[$name]['delete'] = [ + 'href' => $this->urlBuilder->getUrl( + self::CUSTOMER_ADDRESS_PATH_DELETE, + ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] + ), + 'label' => __('Delete'), + 'confirm' => [ + 'title' => __('Delete address'), + 'message' => __( + 'Are you sure you want to delete the address with ID: %1?', + $item['entity_id'] + ) + ] + ]; + } + } + } + + return $dataSource; + } +} diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php new file mode 100644 index 0000000000000..438a16ec3ba55 --- /dev/null +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php @@ -0,0 +1,37 @@ +countryCollectionFactory = $collectionFactory; + } + + /** + * Get list of countries with country id as value and code as label + * + * @return array + */ + public function toOptionArray(): array + { + /** @var \Magento\Directory\Model\ResourceModel\Country\Collection $countryCollection */ + $countryCollection = $this->countryCollectionFactory->create(); + return $countryCollection->toOptionArray(); + } +} diff --git a/app/code/Magento/Customer/etc/db_schema.xml b/app/code/Magento/Customer/etc/db_schema.xml index 7971627521740..7e0b911e26184 100644 --- a/app/code/Magento/Customer/etc/db_schema.xml +++ b/app/code/Magento/Customer/etc/db_schema.xml @@ -120,6 +120,15 @@ + + + + + + + + + diff --git a/app/code/Magento/Customer/etc/di.xml b/app/code/Magento/Customer/etc/di.xml index 6e8c3dc68ed28..a63bff59f1dcf 100644 --- a/app/code/Magento/Customer/etc/di.xml +++ b/app/code/Magento/Customer/etc/di.xml @@ -223,6 +223,7 @@ Magento\Customer\Model\ResourceModel\Grid\CollectionMagento\Customer\Model\ResourceModel\Online\Grid\CollectionMagento\Customer\Model\ResourceModel\Group\Grid\Collection + Magento\Customer\Model\ResourceModel\Address\Grid\Collection @@ -449,6 +450,14 @@ Magento\Customer\Model\ResourceModel\Group + + + customer_address_entity + customer_address_entity_grid_collection + customer_address_entity_grid_collection + Magento\Customer\Model\ResourceModel\Address + + diff --git a/app/code/Magento/Customer/view/adminhtml/layout/customer_address_edit.xml b/app/code/Magento/Customer/view/adminhtml/layout/customer_address_edit.xml new file mode 100644 index 0000000000000..3acae3acec8aa --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/layout/customer_address_edit.xml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml new file mode 100644 index 0000000000000..a628c1b1b0b1e --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -0,0 +1,230 @@ + + +
+ + + customer_address_form.customer_address_form_data_source + + Update Address + true + templates/form/collapsible + + + + + + - address + shipping-address + Default Shipping Address + customer-default-shipping-address-content + The customer does not have default shipping address - - true - - text - - - - - - address - - - - - true - - text - ${ $.provider }:data.customer.website_id + ${ $.provider}:data.default_shipping_address - - - - - address - - - - text - false - - - - - - address - - - - - true - - text - - - - - - - - - address - - - - - true - - text - - - - - - address - - - - text - - - - - - address - - - - - 0 - - text - - - - - - address + + + + + - ui/form/element/checkbox - boolean + + + - - - - Default Shipping Address - - - - + + + + + customer_address_edit + 1 + + false + ${ $.parentName } + ${ $.ns }.customer_address_form_data_source + customer_address_form + + + + + + + false + true + + customer_address_listing.customer_address_listing_data_source + customer_address_listing.customer_address_listing.customer_address_listing_columns.ids + true + customer_address_listing + customer_address_listing + + ${ $.externalProvider }:params.parent_id + + + ${ $.provider }:data.customer.entity_id + + + diff --git a/app/code/Magento/Ui/Component/Form/Element/DataType/Media.php b/app/code/Magento/Ui/Component/Form/Element/DataType/Media.php index c2460fd8385c1..f15a97e96c549 100644 --- a/app/code/Magento/Ui/Component/Form/Element/DataType/Media.php +++ b/app/code/Magento/Ui/Component/Form/Element/DataType/Media.php @@ -35,6 +35,7 @@ public function prepare() $this->getData(), [ 'config' => [ + 'dataScope' => $this->getName(), 'uploaderConfig' => [ 'url' => $url ], diff --git a/app/code/Magento/Ui/Component/Form/Fieldset.php b/app/code/Magento/Ui/Component/Form/Fieldset.php index 745068ca0fa8a..ebfe58f3abb89 100644 --- a/app/code/Magento/Ui/Component/Form/Fieldset.php +++ b/app/code/Magento/Ui/Component/Form/Fieldset.php @@ -5,11 +5,7 @@ */ namespace Magento\Ui\Component\Form; -use Magento\Ui\Component\Container; use Magento\Ui\Component\AbstractComponent; -use Magento\Framework\View\Element\UiComponentFactory; -use Magento\Framework\View\Element\UiComponentInterface; -use Magento\Framework\View\Element\UiComponent\ContextInterface; /** * @api @@ -33,4 +29,14 @@ public function getComponentName() { return static::NAME; } + + /** + * Check that fieldset can be shown. + * + * @return bool + */ + public function canShow(): bool + { + return true; + } } diff --git a/app/code/Magento/Ui/Component/Layout/Tabs.php b/app/code/Magento/Ui/Component/Layout/Tabs.php index 8ceac716ae218..02e8979f525ef 100644 --- a/app/code/Magento/Ui/Component/Layout/Tabs.php +++ b/app/code/Magento/Ui/Component/Layout/Tabs.php @@ -11,6 +11,7 @@ use Magento\Framework\View\Element\UiComponent\LayoutInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Framework\View\Element\UiComponentInterface; +use Magento\Ui\Component\Form\Fieldset; use Magento\Ui\Component\Layout\Tabs\TabInterface; /** @@ -89,6 +90,9 @@ protected function addChildren(array &$topNode, UiComponentInterface $component, $this->addWrappedBlock($childComponent, $childrenAreas); continue; } + if ($childComponent instanceof Fieldset && false === $childComponent->canShow()) { + continue; + } $name = $childComponent->getName(); $config = $childComponent->getData('config'); diff --git a/app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php b/app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php new file mode 100644 index 0000000000000..9a8cf28ae0719 --- /dev/null +++ b/app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php @@ -0,0 +1,69 @@ +context = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\ContextInterface::class) + ->getMockForAbstractClass(); + + $this->fieldset = new Fieldset( + $this->context, + [], + [] + ); + } + + /** + * Run test for getComponentName() method + * + * @return void + * + */ + public function testGetComponentName() + { + $this->assertEquals(self::NAME, $this->fieldset->getComponentName()); + } + + /** + * Run test for canShow() method + * + * @return void + * + */ + public function testCanShow() + { + $this->assertEquals(true, $this->fieldset->canShow()); + } +} diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/collection.js b/app/code/Magento/Ui/view/base/web/js/form/components/collection.js index 2c12486ceb519..dbea61b13e626 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/collection.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/collection.js @@ -5,6 +5,9 @@ /** * @api + * @deprecated as customer addresses are handled by ui components. + * This collection component manages rendering address list in Addresses tab of customer. + * Now address list is rendered with ui component listing. */ define([ 'underscore', @@ -46,6 +49,7 @@ define([ * @param {Object} elem - Incoming child. */ initElement: function (elem) { + debugger; this._super(); elem.activate(); @@ -153,6 +157,7 @@ define([ * Creates function that removes element * from collection using '_removeChild' method. * @param {Object} elem - Element that should be removed. + * @deprecated Not used anymore */ removeAddress: function (elem) { var self = this; @@ -169,7 +174,7 @@ define([ }, /** - * Removes elememt from both collection and data storage, + * Removes element from both collection and data storage, * activates first element if removed one was active, * triggers 'update' event. * diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js b/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js index c2a65371471c5..ae7f375bdca02 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js @@ -5,6 +5,9 @@ /** * @api + * @deprecated as customer addresses are handled by ui components. + * This item component renders address list item preview in Addresses tab. + * But now address list item is rendered with ui component in customer form. */ define([ 'underscore', @@ -19,7 +22,7 @@ define([ }; /** - * Parses incoming data and returnes result merged with default preview config + * Parses incoming data and returns result merged with default preview config * * @param {Object|String} data * @return {Object} diff --git a/app/code/Magento/Ui/view/base/web/templates/form/insert.html b/app/code/Magento/Ui/view/base/web/templates/form/insert.html index e19b2784e6bc6..e590b5e2adedf 100644 --- a/app/code/Magento/Ui/view/base/web/templates/form/insert.html +++ b/app/code/Magento/Ui/view/base/web/templates/form/insert.html @@ -6,9 +6,6 @@ -->
diff --git a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less index a80cc9a5163f8..8a5fe698eb196 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less @@ -3,14 +3,72 @@ // * See COPYING.txt for license details. // */ -// General rule hides group legend and shows first field label instead -// in app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less -// This must be reset for Customer Address page -.address-item-edit-content { +.customer_form_areas_address_address_customer_address_update_modal_update_customer_address_form_loader { .admin__field { - legend { - &.admin__field-label { - opacity: 1; + .admin__field { + .admin__field-label { + background: none; + } + } + } +} + +.customer-address-form { + + *, *:before, *:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + + address { + font-style: normal; + } + + .customer-default-billing-address-content, + .customer-default-shipping-address-content + { + float: left; + width: 550px; + } + + .edit-default-billing-address-button, + .edit-default-shipping-address-button { + float: left; + position: relative; + top: 2px; + } + + .edit-default-billing-address-button { + left: -336px; + } + + .edit-default-shipping-address-button { + left: -315px; + } + + .customer_form_areas_address_address_customer_address_listing { + clear: both; + } + + .add-new-address-button { + position: relative; + clear: both; + float: right; + margin-bottom: 30px; + } + + .address-information { + float: left; + margin-bottom: 20px; + + address { + float: left; + + .address_caption { + font-size: 18px; + font-weight: bold; + margin-bottom: 16px; } } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php new file mode 100644 index 0000000000000..6a41dd70e89e2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php @@ -0,0 +1,222 @@ +customerRepository = Bootstrap::getObjectManager()->get( + \Magento\Customer\Api\CustomerRepositoryInterface::class + ); + $this->accountManagement = Bootstrap::getObjectManager()->get( + \Magento\Customer\Api\AccountManagementInterface::class + ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->customerAddress = $this->objectManager->get(\Magento\Customer\Controller\Adminhtml\Address\Save::class); + } + + /** + * @inheritDoc + */ + protected function tearDown() + { + /** + * Unset customer data + */ + Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->setCustomerData(null); + + /** + * Unset messages + */ + Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getMessages(true); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * + * Check that customer id set and addresses saved + */ + public function testSaveActionWithValidAddressData() + { + $customer = $this->customerRepository->get('customer5@example.com'); + $customerId = $customer->getId(); + $post = [ + 'parent_id' => $customerId, + 'firstname' => 'test firstname', + 'lastname' => 'test lastname', + 'street' => ['test street'], + 'city' => 'test city', + 'region_id' => 10, + 'country_id' => 'US', + 'postcode' => '01001', + 'telephone' => '+7000000001', + ]; + $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); + + $this->objectManager->get(\Magento\Backend\Model\Session::class)->setCustomerFormData($post); + + $this->customerAddress->execute(); + /** + * Check that errors was generated and set to session + */ + $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); + + /** + * Check that customer data were cleaned after it was saved successfully + */ + $this->assertEmpty($this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerData()); + + /** + * Check that success message is set + */ + $this->assertSessionMessages( + $this->logicalNot($this->isEmpty()), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + + $customer = $this->customerRepository->getById($customerId); + + $this->assertEquals('Firstname', $customer->getFirstname()); + $addresses = $customer->getAddresses(); + $this->assertCount(1, $addresses); + $this->assertNull($this->accountManagement->getDefaultBillingAddress($customerId)); + $this->assertNull($this->accountManagement->getDefaultShippingAddress($customerId)); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * + * Check that customer id set and addresses saved + */ + public function testSaveActionWithDefaultShippingAndBilling() + { + $customer = $this->customerRepository->get('customer5@example.com'); + $customerId = $customer->getId(); + $post = [ + 'parent_id' => $customerId, + 'firstname' => 'test firstname', + 'lastname' => 'test lastname', + 'street' => ['test street'], + 'city' => 'test city', + 'region_id' => 10, + 'country_id' => 'US', + 'postcode' => '01001', + 'telephone' => '+7000000001', + 'default_billing' => true, + 'default_shipping' => true + ]; + $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); + + $this->objectManager->get(\Magento\Backend\Model\Session::class)->setCustomerFormData($post); + + $this->customerAddress->execute(); + /** + * Check that errors was generated and set to session + */ + $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); + + /** + * Check that customer data were cleaned after it was saved successfully + */ + $this->assertEmpty($this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerData()); + + /** + * Check that success message is set + */ + $this->assertSessionMessages( + $this->logicalNot($this->isEmpty()), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + + /** + * Remove stored customer from registry + */ + $this->objectManager->get(\Magento\Customer\Model\CustomerRegistry::class)->remove($customerId); + $customer = $this->customerRepository->get('customer5@example.com'); + $this->assertEquals('Firstname', $customer->getFirstname()); + $addresses = $customer->getAddresses(); + $this->assertCount(1, $addresses); + + $this->assertNotNull($this->accountManagement->getDefaultBillingAddress($customerId)); + $this->assertNotNull($this->accountManagement->getDefaultShippingAddress($customerId)); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer_sample.php + * + * Check that customer id set and addresses saved + */ + public function testSaveActionWithExistingAdresses() + { + $customer = $this->customerRepository->get('customer@example.com'); + $customerId = $customer->getId(); + $post = [ + 'parent_id' => $customerId, + 'firstname' => 'test firstname', + 'lastname' => 'test lastname', + 'street' => ['test street'], + 'city' => 'test city', + 'region_id' => 10, + 'country_id' => 'US', + 'postcode' => '01001', + 'telephone' => '+7000000001', + ]; + $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); + + $this->objectManager->get(\Magento\Backend\Model\Session::class)->setCustomerFormData($post); + + $this->customerAddress->execute(); + /** + * Check that errors was generated and set to session + */ + $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); + + /** + * Check that customer data were cleaned after it was saved successfully + */ + $this->assertEmpty($this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerData()); + + /** + * Check that success message is set + */ + $this->assertSessionMessages( + $this->logicalNot($this->isEmpty()), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + + $customer = $this->customerRepository->getById($customerId); + + $this->assertEquals('test firstname', $customer->getFirstname()); + $addresses = $customer->getAddresses(); + $this->assertCount(4, $addresses); + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt index 877583e5b6a29..5c5c380c4dd33 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt @@ -1 +1 @@ -# Format: or simply +# Format: or simply \ No newline at end of file diff --git a/setup/performance-toolkit/config/customerConfig.xml b/setup/performance-toolkit/config/customerConfig.xml index 8fd74d7b53885..b77d9f6d4c941 100644 --- a/setup/performance-toolkit/config/customerConfig.xml +++ b/setup/performance-toolkit/config/customerConfig.xml @@ -6,5 +6,5 @@ */ --> - 2 + 5 From e2b3f791b49e961f757685eaa06b3f221b718148 Mon Sep 17 00:00:00 2001 From: Ruslan Kostiv Date: Fri, 26 Oct 2018 16:35:24 +0300 Subject: [PATCH 02/77] MAGETWO-95671: Remove address id from dialog alerts --- .../Component/Listing/Address/Column/Actions.php | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php index 75c02974ded8f..e0fea85bd3d80 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php @@ -89,10 +89,7 @@ public function prepareDataSource(array $dataSource): array 'label' => __('Set as default shipping'), 'confirm' => [ 'title' => __('Set address as default shipping'), - 'message' => __( - 'Are you sure you want to set the address with ID: %1 as default shipping address?', - $item['entity_id'] - ) + 'message' => __('Are you sure you want to set the address as default shipping address?') ] ]; @@ -104,10 +101,7 @@ public function prepareDataSource(array $dataSource): array 'label' => __('Set as default billing'), 'confirm' => [ 'title' => __('Set address as default billing'), - 'message' => __( - 'Are you sure you want to set the address with ID: %1 as default billing address?', - $item['entity_id'] - ) + 'message' => __('Are you sure you want to set the address as default billing address?') ] ]; @@ -119,10 +113,7 @@ public function prepareDataSource(array $dataSource): array 'label' => __('Delete'), 'confirm' => [ 'title' => __('Delete address'), - 'message' => __( - 'Are you sure you want to delete the address with ID: %1?', - $item['entity_id'] - ) + 'message' => __('Are you sure you want to delete the address?') ] ]; } From f1a23e4418aeb12f0011af06dc40e2b3b1f17539 Mon Sep 17 00:00:00 2001 From: Ruslan Kostiv Date: Fri, 26 Oct 2018 16:40:35 +0300 Subject: [PATCH 03/77] MAGETWO-95671: Remove address id from dialog alerts --- .../Listing/Address/Column/Actions.php | 20 +++++++++---------- .../ui_component/customer_address_form.xml | 12 +++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php index e0fea85bd3d80..e44dc7988761f 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php @@ -81,27 +81,27 @@ public function prepareDataSource(array $dataSource): array 'hidden' => false, ]; - $item[$name]['set_default_shipping'] = [ + $item[$name]['set_default_billing'] = [ 'href' => $this->urlBuilder->getUrl( - self::CUSTOMER_ADDRESS_PATH_DEFAULT_SHIPPING, + self::CUSTOMER_ADDRESS_PATH_DEFAULT_BILLING, ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] ), - 'label' => __('Set as default shipping'), + 'label' => __('Set as default billing'), 'confirm' => [ - 'title' => __('Set address as default shipping'), - 'message' => __('Are you sure you want to set the address as default shipping address?') + 'title' => __('Set address as default billing'), + 'message' => __('Are you sure you want to set the address as default billing address?') ] ]; - $item[$name]['set_default_billing'] = [ + $item[$name]['set_default_shipping'] = [ 'href' => $this->urlBuilder->getUrl( - self::CUSTOMER_ADDRESS_PATH_DEFAULT_BILLING, + self::CUSTOMER_ADDRESS_PATH_DEFAULT_SHIPPING, ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] ), - 'label' => __('Set as default billing'), + 'label' => __('Set as default shipping'), 'confirm' => [ - 'title' => __('Set address as default billing'), - 'message' => __('Are you sure you want to set the address as default billing address?') + 'title' => __('Set address as default shipping'), + 'message' => __('Are you sure you want to set the address as default shipping address?') ] ]; diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index a628c1b1b0b1e..9e432f9f10e0c 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -60,7 +60,7 @@ text
- + 0 @@ -68,8 +68,8 @@ boolean - - default_shipping + + default_billing @@ -83,7 +83,7 @@ - + 0 @@ -91,8 +91,8 @@ boolean - - default_billing + + default_shipping From d51c135b4b27c69e36bf45f6caf7ba92c0c67131 Mon Sep 17 00:00:00 2001 From: Ruslan Kostiv Date: Fri, 26 Oct 2018 18:23:15 +0300 Subject: [PATCH 04/77] MAGETWO-95687: Remove old implementation of customer addresses tab --- .../Adminhtml/Edit/Address/CancelButton.php | 2 +- .../Adminhtml/Edit/Address/SaveButton.php | 2 +- .../Controller/Adminhtml/Address/Save.php | 3 +- .../Controller/Adminhtml/Address/Validate.php | 4 +-- .../Controller/Adminhtml/Index/Save.php | 2 ++ .../Controller/Adminhtml/Index/Validate.php | 3 ++ .../Customer/Model/Address/DataProvider.php | 31 +++++-------------- .../Customer/Model/Customer/DataProvider.php | 1 + .../DataProviderWithDefaultAddresses.php | 20 +----------- .../Customer/Model/Metadata/Form/File.php | 17 +++++----- .../ResourceModel/Address/Grid/Collection.php | 4 +-- .../Test/Unit/Controller/Address/SaveTest.php | 2 -- .../Unit/Model/Customer/DataProviderTest.php | 1 + .../Magento/Ui/Component/Form/Fieldset.php | 2 ++ 14 files changed, 34 insertions(+), 60 deletions(-) diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php index 80d9780f819d0..6270e8073d0e8 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php @@ -14,7 +14,7 @@ class CancelButton extends GenericButton implements ButtonProviderInterface { /** - * {@inheritdoc} + * @inheritdoc * * @return array */ diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php index 706ef32c9e5a5..0d3a9f57ff5a3 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php @@ -14,7 +14,7 @@ class SaveButton extends GenericButton implements ButtonProviderInterface { /** - * {@inheritdoc} + * @inheritdoc * * @return array */ diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php index df041ac4e1202..e1d605a8d0890 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php @@ -135,7 +135,8 @@ public function execute(): Redirect $this->logger->critical($e); } catch (\Exception $e) { $this->messageManager->addExceptionMessage( - $e, __('We can\'t change customer address right now.') + $e, + __('We can\'t change customer address right now.') ); } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php index 01ce720a20e63..696bf3099db11 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php @@ -33,9 +33,9 @@ class Validate extends \Magento\Backend\App\Action implements HttpPostActionInte private $formFactory; /** - * @param Action\Context $context + * @param Action\Context $context * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory - * @param \Magento\Customer\Model\Metadata\FormFactory $formFactory + * @param \Magento\Customer\Model\Metadata\FormFactory $formFactory */ public function __construct( Action\Context $context, diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index aed7908337ee1..d80c712914ef0 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -15,6 +15,8 @@ use Magento\Framework\Exception\LocalizedException; /** + * Save customer action. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Save extends \Magento\Customer\Controller\Adminhtml\Index implements HttpPostActionInterface diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php index 67adf98d6c718..d91bc7424bffe 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php @@ -11,6 +11,9 @@ use Magento\Framework\Message\Error; use Magento\Customer\Controller\Adminhtml\Index as CustomerAction; +/** + * Class for validation of customer + */ class Validate extends CustomerAction implements HttpPostActionInterface, HttpGetActionInterface { /** diff --git a/app/code/Magento/Customer/Model/Address/DataProvider.php b/app/code/Magento/Customer/Model/Address/DataProvider.php index 34f4b8b4eca89..c92cd731db743 100644 --- a/app/code/Magento/Customer/Model/Address/DataProvider.php +++ b/app/code/Magento/Customer/Model/Address/DataProvider.php @@ -27,9 +27,9 @@ use Magento\Customer\Model\FileProcessorFactory; /** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * * Dataprovider for customer address grid. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider { @@ -53,11 +53,6 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider */ private $loadedData; - /** - * @var Config - */ - private $eavConfig; - /** * EAV attribute properties to fetch from meta storage * @var array @@ -154,6 +149,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider * @param ContextInterface $context * @param FileProcessorFactory $fileProcessorFactory * @param \Magento\Customer\Model\Config\Share $shareConfig + * @param CountryWithWebsites $countryWithWebsites * @param array $meta * @param array $data * @param bool $allowToShowHiddenAttributes @@ -170,6 +166,7 @@ public function __construct( ContextInterface $context, FileProcessorFactory $fileProcessorFactory, \Magento\Customer\Model\Config\Share $shareConfig, + CountryWithWebsites $countryWithWebsites, array $meta = [], array $data = [], $allowToShowHiddenAttributes = true @@ -182,6 +179,7 @@ public function __construct( $this->allowToShowHiddenAttributes = $allowToShowHiddenAttributes; $this->context = $context; $this->fileProcessorFactory = $fileProcessorFactory; + $this->countryWithWebsiteSource = $countryWithWebsites; $this->shareConfig = $shareConfig; $this->meta['general']['children'] = $this->getAttributesMeta( $eavConfig->getEntityType('customer_address') @@ -323,7 +321,7 @@ protected function getAttributesMeta(Type $entityType): array if ($attribute->usesSource()) { if ($code == AddressInterface::COUNTRY_ID) { - $meta[$code]['arguments']['data']['config']['options'] = $this->getCountryWithWebsiteSource() + $meta[$code]['arguments']['data']['config']['options'] = $this->countryWithWebsiteSource ->getAllOptions(); } else { $meta[$code]['arguments']['data']['config']['options'] = $attribute->getSource()->getAllOptions(); @@ -364,25 +362,10 @@ private function processFrontendInput(AttributeInterface $attribute, array &$met } } - /** - * Retrieve Country With Websites Source - * - * @return CountryWithWebsites - * @deprecated 100.2.0 - */ - private function getCountryWithWebsiteSource(): CountryWithWebsites - { - if (!$this->countryWithWebsiteSource) { - $this->countryWithWebsiteSource = ObjectManager::getInstance()->get(CountryWithWebsites::class); - } - - return $this->countryWithWebsiteSource; - } - /** * Detect can we show attribute on specific form or not * - * @param Attribute $customerAttribute + * @param AbstractAttribute $customerAttribute * @return bool */ private function canShowAttribute(AbstractAttribute $customerAttribute): bool diff --git a/app/code/Magento/Customer/Model/Customer/DataProvider.php b/app/code/Magento/Customer/Model/Customer/DataProvider.php index ce976d3f62c74..c834dd26824cd 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProvider.php +++ b/app/code/Magento/Customer/Model/Customer/DataProvider.php @@ -31,6 +31,7 @@ /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * + * @deprecated \Magento\Customer\Model\Address\DataProvider is used instead * @api * @since 100.0.2 */ diff --git a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php index d52c94fb034c6..2e47787b9adbc 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php +++ b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php @@ -186,9 +186,6 @@ public function __construct( $this->meta['customer']['children'] = $this->getAttributesMeta( $eavConfig->getEntityType('customer') ); -// $this->meta['address']['children'] = $this->getAttributesMeta( -// $eavConfig->getEntityType('customer_address') -// ); } /** @@ -300,7 +297,7 @@ private function getFileUploaderData( $file = $customerData[$attributeCode] ?? ''; /** @var FileProcessor $fileProcessor */ - $fileProcessor = $this->getFileProcessorFactory()->create([ + $fileProcessor = $this->fileProcessorFactory->create([ 'entityTypeCode' => $entityType->getEntityTypeCode(), ]); @@ -567,19 +564,4 @@ protected function prepareAddressData($addressId, array &$addresses, array $cust $addresses[$addressId]['street'] = explode("\n", $addresses[$addressId]['street']); } } - - /** - * Get FileProcessorFactory instance - * - * @return FileProcessorFactory - * @deprecated 100.1.3 - */ - private function getFileProcessorFactory(): FileProcessorFactory - { - if ($this->fileProcessorFactory === null) { - $this->fileProcessorFactory = ObjectManager::getInstance() - ->get(\Magento\Customer\Model\FileProcessorFactory::class); - } - return $this->fileProcessorFactory; - } } diff --git a/app/code/Magento/Customer/Model/Metadata/Form/File.php b/app/code/Magento/Customer/Model/Metadata/Form/File.php index aca5b277186ca..b9bec01f9ba7c 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/File.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/File.php @@ -15,6 +15,8 @@ use Magento\Framework\Filesystem; /** + * Processes files that are save for customer. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class File extends AbstractData @@ -66,7 +68,7 @@ class File extends AbstractData * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute * @param \Magento\Framework\Locale\ResolverInterface $localeResolver - * @param null $value + * @param array|string $value * @param string $entityTypeCode * @param bool $isAjax * @param \Magento\Framework\Url\EncoderInterface $urlEncoder @@ -101,7 +103,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function extractValue(\Magento\Framework\App\RequestInterface $request) @@ -160,8 +162,7 @@ public function extractValue(\Magento\Framework\App\RequestInterface $request) } /** - * Validate file by attribute validate rules - * Return array of errors + * Validate file by attribute validate rules. Returns array of errors. * * @param array $value * @return string[] @@ -232,7 +233,7 @@ protected function _isUploadedFile($filename) } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -273,7 +274,7 @@ public function validateValue($value) } /** - * {@inheritdoc} + * @inheritdoc * * @return ImageContentInterface|array|string|null */ @@ -358,7 +359,7 @@ protected function processInputFieldValue($value) } /** - * {@inheritdoc} + * @inheritdoc */ public function restoreValue($value) { @@ -366,7 +367,7 @@ public function restoreValue($value) } /** - * {@inheritdoc} + * @inheritdoc */ public function outputValue($format = \Magento\Customer\Model\Metadata\ElementFactory::OUTPUT_FORMAT_TEXT) { diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php index 83129fee9b59b..8026349563867 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php @@ -92,7 +92,7 @@ protected function _initSelect() } /** - * {@inheritdoc} + * @inheritdoc * * @return AggregationInterface */ @@ -102,7 +102,7 @@ public function getAggregations() } /** - * {@inheritdoc} + * @inheritdoc * * @param AggregationInterface $aggregations * @return $this diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php index 47088eece23ed..863ce260ab68c 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php @@ -24,7 +24,6 @@ class SaveTest extends \PHPUnit\Framework\TestCase */ private $addressRepositoryMock; - /** * @var \Magento\Customer\Model\Metadata\FormFactory|\PHPUnit_Framework_MockObject_MockObject */ @@ -65,7 +64,6 @@ class SaveTest extends \PHPUnit\Framework\TestCase */ private $messageManagerMock; - /** * @inheritdoc */ diff --git a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php index 50c21379054bf..bf6d3f0f9bbc5 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php @@ -21,6 +21,7 @@ * * Test for class \Magento\Customer\Model\Customer\DataProvider * + * @deprecated tested class is not used. * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DataProviderTest extends \PHPUnit\Framework\TestCase diff --git a/app/code/Magento/Ui/Component/Form/Fieldset.php b/app/code/Magento/Ui/Component/Form/Fieldset.php index ebfe58f3abb89..ef115fe459eba 100644 --- a/app/code/Magento/Ui/Component/Form/Fieldset.php +++ b/app/code/Magento/Ui/Component/Form/Fieldset.php @@ -8,6 +8,8 @@ use Magento\Ui\Component\AbstractComponent; /** + * Fieldset UI Component. + * * @api * @since 100.0.2 */ From dc506771ce4353b0d3a272368acd7322dbf391c8 Mon Sep 17 00:00:00 2001 From: Ruslan Kostiv Date: Fri, 26 Oct 2018 19:16:12 +0300 Subject: [PATCH 05/77] MAGETWO-95687: Remove old implementation of customer addresses tab --- .../Customer/Model/Address/DataProvider.php | 2 +- .../DataProviderWithDefaultAddresses.php | 25 ------------------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/app/code/Magento/Customer/Model/Address/DataProvider.php b/app/code/Magento/Customer/Model/Address/DataProvider.php index c92cd731db743..7f23deddd89a6 100644 --- a/app/code/Magento/Customer/Model/Address/DataProvider.php +++ b/app/code/Magento/Customer/Model/Address/DataProvider.php @@ -384,7 +384,7 @@ private function canShowAttribute(AbstractAttribute $customerAttribute): bool /** * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... * - * @param Attribute $customerAttribute + * @param AbstractAttribute $customerAttribute * @return bool */ private function canShowAttributeInForm(AbstractAttribute $customerAttribute): bool diff --git a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php index 2e47787b9adbc..7ba6484dc3f2b 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php +++ b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php @@ -539,29 +539,4 @@ private function processFrontendInput(AttributeInterface $attribute, array &$met ]; } } - - /** - * Prepare address data - * - * @param int $addressId - * @param array $addresses - * @param array $customer - * @return void - */ - protected function prepareAddressData($addressId, array &$addresses, array $customer) - { - if (isset($customer['default_billing']) - && $addressId == $customer['default_billing'] - ) { - $addresses[$addressId]['default_billing'] = $customer['default_billing']; - } - if (isset($customer['default_shipping']) - && $addressId == $customer['default_shipping'] - ) { - $addresses[$addressId]['default_shipping'] = $customer['default_shipping']; - } - if (isset($addresses[$addressId]['street']) && !\is_array($addresses[$addressId]['street'])) { - $addresses[$addressId]['street'] = explode("\n", $addresses[$addressId]['street']); - } - } } From 63d8818f8ed7ae112cb758e15f1ca967c10ca9f2 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Tue, 30 Oct 2018 18:25:45 +0200 Subject: [PATCH 06/77] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Remove redundant js files; - Refactored customer and customer address data providers; --- .../Address/AbstractDefaultAddress.php | 2 +- .../Controller/Adminhtml/Address/Delete.php | 2 +- .../Adminhtml/Address/MassDelete.php | 6 +- .../Controller/Adminhtml/Address/Save.php | 2 +- .../Controller/Adminhtml/Address/Validate.php | 6 +- .../Customer/Model/Address/DataProvider.php | 411 ++---------------- .../Model/AttributeMetadataResolver.php | 238 ++++++++++ .../DataProviderWithDefaultAddresses.php | 398 ++--------------- .../Model/FileUploaderDataResolver.php | 201 +++++++++ .../ui_component/customer_address_form.xml | 4 +- .../web/js/address/default-address-block.js | 17 - .../view/adminhtml/web/js/address/modal.js | 203 --------- .../web/js/form/components/insert-form.js | 164 ------- .../view/base/ui_component/customer_form.xml | 9 +- .../base/web/js/form/components/collection.js | 4 - .../web/js/form/components/collection/item.js | 3 - .../Test/Php/_files/blacklist/strict_type.txt | 2 +- .../config/customerConfig.xml | 2 +- 18 files changed, 524 insertions(+), 1150 deletions(-) create mode 100644 app/code/Magento/Customer/Model/AttributeMetadataResolver.php create mode 100644 app/code/Magento/Customer/Model/FileUploaderDataResolver.php delete mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/address/default-address-block.js delete mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/address/modal.js delete mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php index a2f9d12282188..75b888bb06675 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php @@ -49,7 +49,7 @@ public function __construct( } /** - * Execute action to change customer default address + * Execute action to set customer default billing or shipping address * * @return \Magento\Framework\Controller\Result\Redirect */ diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php index 8443c777546f6..1620f343700ed 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php @@ -40,7 +40,7 @@ public function __construct( } /** - * Delete action + * Delete customer address action * * @return \Magento\Framework\Controller\Result\Redirect * @throws \Magento\Framework\Exception\LocalizedException diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php index f022ea36f420d..2ea4b79f78028 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php @@ -9,7 +9,6 @@ use Magento\Backend\App\Action\Context; use Magento\Ui\Component\MassAction\Filter; use Magento\Customer\Model\ResourceModel\Address\CollectionFactory; -use Magento\Backend\Model\View\Result\Redirect; use Magento\Customer\Api\AddressRepositoryInterface; /** @@ -58,7 +57,7 @@ public function __construct( } /** - * Execute action + * Delete specified customer addresses using grid massaction * * @return \Magento\Backend\Model\View\Result\Redirect * @throws \Magento\Framework\Exception\LocalizedException|\Exception @@ -69,8 +68,7 @@ public function execute() $collection = $this->filter->getCollection($this->collectionFactory->create()); $collectionSize = $collection->getSize(); - // Get id of the first item from addresses collection for providing it to the ResultRedirect and build a - // proper redirect URL + // Get id of the first item from addresses collection for the ResultRedirect and build a correct redirect URL $customerId = $collection->getFirstItem()->getParentId(); /** @var \Magento\Customer\Model\Address $address */ diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php index e1d605a8d0890..9113640112e3b 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php @@ -79,7 +79,7 @@ public function __construct( } /** - * Execute action to save customer address + * Save customer address action * * @return \Magento\Framework\Controller\Result\Redirect * @throws \Magento\Framework\Exception\LocalizedException diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php index 696bf3099db11..e4583230d5294 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php @@ -48,7 +48,7 @@ public function __construct( } /** - * AJAX customer validation action + * AJAX customer address validation action * * @return \Magento\Framework\Controller\Result\Json */ @@ -56,7 +56,7 @@ public function execute() { /** @var \Magento\Framework\DataObject $response */ $response = new \Magento\Framework\DataObject(); - $response->setError(0); + $response->setError(false); /** @var \Magento\Framework\DataObject $validatedResponse */ $validatedResponse = $this->validateCustomerAddress($response); @@ -89,7 +89,7 @@ private function validateCustomerAddress(\Magento\Framework\DataObject $response $messages[] = $error; } $response->setMessages($messages); - $response->setError(1); + $response->setError(true); } return $response; diff --git a/app/code/Magento/Customer/Model/Address/DataProvider.php b/app/code/Magento/Customer/Model/Address/DataProvider.php index 7f23deddd89a6..5fd7c884cf6b5 100644 --- a/app/code/Magento/Customer/Model/Address/DataProvider.php +++ b/app/code/Magento/Customer/Model/Address/DataProvider.php @@ -1,9 +1,9 @@ 'frontend_input', - 'visible' => 'is_visible', - 'required' => 'is_required', - 'label' => 'frontend_label', - 'sortOrder' => 'sort_order', - 'notice' => 'note', - 'default' => 'default_value', - 'size' => 'multiline_count', - ]; - - /** - * Form element mapping - * - * @var array - */ - private $formElement = [ - 'text' => 'input', - 'hidden' => 'input', - 'boolean' => 'checkbox', - ]; - - /** - * @var EavValidationRules - */ - private $eavValidationRules; - - /** - * @var CountryWithWebsites - */ - private $countryWithWebsiteSource; - /** * Allow to manage attributes, even they are hidden on storefront * @@ -101,26 +44,6 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider */ private $context; - /** - * File types allowed for file_uploader UI component - * - * @var array - */ - private $fileUploaderTypes = [ - 'image', - 'file', - ]; - - /** - * @var \Magento\Customer\Model\Config\Share - */ - private $shareConfig; - - /** - * @var FileProcessorFactory - */ - private $fileProcessorFactory; - /** * @var array */ @@ -129,7 +52,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider /** * @var array */ - private $attributesToEliminate = [ + private static $attributesToEliminate = [ 'region', 'vat_is_valid', 'vat_request_date', @@ -137,6 +60,16 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider 'vat_request_success' ]; + /** + * @var FileUploaderDataResolver + */ + private $fileUploaderDataResolver; + + /** + * @var AttributeMetadataResolver + */ + private $attributeMetadataResolver; + /** * DataProvider constructor. * @param string $name @@ -145,11 +78,9 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider * @param CollectionFactory $addressCollectionFactory * @param CustomerRepositoryInterface $customerRepository * @param Config $eavConfig - * @param EavValidationRules $eavValidationRules * @param ContextInterface $context - * @param FileProcessorFactory $fileProcessorFactory - * @param \Magento\Customer\Model\Config\Share $shareConfig - * @param CountryWithWebsites $countryWithWebsites + * @param FileUploaderDataResolver $fileUploaderDataResolver + * @param AttributeMetadataResolver $attributeMetadataResolver * @param array $meta * @param array $data * @param bool $allowToShowHiddenAttributes @@ -162,11 +93,9 @@ public function __construct( CollectionFactory $addressCollectionFactory, CustomerRepositoryInterface $customerRepository, Config $eavConfig, - EavValidationRules $eavValidationRules, ContextInterface $context, - FileProcessorFactory $fileProcessorFactory, - \Magento\Customer\Model\Config\Share $shareConfig, - CountryWithWebsites $countryWithWebsites, + FileUploaderDataResolver $fileUploaderDataResolver, + AttributeMetadataResolver $attributeMetadataResolver, array $meta = [], array $data = [], $allowToShowHiddenAttributes = true @@ -175,12 +104,10 @@ public function __construct( $this->collection = $addressCollectionFactory->create(); $this->collection->addAttributeToSelect('*'); $this->customerRepository = $customerRepository; - $this->eavValidationRules = $eavValidationRules; $this->allowToShowHiddenAttributes = $allowToShowHiddenAttributes; $this->context = $context; - $this->fileProcessorFactory = $fileProcessorFactory; - $this->countryWithWebsiteSource = $countryWithWebsites; - $this->shareConfig = $shareConfig; + $this->fileUploaderDataResolver = $fileUploaderDataResolver; + $this->attributeMetadataResolver = $attributeMetadataResolver; $this->meta['general']['children'] = $this->getAttributesMeta( $eavConfig->getEntityType('customer_address') ); @@ -193,7 +120,7 @@ public function __construct( * @throws \Magento\Framework\Exception\LocalizedException * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function getData() + public function getData(): array { if (null !== $this->loadedData) { return $this->loadedData; @@ -210,7 +137,8 @@ public function getData() $defaultBilling = $customer->getDefaultBilling(); $defaultShipping = $customer->getDefaultShipping(); $this->prepareAddressData($addressId, $this->loadedData, $defaultBilling, $defaultShipping); - $this->overrideFileUploaderData($item, $this->loadedData[$addressId]); + + $this->fileUploaderDataResolver->overrideFileUploaderData($item, $this->loadedData[$addressId]); } if (null === $this->loadedData) { @@ -229,15 +157,15 @@ public function getData() * @param string|null $defaultShipping * @return void */ - private function prepareAddressData($addressId, array &$addresses, $defaultBilling, $defaultShipping) + private function prepareAddressData($addressId, array &$addresses, $defaultBilling, $defaultShipping): void { - if (null !== $defaultBilling && $addressId == $defaultBilling) { + if (null !== $defaultBilling && $addressId === $defaultBilling) { $addresses[$addressId]['default_billing'] = '1'; } - if (null !== $defaultShipping && $addressId == $defaultShipping) { + if (null !== $defaultShipping && $addressId === $defaultShipping) { $addresses[$addressId]['default_shipping'] = '1'; } - if (null !== $addresses[$addressId]['street'] && !is_array($addresses[$addressId]['street'])) { + if (null !== $addresses[$addressId]['street'] && !\is_array($addresses[$addressId]['street'])) { $addresses[$addressId]['street'] = explode("\n", $addresses[$addressId]['street']); } } @@ -249,7 +177,7 @@ private function prepareAddressData($addressId, array &$addresses, $defaultBilli * @throws \Magento\Framework\Exception\NoSuchEntityException * @return array */ - private function getDefaultData() + private function getDefaultData(): array { $parentId = $this->context->getRequestParam('parent_id'); $customer = $this->customerRepository->getById($parentId); @@ -262,30 +190,6 @@ private function getDefaultData() return $data; } - /** - * Override file uploader UI component data - * - * Overrides data for attributes with frontend_input equal to 'image' or 'file'. - * - * @param Address $entity - * @param array $entityData - * @return void - */ - private function overrideFileUploaderData($entity, array &$entityData) - { - $attributes = $entity->getAttributes(); - foreach ($attributes as $attribute) { - /** @var Attribute $attribute */ - if (in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { - $entityData[$attribute->getAttributeCode()] = $this->getFileUploaderData( - $entity->getEntityType(), - $attribute, - $entityData - ); - } - } - } - /** * Get attributes meta * @@ -299,259 +203,22 @@ protected function getAttributesMeta(Type $entityType): array $attributes = $entityType->getAttributeCollection(); /* @var AbstractAttribute $attribute */ foreach ($attributes as $attribute) { - $this->processFrontendInput($attribute, $meta); - - $code = $attribute->getAttributeCode(); - - if (in_array($attribute->getFrontendInput(), $this->bannedInputTypes)) { + if (\in_array($attribute->getFrontendInput(), $this->bannedInputTypes, true)) { continue; } - if (in_array($attribute->getAttributeCode(), $this->attributesToEliminate)) { + if (\in_array($attribute->getAttributeCode(), self::$attributesToEliminate, true)) { continue; } - // use getDataUsingMethod, since some getters are defined and apply additional processing of returning value - foreach ($this->metaProperties as $metaName => $origName) { - $value = $attribute->getDataUsingMethod($origName); - $meta[$code]['arguments']['data']['config'][$metaName] = ($metaName === 'label') ? __($value) : $value; - if ('frontend_input' === $origName) { - $meta[$code]['arguments']['data']['config']['formElement'] = $this->formElement[$value] ?? $value; - } - } - - if ($attribute->usesSource()) { - if ($code == AddressInterface::COUNTRY_ID) { - $meta[$code]['arguments']['data']['config']['options'] = $this->countryWithWebsiteSource - ->getAllOptions(); - } else { - $meta[$code]['arguments']['data']['config']['options'] = $attribute->getSource()->getAllOptions(); - } - } - - $rules = $this->eavValidationRules->build($attribute, $meta[$code]['arguments']['data']['config']); - if (!empty($rules)) { - $meta[$code]['arguments']['data']['config']['validation'] = $rules; - } - - $meta[$code]['arguments']['data']['config']['componentType'] = Field::NAME; - $meta[$code]['arguments']['data']['config']['visible'] = $this->canShowAttribute($attribute); - - $this->overrideFileUploaderMetadata($entityType, $attribute, $meta[$code]['arguments']['data']['config']); + $meta[$attribute->getAttributeCode()] = $this->attributeMetadataResolver->getAttributesMeta( + $attribute, + $entityType, + $this->allowToShowHiddenAttributes, + $this->getRequestFieldName() + ); } + $this->attributeMetadataResolver->processWebsiteMeta($meta); - $this->processWebsiteMeta($meta); return $meta; } - - /** - * Process attributes by frontend input type - * - * @param AttributeInterface $attribute - * @param array $meta - * @return void - */ - private function processFrontendInput(AttributeInterface $attribute, array &$meta) - { - $code = $attribute->getAttributeCode(); - if ($attribute->getFrontendInput() === 'boolean') { - $meta[$code]['arguments']['data']['config']['prefer'] = 'toggle'; - $meta[$code]['arguments']['data']['config']['valueMap'] = [ - 'true' => '1', - 'false' => '0', - ]; - } - } - - /** - * Detect can we show attribute on specific form or not - * - * @param AbstractAttribute $customerAttribute - * @return bool - */ - private function canShowAttribute(AbstractAttribute $customerAttribute): bool - { - $userDefined = (bool) $customerAttribute->getIsUserDefined(); - if (!$userDefined) { - return $customerAttribute->getIsVisible(); - } - - $canShowOnForm = $this->canShowAttributeInForm($customerAttribute); - - return ($this->allowToShowHiddenAttributes && $canShowOnForm) || - (!$this->allowToShowHiddenAttributes && $canShowOnForm && $customerAttribute->getIsVisible()); - } - - /** - * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... - * - * @param AbstractAttribute $customerAttribute - * @return bool - */ - private function canShowAttributeInForm(AbstractAttribute $customerAttribute): bool - { - $isRegistration = $this->context->getRequestParam($this->getRequestFieldName()) === null; - - if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { - return is_array($customerAttribute->getUsedInForms()) && - ( - (in_array('customer_account_create', $customerAttribute->getUsedInForms()) && $isRegistration) || - (in_array('customer_account_edit', $customerAttribute->getUsedInForms()) && !$isRegistration) - ); - } - return is_array($customerAttribute->getUsedInForms()) && - in_array('customer_address_edit', $customerAttribute->getUsedInForms()); - } - - /** - * Override file uploader UI component metadata - * - * Overrides metadata for attributes with frontend_input equal to 'image' or 'file'. - * - * @param Type $entityType - * @param AbstractAttribute $attribute - * @param array $config - * @return void - */ - private function overrideFileUploaderMetadata( - Type $entityType, - AbstractAttribute $attribute, - array &$config - ) { - if (in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { - $maxFileSize = self::MAX_FILE_SIZE; - - if (isset($config['validation']['max_file_size'])) { - $maxFileSize = (int)$config['validation']['max_file_size']; - } - - $allowedExtensions = []; - - if (isset($config['validation']['file_extensions'])) { - $allowedExtensions = explode(',', $config['validation']['file_extensions']); - array_walk($allowedExtensions, function (&$value) { - $value = strtolower(trim($value)); - }); - } - - $allowedExtensions = implode(' ', $allowedExtensions); - - $entityTypeCode = $entityType->getEntityTypeCode(); - $url = $this->getFileUploadUrl($entityTypeCode); - - $config = [ - 'formElement' => 'fileUploader', - 'componentType' => 'fileUploader', - 'maxFileSize' => $maxFileSize, - 'allowedExtensions' => $allowedExtensions, - 'uploaderConfig' => [ - 'url' => $url, - ], - 'label' => $this->getMetadataValue($config, 'label'), - 'sortOrder' => $this->getMetadataValue($config, 'sortOrder'), - 'required' => $this->getMetadataValue($config, 'required'), - 'visible' => $this->getMetadataValue($config, 'visible'), - 'validation' => $this->getMetadataValue($config, 'validation'), - ]; - } - } - - /** - * Add global scope parameter and filter options to website meta - * - * @param array $meta - * @return void - */ - private function processWebsiteMeta(&$meta) - { - if (isset($meta[CustomerInterface::WEBSITE_ID]) && $this->shareConfig->isGlobalScope()) { - $meta[CustomerInterface::WEBSITE_ID]['arguments']['data']['config']['isGlobalScope'] = 1; - } - - if (isset($meta[AddressInterface::COUNTRY_ID]) && !$this->shareConfig->isGlobalScope()) { - $meta[AddressInterface::COUNTRY_ID]['arguments']['data']['config']['filterBy'] = [ - 'target' => 'customer_form.customer_form_data_source:data.customer.website_id', - 'field' => 'website_ids' - ]; - } - } - - /** - * Retrieve metadata value - * - * @param array $config - * @param string $name - * @param mixed $default - * @return mixed - */ - private function getMetadataValue($config, $name, $default = null) - { - return $config[$name] ?? $default; - } - - /** - * Retrieve URL to file upload - * - * @param string $entityTypeCode - * @return string - */ - private function getFileUploadUrl($entityTypeCode): string - { - switch ($entityTypeCode) { - case CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER: - $url = 'customer/file/customer_upload'; - break; - - case AddressMetadataInterface::ENTITY_TYPE_ADDRESS: - $url = 'customer/file/address_upload'; - break; - - default: - $url = ''; - break; - } - return $url; - } - - /** - * Retrieve array of values required by file uploader UI component - * - * @param Type $entityType - * @param Attribute $attribute - * @param array $customerData - * @return array - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - private function getFileUploaderData( - Type $entityType, - Attribute $attribute, - array $customerData - ): array { - $attributeCode = $attribute->getAttributeCode(); - - $file = $customerData[$attributeCode] ?? ''; - - /** @var FileProcessor $fileProcessor */ - $fileProcessor = $this->fileProcessorFactory->create([ - 'entityTypeCode' => $entityType->getEntityTypeCode(), - ]); - - if (!empty($file) - && $fileProcessor->isExist($file) - ) { - $stat = $fileProcessor->getStat($file); - $viewUrl = $fileProcessor->getViewUrl($file, $attribute->getFrontendInput()); - - return [ - [ - 'file' => $file, - 'size' => null !== $stat ? $stat['size'] : 0, - 'url' => $viewUrl ?? '', - 'name' => basename($file), - 'type' => $fileProcessor->getMimeType($file), - ], - ]; - } - - return []; - } } diff --git a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php new file mode 100644 index 0000000000000..36ffcb2f5495b --- /dev/null +++ b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php @@ -0,0 +1,238 @@ + 'frontend_input', + 'visible' => 'is_visible', + 'required' => 'is_required', + 'label' => 'frontend_label', + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + ]; + + /** + * Form element mapping + * + * @var array + */ + private static $formElement = [ + 'text' => 'input', + 'hidden' => 'input', + 'boolean' => 'checkbox', + ]; + + /** + * @var CountryWithWebsites + */ + private $countryWithWebsiteSource; + + /** + * @var EavValidationRules + */ + private $eavValidationRules; + + /** + * @var FileUploaderDataResolver + */ + private $fileUploaderDataResolver; + + /** + * @var ContextInterface + */ + private $context; + + /** + * @var ShareConfig + */ + private $shareConfig; + + /** + * @param CountryWithWebsites $countryWithWebsiteSource + * @param EavValidationRules $eavValidationRules + * @param \Magento\Customer\Model\FileUploaderDataResolver $fileUploaderDataResolver + * @param ContextInterface $context + * @param ShareConfig $shareConfig + */ + public function __construct( + CountryWithWebsites $countryWithWebsiteSource, + EavValidationRules $eavValidationRules, + fileUploaderDataResolver $fileUploaderDataResolver, + ContextInterface $context, + ShareConfig $shareConfig + ) { + $this->countryWithWebsiteSource = $countryWithWebsiteSource; + $this->eavValidationRules = $eavValidationRules; + $this->fileUploaderDataResolver = $fileUploaderDataResolver; + $this->context = $context; + $this->shareConfig = $shareConfig; + } + + /** + * Get meta data of the customer or customer address attribute + * + * @param AbstractAttribute $attribute + * @param Type $entityType + * @param bool $allowToShowHiddenAttributes + * @param string $requestFieldName + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function getAttributesMeta( + AbstractAttribute $attribute, + Type $entityType, + bool $allowToShowHiddenAttributes, + string $requestFieldName + ): array { + $meta = $this->modifyBooleanAttributeMeta($attribute); + // use getDataUsingMethod, since some getters are defined and apply additional processing of returning value + foreach (self::$metaProperties as $metaName => $origName) { + $value = $attribute->getDataUsingMethod($origName); + $meta['arguments']['data']['config'][$metaName] = ($metaName === 'label') ? __($value) : $value; + if ('frontend_input' === $origName) { + $meta['arguments']['data']['config']['formElement'] = self::$formElement[$value] ?? $value; + } + } + + if ($attribute->usesSource()) { + if ($attribute->getAttributeCode() === AddressInterface::COUNTRY_ID) { + $meta['arguments']['data']['config']['options'] = $this->countryWithWebsiteSource + ->getAllOptions(); + } else { + $meta['arguments']['data']['config']['options'] = $attribute->getSource()->getAllOptions(); + } + } + + $rules = $this->eavValidationRules->build($attribute, $meta['arguments']['data']['config']); + if (!empty($rules)) { + $meta['arguments']['data']['config']['validation'] = $rules; + } + + $meta['arguments']['data']['config']['componentType'] = Field::NAME; + $meta['arguments']['data']['config']['visible'] = $this->canShowAttribute( + $attribute, + $requestFieldName, + $allowToShowHiddenAttributes + ); + + $this->fileUploaderDataResolver->overrideFileUploaderMetadata( + $entityType, + $attribute, + $meta['arguments']['data']['config'] + ); + + return $meta; + } + + /** + * Detect can we show attribute on specific form or not + * + * @param AbstractAttribute $customerAttribute + * @param string $requestFieldName + * @param bool $allowToShowHiddenAttributes + * @return bool + */ + private function canShowAttribute( + AbstractAttribute $customerAttribute, + string $requestFieldName, + bool $allowToShowHiddenAttributes + ) { + $userDefined = (bool)$customerAttribute->getIsUserDefined(); + if (!$userDefined) { + return $customerAttribute->getIsVisible(); + } + + $canShowOnForm = $this->canShowAttributeInForm($customerAttribute, $requestFieldName); + + return ($allowToShowHiddenAttributes && $canShowOnForm) || + (!$allowToShowHiddenAttributes && $canShowOnForm && $customerAttribute->getIsVisible()); + } + + /** + * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... + * + * @param AbstractAttribute $customerAttribute + * @param string $requestFieldName + * @return bool + */ + private function canShowAttributeInForm(AbstractAttribute $customerAttribute, string $requestFieldName): bool + { + $isRegistration = $this->context->getRequestParam($requestFieldName) === null; + + if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { + return \is_array($customerAttribute->getUsedInForms()) && + ( + (\in_array('customer_account_create', $customerAttribute->getUsedInForms(), true) && $isRegistration) || + (\in_array('customer_account_edit', $customerAttribute->getUsedInForms(), true) && !$isRegistration) + ); + } + return \is_array($customerAttribute->getUsedInForms()) && + \in_array('customer_address_edit', $customerAttribute->getUsedInForms(), true); + } + + /** + * Modify boolean attribute meta data + * + * @param AttributeInterface $attribute + * @return array + */ + private function modifyBooleanAttributeMeta(AttributeInterface $attribute): array + { + $meta = []; + if ($attribute->getFrontendInput() === 'boolean') { + $meta['arguments']['data']['config']['prefer'] = 'toggle'; + $meta['arguments']['data']['config']['valueMap'] = [ + 'true' => '1', + 'false' => '0', + ]; + } + + return $meta; + } + + /** + * Add global scope parameter and filter options to website meta + * + * @param array $meta + * @return void + */ + public function processWebsiteMeta(&$meta): void + { + if (isset($meta[CustomerInterface::WEBSITE_ID]) && $this->shareConfig->isGlobalScope()) { + $meta[CustomerInterface::WEBSITE_ID]['arguments']['data']['config']['isGlobalScope'] = 1; + } + + if (isset($meta[AddressInterface::COUNTRY_ID]) && !$this->shareConfig->isGlobalScope()) { + $meta[AddressInterface::COUNTRY_ID]['arguments']['data']['config']['filterBy'] = [ + 'target' => 'customer_form.customer_form_data_source:data.customer.website_id', + 'field' => 'website_ids' + ]; + } + } +} diff --git a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php index 7ba6484dc3f2b..9131fb2ccdce2 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php +++ b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php @@ -6,112 +6,37 @@ */ namespace Magento\Customer\Model\Customer; -use Magento\Customer\Api\AddressMetadataInterface; -use Magento\Customer\Api\CustomerMetadataInterface; -use Magento\Customer\Api\Data\AddressInterface; -use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Model\Address; -use Magento\Customer\Model\Attribute; use Magento\Customer\Model\Customer; -use Magento\Customer\Model\FileProcessor; -use Magento\Customer\Model\FileProcessorFactory; -use Magento\Customer\Model\ResourceModel\Address\Attribute\Source\CountryWithWebsites; use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory as CustomerCollectionFactory; -use Magento\Eav\Api\Data\AttributeInterface; use Magento\Eav\Model\Config; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; use Magento\Eav\Model\Entity\Type; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Session\SessionManagerInterface; -use Magento\Framework\View\Element\UiComponent\ContextInterface; -use Magento\Ui\Component\Form\Field; -use Magento\Ui\DataProvider\EavValidationRules; +use Magento\Customer\Model\FileUploaderDataResolver; +use Magento\Customer\Model\AttributeMetadataResolver; /** * Refactored version of Magento\Customer\Model\Customer\DataProvider with eliminated usage of addresses collection. - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DataProviderWithDefaultAddresses extends \Magento\Ui\DataProvider\AbstractDataProvider { - /** - * Maximum file size allowed for file_uploader UI component - */ - const MAX_FILE_SIZE = 2097152; - /** * @var array */ private $loadedData; - /** - * @var CountryWithWebsites - */ - private $countryWithWebsiteSource; - - /** - * @var \Magento\Customer\Model\Config\Share - */ - private $shareConfig; - - /** - * EAV attribute properties to fetch from meta storage - * @var array - */ - private $metaProperties = [ - 'dataType' => 'frontend_input', - 'visible' => 'is_visible', - 'required' => 'is_required', - 'label' => 'frontend_label', - 'sortOrder' => 'sort_order', - 'notice' => 'note', - 'default' => 'default_value', - 'size' => 'multiline_count', - ]; - - /** - * Form element mapping - * - * @var array - */ - private $formElement = [ - 'text' => 'input', - 'hidden' => 'input', - 'boolean' => 'checkbox', - ]; - - /** - * @var EavValidationRules - */ - private $eavValidationRules; - /** * @var SessionManagerInterface - * @since 100.1.0 */ private $session; - /** - * @var FileProcessorFactory - */ - private $fileProcessorFactory; - - /** - * File types allowed for file_uploader UI component - * - * @var array - */ - private $fileUploaderTypes = [ - 'image', - 'file', - ]; - /** * Customer fields that must be removed * * @var array */ - private $forbiddenCustomerFields = [ + private static $forbiddenCustomerFields = [ 'password_hash', 'rp_token', 'confirmation', @@ -135,54 +60,52 @@ class DataProviderWithDefaultAddresses extends \Magento\Ui\DataProvider\Abstract private $countryFactory; /** - * DataProviderWithDefaultAddresses constructor. - * + * @var FileUploaderDataResolver + */ + private $fileUploaderDataResolver; + + /** + * @var AttributeMetadataResolver + */ + private $attributeMetadataResolver; + + /** * @param string $name * @param string $primaryFieldName * @param string $requestFieldName - * @param EavValidationRules $eavValidationRules * @param CustomerCollectionFactory $customerCollectionFactory * @param Config $eavConfig - * @param FileProcessorFactory $fileProcessorFactory - * @param ContextInterface $context * @param \Magento\Directory\Model\CountryFactory $countryFactory * @param SessionManagerInterface $session - * @param \Magento\Customer\Model\Config\Share $shareConfig - * @param CountryWithWebsites $countryWithWebsites + * @param FileUploaderDataResolver $fileUploaderDataResolver + * @param AttributeMetadataResolver $attributeMetadataResolver * @param bool $allowToShowHiddenAttributes * @param array $meta * @param array $data - * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @throws \Magento\Framework\Exception\LocalizedException */ public function __construct( string $name, string $primaryFieldName, string $requestFieldName, - EavValidationRules $eavValidationRules, CustomerCollectionFactory $customerCollectionFactory, Config $eavConfig, - FileProcessorFactory $fileProcessorFactory, - ContextInterface $context, \Magento\Directory\Model\CountryFactory $countryFactory, - \Magento\Framework\Session\SessionManagerInterface $session, - \Magento\Customer\Model\Config\Share $shareConfig, - CountryWithWebsites $countryWithWebsites, + SessionManagerInterface $session, + FileUploaderDataResolver $fileUploaderDataResolver, + AttributeMetadataResolver $attributeMetadataResolver, $allowToShowHiddenAttributes = true, array $meta = [], array $data = [] ) { parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); - $this->eavValidationRules = $eavValidationRules; $this->collection = $customerCollectionFactory->create(); $this->collection->addAttributeToSelect('*'); - $this->fileProcessorFactory = $fileProcessorFactory; - $this->context = $context ?: ObjectManager::getInstance()->get(ContextInterface::class); $this->allowToShowHiddenAttributes = $allowToShowHiddenAttributes; $this->session = $session; - $this->countryWithWebsiteSource = $countryWithWebsites; - $this->shareConfig = $shareConfig; $this->countryFactory = $countryFactory; + $this->fileUploaderDataResolver = $fileUploaderDataResolver; + $this->attributeMetadataResolver = $attributeMetadataResolver; $this->meta['customer']['children'] = $this->getAttributesMeta( $eavConfig->getEntityType('customer') ); @@ -193,7 +116,7 @@ public function __construct( * * @return array */ - public function getData() + public function getData(): array { if (null !== $this->loadedData) { return $this->loadedData; @@ -203,11 +126,11 @@ public function getData() foreach ($items as $customer) { $result['customer'] = $customer->getData(); - $this->overrideFileUploaderData($customer, $result['customer']); + $this->fileUploaderDataResolver->overrideFileUploaderData($customer, $result['customer']); $result['customer'] = array_diff_key( $result['customer'], - array_flip($this->forbiddenCustomerFields) + array_flip(self::$forbiddenCustomerFields) ); unset($result['address']); @@ -254,73 +177,6 @@ private function prepareDefaultAddress($address): array return $addressData; } - /** - * Override file uploader UI component data - * - * Overrides data for attributes with frontend_input equal to 'image' or 'file'. - * - * @param Customer|Address $entity - * @param array $entityData - * @return void - */ - private function overrideFileUploaderData($entity, array &$entityData) - { - $attributes = $entity->getAttributes(); - foreach ($attributes as $attribute) { - /** @var Attribute $attribute */ - if (\in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { - $entityData[$attribute->getAttributeCode()] = $this->getFileUploaderData( - $entity->getEntityType(), - $attribute, - $entityData - ); - } - } - } - - /** - * Retrieve array of values required by file uploader UI component - * - * @param Type $entityType - * @param Attribute $attribute - * @param array $customerData - * @return array - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - private function getFileUploaderData( - Type $entityType, - Attribute $attribute, - array $customerData - ): array { - $attributeCode = $attribute->getAttributeCode(); - - $file = $customerData[$attributeCode] ?? ''; - - /** @var FileProcessor $fileProcessor */ - $fileProcessor = $this->fileProcessorFactory->create([ - 'entityTypeCode' => $entityType->getEntityTypeCode(), - ]); - - if (!empty($file) - && $fileProcessor->isExist($file) - ) { - $stat = $fileProcessor->getStat($file); - $viewUrl = $fileProcessor->getViewUrl($file, $attribute->getFrontendInput()); - - return [ - [ - 'file' => $file, - 'size' => null !== $stat ? $stat['size'] : 0, - 'url' => $viewUrl ?? '', - 'name' => basename($file), - 'type' => $fileProcessor->getMimeType($file), - ], - ]; - } - - return []; - } - /** * Get attributes meta * @@ -334,209 +190,15 @@ protected function getAttributesMeta(Type $entityType): array $attributes = $entityType->getAttributeCollection(); /* @var AbstractAttribute $attribute */ foreach ($attributes as $attribute) { - $this->processFrontendInput($attribute, $meta); - - $code = $attribute->getAttributeCode(); - - // use getDataUsingMethod, since some getters are defined and apply additional processing of returning value - foreach ($this->metaProperties as $metaName => $origName) { - $value = $attribute->getDataUsingMethod($origName); - $meta[$code]['arguments']['data']['config'][$metaName] = ($metaName === 'label') ? __($value) : $value; - if ('frontend_input' === $origName) { - $meta[$code]['arguments']['data']['config']['formElement'] = $this->formElement[$value] ?? $value; - } - } - - if ($attribute->usesSource()) { - if ($code == AddressInterface::COUNTRY_ID) { - $meta[$code]['arguments']['data']['config']['options'] = $this->countryWithWebsiteSource - ->getAllOptions(); - } else { - $meta[$code]['arguments']['data']['config']['options'] = $attribute->getSource()->getAllOptions(); - } - } - - $rules = $this->eavValidationRules->build($attribute, $meta[$code]['arguments']['data']['config']); - if (!empty($rules)) { - $meta[$code]['arguments']['data']['config']['validation'] = $rules; - } - - $meta[$code]['arguments']['data']['config']['componentType'] = Field::NAME; - $meta[$code]['arguments']['data']['config']['visible'] = $this->canShowAttribute($attribute); - - $this->overrideFileUploaderMetadata($entityType, $attribute, $meta[$code]['arguments']['data']['config']); + $meta[$attribute->getAttributeCode()] = $this->attributeMetadataResolver->getAttributesMeta( + $attribute, + $entityType, + $this->allowToShowHiddenAttributes, + $this->getRequestFieldName() + ); } + $this->attributeMetadataResolver->processWebsiteMeta($meta); - $this->processWebsiteMeta($meta); return $meta; } - - /** - * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... - * - * @param AbstractAttribute $customerAttribute - * @return bool - */ - private function canShowAttributeInForm(AbstractAttribute $customerAttribute) - { - $isRegistration = $this->context->getRequestParam($this->getRequestFieldName()) === null; - - if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { - return \is_array($customerAttribute->getUsedInForms()) && - ( - (\in_array('customer_account_create', $customerAttribute->getUsedInForms()) && $isRegistration) || - (\in_array('customer_account_edit', $customerAttribute->getUsedInForms()) && !$isRegistration) - ); - } - return \is_array($customerAttribute->getUsedInForms()) && - \in_array('customer_address_edit', $customerAttribute->getUsedInForms()); - } - - /** - * Detect can we show attribute on specific form or not - * - * @param AbstractAttribute $customerAttribute - * @return bool - */ - private function canShowAttribute(AbstractAttribute $customerAttribute) - { - $userDefined = (bool) $customerAttribute->getIsUserDefined(); - if (!$userDefined) { - return $customerAttribute->getIsVisible(); - } - - $canShowOnForm = $this->canShowAttributeInForm($customerAttribute); - - return ($this->allowToShowHiddenAttributes && $canShowOnForm) || - (!$this->allowToShowHiddenAttributes && $canShowOnForm && $customerAttribute->getIsVisible()); - } - - /** - * Add global scope parameter and filter options to website meta - * - * @param array $meta - * @return void - */ - private function processWebsiteMeta(&$meta) - { - if (isset($meta[CustomerInterface::WEBSITE_ID]) && $this->shareConfig->isGlobalScope()) { - $meta[CustomerInterface::WEBSITE_ID]['arguments']['data']['config']['isGlobalScope'] = 1; - } - - if (isset($meta[AddressInterface::COUNTRY_ID]) && !$this->shareConfig->isGlobalScope()) { - $meta[AddressInterface::COUNTRY_ID]['arguments']['data']['config']['filterBy'] = [ - 'target' => '${ $.provider }:data.customer.website_id', - 'field' => 'website_ids' - ]; - } - } - - /** - * Override file uploader UI component metadata - * - * Overrides metadata for attributes with frontend_input equal to 'image' or 'file'. - * - * @param Type $entityType - * @param AbstractAttribute $attribute - * @param array $config - * @return void - */ - private function overrideFileUploaderMetadata( - Type $entityType, - AbstractAttribute $attribute, - array &$config - ) { - if (\in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { - $maxFileSize = self::MAX_FILE_SIZE; - - if (isset($config['validation']['max_file_size'])) { - $maxFileSize = (int)$config['validation']['max_file_size']; - } - - $allowedExtensions = []; - - if (isset($config['validation']['file_extensions'])) { - $allowedExtensions = explode(',', $config['validation']['file_extensions']); - array_walk($allowedExtensions, function (&$value) { - $value = strtolower(trim($value)); - }); - } - - $allowedExtensions = implode(' ', $allowedExtensions); - - $entityTypeCode = $entityType->getEntityTypeCode(); - $url = $this->getFileUploadUrl($entityTypeCode); - - $config = [ - 'formElement' => 'fileUploader', - 'componentType' => 'fileUploader', - 'maxFileSize' => $maxFileSize, - 'allowedExtensions' => $allowedExtensions, - 'uploaderConfig' => [ - 'url' => $url, - ], - 'label' => $this->getMetadataValue($config, 'label'), - 'sortOrder' => $this->getMetadataValue($config, 'sortOrder'), - 'required' => $this->getMetadataValue($config, 'required'), - 'visible' => $this->getMetadataValue($config, 'visible'), - 'validation' => $this->getMetadataValue($config, 'validation'), - ]; - } - } - - /** - * Retrieve metadata value - * - * @param array $config - * @param string $name - * @param mixed $default - * @return mixed - */ - private function getMetadataValue($config, $name, $default = null) - { - return $config[$name] ?? $default; - } - - /** - * Retrieve URL to file upload - * - * @param string $entityTypeCode - * @return string - */ - private function getFileUploadUrl($entityTypeCode): string - { - switch ($entityTypeCode) { - case CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER: - $url = 'customer/file/customer_upload'; - break; - - case AddressMetadataInterface::ENTITY_TYPE_ADDRESS: - $url = 'customer/file/address_upload'; - break; - - default: - $url = ''; - break; - } - return $url; - } - - /** - * Process attributes by frontend input type - * - * @param AttributeInterface $attribute - * @param array $meta - * @return void - */ - private function processFrontendInput(AttributeInterface $attribute, array &$meta) - { - $code = $attribute->getAttributeCode(); - if ($attribute->getFrontendInput() === 'boolean') { - $meta[$code]['arguments']['data']['config']['prefer'] = 'toggle'; - $meta[$code]['arguments']['data']['config']['valueMap'] = [ - 'true' => '1', - 'false' => '0', - ]; - } - } } diff --git a/app/code/Magento/Customer/Model/FileUploaderDataResolver.php b/app/code/Magento/Customer/Model/FileUploaderDataResolver.php new file mode 100644 index 0000000000000..a132507deaa2f --- /dev/null +++ b/app/code/Magento/Customer/Model/FileUploaderDataResolver.php @@ -0,0 +1,201 @@ +fileProcessorFactory = $fileProcessorFactory; + } + + /** + * Override file uploader UI component data + * + * Overrides data for attributes with frontend_input equal to 'image' or 'file'. + * + * @param Customer|Address $entity + * @param array $entityData + * @return void + */ + public function overrideFileUploaderData($entity, array &$entityData): void + { + $attributes = $entity->getAttributes(); + foreach ($attributes as $attribute) { + /** @var Attribute $attribute */ + if (\in_array($attribute->getFrontendInput(), $this->fileUploaderTypes, true)) { + $entityData[$attribute->getAttributeCode()] = $this->getFileUploaderData( + $entity->getEntityType(), + $attribute, + $entityData + ); + } + } + } + + /** + * Retrieve array of values required by file uploader UI component + * + * @param Type $entityType + * @param Attribute $attribute + * @param array $customerData + * @return array + */ + private function getFileUploaderData( + Type $entityType, + Attribute $attribute, + array $customerData + ): array { + $attributeCode = $attribute->getAttributeCode(); + + $file = $customerData[$attributeCode] ?? ''; + + /** @var FileProcessor $fileProcessor */ + $fileProcessor = $this->fileProcessorFactory->create([ + 'entityTypeCode' => $entityType->getEntityTypeCode(), + ]); + + if (!empty($file) + && $fileProcessor->isExist($file) + ) { + $stat = $fileProcessor->getStat($file); + $viewUrl = $fileProcessor->getViewUrl($file, $attribute->getFrontendInput()); + + return [ + [ + 'file' => $file, + 'size' => null !== $stat ? $stat['size'] : 0, + 'url' => $viewUrl ?? '', + 'name' => basename($file), + 'type' => $fileProcessor->getMimeType($file), + ], + ]; + } + + return []; + } + + /** + * Override file uploader UI component metadata + * + * Overrides metadata for attributes with frontend_input equal to 'image' or 'file'. + * + * @param Type $entityType + * @param AbstractAttribute $attribute + * @param array $config + * @return void + */ + public function overrideFileUploaderMetadata( + Type $entityType, + AbstractAttribute $attribute, + array &$config + ): void { + if (\in_array($attribute->getFrontendInput(), $this->fileUploaderTypes, true)) { + $maxFileSize = self::MAX_FILE_SIZE; + + if (isset($config['validation']['max_file_size'])) { + $maxFileSize = (int)$config['validation']['max_file_size']; + } + + $allowedExtensions = []; + + if (isset($config['validation']['file_extensions'])) { + $allowedExtensions = explode(',', $config['validation']['file_extensions']); + array_walk($allowedExtensions, function (&$value) { + $value = strtolower(trim($value)); + }); + } + + $allowedExtensions = implode(' ', $allowedExtensions); + + $entityTypeCode = $entityType->getEntityTypeCode(); + $url = $this->getFileUploadUrl($entityTypeCode); + + $config = [ + 'formElement' => 'fileUploader', + 'componentType' => 'fileUploader', + 'maxFileSize' => $maxFileSize, + 'allowedExtensions' => $allowedExtensions, + 'uploaderConfig' => [ + 'url' => $url, + ], + 'label' => $this->getMetadataValue($config, 'label'), + 'sortOrder' => $this->getMetadataValue($config, 'sortOrder'), + 'required' => $this->getMetadataValue($config, 'required'), + 'visible' => $this->getMetadataValue($config, 'visible'), + 'validation' => $this->getMetadataValue($config, 'validation'), + ]; + } + } + + /** + * Retrieve metadata value + * + * @param array $config + * @param string $name + * @param mixed $default + * @return mixed + */ + private function getMetadataValue($config, $name, $default = null) + { + return $config[$name] ?? $default; + } + + /** + * Retrieve URL to file upload + * + * @param string $entityTypeCode + * @return string + */ + private function getFileUploadUrl($entityTypeCode): string + { + switch ($entityTypeCode) { + case CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER: + $url = 'customer/file/customer_upload'; + break; + + case AddressMetadataInterface::ENTITY_TYPE_ADDRESS: + $url = 'customer/file/address_upload'; + break; + + default: + $url = ''; + break; + } + return $url; + } +} diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index 9e432f9f10e0c..1c30d08879eed 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -60,7 +60,7 @@ text - + 0 @@ -83,7 +83,7 @@ - + 0 diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address-block.js b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address-block.js deleted file mode 100644 index a715aae1ebd96..0000000000000 --- a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address-block.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'uiElement' -], function($, Component) { - 'use strict'; - - return Component.extend({ - defaults: { - template: 'Magento_Customer/default-address' - } - }); -}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/address/modal.js b/app/code/Magento/Customer/view/adminhtml/web/js/address/modal.js deleted file mode 100644 index 94701f4c1af99..0000000000000 --- a/app/code/Magento/Customer/view/adminhtml/web/js/address/modal.js +++ /dev/null @@ -1,203 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'Magento_Ui/js/modal/modal-component', - 'uiRegistry', - 'underscore' -], function ($, Modal, registry, _) { - 'use strict'; - - return Modal.extend({ - defaults: { - modules: { - emailProvider: '${ $.emailProvider }' - } - }, - - /** - * Initializes component. - * - * @returns {Object} Chainable. - */ - initialize: function () { - this._super(); - - // console.log(this.name); - - return this; - }, - - /** - * Open modal. - */ - openModal: function (data) { - debugger; - if (data == null){ - // add - this.setTitle(this.options.title); - this._super(); - } else { - // edit - var addressId = data.uuid; - var address = { - 'city': 'city', - 'company': 'company', - 'country_id': 'country_id', - 'customer_id': 'customer_id', - 'created_at': 'created_at', - 'default_billing': 'default_billing', - 'default_shipping': 'default_shipping', - 'entity_id': 'entity_id', - 'fax': 'fax', - 'firstname': 'firstname', - 'increment_id': 'increment_id', - 'is_active': 'is_active', - 'lastname': 'lastname', - 'middlename': 'middlename', - 'parent_id': 'parent_id', - 'postcode': 'postcode', - 'prefix': 'prefix', - 'region': 'region', - 'region_id': 'region_id', - 'street': [0, 1], - 'suffix': 'suffix', - 'telephone': 'telephone', - 'updated_at': 'updated_at', - 'vat_id': 'vat_id', - 'vat_is_valid': 'vat_is_valid', - 'vat_request_date': 'vat_request_date', - 'vat_request_id': 'vat_request_id', - 'vat_request_success': 'vat_request_success' - }; - - var source = registry.get('customer_form.customer_form_data_source'); - var modal = 'data.address_listing.address_form.update_customer_address_form_modal'; - - _.each(address, function(value, key) { - if (key === 'default_billing' || key === 'default_shipping') { - var defaultValue = source.get('data.address.' + addressId + '.' + value); - // convert boolean to integer - var val = +defaultValue; - source.set(modal + '.' + key, val.toString()); - } else if (key === 'street' && _.isArray(address[key])) { - _.each(address[key], function(element, index) { - source.set(modal + '.' + key + '[' + index + ']', source.get('data.address.' + addressId + '.' + key + '.' + element)); - }); - } else { - source.set(modal + '.' + key, source.get('data.address.' + addressId + '.' + value)); - } - }); - - this.setTitle(this.options.title); - this._super(); - } - }, - - /** - * Close popup modal. - * @public - */ - closeModal: function () { - debugger; - this._clearData(); - this._super(); - }, - - /** - * Clear modal data. - * - * @private - */ - _clearData: function () { - debugger; - var address = { - 'city': '', - 'company': '', - 'country_id': '', - 'default_billing': "0", - 'default_shipping': "0", - 'entity_id': '', - 'firstname': '', - 'is_active': '', - 'lastname': '', - 'middlename': '', - 'postcode': '', - 'prefix': '', - 'region': '', - 'region_id': '', - 'street[0]': '', - 'street[1]': '', - 'suffix': '', - 'telephone': '', - 'vat_id': '' - }; - - var source = registry.get('customer_form.customer_form_data_source'); - var modal = 'data.address_listing.address_form.update_customer_address_form_modal'; - - _.each(address, function(value, key) { - source.set(modal + '.' + key, value); - }); - }, - - /** - * Open modal. - */ - save: function () { - debugger; - - var address = { - 'city': 'city', - 'company': 'company', - 'country_id': 'country_id', - 'customer_id': 'customer_id', - 'created_at': 'created_at', - 'default_billing': 'default_billing', - 'default_shipping': 'default_shipping', - 'entity_id': 'entity_id', - 'fax': 'fax', - 'firstname': 'firstname', - 'increment_id': 'increment_id', - 'is_active': 'is_active', - 'lastname': 'lastname', - 'middlename': 'middlename', - 'parent_id': 'parent_id', - 'postcode': 'postcode', - 'prefix': 'prefix', - 'region': 'region', - 'region_id': 'region_id', - 'street': ['street[0]', 'street[1]'], - 'suffix': 'region_id', - 'telephone': 'telephone', - 'updated_at': 'updated_at', - 'vat_id': 'vat_id', - 'vat_is_valid': 'vat_is_valid', - 'vat_request_date': 'vat_request_date', - 'vat_request_id': 'vat_request_id', - 'vat_request_success': 'vat_request_success' - }; - - var source = registry.get('customer_form.customer_form_data_source'); - var formData = source.get('data.address_listing.address_form.update_customer_address_form_modal'); - var entityId = formData.entity_id; - - $.ajax({ - url: this.options.url, - showLoader: true, - data: formData, - type: "POST", - dataType: 'json', - success: function(data) { - console.log('SUCCESS: ', data); - }, - error: function(data) { - console.log('ERROR: ', data); - } - }); - } - }); -}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js deleted file mode 100644 index edfb004654a9b..0000000000000 --- a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'Magento_Ui/js/form/components/insert-form' -], function (Insert) { - 'use strict'; - - return Insert.extend({ - // Should be refactored after form save.!!!!! - // defaults: { - // updateModalProvider: '${ $.parentName }', - // subTitlePrefix: $t('Belongs to '), - // switcherSelector: '.store-switcher', - // toRemove: [], - // // imports: { - // // removeResponseData: '${ $.removeResponseProvider }', - // // modalTitle: '${ $.modalTitleProvider }', - // // modalSubTitle: '${ $.modalSubTitleProvider }', - // // destroyClosedModalContents: '${ $.updateModalProvider }:state' - // // }, - // // listens: { - // // responseData: 'afterUpdate', - // // removeResponseData: 'afterRemove', - // // modalTitle: 'changeModalTitle', - // // modalSubTitle: 'changeModalSubTitle' - // // }, - // modules: { - // updateModal: '${ $.updateModalProvider }', - // removeModal: '${ $.removeModalProvider }', - // upcomingListing: 'index = ${ $.upcomingListingProvider }' - // } - // }, - // - // /** @inheritdoc **/ - // initialize: function () { - // _.bindAll(this, 'onSwitcherSelect'); - // this._super(); - // this.updateModal(this.initSwitcherHandler.bind(this)); - // - // return this; - // }, - // - // initConfig: function (options) { - // debugger; - // return this._super(); - // }, - // - // /** @inheritdoc */ - // destroyInserted: function () { - // if (this.isRendered) { - // _.each(this.toRemove, function (componentName) { - // registry.get(componentName, function (component) { - // if (component.hasOwnProperty('delegate')) { - // component.delegate('destroy'); - // } else { - // component.destroy(); - // } - // }); - // }); - // } - // - // this._super(); - // }, - // - // // /** - // // * Form save callback. - // // * - // // * @param {Object} data - // // */ - // // afterUpdate: function (data) { - // // if (!data.error) { - // // this.updateModal('closeModal'); - // // this.upcomingListing('reload'); - // // } - // // }, - // - // // /** - // // * Form remove callback. - // // * - // // * @param {Object} data - // // */ - // // afterRemove: function (data) { - // // if (!data.error) { - // // this.removeModal('closeModal'); - // // this.afterUpdate(data); - // // } - // // }, - // - // // /** - // // * Change modal title. - // // * - // // * @param {String} title - // // */ - // // changeModalTitle: function (title) { - // // this.updateModal('setTitle', title); - // // }, - // // - // // /** - // // * Change modal sub title. - // // * - // // * @param {String} subTitle - // // */ - // // changeModalSubTitle: function (subTitle) { - // // subTitle = subTitle ? - // // this.subTitlePrefix + this.modalTitle + ' ' + subTitle : - // // ''; - // // - // // this.updateModal('setSubTitle', subTitle); - // // }, - // - // /** - // * Destroy contents of modal when it is closed - // * - // * @param {Boolean} state - // */ - // destroyClosedModalContents: function (state) { - // if (state === false) { - // this.destroyInserted(); - // } - // }, - // - // /** - // * Switcher initialization. - // */ - // initSwitcherHandler: function () { - // var switcherSelector = this.updateModal().rootSelector + ' ' + this.switcherSelector, - // self = this; - // - // $.async(switcherSelector, function (switcher) { - // $(switcher).on('click', 'li a', self.onSwitcherSelect); - // }); - // }, - // - // /** - // * Store switcher selection handler. - // * @param {Object} e - event object. - // */ - // onSwitcherSelect: function (e) { - // var self = this, - // param = $(e.currentTarget).data('param'), - // params = { - // store: 0 - // }; - // - // params[param] = $(e.currentTarget).data('value'); - // - // uiConfirm({ - // content: $t('Please confirm scope switching. All data that hasn\'t been saved will be lost.'), - // actions: { - // - // /** Confirm callback. */ - // confirm: function () { - // self.destroyInserted(); - // params = _.extend(self.previousParams, params); - // self.render(params); - // } - // } - // }); - // } - }); -}); diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index 1eb3652628c28..6fc8fcac6099f 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -351,7 +351,7 @@ button - Edit + Edit true ${ $.provider}:data.default_billing_address.entity_id @@ -398,7 +398,7 @@ button - Edit + Edit true ${ $.provider}:data.default_shipping_address.entity_id @@ -428,7 +428,7 @@ - Add New Address + Add New Address ${ $.provider}:data.customer_id @@ -440,9 +440,8 @@ - + - customer_address_edit 1 diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/collection.js b/app/code/Magento/Ui/view/base/web/js/form/components/collection.js index dbea61b13e626..96d2a9e83a8f5 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/collection.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/collection.js @@ -5,9 +5,6 @@ /** * @api - * @deprecated as customer addresses are handled by ui components. - * This collection component manages rendering address list in Addresses tab of customer. - * Now address list is rendered with ui component listing. */ define([ 'underscore', @@ -49,7 +46,6 @@ define([ * @param {Object} elem - Incoming child. */ initElement: function (elem) { - debugger; this._super(); elem.activate(); diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js b/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js index ae7f375bdca02..045c25ab7911f 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js @@ -5,9 +5,6 @@ /** * @api - * @deprecated as customer addresses are handled by ui components. - * This item component renders address list item preview in Addresses tab. - * But now address list item is rendered with ui component in customer form. */ define([ 'underscore', diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt index 5c5c380c4dd33..877583e5b6a29 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt @@ -1 +1 @@ -# Format: or simply \ No newline at end of file +# Format: or simply diff --git a/setup/performance-toolkit/config/customerConfig.xml b/setup/performance-toolkit/config/customerConfig.xml index b77d9f6d4c941..8fd74d7b53885 100644 --- a/setup/performance-toolkit/config/customerConfig.xml +++ b/setup/performance-toolkit/config/customerConfig.xml @@ -6,5 +6,5 @@ */ --> - 5 + 2 From 3478e785f5a08e6220ff604836cec9199c2f30ba Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Wed, 31 Oct 2018 17:15:41 +0200 Subject: [PATCH 07/77] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Fix static tests; --- .../Adminhtml/Edit/Address/CancelButton.php | 1 + .../Adminhtml/Edit/Address/SaveButton.php | 1 + .../Address/AbstractDefaultAddress.php | 4 +- .../Address/DefaultBillingAddress.php | 1 + .../Address/DefaultShippingAddress.php | 1 + .../Controller/Adminhtml/Address/Delete.php | 4 +- .../Adminhtml/Address/MassDelete.php | 10 +- .../Controller/Adminhtml/Address/Save.php | 4 +- .../Controller/Adminhtml/Address/Validate.php | 14 ++- .../Adminhtml/File/Address/Upload.php | 3 +- .../Customer/Model/Address/DataProvider.php | 1 + .../Model/AttributeMetadataResolver.php | 13 +- .../Customer/Model/Customer/DataProvider.php | 14 ++- .../DataProviderWithDefaultAddresses.php | 6 +- .../Model/FileUploaderDataResolver.php | 4 +- .../Model/ResourceModel/Address/Relation.php | 2 - .../Unit/Model/Address/DataProviderTest.php | 62 ++++------ .../ResourceModel/CustomerRepositoryTest.php | 6 - .../Ui/Component/Form/AddressFieldsetTest.php | 2 +- .../Listing/Address/Column/Countries.php | 6 +- .../ui_component/customer_address_form.xml | 2 +- .../web/js/address/default-address.js | 15 ++- app/code/Magento/Ui/Component/Layout/Tabs.php | 115 ++++++++++-------- .../web/css/source/_module.less | 15 +-- 24 files changed, 167 insertions(+), 139 deletions(-) diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php index 6270e8073d0e8..d94b956918370 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php @@ -1,4 +1,5 @@ filter->getCollection($this->collectionFactory->create()); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php index 9113640112e3b..8b70263d3f95b 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php @@ -1,4 +1,5 @@ formFactory->create('customer_address', 'adminhtml_customer_address'); $formData = $addressForm->extractData($this->getRequest()); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php b/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php index be1b1aec7b3a3..afa62d2148eb4 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php @@ -10,6 +10,7 @@ use Magento\Customer\Api\AddressMetadataInterface; use Magento\Customer\Model\FileUploader; use Magento\Customer\Model\FileUploaderFactory; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Exception\LocalizedException; use Psr\Log\LoggerInterface; @@ -17,7 +18,7 @@ /** * Uploads files for customer address */ -class Upload extends Action +class Upload extends Action implements HttpGetActionInterface { /** * Authorization level of a basic admin session diff --git a/app/code/Magento/Customer/Model/Address/DataProvider.php b/app/code/Magento/Customer/Model/Address/DataProvider.php index 5fd7c884cf6b5..1b4bcf94fec15 100644 --- a/app/code/Magento/Customer/Model/Address/DataProvider.php +++ b/app/code/Magento/Customer/Model/Address/DataProvider.php @@ -85,6 +85,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider * @param array $data * @param bool $allowToShowHiddenAttributes * @throws \Magento\Framework\Exception\LocalizedException + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( $name, diff --git a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php index 36ffcb2f5495b..6b006d7f44e7e 100644 --- a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php +++ b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php @@ -19,6 +19,7 @@ /** * Class to build meta data of the customer or customer address attribute + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AttributeMetadataResolver { @@ -188,8 +189,16 @@ private function canShowAttributeInForm(AbstractAttribute $customerAttribute, st if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { return \is_array($customerAttribute->getUsedInForms()) && ( - (\in_array('customer_account_create', $customerAttribute->getUsedInForms(), true) && $isRegistration) || - (\in_array('customer_account_edit', $customerAttribute->getUsedInForms(), true) && !$isRegistration) + (\in_array( + 'customer_account_create', + $customerAttribute->getUsedInForms(), + true + ) && $isRegistration) || + (\in_array( + 'customer_account_edit', + $customerAttribute->getUsedInForms(), + true + ) && !$isRegistration) ); } return \is_array($customerAttribute->getUsedInForms()) && diff --git a/app/code/Magento/Customer/Model/Customer/DataProvider.php b/app/code/Magento/Customer/Model/Customer/DataProvider.php index c834dd26824cd..7b770d20263dc 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProvider.php +++ b/app/code/Magento/Customer/Model/Customer/DataProvider.php @@ -29,6 +29,8 @@ use Magento\Ui\DataProvider\EavValidationRules; /** + * Data provider for customer and customer address data and meta data + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * * @deprecated \Magento\Customer\Model\Address\DataProvider is used instead @@ -148,18 +150,20 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider private $allowToShowHiddenAttributes; /** - * @param string $name - * @param string $primaryFieldName - * @param string $requestFieldName + * DataProvider constructor. + * @param $name + * @param $primaryFieldName + * @param $requestFieldName * @param EavValidationRules $eavValidationRules * @param CustomerCollectionFactory $customerCollectionFactory * @param Config $eavConfig * @param FilterPool $filterPool - * @param FileProcessorFactory $fileProcessorFactory - * @param ContextInterface $context + * @param FileProcessorFactory|null $fileProcessorFactory * @param array $meta * @param array $data + * @param ContextInterface|null $context * @param bool $allowToShowHiddenAttributes + * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( diff --git a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php index 9131fb2ccdce2..eafa5907efac9 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php +++ b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php @@ -42,11 +42,6 @@ class DataProviderWithDefaultAddresses extends \Magento\Ui\DataProvider\Abstract 'confirmation', ]; - /* - * @var ContextInterface - */ - private $context; - /** * Allow to manage attributes, even they are hidden on storefront * @@ -83,6 +78,7 @@ class DataProviderWithDefaultAddresses extends \Magento\Ui\DataProvider\Abstract * @param array $meta * @param array $data * @throws \Magento\Framework\Exception\LocalizedException + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( string $name, diff --git a/app/code/Magento/Customer/Model/FileUploaderDataResolver.php b/app/code/Magento/Customer/Model/FileUploaderDataResolver.php index a132507deaa2f..9dd5084739c74 100644 --- a/app/code/Magento/Customer/Model/FileUploaderDataResolver.php +++ b/app/code/Magento/Customer/Model/FileUploaderDataResolver.php @@ -39,7 +39,9 @@ class FileUploaderDataResolver /** * @param FileProcessorFactory $fileProcessorFactory */ - public function __construct(FileProcessorFactory $fileProcessorFactory) { + public function __construct( + FileProcessorFactory $fileProcessorFactory + ) { $this->fileProcessorFactory = $fileProcessorFactory; } diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php index cbfebe87812bc..37a633d47f512 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php @@ -46,7 +46,6 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) $changedAddresses['default_billing'] = $object->getId(); } elseif ($customer->getDefaultBillingAddress() && (int)$customer->getDefaultBillingAddress()->getId() === (int)$object->getId() - && !$object->getIsDefaultBilling() ) { $changedAddresses['default_billing'] = null; } @@ -55,7 +54,6 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) $changedAddresses['default_shipping'] = $object->getId(); } elseif ($customer->getDefaultShippingAddress() && (int)$customer->getDefaultShippingAddress()->getId() === (int)$object->getId() - && !$object->getIsDefaultShipping() ) { $changedAddresses['default_shipping'] = null; } diff --git a/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php index 815a47288e370..81266a8934c87 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php @@ -7,16 +7,15 @@ namespace Magento\Customer\Test\Unit\Model\Address; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\AttributeMetadataResolver; +use Magento\Customer\Model\FileUploaderDataResolver; use Magento\Customer\Model\ResourceModel\Address\CollectionFactory; use Magento\Customer\Model\ResourceModel\Address\Collection as AddressCollection; use Magento\Eav\Model\Config; use Magento\Eav\Model\Entity\Type; -use Magento\Ui\DataProvider\EavValidationRules; -use Magento\Customer\Model\Attribute as AttributeModel; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Customer\Model\Address as AddressModel; -use Magento\Customer\Model\FileProcessorFactory; class DataProviderTest extends \PHPUnit\Framework\TestCase { @@ -45,26 +44,11 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase */ private $eavConfig; - /** - * @var EavValidationRules|\PHPUnit_Framework_MockObject_MockObject - */ - private $eavValidationRules; - /* * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject */ private $context; - /** - * @var \Magento\Customer\Model\Config\Share|\PHPUnit_Framework_MockObject_MockObject - */ - private $shareConfig; - - /** - * @var FileProcessorFactory|\PHPUnit_Framework_MockObject_MockObject - */ - private $fileProcessorFactory; - /** * @var Type|\PHPUnit_Framework_MockObject_MockObject */ @@ -76,9 +60,14 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase private $address; /** - * @var AttributeModel|\PHPUnit_Framework_MockObject_MockObject + * @var FileUploaderDataResolver|\PHPUnit_Framework_MockObject_MockObject + */ + private $fileUploaderDataResolver; + + /** + * @var AttributeMetadataResolver|\PHPUnit_Framework_MockObject_MockObject */ - private $attribute; + private $attributeMetadataResolver; /** * @var \Magento\Customer\Model\Address\DataProvider @@ -88,6 +77,12 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase protected function setUp() { $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->fileUploaderDataResolver = $this->getMockBuilder(FileUploaderDataResolver::class) + ->disableOriginalConstructor() + ->getMock(); + $this->attributeMetadataResolver = $this->getMockBuilder(AttributeMetadataResolver::class) + ->disableOriginalConstructor() + ->getMock(); $this->addressCollectionFactory = $this->getMockBuilder(CollectionFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) @@ -96,13 +91,7 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); $this->customerRepository = $this->getMockForAbstractClass(CustomerRepositoryInterface::class); - $this->eavValidationRules = $this->createMock(EavValidationRules::class); $this->context = $this->getMockForAbstractClass(ContextInterface::class); - $this->fileProcessorFactory = $this->getMockBuilder(FileProcessorFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->shareConfig = $this->createMock(\Magento\Customer\Model\Config\Share::class); $this->addressCollectionFactory->expects($this->once()) ->method('create') ->willReturn($this->collection); @@ -122,9 +111,6 @@ protected function setUp() $this->address = $this->getMockBuilder(AddressModel::class) ->disableOriginalConstructor() ->getMock(); - $this->attribute = $this->getMockBuilder(AttributeModel::class) - ->disableOriginalConstructor() - ->getMock(); $this->model = $objectManagerHelper->getObject( \Magento\Customer\Model\Address\DataProvider::class, @@ -135,10 +121,9 @@ protected function setUp() 'addressCollectionFactory' => $this->addressCollectionFactory, 'customerRepository' => $this->customerRepository, 'eavConfig' => $this->eavConfig, - 'eavValidationRules' => $this->eavValidationRules, 'context' => $this->context, - 'fileProcessorFactory' => $this->fileProcessorFactory, - 'shareConfig' => $this->shareConfig, + 'fileUploaderDataResolver' => $this->fileUploaderDataResolver, + 'attributeMetadataResolver' => $this->attributeMetadataResolver, [], [], true @@ -146,7 +131,7 @@ protected function setUp() ); } - public function testGetDefaultData() + public function testGetDefaultData(): void { $expectedData = [ '' => [ @@ -176,7 +161,7 @@ public function testGetDefaultData() $this->assertEquals($expectedData, $this->model->getData()); } - public function testGetData() + public function testGetData(): void { $expectedData = [ '3' => [ @@ -221,12 +206,9 @@ public function testGetData() 'lastname' => 'Doe', 'street' => "42000 Ave W 55 Cedar City\nApt. 33" ]); - $this->address->expects($this->once()) - ->method('getAttributes') - ->willReturn([$this->attribute]); - $this->attribute->expects($this->once()) - ->method('getFrontendInput') - ->willReturn(null); + $this->fileUploaderDataResolver->expects($this->once()) + ->method('overrideFileUploaderData') + ->willReturnSelf(); $this->assertEquals($expectedData, $this->model->getData()); } diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php index 1d262e7549873..034832ce72bfb 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php @@ -377,12 +377,6 @@ public function testSaveWithPasswordHash() 'getFirstFailure', 'getLockExpires', ]); - $region = $this->getMockForAbstractClass( - \Magento\Customer\Api\Data\RegionInterface::class, - [], - '', - false - ); $origCustomer = $this->customer; $customerModel = $this->createPartialMock(\Magento\Customer\Model\Customer::class, [ diff --git a/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php b/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php index 82ce4249bcd85..31a8a1bfacc64 100644 --- a/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php +++ b/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -namespace Magento\Ui\Test\Unit\Component\Form; +namespace Magento\Customer\Test\Unit\Ui\Component\Form; use Magento\Customer\Ui\Component\Form\AddressFieldset; use Magento\Framework\View\Element\UiComponent\ContextInterface; diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php index 438a16ec3ba55..d05d5d1c592a7 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php @@ -1,5 +1,9 @@ text - + 0 diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js index 3a1500e5dc99e..b4ec734cb4033 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js @@ -9,16 +9,23 @@ define([ 'use strict'; return Button.extend({ + defaults: { + entity_id: null, + parent_id: null + }, + + /** + * Initializes component. + * + * @returns {Button} + */ initialize: function () { this._super(); if (!this.parent_id) { this.visible(this.entity_id); } - }, - defaults: { - entity_id: null, - parent_id: null + return this; }, /** diff --git a/app/code/Magento/Ui/Component/Layout/Tabs.php b/app/code/Magento/Ui/Component/Layout/Tabs.php index 02e8979f525ef..f5a9c86977c3c 100644 --- a/app/code/Magento/Ui/Component/Layout/Tabs.php +++ b/app/code/Magento/Ui/Component/Layout/Tabs.php @@ -5,10 +5,8 @@ */ namespace Magento\Ui\Component\Layout; -use Magento\Framework\View\Element\Template; use Magento\Framework\View\Element\UiComponent\BlockWrapperInterface; use Magento\Framework\View\Element\UiComponent\DataSourceInterface; -use Magento\Framework\View\Element\UiComponent\LayoutInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Framework\View\Element\UiComponentInterface; use Magento\Ui\Component\Form\Fieldset; @@ -17,7 +15,7 @@ /** * Class Tabs */ -class Tabs extends \Magento\Framework\View\Layout\Generic implements LayoutInterface +class Tabs extends \Magento\Framework\View\Layout\Generic { /** * @var string @@ -97,54 +95,8 @@ protected function addChildren(array &$topNode, UiComponentInterface $component, $name = $childComponent->getName(); $config = $childComponent->getData('config'); $collectedComponents[$name] = true; - if (isset($config['is_collection']) && $config['is_collection'] === true) { - $label = $childComponent->getData('config/label'); - $this->component->getContext()->addComponentDefinition( - 'collection', - [ - 'component' => 'Magento_Ui/js/form/components/collection', - 'extends' => $this->namespace - ] - ); - - /** - * @var UiComponentInterface $childComponent - * @var array $structure - */ - list($childComponent, $structure) = $this->prepareChildComponents($childComponent, $name); - - $childrenStructure = $structure[$name]['children']; - - $structure[$name]['children'] = [ - $name . '_collection' => [ - 'type' => 'collection', - 'config' => [ - 'active' => 1, - 'removeLabel' => __('Remove %1', $label), - 'addLabel' => __('Add New %1', $label), - 'removeMessage' => $childComponent->getData('config/removeMessage'), - 'itemTemplate' => 'item_template', - ], - 'children' => [ - 'item_template' => ['type' => $this->namespace, - 'isTemplate' => true, - 'component' => 'Magento_Ui/js/form/components/collection/item', - 'childType' => 'group', - 'config' => [ - 'label' => __('New %1', $label), - ], - 'children' => $childrenStructure - ] - ] - ] - ]; - } else { - /** - * @var UiComponentInterface $childComponent - * @var array $structure - */ - list($childComponent, $structure) = $this->prepareChildComponents($childComponent, $name); - } + + [$childComponent, $structure] = $this->buildChildComponentStructure($config, $childComponent); $tabComponent = $this->createTabComponent($childComponent, $name); @@ -172,6 +124,67 @@ protected function addChildren(array &$topNode, UiComponentInterface $component, $topNode = $this->structure; } + /** + * Build child components structure of the tab + * + * @param array $config + * @param UiComponentInterface $childComponent + * @return array + */ + private function buildChildComponentStructure(array $config, $childComponent): array + { + $name = $childComponent->getName(); + if (isset($config['is_collection']) && $config['is_collection'] === true) { + $label = $childComponent->getData('config/label'); + $this->component->getContext()->addComponentDefinition( + 'collection', + [ + 'component' => 'Magento_Ui/js/form/components/collection', + 'extends' => $this->namespace + ] + ); + /** + * @var UiComponentInterface $childComponent + * @var array $structure + */ + [$childComponent, $structure] = $this->prepareChildComponents($childComponent, $name); + + $childrenStructure = $structure[$name]['children']; + + $structure[$name]['children'] = [ + $name . '_collection' => [ + 'type' => 'collection', + 'config' => [ + 'active' => 1, + 'removeLabel' => __('Remove %1', $label), + 'addLabel' => __('Add New %1', $label), + 'removeMessage' => $childComponent->getData('config/removeMessage'), + 'itemTemplate' => 'item_template', + ], + 'children' => [ + 'item_template' => ['type' => $this->namespace, + 'isTemplate' => true, + 'component' => 'Magento_Ui/js/form/components/collection/item', + 'childType' => 'group', + 'config' => [ + 'label' => __('New %1', $label), + ], + 'children' => $childrenStructure + ] + ] + ] + ]; + } else { + /** + * @var UiComponentInterface $childComponent + * @var array $structure + */ + [$childComponent, $structure] = $this->prepareChildComponents($childComponent, $name); + } + + return [$childComponent, $structure]; + } + /** * Add wrapped layout block * diff --git a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less index 8a5fe698eb196..876859ff38d1a 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less @@ -3,7 +3,7 @@ // * See COPYING.txt for license details. // */ -.customer_form_areas_address_address_customer_address_update_modal_update_customer_address_form_loader { +.customer_form_areas_address_address_customer_address_update_modal_update_customer_address_form_loader { .admin__field { .admin__field { .admin__field-label { @@ -15,10 +15,12 @@ .customer-address-form { - *, *:before, *:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; + *, + *:before, + *:after { box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; } address { @@ -26,8 +28,7 @@ } .customer-default-billing-address-content, - .customer-default-shipping-address-content - { + .customer-default-shipping-address-content { float: left; width: 550px; } @@ -52,10 +53,10 @@ } .add-new-address-button { - position: relative; clear: both; float: right; margin-bottom: 30px; + position: relative; } .address-information { From 5646ab9d0decf6012ba38dc6d8e974d4d0c51330 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Thu, 1 Nov 2018 10:01:32 +0200 Subject: [PATCH 08/77] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Fix fail of code compiler; --- app/code/Magento/Customer/Model/Customer/DataProvider.php | 7 +++---- app/code/Magento/Customer/Model/Metadata/Form/File.php | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Customer/Model/Customer/DataProvider.php b/app/code/Magento/Customer/Model/Customer/DataProvider.php index 7b770d20263dc..18e78c6bd7baf 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProvider.php +++ b/app/code/Magento/Customer/Model/Customer/DataProvider.php @@ -150,10 +150,9 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider private $allowToShowHiddenAttributes; /** - * DataProvider constructor. - * @param $name - * @param $primaryFieldName - * @param $requestFieldName + * @param string $name + * @param string $primaryFieldName + * @param string $requestFieldName * @param EavValidationRules $eavValidationRules * @param CustomerCollectionFactory $customerCollectionFactory * @param Config $eavConfig diff --git a/app/code/Magento/Customer/Model/Metadata/Form/File.php b/app/code/Magento/Customer/Model/Metadata/Form/File.php index b9bec01f9ba7c..3959d404d9f55 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/File.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/File.php @@ -68,7 +68,7 @@ class File extends AbstractData * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute * @param \Magento\Framework\Locale\ResolverInterface $localeResolver - * @param array|string $value + * @param null $value * @param string $entityTypeCode * @param bool $isAjax * @param \Magento\Framework\Url\EncoderInterface $urlEncoder From 530d3f5356b09a2c8d0184a2f2da5d29d3224074 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Thu, 1 Nov 2018 11:48:04 +0200 Subject: [PATCH 09/77] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Static tests fix; --- .../Model/AttributeMetadataResolver.php | 14 ++--- .../Customer/Model/Metadata/Form/File.php | 2 +- .../Model/ResourceModel/Address/Relation.php | 56 +++++++++++++------ .../Unit/Model/Address/DataProviderTest.php | 3 + 4 files changed, 48 insertions(+), 27 deletions(-) diff --git a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php index 6b006d7f44e7e..c22cc9a4f23f4 100644 --- a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php +++ b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php @@ -189,16 +189,10 @@ private function canShowAttributeInForm(AbstractAttribute $customerAttribute, st if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { return \is_array($customerAttribute->getUsedInForms()) && ( - (\in_array( - 'customer_account_create', - $customerAttribute->getUsedInForms(), - true - ) && $isRegistration) || - (\in_array( - 'customer_account_edit', - $customerAttribute->getUsedInForms(), - true - ) && !$isRegistration) + (\in_array('customer_account_create', $customerAttribute->getUsedInForms(), true) + && $isRegistration) || + (\in_array('customer_account_edit', $customerAttribute->getUsedInForms(), true) + && !$isRegistration) ); } return \is_array($customerAttribute->getUsedInForms()) && diff --git a/app/code/Magento/Customer/Model/Metadata/Form/File.php b/app/code/Magento/Customer/Model/Metadata/Form/File.php index 3959d404d9f55..227e85ed98f91 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/File.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/File.php @@ -68,7 +68,7 @@ class File extends AbstractData * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute * @param \Magento\Framework\Locale\ResolverInterface $localeResolver - * @param null $value + * @param string|array $value * @param string $entityTypeCode * @param bool $isAjax * @param \Magento\Framework\Url\EncoderInterface $urlEncoder diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php index 37a633d47f512..a2ba37e7ba1dd 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php @@ -7,6 +7,8 @@ */ namespace Magento\Customer\Model\ResourceModel\Address; +use Magento\Customer\Model\Address\AddressModelInterface; +use Magento\Customer\Model\Customer; use Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationInterface; /** @@ -40,23 +42,9 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) */ if (!$object->getIsCustomerSaveTransaction() && $object->getId()) { $customer = $this->customerFactory->create()->load($object->getCustomerId()); - $changedAddresses = []; - if ($object->getIsDefaultBilling()) { - $changedAddresses['default_billing'] = $object->getId(); - } elseif ($customer->getDefaultBillingAddress() - && (int)$customer->getDefaultBillingAddress()->getId() === (int)$object->getId() - ) { - $changedAddresses['default_billing'] = null; - } - - if ($object->getIsDefaultShipping()) { - $changedAddresses['default_shipping'] = $object->getId(); - } elseif ($customer->getDefaultShippingAddress() - && (int)$customer->getDefaultShippingAddress()->getId() === (int)$object->getId() - ) { - $changedAddresses['default_shipping'] = null; - } + $changedAddresses['default_billing'] = $this->getDefaultBillingChangedAddress($object, $customer); + $changedAddresses['default_shipping'] = $this->getDefaultShippingChangedAddress($object, $customer); if ($changedAddresses) { $customer->getResource()->getConnection()->update( @@ -68,6 +56,42 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) } } + /** + * Get default billing changed address + * + * @param AddressModelInterface $object + * @param Customer $customer + * @return int|null + */ + private function getDefaultBillingChangedAddress(AddressModelInterface $object, Customer $customer): ?int + { + if ($object->getIsDefaultBilling()) { + return $object->getId(); + } elseif ($customer->getDefaultBillingAddress() + && (int)$customer->getDefaultBillingAddress()->getId() === (int)$object->getId() + ) { + return null; + } + } + + /** + * Get default shipping changed address + * + * @param AddressModelInterface $object + * @param Customer $customer + * @return int|null + */ + private function getDefaultShippingChangedAddress(AddressModelInterface $object, Customer $customer): ?int + { + if ($object->getIsDefaultShipping()) { + return $object->getId(); + } elseif ($customer->getDefaultShippingAddress() + && (int)$customer->getDefaultShippingAddress()->getId() === (int)$object->getId() + ) { + return null; + } + } + /** * Checks if address has chosen as default and has had an id * diff --git a/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php index 81266a8934c87..92a15ac01fafa 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php @@ -17,6 +17,9 @@ use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Customer\Model\Address as AddressModel; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class DataProviderTest extends \PHPUnit\Framework\TestCase { /** From 89e371f3638d26d22df0b44af4a16a8adb0abdef Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Thu, 1 Nov 2018 12:12:18 +0200 Subject: [PATCH 10/77] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Static tests fix; --- .../Customer/Model/ResourceModel/Address/Grid/Collection.php | 1 + .../Customer/Test/Unit/Model/Address/DataProviderTest.php | 1 + .../Model/Customer/DataProviderWithDefaultAddressesTest.php | 1 + .../Test/Unit/Ui/Component/Form/AddressFieldsetTest.php | 2 +- app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php | 2 +- .../Magento/Customer/Controller/Adminhtml/Address/SaveTest.php | 1 + 6 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php index 8026349563867..09aef78b6cebc 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php @@ -1,4 +1,5 @@ Date: Thu, 1 Nov 2018 19:05:12 +0200 Subject: [PATCH 11/77] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Fix address relation model; - Unit tests updated; --- .../Adminhtml/File/Address/Upload.php | 3 +- .../Model/ResourceModel/Address/Relation.php | 45 ++++++++++++------- .../Adminhtml/File/Address/UploadTest.php | 40 +++++++++-------- .../ResourceModel/Address/RelationTest.php | 16 ++++++- 4 files changed, 66 insertions(+), 38 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php b/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php index afa62d2148eb4..e9034c8050383 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php @@ -11,6 +11,7 @@ use Magento\Customer\Model\FileUploader; use Magento\Customer\Model\FileUploaderFactory; use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Exception\LocalizedException; use Psr\Log\LoggerInterface; @@ -18,7 +19,7 @@ /** * Uploads files for customer address */ -class Upload extends Action implements HttpGetActionInterface +class Upload extends Action implements HttpGetActionInterface, HttpPostActionInterface { /** * Authorization level of a basic admin session diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php index a2ba37e7ba1dd..a82a814d40772 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php @@ -7,7 +7,7 @@ */ namespace Magento\Customer\Model\ResourceModel\Address; -use Magento\Customer\Model\Address\AddressModelInterface; +use Magento\Customer\Model\Address; use Magento\Customer\Model\Customer; use Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationInterface; @@ -38,13 +38,14 @@ public function __construct(\Magento\Customer\Model\CustomerFactory $customerFac public function processRelation(\Magento\Framework\Model\AbstractModel $object) { /** - * @var $object \Magento\Customer\Model\Address + * @var $object Address */ if (!$object->getIsCustomerSaveTransaction() && $object->getId()) { $customer = $this->customerFactory->create()->load($object->getCustomerId()); - $changedAddresses['default_billing'] = $this->getDefaultBillingChangedAddress($object, $customer); - $changedAddresses['default_shipping'] = $this->getDefaultShippingChangedAddress($object, $customer); + $changedAddresses = []; + $changedAddresses = $this->getDefaultBillingChangedAddress($object, $customer, $changedAddresses); + $changedAddresses = $this->getDefaultShippingChangedAddress($object, $customer, $changedAddresses); if ($changedAddresses) { $customer->getResource()->getConnection()->update( @@ -59,37 +60,49 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) /** * Get default billing changed address * - * @param AddressModelInterface $object + * @param Address $object * @param Customer $customer - * @return int|null + * @param array $changedAddresses + * @return array */ - private function getDefaultBillingChangedAddress(AddressModelInterface $object, Customer $customer): ?int - { + private function getDefaultBillingChangedAddress( + Address $object, + Customer $customer, + array $changedAddresses + ): array { if ($object->getIsDefaultBilling()) { - return $object->getId(); + $changedAddresses['default_billing'] = $object->getId(); } elseif ($customer->getDefaultBillingAddress() && (int)$customer->getDefaultBillingAddress()->getId() === (int)$object->getId() ) { - return null; + $changedAddresses['default_billing'] = null; } + + return $changedAddresses; } /** * Get default shipping changed address * - * @param AddressModelInterface $object + * @param Address $object * @param Customer $customer - * @return int|null + * @param array $changedAddresses + * @return array */ - private function getDefaultShippingChangedAddress(AddressModelInterface $object, Customer $customer): ?int - { + private function getDefaultShippingChangedAddress( + Address $object, + Customer $customer, + array $changedAddresses + ): array { if ($object->getIsDefaultShipping()) { - return $object->getId(); + $changedAddresses['default_shipping'] = $object->getId(); } elseif ($customer->getDefaultShippingAddress() && (int)$customer->getDefaultShippingAddress()->getId() === (int)$object->getId() ) { - return null; + $changedAddresses['default_shipping'] = null; } + + return $changedAddresses; } /** diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/File/Address/UploadTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/File/Address/UploadTest.php index 20177ab0b0db6..8f8ed0e37a46b 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/File/Address/UploadTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/File/Address/UploadTest.php @@ -70,7 +70,8 @@ protected function setUp() $this->context, $this->fileUploaderFactory, $this->addressMetadataService, - $this->logger + $this->logger, + 'address' ); } @@ -104,27 +105,27 @@ public function testExecuteEmptyFiles() public function testExecute() { - $attributeCode = 'attribute_code'; + $attributeCode = 'file_address_attribute'; + $resultFileSize = 20000; + $resultFileName = 'text.txt'; + $resultType = 'text/plain'; $_FILES = [ - 'address' => [ - 'name' => [ - 'new_0' => [ - $attributeCode => 'filename', - ], - ], + $attributeCode => [ + 'name' => $resultFileName, + 'type' => $resultType, + 'size' => $resultFileSize ], ]; - $resultFileName = '/filename.ext1'; $resultFilePath = 'filepath'; $resultFileUrl = 'viewFileUrl'; $result = [ 'name' => $resultFileName, - 'file' => $resultFileName, - 'path' => $resultFilePath, - 'tmp_name' => $resultFilePath . $resultFileName, + 'type' => $resultType, + 'size' => $resultFileSize, + 'tmp_name' => $resultFilePath . '/' . $resultFileName, 'url' => $resultFileUrl, ]; @@ -173,15 +174,16 @@ public function testExecute() public function testExecuteWithErrors() { - $attributeCode = 'attribute_code'; + $attributeCode = 'file_address_attribute'; + $resultFileSize = 20000; + $resultFileName = 'text.txt'; + $resultType = 'text/plain'; $_FILES = [ - 'address' => [ - 'name' => [ - 'new_0' => [ - $attributeCode => 'filename', - ], - ], + $attributeCode => [ + 'name' => $resultFileName, + 'type' => $resultType, + 'size' => $resultFileSize ], ]; diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/Address/RelationTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/Address/RelationTest.php index e81637cfb23b2..319179c5e279a 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/Address/RelationTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/Address/RelationTest.php @@ -6,6 +6,7 @@ namespace Magento\Customer\Test\Unit\Model\ResourceModel\Address; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Customer\Model\Address; /** * Class AddressTest @@ -40,7 +41,7 @@ protected function setUp() */ public function testProcessRelation($addressId, $isDefaultBilling, $isDefaultShipping) { - $addressModel = $this->createPartialMock(\Magento\Framework\Model\AbstractModel::class, [ + $addressModel = $this->createPartialMock(Address::class, [ '__wakeup', 'getId', 'getEntityTypeId', @@ -55,7 +56,17 @@ public function testProcessRelation($addressId, $isDefaultBilling, $isDefaultShi ]); $customerModel = $this->createPartialMock( \Magento\Customer\Model\Customer::class, - ['__wakeup', 'setDefaultBilling', 'setDefaultShipping', 'save', 'load', 'getResource', 'getId'] + [ + '__wakeup', + 'setDefaultBilling', + 'setDefaultShipping', + 'save', + 'load', + 'getResource', + 'getId', + 'getDefaultShippingAddress', + 'getDefaultBillingAddress' + ] ); $customerResource = $this->getMockForAbstractClass( \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, @@ -88,6 +99,7 @@ public function testProcessRelation($addressId, $isDefaultBilling, $isDefaultShi $this->customerFactoryMock->expects($this->any()) ->method('create') ->willReturn($customerModel); + if ($addressId && ($isDefaultBilling || $isDefaultShipping)) { $customerId = 1; $customerResource->expects($this->exactly(2))->method('getConnection')->willReturn($connectionMock); From fc5eb486c48c3c6fd709d2a042b101e0bf32c976 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Fri, 2 Nov 2018 10:39:49 +0200 Subject: [PATCH 12/77] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Index in db_schema.xml fixed --- app/code/Magento/Customer/etc/db_schema.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/etc/db_schema.xml b/app/code/Magento/Customer/etc/db_schema.xml index 7e0b911e26184..7cf4c5be641e8 100644 --- a/app/code/Magento/Customer/etc/db_schema.xml +++ b/app/code/Magento/Customer/etc/db_schema.xml @@ -120,8 +120,8 @@ - - + + From 57e59d8044cca1d85873ecde5d6a00660f4ffb31 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Fri, 2 Nov 2018 15:20:27 +0200 Subject: [PATCH 13/77] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Default addresses changes --- .../Model/Customer/DataProviderWithDefaultAddresses.php | 4 ++-- .../Magento/Customer/Model/ResourceModel/Address/Relation.php | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php index eafa5907efac9..30edafe3d504c 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php +++ b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php @@ -24,7 +24,7 @@ class DataProviderWithDefaultAddresses extends \Magento\Ui\DataProvider\Abstract /** * @var array */ - private $loadedData; + private $loadedData = []; /** * @var SessionManagerInterface @@ -114,7 +114,7 @@ public function __construct( */ public function getData(): array { - if (null !== $this->loadedData) { + if (!empty($this->loadedData)) { return $this->loadedData; } $items = $this->collection->getItems(); diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php index a82a814d40772..ae342a1b10dd8 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php @@ -73,6 +73,7 @@ private function getDefaultBillingChangedAddress( if ($object->getIsDefaultBilling()) { $changedAddresses['default_billing'] = $object->getId(); } elseif ($customer->getDefaultBillingAddress() + && $object->getIsDefaultBilling() === false && (int)$customer->getDefaultBillingAddress()->getId() === (int)$object->getId() ) { $changedAddresses['default_billing'] = null; @@ -97,6 +98,7 @@ private function getDefaultShippingChangedAddress( if ($object->getIsDefaultShipping()) { $changedAddresses['default_shipping'] = $object->getId(); } elseif ($customer->getDefaultShippingAddress() + && $object->getIsDefaultShipping() === false && (int)$customer->getDefaultShippingAddress()->getId() === (int)$object->getId() ) { $changedAddresses['default_shipping'] = null; From 3785225428b54e81f0b6a09e0528af0978354044 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Mon, 5 Nov 2018 18:33:04 +0200 Subject: [PATCH 14/77] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Static fixes; --- .../adminhtml/web/js/address/default-address.js | 16 ++++++++-------- .../Magento_Customer/web/css/source/_module.less | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js index b4ec734cb4033..530df8544c841 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js @@ -10,8 +10,8 @@ define([ return Button.extend({ defaults: { - entity_id: null, - parent_id: null + entityId: null, + parentId: null }, /** @@ -21,8 +21,8 @@ define([ */ initialize: function () { this._super(); - if (!this.parent_id) { - this.visible(this.entity_id); + if (!this.parentId) { + this.visible(this.entityId); } return this; @@ -36,12 +36,12 @@ define([ */ applyAction: function (action) { if (action.params && action.params[0]) { - action.params[0].entity_id = this.entity_id; - action.params[0].parent_id = this.parent_id; + action.params[0].entityId = this.entityId; + action.params[0].parentId = this.parentId; } else { action.params = [{ - entity_id: this.entity_id, - parent_id: this.parent_id + entityId: this.entityId, + parentId: this.parentId }]; } diff --git a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less index 876859ff38d1a..7cb02b61fdbd8 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less @@ -16,8 +16,8 @@ .customer-address-form { *, - *:before, - *:after { + *:after, + *:before { box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; From 965f838c0e993c8a588f22cc5edc2db55f936a44 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Tue, 6 Nov 2018 11:45:36 +0200 Subject: [PATCH 15/77] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Ability to save addresses through CustomerRepository rolled back - Integration tests fixes --- .../ResourceModel/CustomerRepository.php | 57 +++- .../Customer/etc/db_schema_whitelist.json | 3 +- .../ui_component/customer_address_listing.xml | 29 +- .../view/base/ui_component/customer_form.xml | 9 +- .../Controller/Adminhtml/Address/SaveTest.php | 36 ++- .../Controller/Adminhtml/IndexTest.php | 275 +----------------- 6 files changed, 106 insertions(+), 303 deletions(-) diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php index 5d88cd92c1730..16c650be15b61 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php @@ -170,18 +170,22 @@ public function save(CustomerInterface $customer, $passwordHash = null) /** @var NewOperation|null $delegatedNewOperation */ $delegatedNewOperation = !$customer->getId() ? $this->delegatedStorage->consumeNewOperation() : null; $prevCustomerData = null; + $prevCustomerDataArr = null; if ($customer->getId()) { $prevCustomerData = $this->getById($customer->getId()); + $prevCustomerDataArr = $prevCustomerData->__toArray(); } - + /** @var $customer \Magento\Customer\Model\Data\Customer */ + $customerArr = $customer->__toArray(); $customer = $this->imageProcessor->save( $customer, CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, $prevCustomerData ); - - /** @var array $customerData */ + $origAddresses = $customer->getAddresses(); + $customer->setAddresses([]); $customerData = $this->extensibleDataObjectConverter->toNestedArray($customer, [], CustomerInterface::class); + $customer->setAddresses($origAddresses); /** @var Customer $customerModel */ $customerModel = $this->customerFactory->create(['data' => $customerData]); //Model's actual ID field maybe different than "id" so "id" field from $customerData may be ignored. @@ -190,16 +194,61 @@ public function save(CustomerInterface $customer, $passwordHash = null) if ($storeId === null) { $customerModel->setStoreId($this->storeManager->getStore()->getId()); } + // Need to use attribute set or future updates can cause data loss + if (!$customerModel->getAttributeSetId()) { + $customerModel->setAttributeSetId(CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER); + } $this->populateCustomerWithSecureData($customerModel, $passwordHash); // If customer email was changed, reset RpToken info if ($prevCustomerData && $prevCustomerData->getEmail() !== $customerModel->getEmail()) { $customerModel->setRpToken(null); $customerModel->setRpTokenCreatedAt(null); } + if (!array_key_exists('default_billing', $customerArr) + && null !== $prevCustomerDataArr + && array_key_exists('default_billing', $prevCustomerDataArr) + ) { + $customerModel->setDefaultBilling($prevCustomerDataArr['default_billing']); + } + if (!array_key_exists('default_shipping', $customerArr) + && null !== $prevCustomerDataArr + && array_key_exists('default_shipping', $prevCustomerDataArr) + ) { + $customerModel->setDefaultShipping($prevCustomerDataArr['default_shipping']); + } $customerModel->save(); $this->customerRegistry->push($customerModel); $customerId = $customerModel->getId(); - + if (!$customer->getAddresses() + && $delegatedNewOperation + && $delegatedNewOperation->getCustomer()->getAddresses() + ) { + $customer->setAddresses($delegatedNewOperation->getCustomer()->getAddresses()); + } + if ($customer->getAddresses() !== null) { + if ($customer->getId()) { + $existingAddresses = $this->getById($customer->getId())->getAddresses(); + $getIdFunc = function ($address) { + return $address->getId(); + }; + $existingAddressIds = array_map($getIdFunc, $existingAddresses); + } else { + $existingAddressIds = []; + } + $savedAddressIds = []; + foreach ($customer->getAddresses() as $address) { + $address->setCustomerId($customerId) + ->setRegion($address->getRegion()); + $this->addressRepository->save($address); + if ($address->getId()) { + $savedAddressIds[] = $address->getId(); + } + } + $addressIdsToDelete = array_diff($existingAddressIds, $savedAddressIds); + foreach ($addressIdsToDelete as $addressId) { + $this->addressRepository->deleteById($addressId); + } + } $this->customerRegistry->remove($customerId); $savedCustomer = $this->get($customer->getEmail(), $customer->getWebsiteId()); $this->eventManager->dispatch( diff --git a/app/code/Magento/Customer/etc/db_schema_whitelist.json b/app/code/Magento/Customer/etc/db_schema_whitelist.json index 4aada8f0d81fe..ec7a53945aba3 100644 --- a/app/code/Magento/Customer/etc/db_schema_whitelist.json +++ b/app/code/Magento/Customer/etc/db_schema_whitelist.json @@ -73,7 +73,8 @@ "vat_request_success": true }, "index": { - "CUSTOMER_ADDRESS_ENTITY_PARENT_ID": true + "CUSTOMER_ADDRESS_ENTITY_PARENT_ID": true, + "FTI_BA70344390184AC3F063AB2EB38BC0ED": true }, "constraint": { "PRIMARY": true, diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml index cfc62fc99b10e..28b19d61fbdde 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml @@ -39,25 +39,18 @@ - - - - customer_address_listing.customer_address_listing_data_source - customer_address_listing.customer_address_listing.listing_top.listing_filters_chips - - customer_address_listing.customer_address_listing.listing_top.bookmarks - current.search - - - + + + customer_address_listing.customer_address_listing.listing_top.listing_filters_chips + + customer_address_listing.customer_address_listing.listing_top.bookmarks + current.search + + - - customer_address_listing.customer_address_listing.listing_top.bookmarks - current.filters - customer_address_listing.customer_address_listing.listing_top.listing_filters @@ -66,6 +59,12 @@ + + + customer_address_listing.customer_address_listing.listing_top.bookmarks + current.filters + + diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index 6fc8fcac6099f..17d4e7aab5cd3 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -328,10 +328,9 @@ - - @@ -365,6 +369,9 @@ Default Shipping Address customer-default-shipping-address-content The customer does not have default shipping address + + true + @@ -399,7 +406,8 @@ Edit true - ${ $.provider}:data.default_shipping_address.entity_id + ${ $.provider}:data.default_shipping_address.entity_id + ${ $.provider}:data.default_shipping_address @@ -427,7 +435,7 @@ Add New Address - ${ $.provider}:data.customer_id + ${ $.provider}:data.customer_id @@ -437,8 +445,15 @@ - + + + + ns = customer_address_listing, index = customer_address_listing + ${ $.parentName } + + + ajax customer_address_edit 1 @@ -447,10 +462,16 @@ ${ $.parentName } ${ $.ns }.customer_address_form_data_source customer_address_form + + ${ $.externalProvider }:data.parent_id + + + ${ $.provider}:data.customer_id + - + false @@ -466,6 +487,8 @@ ${ $.provider }:data.customer.entity_id + ns = ${ $.ns }, index = actions:action + ns = ${ $.ns }, index = listing_massaction:massaction From 97da50434f0577ae602dfd79493323793f91baee Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Fri, 9 Nov 2018 10:42:59 +0200 Subject: [PATCH 21/77] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Add custom DataProvider for customer addresses listing; --- .../Listing/Address/DataProvider.php | 52 +++++++++++++++++++ .../ui_component/customer_address_listing.xml | 2 +- 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php new file mode 100644 index 0000000000000..df92e7c1c9421 --- /dev/null +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php @@ -0,0 +1,52 @@ +collection = $collectionFactory->create(); + } + + /** + * Add country key for default billing/shipping blocks on customer addresses tab + * + * @return array + */ + public function getData(): array + { + $collection = $this->getCollection(); + $data = $collection->toArray(); + foreach ($data['items'] as $key => $item) { + if (isset($item['country_id']) && !isset($item['country'])) { + $data['items'][$key]['country'] = $item['country_id']; + } + } + + return $data; + } +} diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml index 6c97a6da5e9e2..7fb8c49749f2d 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml @@ -28,7 +28,7 @@ Magento_Customer::manage - + id entity_id From 479a95f1e3fb58044d23f15e904f221b2e017574 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Mon, 12 Nov 2018 15:01:31 +0200 Subject: [PATCH 22/77] MAGETWO-96208: Mass action "Delete" removes all addresses --- .../Controller/Adminhtml/Address/MassDelete.php | 8 +++++++- .../ResourceModel/Address/Grid/Collection.php | 16 ---------------- .../Component/Listing/Address/DataProvider.php | 14 +++++++++++++- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php index 09523f9e6a956..10638739f2b0a 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php @@ -87,12 +87,18 @@ public function __construct( */ public function execute(): Json { + $customerData = $this->_session->getData('customer_data'); /** @var \Magento\Customer\Model\ResourceModel\Address\Collection $collection */ $collection = $this->filter->getCollection($this->collectionFactory->create()); - $collectionSize = $collection->getSize(); $error = false; try { + if ($customerData && $customerData['customer_id']) { + $collection->addFieldToFilter('parent_id', $customerData['customer_id']); + } else { + throw new \Exception(); + } + $collectionSize = $collection->getSize(); /** @var \Magento\Customer\Model\Address $address */ foreach ($collection as $address) { $this->addressRepository->deleteById($address->getId()); diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php index 09aef78b6cebc..b0e4c3d8dcc24 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php @@ -76,22 +76,6 @@ public function __construct( ); } - /** - * Resource initialization - * - * @return $this - */ - protected function _initSelect() - { - parent::_initSelect(); - $parentId = $this->context->getRequestParam('parent_id'); - if ($parentId !== null) { - $this->getSelect()->where('parent_id=?', $parentId); - } - - return $this; - } - /** * @inheritdoc * diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php index df92e7c1c9421..e034e5a894e96 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php @@ -12,11 +12,17 @@ */ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider { + /** + * @var \Magento\Framework\App\RequestInterface $request, + */ + private $request; + /** * @param string $name * @param string $primaryFieldName * @param string $requestFieldName * @param CollectionFactory $collectionFactory + * @param \Magento\Framework\App\RequestInterface $request * @param array $meta * @param array $data */ @@ -25,11 +31,13 @@ public function __construct( $primaryFieldName, $requestFieldName, CollectionFactory $collectionFactory, + \Magento\Framework\App\RequestInterface $request, array $meta = [], array $data = [] ) { parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); $this->collection = $collectionFactory->create(); + $this->request = $request; } /** @@ -40,7 +48,11 @@ public function __construct( public function getData(): array { $collection = $this->getCollection(); - $data = $collection->toArray(); + $data['items'] = []; + if ($this->request->getParam('parent_id')) { + $collection->addFieldToFilter('parent_id', $this->request->getParam('parent_id')); + $data = $collection->toArray(); + } foreach ($data['items'] as $key => $item) { if (isset($item['country_id']) && !isset($item['country'])) { $data['items'][$key]['country'] = $item['country_id']; From 36bc920210d45be31dbd724e3cff1675feae8b63 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Mon, 12 Nov 2018 19:32:01 +0200 Subject: [PATCH 23/77] MAGETWO-96182: Adopt integration and API tests for implemented changes --- .../ui_component/customer_address_listing.xml | 26 ++++---- .../Controller/Adminhtml/Address/SaveTest.php | 60 +------------------ 2 files changed, 13 insertions(+), 73 deletions(-) diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml index 4db5a7fb8d545..b138a81e395a3 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml @@ -63,22 +63,20 @@ - - - - - true - - Are you sure to delete selected address? - Delete items - - - delete - Delete + + + + true + + Are you sure to delete selected address? + Delete items + + delete + Delete - - + + diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php index 5a4a426b58cda..cbfa794f94dc0 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php @@ -89,19 +89,9 @@ public function testSaveActionWithValidAddressData() $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); - /** - * Check that customer data were cleaned after it was saved successfully - */ + /** Check that customer data were cleaned after it was saved successfully*/ $this->assertEmpty($this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerData()); - /** - * Check that success message is set - */ - $this->assertSessionMessages( - $this->logicalNot($this->isEmpty()), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - $customer = $this->customerRepository->getById($customerId); $this->assertEquals('Firstname', $customer->getFirstname()); @@ -148,14 +138,6 @@ public function testSaveActionWithDefaultShippingAndBilling() */ $this->assertEmpty($this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerData()); - /** - * Check that success message is set - */ - $this->assertSessionMessages( - $this->logicalNot($this->isEmpty()), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - /** * Remove stored customer from registry */ @@ -204,50 +186,10 @@ public function testSaveActionWithExistingAdresses() */ $this->assertEmpty($this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerData()); - /** - * Check that success message is set - */ - $this->assertSessionMessages( - $this->logicalNot($this->isEmpty()), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - $customer = $this->customerRepository->getById($customerId); $this->assertEquals('test firstname', $customer->getFirstname()); $addresses = $customer->getAddresses(); $this->assertCount(4, $addresses); } - - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/Customer/_files/customer_address.php - */ - public function testValidateCustomerWithAddressFailure() - { - $customer = $this->customerRepository->get('customer@example.com'); - $customerId = $customer->getId(); - $post = [ - 'parent_id' => $customerId, - 'firstname' => '', - 'lastname' => '', - 'street' => ['update street'], - 'city' => 'update city', - 'postcode' => '01001', - 'telephone' => '', - ]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - - $this->objectManager->get(\Magento\Backend\Model\Session::class)->setCustomerFormData($post); - - $this->customerAddress->execute(); - - /** - * Check that errors was generated and set to session - */ - $this->assertSessionMessages( - $this->equalTo(['One or more input exceptions have occurred.']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - } } From 329f4194904dc0da6be8958278f5c9ebad2b54d5 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko Date: Mon, 12 Nov 2018 13:23:06 -0600 Subject: [PATCH 24/77] MAGETWO-96178: Customer addresses should be saved using Ajax --- .../Listing/Address/DataProvider.php | 10 +++++- .../ui_component/customer_address_form.xml | 4 +-- .../web/js/form/components/insert-listing.js | 36 +++++++++---------- .../adminhtml/web/js/form/element/country.js | 27 ++++++++++++++ .../adminhtml/web/js/form/element/region.js | 27 ++++++++++++++ .../view/adminhtml/web/js/grid/massactions.js | 3 +- .../web/template/default-address.html | 4 +-- 7 files changed, 87 insertions(+), 24 deletions(-) create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/form/element/country.js create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/form/element/region.js diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php index e034e5a894e96..75f9dca9fd6cf 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php @@ -17,12 +17,18 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider */ private $request; + /** + * @var \Magento\Directory\Model\CountryFactory + */ + private $countryDirectory; + /** * @param string $name * @param string $primaryFieldName * @param string $requestFieldName * @param CollectionFactory $collectionFactory * @param \Magento\Framework\App\RequestInterface $request + * @param \Magento\Directory\Model\CountryFactory $countryFactory, * @param array $meta * @param array $data */ @@ -32,11 +38,13 @@ public function __construct( $requestFieldName, CollectionFactory $collectionFactory, \Magento\Framework\App\RequestInterface $request, + \Magento\Directory\Model\CountryFactory $countryFactory, array $meta = [], array $data = [] ) { parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); $this->collection = $collectionFactory->create(); + $this->countryDirectory = $countryFactory->create(); $this->request = $request; } @@ -55,7 +63,7 @@ public function getData(): array } foreach ($data['items'] as $key => $item) { if (isset($item['country_id']) && !isset($item['country'])) { - $data['items'][$key]['country'] = $item['country_id']; + $data['items'][$key]['country'] = $this->countryDirectory->loadByCode($item['country_id'])->getName(); } } diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index bcc5b86c5e65d..4aeb7d67ace68 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -181,7 +181,7 @@ - + true @@ -196,7 +196,7 @@ - + true diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-listing.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-listing.js index 0dd828b03b419..4e8952e9e2a9d 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-listing.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-listing.js @@ -4,8 +4,9 @@ */ define([ - 'Magento_Ui/js/form/components/insert-listing' -], function (Insert) { + 'Magento_Ui/js/form/components/insert-listing', + 'underscore' +], function (Insert, _) { 'use strict'; return Insert.extend({ @@ -25,29 +26,28 @@ define([ }, deleteAction: function (data) { + this._delete([parseFloat(data[data['id_field_name']])]); + }, + + deleteMassaction: function (data) { + var ids = _.map(data, function (val) { + return parseFloat(val); + }); + + this._delete(ids); + }, + + _delete: function (ids) { var defaultShippingId = parseFloat(this.source.get('data.default_shipping_address.entity_id')), defaultBillingId = parseFloat(this.source.get('data.default_billing_address.entity_id')); - if (parseFloat(data[data['id_field_name']]) === defaultShippingId) { + if (ids.indexOf(defaultShippingId) !== -1) { this.source.set('data.default_shipping_address', []); } - if (parseFloat(data[data['id_field_name']]) === defaultBillingId) { + + if (ids.indexOf(defaultBillingId) !== -1) { this.source.set('data.default_billing_address', []); } - }, - - //TODO: release logic with massaction - deleteMassaction: function (data) { - debugger; - // var defaultShippingId = parseFloat(this.source.get('data.default_shipping_address.entity_id')), - // defaultBillingId = parseFloat(this.source.get('data.default_billing_address.entity_id')); - // - // if (parseFloat(data[data['id_field_name']]) === defaultShippingId) { - // this.source.set('data.default_shipping_address', []); - // } - // if (parseFloat(data[data['id_field_name']]) === defaultBillingId) { - // this.source.set('data.default_billing_address', []); - // } } }); }); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/element/country.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/element/country.js new file mode 100644 index 0000000000000..bc60cf7a08fda --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/element/country.js @@ -0,0 +1,27 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * @api + */ +define([ + 'Magento_Ui/js/form/element/country' +], function (Country) { + 'use strict'; + + return Country.extend({ + defaults: { + countryScope: 'data.country' + }, + + setDifferedFromDefault: function (value) { + this._super(); + + if (value) { + this.source.set(this.countryScope, this.indexedOptions[value].label); + } + } + }); +}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/element/region.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/element/region.js new file mode 100644 index 0000000000000..a9013577a1026 --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/element/region.js @@ -0,0 +1,27 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * @api + */ +define([ + 'Magento_Ui/js/form/element/region' +], function (Region) { + 'use strict'; + + return Region.extend({ + defaults: { + regionScope: 'data.region' + }, + + setDifferedFromDefault: function (value) { + this._super(); + + if (parseFloat(value)) { + this.source.set(this.regionScope, this.indexedOptions[value].label); + } + } + }); +}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js b/app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js index 1aefa2e249958..2477ef2672e68 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js @@ -49,11 +49,12 @@ define([ _.extend(selections, data.params || {}); + console.log(selections); this.request(action.url, selections).done(function (response) { if (!response.error) { this.trigger('massaction', { action: action.type, - data: selections + data: this.selections().selected() }); } }.bind(this)); diff --git a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html index 96158e9921e22..96de88a1145f4 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html +++ b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html @@ -16,8 +16,8 @@
- - + +
From ffa44b557173be5fec38dac0494c42925e2ae677 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Tue, 13 Nov 2018 10:50:09 +0200 Subject: [PATCH 25/77] MAGETWO-96182: Adopt integration and API tests for implemented changes - Change mass delete action in customer address listing structure; --- .../ui_component/customer_address_listing.xml | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml index b138a81e395a3..6d59977cda140 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml @@ -63,20 +63,18 @@
- - - - true - - Are you sure to delete selected address? - Delete items - - - delete - Delete - - - + + + true + + Are you sure to delete selected address? + Delete items + + + delete + + + From 08ac92e88a9420e358818f29091723454d937f9c Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Tue, 13 Nov 2018 19:02:27 +0200 Subject: [PATCH 26/77] MAGETWO-96306: Customer address street data is displayed incorrectly on default billing/shipping blocks --- .../Customer/view/adminhtml/web/template/default-address.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html index 96de88a1145f4..1dd583b990202 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html +++ b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html @@ -17,7 +17,8 @@ - +
From ce1a7cec2053f19753c5b003fae74d538428a7ee Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Tue, 13 Nov 2018 19:31:29 +0200 Subject: [PATCH 27/77] MAGETWO-96209: Customer address grid actions send GET instead of POST request --- .../Magento/Customer/Controller/Adminhtml/Address/Delete.php | 4 ++-- .../Customer/Ui/Component/Listing/Address/Column/Actions.php | 1 + .../Customer/view/adminhtml/web/js/grid/columns/actions.js | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php index 65da3bca49e9b..711cd2473cdc5 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php @@ -8,7 +8,7 @@ namespace Magento\Customer\Controller\Adminhtml\Address; use Magento\Backend\App\Action; -use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\Result\JsonFactory; use Magento\Customer\Api\AddressRepositoryInterface; @@ -17,7 +17,7 @@ /** * Button for deletion of customer address in admin */ -class Delete extends Action implements HttpGetActionInterface +class Delete extends Action implements HttpPostActionInterface { /** * Authorization level of a basic admin session diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php index 490a14169e7b7..7da6d3a6a86c5 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php @@ -113,6 +113,7 @@ public function prepareDataSource(array $dataSource): array ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] ), 'label' => __('Delete'), + 'post' => true, 'isAjax' => true, 'confirm' => [ 'title' => __('Delete address'), diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js b/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js index 43ab06c8014ee..4a62ce2e6eccf 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js @@ -18,8 +18,9 @@ define([ return Actions.extend({ defaults: { ajaxSettings: { - method: 'GET', - dataType: 'json' + method: 'POST', + dataType: 'json', + formKey: $('input[name="form_key"]').val() }, listens: { action: 'onAction' From cefc00668a9ccf72308c17e5a38aa8349e0d9058 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Wed, 14 Nov 2018 11:55:58 +0200 Subject: [PATCH 28/77] MAGETWO-96209: Customer address grid actions send GET instead of POST request - Pass form key parameter for post action in ajax request; - Add comments; --- .../Address/AbstractDefaultAddress.php | 4 ++-- .../Listing/Address/Column/Actions.php | 1 - .../adminhtml/web/js/grid/columns/actions.js | 18 +++++++++++++++--- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php index 9ec38f4929a87..64062139ef0fb 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php @@ -9,7 +9,7 @@ use Magento\Backend\App\Action; use Magento\Customer\Api\AddressRepositoryInterface; -use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\Result\JsonFactory; use Magento\Framework\Phrase; @@ -18,7 +18,7 @@ /** * Abstract class for customer default addresses changing */ -abstract class AbstractDefaultAddress extends Action implements HttpGetActionInterface +abstract class AbstractDefaultAddress extends Action implements HttpPostActionInterface { /** * Authorization level of a basic admin session diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php index 7da6d3a6a86c5..490a14169e7b7 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php @@ -113,7 +113,6 @@ public function prepareDataSource(array $dataSource): array ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] ), 'label' => __('Delete'), - 'post' => true, 'isAjax' => true, 'confirm' => [ 'title' => __('Delete address'), diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js b/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js index 4a62ce2e6eccf..405e3e4b0dc69 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js @@ -19,14 +19,18 @@ define([ defaults: { ajaxSettings: { method: 'POST', - dataType: 'json', - formKey: $('input[name="form_key"]').val() + dataType: 'json' }, listens: { action: 'onAction' } }, + /** + * Reload customer address listing data source after customer address delete action + * + * @param {Object} data + */ onAction: function (data) { if (data.action === 'delete') { this.source().reload({ @@ -66,9 +70,17 @@ define([ } }, + /** + * Send customer address listing ajax request + * + * @param {String} href + */ request: function (href) { var settings = _.extend({}, this.ajaxSettings, { - url: href + url: href, + data: { + 'form_key': window.FORM_KEY + } }); $('body').trigger('processStart'); From c9f4db3d2421f3b3b8b648e6c9aac129cd68330b Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Wed, 14 Nov 2018 16:58:11 +0200 Subject: [PATCH 29/77] MAGETWO-96312: Delete Address button on customer address modal does not close and returns json --- .../Adminhtml/Edit/Address/DeleteButton.php | 32 +++++++++-- .../Controller/Adminhtml/Address/Delete.php | 3 +- .../ui_component/customer_address_form.xml | 2 +- .../adminhtml/web/js/form/components/form.js | 53 +++++++++++++++++++ 4 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php index 8375aa13fdeff..88392139d137e 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php @@ -23,14 +23,36 @@ class DeleteButton extends GenericButton implements ButtonProviderInterface public function getButtonData() { $data = []; + $confirm = __('Are you sure you want to delete this address?'); if ($this->getAddressId()) { $data = [ 'label' => __('Delete'), - 'class' => 'delete', - 'on_click' => 'deleteConfirm(\'' . __( - 'Are you sure you want to delete this address?' - ) . '\', \'' . $this->getDeleteUrl() . '\')', - 'sort_order' => 15, + 'on_click' => '', + 'data_attribute' => [ + 'mage-init' => [ + 'Magento_Ui/js/form/button-adapter' => [ + 'actions' => [ + [ + 'targetName' => 'customer_address_form.customer_address_form', + 'actionName' => 'delete', + 'params' => [ + $this->getDeleteUrl(), + ], + + ], + [ + 'targetName' => 'customer_form.areas.address.address.customer_address_update_modal', + 'actionName' => 'closeModal' + ], + [ + 'targetName' => 'customer_address_listing.customer_address_listing', + 'actionName' => 'reload' + ] + ], + ], + ], + ], + 'sort_order' => 20 ]; } return $data; diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php index 65da3bca49e9b..d14cf86e4084e 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php @@ -9,6 +9,7 @@ use Magento\Backend\App\Action; use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\Result\JsonFactory; use Magento\Customer\Api\AddressRepositoryInterface; @@ -17,7 +18,7 @@ /** * Button for deletion of customer address in admin */ -class Delete extends Action implements HttpGetActionInterface +class Delete extends Action implements HttpPostActionInterface { /** * Authorization level of a basic admin session diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index 4aeb7d67ace68..8e61c458fb812 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -
+ customer_address_form.customer_address_form_data_source diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js new file mode 100644 index 0000000000000..893388a869090 --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js @@ -0,0 +1,53 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'jquery', + 'Magento_Ui/js/modal/alert', + 'Magento_Ui/js/form/form', + 'mage/translate' +], function ($, uiAlert, Form, $t) { + 'use strict'; + + return Form.extend({ + defaults: { + ajaxSettings: { + method: 'POST', + dataType: 'json' + } + }, + + /** + * Perform asynchronous DELETE request to server. + * @param {String} url - ajax url + * @returns {Deferred} + */ + delete: function (url) { + var settings = _.extend({}, this.ajaxSettings, { + url: url, + data: { + 'form_key': window.FORM_KEY + } + }); + + return $.ajax(settings) + .done(function (response) { + if (response.error) { + uiAlert({ + content: response.message + }); + } + }) + .fail(function () { + uiAlert({ + content: $t('Sorry, there has been an error processing your request. Please try again later.') + }); + }) + .always(function () { + $('body').trigger('processStop'); + }); + + } + }); +}); From e9e2551c43ff513d28350ba4dbeb7c89f036b781 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko Date: Wed, 14 Nov 2018 10:39:47 -0600 Subject: [PATCH 30/77] MAGETWO-96312: Delete Address button on customer address modal does not close and returns json --- .../Adminhtml/Edit/Address/DeleteButton.php | 10 +-- .../ui_component/customer_address_form.xml | 3 + .../adminhtml/web/js/form/components/form.js | 69 ++++++++++++------- .../web/js/form/components/insert-form.js | 8 +++ .../view/base/ui_component/customer_form.xml | 1 + 5 files changed, 58 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php index 88392139d137e..7296a5167622a 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php @@ -34,19 +34,11 @@ public function getButtonData() 'actions' => [ [ 'targetName' => 'customer_address_form.customer_address_form', - 'actionName' => 'delete', + 'actionName' => 'deleteAddress', 'params' => [ $this->getDeleteUrl(), ], - ], - [ - 'targetName' => 'customer_form.areas.address.address.customer_address_update_modal', - 'actionName' => 'closeModal' - ], - [ - 'targetName' => 'customer_address_listing.customer_address_listing', - 'actionName' => 'reload' ] ], ], diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index 8e61c458fb812..afe2396a9062f 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -10,6 +10,9 @@ customer_address_form.customer_address_form_data_source + + Are you sure you want to delete this address? + Update Address true templates/form/collapsible diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js index 893388a869090..34cbbfefce368 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js @@ -5,48 +5,69 @@ define([ 'jquery', 'Magento_Ui/js/modal/alert', + 'Magento_Ui/js/modal/confirm', 'Magento_Ui/js/form/form', 'mage/translate' -], function ($, uiAlert, Form, $t) { +], function ($, uiAlert, uiConfirm, Form, $t) { 'use strict'; return Form.extend({ defaults: { + confirmationMessage: '', ajaxSettings: { - method: 'POST', - dataType: 'json' + method: 'POST', + dataType: 'json' } }, + deleteAddress: function (url) { + var that = this; + + uiConfirm({ + content: this.confirmationMessage, + actions: { + /** @inheritdoc */ + confirm: function () { + that._delete(url); + } + } + }); + }, + /** * Perform asynchronous DELETE request to server. * @param {String} url - ajax url * @returns {Deferred} */ - delete: function (url) { + _delete: function (url) { var settings = _.extend({}, this.ajaxSettings, { - url: url, - data: { - 'form_key': window.FORM_KEY - } - }); + url: url, + data: { + 'form_key': window.FORM_KEY + } + }), + that = this; + + $('body').trigger('processStart'); return $.ajax(settings) - .done(function (response) { - if (response.error) { - uiAlert({ - content: response.message - }); - } - }) - .fail(function () { - uiAlert({ - content: $t('Sorry, there has been an error processing your request. Please try again later.') - }); - }) - .always(function () { - $('body').trigger('processStop'); - }); + .done(function (response) { + if (response.error) { + uiAlert({ + content: response.message + }); + } else { + that.trigger('deleteAddressAction', that.source.get('data.entity_id')); + } + }) + .fail(function () { + uiAlert({ + content: $t('Sorry, there has been an error processing your request. Please try again later.') + }); + }) + .always(function () { + $('body').trigger('processStop'); + }); } }); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js index 25303899894c4..59c3e8e4b8b0b 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js @@ -50,6 +50,14 @@ define([ ) { this.source.set('data.default_shipping_address', []); } + }, + + onAddressDelete: function (id) { + this.addressModal().closeModal(); + this.addressListing().reload({ + refresh: true + }); + this.addressListing()._delete([parseFloat(id)]); } }); }); diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index d6d61c892e00d..a7c55da2cca2f 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -467,6 +467,7 @@ ${ $.provider}:data.customer_id + ${ $.ns }.${ $.ns }:deleteAddressAction From d15cca6755976100b4559351594bd5fa8f22d832 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Thu, 15 Nov 2018 15:32:22 +0200 Subject: [PATCH 31/77] MAGETWO-96312: Delete Address button on customer address modal does not close and returns json --- .../Adminhtml/Edit/Address/DeleteButton.php | 3 +- .../Controller/Adminhtml/Address/Delete.php | 1 - .../Edit/Address/DeleteButtonTest.php | 106 ++++++++++++++++++ .../ui_component/customer_address_form.xml | 2 +- .../adminhtml/web/js/form/components/form.js | 11 +- .../web/js/form/components/insert-form.js | 6 + .../js/view/form/components/form.test.js | 71 ++++++++++++ 7 files changed, 194 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Address/DeleteButtonTest.php create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/form.test.js diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php index 7296a5167622a..da589a25df28e 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php @@ -23,7 +23,6 @@ class DeleteButton extends GenericButton implements ButtonProviderInterface public function getButtonData() { $data = []; - $confirm = __('Are you sure you want to delete this address?'); if ($this->getAddressId()) { $data = [ 'label' => __('Delete'), @@ -56,7 +55,7 @@ public function getButtonData() * @return string * @throws \Magento\Framework\Exception\LocalizedException */ - public function getDeleteUrl(): string + private function getDeleteUrl(): string { return $this->getUrl( Actions::CUSTOMER_ADDRESS_PATH_DELETE, diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php index d14cf86e4084e..711cd2473cdc5 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php @@ -8,7 +8,6 @@ namespace Magento\Customer\Controller\Adminhtml\Address; use Magento\Backend\App\Action; -use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\Result\JsonFactory; diff --git a/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Address/DeleteButtonTest.php b/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Address/DeleteButtonTest.php new file mode 100644 index 0000000000000..7b0da3bd422a6 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Address/DeleteButtonTest.php @@ -0,0 +1,106 @@ +addressFactory = $this->getMockBuilder(\Magento\Customer\Model\AddressFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->urlBuilder = $this->getMockForAbstractClass(\Magento\Framework\UrlInterface::class); + $this->addressResourceModel = $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Address::class) + ->disableOriginalConstructor() + ->getMock(); + $this->request = $this->getMockForAbstractClass(\Magento\Framework\App\RequestInterface::class); + $this->addressRepository = $this->getMockBuilder( + \Magento\Customer\Model\ResourceModel\AddressRepository::class + ) + ->disableOriginalConstructor() + ->getMock(); + $objectManagerHelper = new ObjectManagerHelper($this); + + $this->deleteButton = $objectManagerHelper->getObject( + \Magento\Customer\Block\Adminhtml\Edit\Address\DeleteButton::class, + [ + 'addressFactory' => $this->addressFactory, + 'urlBuilder' => $this->urlBuilder, + 'addressResourceModel' => $this->addressResourceModel, + 'request' => $this->request, + 'addressRepository' => $this->addressRepository, + ] + ); + } + + /** + * Unit test for \Magento\Customer\Block\Adminhtml\Edit\Address\DeleteButton::getButtonData() method + */ + public function testGetButtonData() + { + $addressId = 1; + $customerId = 2; + + /** @var \Magento\Customer\Model\Address|\PHPUnit_Framework_MockObject_MockObject $address */ + $address = $this->getMockBuilder(\Magento\Customer\Model\Address::class) + ->disableOriginalConstructor() + ->getMock(); + $address->expects($this->atLeastOnce())->method('getEntityId')->willReturn($addressId); + $address->expects($this->atLeastOnce())->method('getCustomerId')->willReturn($customerId); + $this->addressFactory->expects($this->atLeastOnce())->method('create')->willReturn($address); + $this->request->expects($this->atLeastOnce())->method('getParam')->with('entity_id') + ->willReturn($addressId); + $this->addressResourceModel->expects($this->atLeastOnce())->method('load')->with($address, $addressId); + $this->addressRepository->expects($this->atLeastOnce())->method('getById')->with($addressId) + ->willReturn($address); + $this->urlBuilder->expects($this->atLeastOnce())->method('getUrl') + ->with( + \Magento\Customer\Ui\Component\Listing\Address\Column\Actions::CUSTOMER_ADDRESS_PATH_DELETE, + ['parent_id' => $customerId, 'id' => $addressId] + )->willReturn('url'); + + $buttonData = $this->deleteButton->getButtonData(); + $this->assertEquals('Delete', (string)$buttonData['label']); + } +} diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index afe2396a9062f..fdef0e959443b 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -11,7 +11,7 @@ customer_address_form.customer_address_form_data_source - Are you sure you want to delete this address? + Are you sure you want to delete this address? Update Address true diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js index 34cbbfefce368..e1c5f01c731a0 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js @@ -13,18 +13,25 @@ define([ return Form.extend({ defaults: { - confirmationMessage: '', + deleteConfirmationMessage: '', ajaxSettings: { method: 'POST', dataType: 'json' } }, + /** + * Delete customer address by provided url. + * Will call confirmation message to be sure that user is really wants to delete this address + * + * @param {String} url - ajax url + */ deleteAddress: function (url) { + console.log(url); var that = this; uiConfirm({ - content: this.confirmationMessage, + content: this.deleteConfirmationMessage, actions: { /** @inheritdoc */ confirm: function () { diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js index 59c3e8e4b8b0b..45d0ff112f096 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js @@ -52,6 +52,12 @@ define([ } }, + /** + * Event method that closes "Edit customer address" modal and refreshes grid after customer address + * was removed through "Delete" button on the "Edit customer address" modal + * + * @param {string} id - customer address ID to delete + */ onAddressDelete: function (id) { this.addressModal().closeModal(); this.addressListing().reload({ diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/form.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/form.test.js new file mode 100644 index 0000000000000..99ce913e87390 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/form.test.js @@ -0,0 +1,71 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'underscore', + 'uiRegistry', + 'Magento_Customer/js/form/components/form', + 'jquery' +], function (_, registry, Constr, $) { + 'use strict'; + + describe('Magento_Customer/js/form/components/form', function () { + + var obj, + originaljQueryAjax; + + beforeEach (function () { + originaljQueryAjax = $.ajax; + obj = new Constr({ + provider: 'provName', + name: '', + index: '' + }); + }); + + afterEach(function () { + $.ajax = originaljQueryAjax; + }); + + registry.set('provName', { + /** Stub */ + on: function () {}, + + /** Stub */ + get: function () {}, + + /** Stub */ + set: function () {} + }); + + describe('"deleteAddress" method', function () { + it('Check for defined ', function () { + expect(obj.hasOwnProperty('deleteAddress')).toBeDefined(); + }); + it('Check method type', function () { + var type = typeof obj.deleteAddress; + + expect(type).toEqual('function'); + }); + it('Check returned value if method called without arguments', function () { + expect(obj.deleteAddress()).toBeUndefined(); + }); + it('Check returned value type if method called without arguments', function () { + var type = typeof obj.deleteAddress(); + + expect(type).toEqual('undefined'); + }); + it('Should call not call ajax if arguments are empty', function () { + $.ajax = jasmine.createSpy(); + + spyOn(obj, 'deleteAddress'); + + expect(obj.deleteAddress()).toBeUndefined(); + + expect($.ajax).not.toHaveBeenCalled(); + }); + }); + }); +}); From ef2cf53a60cc7a60895685230c8ad52181de0c3e Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Fri, 16 Nov 2018 16:26:41 +0200 Subject: [PATCH 32/77] MAGETWO-96330: Default Shipping Address Edit button is displayed incorrectly --- .../ui_component/customer_address_form.xml | 4 +- .../web/template/default-address-wrapper.html | 7 + .../view/base/ui_component/customer_form.xml | 185 +++++++++--------- .../web/css/source/_module.less | 27 +-- 4 files changed, 111 insertions(+), 112 deletions(-) create mode 100644 app/code/Magento/Customer/view/adminhtml/web/template/default-address-wrapper.html diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index fdef0e959443b..e07c5d97a30a6 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -80,7 +80,7 @@ text
- + 0 @@ -103,7 +103,7 @@ - + 0 diff --git a/app/code/Magento/Customer/view/adminhtml/web/template/default-address-wrapper.html b/app/code/Magento/Customer/view/adminhtml/web/template/default-address-wrapper.html new file mode 100644 index 0000000000000..2af366b03342c --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/template/default-address-wrapper.html @@ -0,0 +1,7 @@ + +
diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index a7c55da2cca2f..aabbbc09de1fb 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -312,105 +312,106 @@ true - - - - billing-address - Default Billing Address - customer-default-billing-address-content - The customer does not have default billing address - - true - - - - - - ${ $.provider}:data.default_billing_address - - - - - - - - - - shipping-address - Default Shipping Address - customer-default-shipping-address-content - The customer does not have default shipping address - - true + + + + ${ $.provider}:data.default_billing_address + + + + - - + + + button + Edit + true + + ${ $.provider}:data.default_shipping_address.entity_id + ${ $.provider}:data.default_shipping_address + + + + - - + + @@ -411,7 +411,7 @@ - + + + + button + Edit + true + + ${ $.provider}:data.default_billing_address.entity_id + ${ $.provider}:data.default_billing_address + + + + @@ -380,37 +380,37 @@ ${ $.provider}:data.default_shipping_address - - + + + button + Edit + true + + ${ $.provider}:data.default_shipping_address.entity_id + ${ $.provider}:data.default_shipping_address + + + + - + + + + button + Edit + true + + ${ $.provider}:data.default_billing_address.entity_id + ${ $.provider}:data.default_billing_address + + + @@ -378,39 +378,39 @@ ${ $.provider}:data.default_shipping_address - + efas - - + + + + button + Edit + true + + ${ $.provider}:data.default_shipping_address.entity_id + ${ $.provider}:data.default_shipping_address + + +
From aaccfc4a6809c0be2f930f5aaa0dc64263b2c3c7 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Fri, 30 Nov 2018 11:25:11 +0200 Subject: [PATCH 61/77] MAGETWO-96181: Adopt unit tests to made changes - Make setup method shorter; --- .../DataProviderWithDefaultAddressesTest.php | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php index 7888b63e0cdbd..2fc3cdb927723 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php @@ -9,7 +9,8 @@ use Magento\Customer\Model\AttributeMetadataResolver; use Magento\Customer\Model\Customer\DataProviderWithDefaultAddresses; use Magento\Customer\Model\FileUploaderDataResolver; -use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; +use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory as CustomerCollectionFactory; +use Magento\Customer\Model\ResourceModel\Customer\Collection as CustomerCollection; use Magento\Eav\Model\Config; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; use Magento\Eav\Model\Entity\Type; @@ -31,7 +32,7 @@ class DataProviderWithDefaultAddressesTest extends \PHPUnit\Framework\TestCase private $eavConfigMock; /** - * @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerCollectionFactory|\PHPUnit_Framework_MockObject_MockObject */ private $customerCollectionFactoryMock; @@ -51,7 +52,7 @@ class DataProviderWithDefaultAddressesTest extends \PHPUnit\Framework\TestCase private $customerMock; /** - * @var \Magento\Customer\Model\ResourceModel\Customer\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerCollection|\PHPUnit_Framework_MockObject_MockObject */ private $customerCollectionMock; @@ -70,37 +71,25 @@ class DataProviderWithDefaultAddressesTest extends \PHPUnit\Framework\TestCase */ protected function setUp(): void { - $this->eavConfigMock = $this->getMockBuilder(\Magento\Eav\Model\Config::class) - ->disableOriginalConstructor() - ->getMock(); - $this->customerCollectionFactoryMock = $this->createPartialMock( - \Magento\Customer\Model\ResourceModel\Customer\CollectionFactory::class, - ['create'] - ); - $this->sessionMock = $this - ->getMockBuilder(\Magento\Framework\Session\SessionManagerInterface::class) + $this->eavConfigMock = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock(); + $this->customerCollectionFactoryMock = $this->createPartialMock(CustomerCollectionFactory::class, ['create']); + $this->sessionMock = $this->getMockBuilder(\Magento\Framework\Session\SessionManagerInterface::class) ->setMethods(['getCustomerFormData', 'unsCustomerFormData']) ->getMockForAbstractClass(); - $this->countryFactoryMock = $this->getMockBuilder(\Magento\Directory\Model\CountryFactory::class) ->disableOriginalConstructor() ->setMethods(['create', 'loadByCode', 'getName']) ->getMock(); - $this->customerMock = $this->getMockBuilder(\Magento\Customer\Model\Customer::class) ->disableOriginalConstructor() ->getMock(); - $this->customerCollectionMock = $this->getMockBuilder( - \Magento\Customer\Model\ResourceModel\Customer\Collection::class - ) + $this->customerCollectionMock = $this->getMockBuilder(CustomerCollection::class) ->disableOriginalConstructor() ->getMock(); $this->customerCollectionMock->expects($this->once())->method('addAttributeToSelect')->with('*'); - $this->customerCollectionFactoryMock - ->expects($this->once()) + $this->customerCollectionFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->customerCollectionMock); - $this->eavConfigMock->expects($this->atLeastOnce()) ->method('getEntityType') ->with('customer') @@ -162,7 +151,6 @@ protected function setUp(): void ], ] ); - $helper = new ObjectManager($this); $this->dataProvider = $helper->getObject( DataProviderWithDefaultAddresses::class, From fd90488aa8cd27c669153a1db1f7b3bf0112cf4c Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Fri, 30 Nov 2018 11:35:46 +0200 Subject: [PATCH 62/77] MAGETWO-96182: Adopt integration and API tests for implemented changes --- .../Customer/Test/Block/Adminhtml/Edit/CustomerForm.php | 5 +++++ .../Customer/Test/Block/Adminhtml/Edit/Tab/Addresses.php | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/CustomerForm.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/CustomerForm.php index 0ea94049a7746..ff1d854faab26 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/CustomerForm.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/CustomerForm.php @@ -65,6 +65,7 @@ class CustomerForm extends FormTabs * @param FixtureInterface $customer * @param FixtureInterface|FixtureInterface[]|null $address * @return $this + * @throws \Exception */ public function fillCustomer(FixtureInterface $customer, $address = null) { @@ -74,6 +75,10 @@ public function fillCustomer(FixtureInterface $customer, $address = null) if ($isHasData) { parent::fill($customer); } + if (null !== $address) { + $this->openTab('addresses'); + $this->fillCustomerAddress($address); + } return $this; } diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/Tab/Addresses.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/Tab/Addresses.php index 54085b060d8a3..99a79a8a4a85c 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/Tab/Addresses.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/Tab/Addresses.php @@ -264,10 +264,9 @@ protected function openCustomerAddress($addressNumber) /** * Check is visible customer address. * - * @param int $addressNumber * @return bool */ - protected function isVisibleCustomerAddress($addressNumber) + protected function isVisibleCustomerAddress() { $customerAddressesGrid = $this->getCustomerAddressesGrid(); $customerAddressesGrid->isFirstRowVisible(); From fd5753587ffb96576504f447def32fda34d221d8 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Fri, 30 Nov 2018 11:45:06 +0200 Subject: [PATCH 63/77] MAGETWO-94397: Adopt existing MTF tests to new addresses grid --- .../Adminhtml/Edit/Tab/Addresses/AddressForm.php | 13 ++++++------- .../Adminhtml/Edit/Tab/Addresses/AddressesGrid.php | 14 +++++++++----- .../AssertCustomerAddressBackendRequiredFields.php | 6 ++++-- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/Tab/Addresses/AddressForm.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/Tab/Addresses/AddressForm.php index 794072040c807..029836d21433c 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/Tab/Addresses/AddressForm.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/Tab/Addresses/AddressForm.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Customer\Test\Block\Adminhtml\Edit\Tab\Addresses; @@ -74,15 +75,13 @@ public function fillAddressData(\Magento\Mtf\Fixture\FixtureInterface $address) * * @param array $fields * @param \Magento\Mtf\Client\Element\SimpleElement|null $contextElement - * @return $this + * @return void * @throws \Exception */ - public function setFieldsData(array $fields, \Magento\Mtf\Client\Element\SimpleElement $contextElement = null) + public function setFieldsData(array $fields, \Magento\Mtf\Client\Element\SimpleElement $contextElement = null): void { $data = $this->dataMapping($fields); $this->_fill($data, $contextElement); - - return $this; } /** @@ -90,7 +89,7 @@ public function setFieldsData(array $fields, \Magento\Mtf\Client\Element\SimpleE * * @return void */ - public function saveAddress() + public function saveAddress(): void { $this->_rootElement->find($this->saveAddressButton)->click(); $this->waitForElementNotVisible($this->loader); @@ -101,7 +100,7 @@ public function saveAddress() * * @return void */ - public function clickCancelButton() + public function clickCancelButton(): void { $this->_rootElement->find($this->cancelButton)->click(); } @@ -111,7 +110,7 @@ public function clickCancelButton() * * @return array */ - public function getJsErrors() + public function getJsErrors(): array { $data = []; $elements = $this->_rootElement->getElements($this->mageErrorField, Locator::SELECTOR_XPATH); diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/Tab/Addresses/AddressesGrid.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/Tab/Addresses/AddressesGrid.php index 232de6216518d..ef1fb09c8f839 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/Tab/Addresses/AddressesGrid.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/Tab/Addresses/AddressesGrid.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Customer\Test\Block\Adminhtml\Edit\Tab\Addresses; @@ -102,8 +103,9 @@ class AddressesGrid extends DataGrid * Search customer address by filter. * * @param array $filter + * @return void */ - public function search(array $filter) + public function search(array $filter): void { parent::search(array_intersect_key($filter, $this->filters)); } @@ -112,9 +114,10 @@ public function search(array $filter) * Delete customer address by filter * * @param array $filter + * @return void * @throws \Exception */ - public function deleteCustomerAddress(array $filter) + public function deleteCustomerAddress(array $filter): void { $this->search($filter); $rowItem = $this->getRow([$filter['firstname']]); @@ -127,8 +130,9 @@ public function deleteCustomerAddress(array $filter) /** * @param \Magento\Mtf\Client\Element\SimpleElement $rowItem + * @return void */ - public function deleteRowItemAddress(\Magento\Mtf\Client\Element\SimpleElement $rowItem) + public function deleteRowItemAddress(\Magento\Mtf\Client\Element\SimpleElement $rowItem): void { $rowItem->find($this->selectAction)->click(); $rowItem->find($this->deleteAddress)->click(); @@ -147,7 +151,7 @@ public function deleteRowItemAddress(\Magento\Mtf\Client\Element\SimpleElement $ * * @return void */ - public function openFirstRow() + public function openFirstRow(): void { $firstRow = $this->getFirstRow(); if ($firstRow->isVisible()) { @@ -163,7 +167,7 @@ public function openFirstRow() * * @return \Magento\Mtf\Client\Element\SimpleElement */ - public function getFirstRow() + public function getFirstRow(): \Magento\Mtf\Client\Element\SimpleElement { return $this->_rootElement->find($this->firstRowSelector, \Magento\Mtf\Client\Locator::SELECTOR_XPATH); } diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerAddressBackendRequiredFields.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerAddressBackendRequiredFields.php index 4358fb2ab12ce..46bb8ce72abb1 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerAddressBackendRequiredFields.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerAddressBackendRequiredFields.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Customer\Test\Constraint; @@ -23,9 +24,10 @@ class AssertCustomerAddressBackendRequiredFields extends AbstractConstraint * Assert required fields on customer address form. * @param CustomerIndexNew $customerNewPage * @param array $expectedRequiredFields + * @return void * @throws \Exception */ - public function processAssert(CustomerIndexNew $customerNewPage, array $expectedRequiredFields) + public function processAssert(CustomerIndexNew $customerNewPage, array $expectedRequiredFields): void { $actualRequiredFields = $customerNewPage->getCustomerForm()->getTab('addresses') ->getCustomerAddressModalForm()->getJsErrors(); @@ -47,7 +49,7 @@ public function processAssert(CustomerIndexNew $customerNewPage, array $expected * * @return string */ - public function toString() + public function toString(): string { return 'All required fields on customer form are highlighted.'; } From decb31e02501fe939dd4ddaeff8f99e5a14009cd Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Fri, 30 Nov 2018 11:50:53 +0200 Subject: [PATCH 64/77] MAGETWO-94397: Adopt existing MTF tests to new addresses grid --- .../Magento/Customer/Test/Block/Adminhtml/Edit/CustomerForm.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/CustomerForm.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/CustomerForm.php index ff1d854faab26..f456635882ed9 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/CustomerForm.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Adminhtml/Edit/CustomerForm.php @@ -84,6 +84,8 @@ public function fillCustomer(FixtureInterface $customer, $address = null) } /** + * Fill customer address by provided in parameter data + * * @param FixtureInterface|FixtureInterface[] $address * @return $this * @throws \Exception From 7d6b0d78e2b1ad4772f5ae8670babb90526d91d6 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Mon, 3 Dec 2018 13:15:53 +0200 Subject: [PATCH 65/77] MAGETWO-96617: Customer address listing fulltext search is not working - Add isAjax param to mass action; --- .../ui_component/customer_address_listing.xml | 5 +++ .../view/adminhtml/web/js/grid/massactions.js | 34 +++++++++++-------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml index c89abc9dd9d04..fb42a2c5a0787 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml @@ -55,6 +55,11 @@ + + + true + + delete diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js b/app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js index 6d6e21972587c..384f48554a917 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js @@ -46,25 +46,29 @@ define([ defaultCallback: function (action, data) { var itemsType, selections; - itemsType = data.excludeMode ? 'excluded' : 'selected'; - selections = {}; + if (action.isAjax) { + itemsType = data.excludeMode ? 'excluded' : 'selected'; + selections = {}; - selections[itemsType] = data[itemsType]; + selections[itemsType] = data[itemsType]; - if (!selections[itemsType].length) { - selections[itemsType] = false; - } + if (!selections[itemsType].length) { + selections[itemsType] = false; + } - _.extend(selections, data.params || {}); + _.extend(selections, data.params || {}); - this.request(action.url, selections).done(function (response) { - if (!response.error) { - this.trigger('massaction', { - action: action.type, - data: this.selections().selected() - }); - } - }.bind(this)); + this.request(action.url, selections).done(function (response) { + if (!response.error) { + this.trigger('massaction', { + action: action.type, + data: selections + }); + } + }.bind(this)); + } else { + this._super(); + } }, /** From 5c645530d4b042a22ce3d4f3c6b27bbf05b70bb7 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Tue, 4 Dec 2018 19:19:21 +0200 Subject: [PATCH 66/77] MAGETWO-96906: [MAGETWO-94346] PR stabilization - Remove AbstractDefaultAddress controller action; --- .../Address/AbstractDefaultAddress.php | 120 ------------------ .../Address/DefaultBillingAddress.php | 106 ++++++++++++++-- .../Address/DefaultShippingAddress.php | 106 ++++++++++++++-- 3 files changed, 196 insertions(+), 136 deletions(-) delete mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php deleted file mode 100644 index 64062139ef0fb..0000000000000 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php +++ /dev/null @@ -1,120 +0,0 @@ -addressRepository = $addressRepository; - $this->logger = $logger; - $this->resultJsonFactory = $resultJsonFactory; - } - - /** - * Execute action to set customer default billing or shipping address - * - * @return Json - */ - public function execute(): Json - { - $customerId = $this->getRequest()->getParam('parent_id', false); - $addressId = $this->getRequest()->getParam('id', false); - $error = false; - $message = ''; - - if ($addressId) { - try { - $address = $this->addressRepository->getById($addressId)->setCustomerId($customerId); - $this->setAddressAsDefault($address); - $this->addressRepository->save($address); - $message = $this->getSuccessMessage(); - } catch (\Exception $e) { - $error = true; - $message = $this->getExceptionMessage(); - $this->logger->critical($e); - } - } - - $resultJson = $this->resultJsonFactory->create(); - $resultJson->setData( - [ - 'message' => $message, - 'error' => $error, - ] - ); - - return $resultJson; - } - - /** - * Set passed address as customer's default address - * - * @param \Magento\Customer\Api\Data\AddressInterface $address - * @return $this - */ - abstract protected function setAddressAsDefault($address); - - /** - * Get success message about default address changed - * - * @return \Magento\Framework\Phrase - */ - abstract protected function getSuccessMessage(): Phrase; - - /** - * Get error message about unsuccessful attempt to change default address - * - * @return \Magento\Framework\Phrase - */ - abstract protected function getExceptionMessage(): Phrase; -} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php index 41a0a6390edf7..14d711539a871 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php @@ -8,32 +8,122 @@ namespace Magento\Customer\Controller\Adminhtml\Address; use Magento\Framework\Phrase; +use Magento\Backend\App\Action; +use Magento\Customer\Model\Data\Address; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Controller\Result\JsonFactory; +use Psr\Log\LoggerInterface; /** - * Class to process default billing address setting + * Class to process set default billing address action */ -class DefaultBillingAddress extends AbstractDefaultAddress +class DefaultBillingAddress extends Action implements HttpPostActionInterface { /** - * @inheritdoc + * Authorization level of a basic admin session + * + * @see _isAllowed() */ - protected function setAddressAsDefault($address) + public const ADMIN_RESOURCE = 'Magento_Customer::manage'; + + /** + * @var AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var JsonFactory + */ + private $resultJsonFactory; + + /** + * @param Action\Context $context + * @param AddressRepositoryInterface $addressRepository + * @param LoggerInterface $logger + * @param JsonFactory $resultJsonFactory + */ + public function __construct( + Action\Context $context, + AddressRepositoryInterface $addressRepository, + LoggerInterface $logger, + JsonFactory $resultJsonFactory + ) { + parent::__construct($context); + $this->addressRepository = $addressRepository; + $this->logger = $logger; + $this->resultJsonFactory = $resultJsonFactory; + } + + /** + * Execute action to set customer default billing address + * + * @return Json + */ + public function execute(): Json + { + $customerId = $this->getRequest()->getParam('parent_id', false); + $addressId = $this->getRequest()->getParam('id', false); + $error = false; + $message = ''; + + if ($addressId) { + try { + $address = $this->addressRepository->getById($addressId)->setCustomerId($customerId); + $this->setAddressAsDefault($address); + $this->addressRepository->save($address); + $message = $this->getSuccessMessage(); + } catch (\Exception $e) { + $error = true; + $message = $this->getExceptionMessage(); + $this->logger->critical($e); + } + } + + $resultJson = $this->resultJsonFactory->create(); + $resultJson->setData( + [ + 'message' => $message, + 'error' => $error, + ] + ); + + return $resultJson; + } + + /** + * Set address as default billing address + * + * @param Address $address + * @return void + */ + private function setAddressAsDefault(Address $address): void { $address->setIsDefaultBilling(true); } /** - * @inheritdoc + * Get success message + * + * @return Phrase */ - protected function getSuccessMessage(): Phrase + private function getSuccessMessage(): Phrase { return __('Default billing address has been changed.'); } /** - * @inheritdoc + * Get exception message + * + * @return Phrase */ - protected function getExceptionMessage(): Phrase + private function getExceptionMessage(): Phrase { return __('We can\'t change default billing address right now.'); } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php index 057b36be79124..e13b6ddf6472f 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php @@ -8,32 +8,122 @@ namespace Magento\Customer\Controller\Adminhtml\Address; use Magento\Framework\Phrase; +use Magento\Backend\App\Action; +use Magento\Customer\Model\Data\Address; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Controller\Result\JsonFactory; +use Psr\Log\LoggerInterface; /** - * Class to process default shipping address setting + * Class to process set default shipping address action */ -class DefaultShippingAddress extends AbstractDefaultAddress +class DefaultShippingAddress extends Action implements HttpPostActionInterface { /** - * @inheritdoc + * Authorization level of a basic admin session + * + * @see _isAllowed() */ - protected function setAddressAsDefault($address) + public const ADMIN_RESOURCE = 'Magento_Customer::manage'; + + /** + * @var AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var JsonFactory + */ + private $resultJsonFactory; + + /** + * @param Action\Context $context + * @param AddressRepositoryInterface $addressRepository + * @param LoggerInterface $logger + * @param JsonFactory $resultJsonFactory + */ + public function __construct( + Action\Context $context, + AddressRepositoryInterface $addressRepository, + LoggerInterface $logger, + JsonFactory $resultJsonFactory + ) { + parent::__construct($context); + $this->addressRepository = $addressRepository; + $this->logger = $logger; + $this->resultJsonFactory = $resultJsonFactory; + } + + /** + * Execute action to set customer default shipping address + * + * @return Json + */ + public function execute(): Json + { + $customerId = $this->getRequest()->getParam('parent_id', false); + $addressId = $this->getRequest()->getParam('id', false); + $error = false; + $message = ''; + + if ($addressId) { + try { + $address = $this->addressRepository->getById($addressId)->setCustomerId($customerId); + $this->setAddressAsDefault($address); + $this->addressRepository->save($address); + $message = $this->getSuccessMessage(); + } catch (\Exception $e) { + $error = true; + $message = $this->getExceptionMessage(); + $this->logger->critical($e); + } + } + + $resultJson = $this->resultJsonFactory->create(); + $resultJson->setData( + [ + 'message' => $message, + 'error' => $error, + ] + ); + + return $resultJson; + } + + /** + * Set address as default shipping address + * + * @param Address $address + * @return void + */ + private function setAddressAsDefault(Address $address): void { $address->setIsDefaultShipping(true); } /** - * @inheritdoc + * Get success message + * + * @return Phrase */ - protected function getSuccessMessage(): Phrase + private function getSuccessMessage(): Phrase { return __('Default shipping address has been changed.'); } /** - * @inheritdoc + * Get exception message + * + * @return Phrase */ - protected function getExceptionMessage(): Phrase + private function getExceptionMessage(): Phrase { return __('We can\'t change default shipping address right now.'); } From f01009392cbed5dd252e30dbc9ce4f935904b441 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Tue, 4 Dec 2018 11:41:30 -0600 Subject: [PATCH 67/77] MAGETWO-96906: [MAGETWO-94346] PR stabilization --- .../Controller/Adminhtml/Index/Save.php | 2 +- .../Model/FileUploaderDataResolver.php | 3 ++- .../ResourceModel/Address/Grid/Collection.php | 15 +------------ .../Unit/Model/Customer/DataProviderTest.php | 1 - .../Ui/Component/Form/AddressFieldsetTest.php | 4 ++-- .../Ui/Component/Form/AddressFieldset.php | 5 +++-- .../Magento/Ui/Component/Form/Fieldset.php | 10 --------- app/code/Magento/Ui/Component/Layout/Tabs.php | 4 ++-- .../Element/ComponentVisibilityInterface.php | 22 +++++++++++++++++++ 9 files changed, 33 insertions(+), 33 deletions(-) create mode 100644 lib/internal/Magento/Framework/View/Element/ComponentVisibilityInterface.php diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index d80c712914ef0..25e0c7e99414a 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -153,7 +153,7 @@ protected function saveDefaultFlags(array $addressIdList, array & $extractedCust /** * Reformat customer addresses data to be compatible with customer service interface * - * @deprecated must be removed because addresses are save separately for now + * @deprecated addresses are save separately for now * @param array $extractedCustomerData * @return array */ diff --git a/app/code/Magento/Customer/Model/FileUploaderDataResolver.php b/app/code/Magento/Customer/Model/FileUploaderDataResolver.php index 9dd5084739c74..535bfe97bc457 100644 --- a/app/code/Magento/Customer/Model/FileUploaderDataResolver.php +++ b/app/code/Magento/Customer/Model/FileUploaderDataResolver.php @@ -18,6 +18,7 @@ class FileUploaderDataResolver { /** * Maximum file size allowed for file_uploader UI component + * This constant was copied from deprecated data provider \Magento\Customer\Model\Customer\DataProvider */ private const MAX_FILE_SIZE = 2097152; @@ -84,7 +85,7 @@ private function getFileUploaderData( ): array { $attributeCode = $attribute->getAttributeCode(); - $file = $customerData[$attributeCode] ?? ''; + $file = $customerData[$attributeCode] ?? null; /** @var FileProcessor $fileProcessor */ $fileProcessor = $this->fileProcessorFactory->create([ diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php index fc9cdcee2a37b..4e0347059086f 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php @@ -9,30 +9,18 @@ use Magento\Framework\Api\Search\SearchResultInterface; use Magento\Framework\Api\Search\AggregationInterface; use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; -use Magento\Framework\View\Element\UiComponent\Context; /** * Class getting collection of addresses assigned to customer */ class Collection extends AbstractCollection implements SearchResultInterface { - /** - * @var string - */ - protected $_idFieldName = 'entity_id'; - - /** - * @var Context - */ - private $context; - /** * @var AggregationInterface */ private $aggregations; /** - * @param Context $context * @param \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy @@ -48,7 +36,6 @@ class Collection extends AbstractCollection implements SearchResultInterface * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - Context $context, \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory, \Psr\Log\LoggerInterface $logger, \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, @@ -61,11 +48,11 @@ public function __construct( $connection = null, \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null ) { - $this->context = $context; $this->_eventPrefix = $eventPrefix; $this->_eventObject = $eventObject; $this->_init($model, $resourceModel); $this->setMainTable($mainTable); + $this->_idFieldName = 'entity_id'; parent::__construct( $entityFactory, $logger, diff --git a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php index bf6d3f0f9bbc5..50c21379054bf 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php @@ -21,7 +21,6 @@ * * Test for class \Magento\Customer\Model\Customer\DataProvider * - * @deprecated tested class is not used. * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DataProviderTest extends \PHPUnit\Framework\TestCase diff --git a/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php b/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php index ba99203c5c6a4..65a0443aed86f 100644 --- a/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php +++ b/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php @@ -51,7 +51,7 @@ public function testCanShow() { $this->context->expects($this->atLeastOnce())->method('getRequestParam')->with('id') ->willReturn(1); - $this->assertEquals(true, $this->fieldset->canShow()); + $this->assertTrue($this->fieldset->isComponentVisible()); } /** @@ -64,6 +64,6 @@ public function testCanShowWithoutId() { $this->context->expects($this->atLeastOnce())->method('getRequestParam')->with('id') ->willReturn(null); - $this->assertEquals(false, $this->fieldset->canShow()); + $this->assertEquals(false, $this->fieldset->isComponentVisible()); } } diff --git a/app/code/Magento/Customer/Ui/Component/Form/AddressFieldset.php b/app/code/Magento/Customer/Ui/Component/Form/AddressFieldset.php index 4d7464b321e85..3e3a6e74166a1 100644 --- a/app/code/Magento/Customer/Ui/Component/Form/AddressFieldset.php +++ b/app/code/Magento/Customer/Ui/Component/Form/AddressFieldset.php @@ -8,11 +8,12 @@ namespace Magento\Customer\Ui\Component\Form; use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\ComponentVisibilityInterface; /** * Customer addresses fieldset class */ -class AddressFieldset extends \Magento\Ui\Component\Form\Fieldset +class AddressFieldset extends \Magento\Ui\Component\Form\Fieldset implements ComponentVisibilityInterface { /** * @param ContextInterface $context @@ -37,7 +38,7 @@ public function __construct( * * @return boolean */ - public function canShow(): bool + public function isComponentVisible(): bool { $customerId = $this->context->getRequestParam('id'); return (bool)$customerId; diff --git a/app/code/Magento/Ui/Component/Form/Fieldset.php b/app/code/Magento/Ui/Component/Form/Fieldset.php index ef115fe459eba..2525d0a4923c1 100644 --- a/app/code/Magento/Ui/Component/Form/Fieldset.php +++ b/app/code/Magento/Ui/Component/Form/Fieldset.php @@ -31,14 +31,4 @@ public function getComponentName() { return static::NAME; } - - /** - * Check that fieldset can be shown. - * - * @return bool - */ - public function canShow(): bool - { - return true; - } } diff --git a/app/code/Magento/Ui/Component/Layout/Tabs.php b/app/code/Magento/Ui/Component/Layout/Tabs.php index f5a9c86977c3c..1c024f8a35eee 100644 --- a/app/code/Magento/Ui/Component/Layout/Tabs.php +++ b/app/code/Magento/Ui/Component/Layout/Tabs.php @@ -9,7 +9,7 @@ use Magento\Framework\View\Element\UiComponent\DataSourceInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Framework\View\Element\UiComponentInterface; -use Magento\Ui\Component\Form\Fieldset; +use Magento\Framework\View\Element\ComponentVisibilityInterface; use Magento\Ui\Component\Layout\Tabs\TabInterface; /** @@ -88,7 +88,7 @@ protected function addChildren(array &$topNode, UiComponentInterface $component, $this->addWrappedBlock($childComponent, $childrenAreas); continue; } - if ($childComponent instanceof Fieldset && false === $childComponent->canShow()) { + if ($childComponent instanceof ComponentVisibilityInterface && false === $childComponent->canShow()) { continue; } diff --git a/lib/internal/Magento/Framework/View/Element/ComponentVisibilityInterface.php b/lib/internal/Magento/Framework/View/Element/ComponentVisibilityInterface.php new file mode 100644 index 0000000000000..8dd64b4674bf6 --- /dev/null +++ b/lib/internal/Magento/Framework/View/Element/ComponentVisibilityInterface.php @@ -0,0 +1,22 @@ + Date: Tue, 4 Dec 2018 20:09:10 +0200 Subject: [PATCH 68/77] MAGETWO-96906: [MAGETWO-94346] PR stabilization - Modify execute method of set default billing and shipping address; --- .../Address/DefaultBillingAddress.php | 35 +++++-------------- .../Address/DefaultShippingAddress.php | 35 +++++-------------- 2 files changed, 16 insertions(+), 54 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php index 14d711539a871..51d8e6dac1dea 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php @@ -7,9 +7,8 @@ */ namespace Magento\Customer\Controller\Adminhtml\Address; -use Magento\Framework\Phrase; use Magento\Backend\App\Action; -use Magento\Customer\Model\Data\Address; +use Magento\Customer\Api\Data\AddressInterface; use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\Result\Json; @@ -71,19 +70,21 @@ public function execute(): Json $customerId = $this->getRequest()->getParam('parent_id', false); $addressId = $this->getRequest()->getParam('id', false); $error = false; - $message = ''; if ($addressId) { try { $address = $this->addressRepository->getById($addressId)->setCustomerId($customerId); $this->setAddressAsDefault($address); $this->addressRepository->save($address); - $message = $this->getSuccessMessage(); + $message = __('Default billing address has been changed.'); } catch (\Exception $e) { $error = true; - $message = $this->getExceptionMessage(); + $message = __('We can\'t change default billing address right now.'); $this->logger->critical($e); } + } else { + $error = true; + $message = __('There is no address id in set default billing request.'); } $resultJson = $this->resultJsonFactory->create(); @@ -100,31 +101,11 @@ public function execute(): Json /** * Set address as default billing address * - * @param Address $address + * @param AddressInterface $address * @return void */ - private function setAddressAsDefault(Address $address): void + private function setAddressAsDefault(AddressInterface $address): void { $address->setIsDefaultBilling(true); } - - /** - * Get success message - * - * @return Phrase - */ - private function getSuccessMessage(): Phrase - { - return __('Default billing address has been changed.'); - } - - /** - * Get exception message - * - * @return Phrase - */ - private function getExceptionMessage(): Phrase - { - return __('We can\'t change default billing address right now.'); - } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php index e13b6ddf6472f..a76d9759b9ac3 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php @@ -7,9 +7,8 @@ */ namespace Magento\Customer\Controller\Adminhtml\Address; -use Magento\Framework\Phrase; use Magento\Backend\App\Action; -use Magento\Customer\Model\Data\Address; +use Magento\Customer\Api\Data\AddressInterface; use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\Result\Json; @@ -71,19 +70,21 @@ public function execute(): Json $customerId = $this->getRequest()->getParam('parent_id', false); $addressId = $this->getRequest()->getParam('id', false); $error = false; - $message = ''; if ($addressId) { try { $address = $this->addressRepository->getById($addressId)->setCustomerId($customerId); $this->setAddressAsDefault($address); $this->addressRepository->save($address); - $message = $this->getSuccessMessage(); + $message = __('Default shipping address has been changed.'); } catch (\Exception $e) { $error = true; - $message = $this->getExceptionMessage(); + $message = __('We can\'t change default shipping address right now.'); $this->logger->critical($e); } + } else { + $error = true; + $message = __('There is no address id in set default shipping request.'); } $resultJson = $this->resultJsonFactory->create(); @@ -100,31 +101,11 @@ public function execute(): Json /** * Set address as default shipping address * - * @param Address $address + * @param AddressInterface $address * @return void */ - private function setAddressAsDefault(Address $address): void + private function setAddressAsDefault(AddressInterface $address): void { $address->setIsDefaultShipping(true); } - - /** - * Get success message - * - * @return Phrase - */ - private function getSuccessMessage(): Phrase - { - return __('Default shipping address has been changed.'); - } - - /** - * Get exception message - * - * @return Phrase - */ - private function getExceptionMessage(): Phrase - { - return __('We can\'t change default shipping address right now.'); - } } From c6aa276cb419e439f38a9d22999f5748eb06f359 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Tue, 4 Dec 2018 22:07:30 +0200 Subject: [PATCH 69/77] MAGETWO-96906: [MAGETWO-94346] PR stabilization - Remove address id else condition from execute; --- .../Adminhtml/Address/DefaultBillingAddress.php | 8 +++----- .../Adminhtml/Address/DefaultShippingAddress.php | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php index 51d8e6dac1dea..bf8ca4aeb5cd7 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php @@ -69,7 +69,8 @@ public function execute(): Json { $customerId = $this->getRequest()->getParam('parent_id', false); $addressId = $this->getRequest()->getParam('id', false); - $error = false; + $error = true; + $message = __('There is no address id in setting default billing address.'); if ($addressId) { try { @@ -77,14 +78,11 @@ public function execute(): Json $this->setAddressAsDefault($address); $this->addressRepository->save($address); $message = __('Default billing address has been changed.'); + $error = false; } catch (\Exception $e) { - $error = true; $message = __('We can\'t change default billing address right now.'); $this->logger->critical($e); } - } else { - $error = true; - $message = __('There is no address id in set default billing request.'); } $resultJson = $this->resultJsonFactory->create(); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php index a76d9759b9ac3..81928ae2d28d0 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php @@ -69,7 +69,8 @@ public function execute(): Json { $customerId = $this->getRequest()->getParam('parent_id', false); $addressId = $this->getRequest()->getParam('id', false); - $error = false; + $error = true; + $message = __('There is no address id in setting default shipping address.'); if ($addressId) { try { @@ -77,14 +78,11 @@ public function execute(): Json $this->setAddressAsDefault($address); $this->addressRepository->save($address); $message = __('Default shipping address has been changed.'); + $error = false; } catch (\Exception $e) { - $error = true; $message = __('We can\'t change default shipping address right now.'); $this->logger->critical($e); } - } else { - $error = true; - $message = __('There is no address id in set default shipping request.'); } $resultJson = $this->resultJsonFactory->create(); From 443230d245a45fc4f012537c7e2ddd6595b6dd83 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Tue, 4 Dec 2018 20:04:11 -0600 Subject: [PATCH 70/77] MAGETWO-96906: [MAGETWO-94346] PR stabilization --- .../Magento/Customer/Model/Address/DataProvider.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/code/Magento/Customer/Model/Address/DataProvider.php b/app/code/Magento/Customer/Model/Address/DataProvider.php index c9d68e7453d36..673c203095e26 100644 --- a/app/code/Magento/Customer/Model/Address/DataProvider.php +++ b/app/code/Magento/Customer/Model/Address/DataProvider.php @@ -15,6 +15,7 @@ use Magento\Customer\Model\Address; use Magento\Customer\Model\FileUploaderDataResolver; use Magento\Customer\Model\AttributeMetadataResolver; +use Magento\Ui\Component\Form\Element\Multiline; /** * Dataprovider of customer addresses for customer address grid. @@ -137,6 +138,17 @@ public function getData(): array $customer = $this->customerRepository->getById($customerId); $defaultBilling = $customer->getDefaultBilling(); $defaultShipping = $customer->getDefaultShipping(); + foreach ($this->meta['general']['children'] as $attributeName => $attributeMeta) { + if ($attributeMeta['arguments']['data']['config']['dataType'] === Multiline::NAME + && isset($this->loadedData[$addressId][$attributeName]) + && !is_array($this->loadedData[$addressId][$attributeName]) + ) { + $this->loadedData[$addressId][$attributeName] = explode( + "\n", + $this->loadedData[$addressId][$attributeName] + ); + } + } $this->prepareAddressData($addressId, $this->loadedData, $defaultBilling, $defaultShipping); $this->fileUploaderDataResolver->overrideFileUploaderData($item, $this->loadedData[$addressId]); } From 3d19d4e5eadcfa5dec16e1ab2263c64b9f1af17b Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Wed, 5 Dec 2018 12:42:57 +0200 Subject: [PATCH 71/77] MAGETWO-96906: [MAGETWO-94346] PR stabilization - Update logic for modifying street and multi line attribute data on customer address form; --- .../Customer/Model/Address/DataProvider.php | 23 ++++++++----------- app/code/Magento/Ui/Component/Layout/Tabs.php | 3 ++- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Customer/Model/Address/DataProvider.php b/app/code/Magento/Customer/Model/Address/DataProvider.php index 673c203095e26..e1dd68207cae5 100644 --- a/app/code/Magento/Customer/Model/Address/DataProvider.php +++ b/app/code/Magento/Customer/Model/Address/DataProvider.php @@ -138,17 +138,6 @@ public function getData(): array $customer = $this->customerRepository->getById($customerId); $defaultBilling = $customer->getDefaultBilling(); $defaultShipping = $customer->getDefaultShipping(); - foreach ($this->meta['general']['children'] as $attributeName => $attributeMeta) { - if ($attributeMeta['arguments']['data']['config']['dataType'] === Multiline::NAME - && isset($this->loadedData[$addressId][$attributeName]) - && !is_array($this->loadedData[$addressId][$attributeName]) - ) { - $this->loadedData[$addressId][$attributeName] = explode( - "\n", - $this->loadedData[$addressId][$attributeName] - ); - } - } $this->prepareAddressData($addressId, $this->loadedData, $defaultBilling, $defaultShipping); $this->fileUploaderDataResolver->overrideFileUploaderData($item, $this->loadedData[$addressId]); } @@ -177,8 +166,16 @@ private function prepareAddressData($addressId, array &$addresses, $defaultBilli if (null !== $defaultShipping && $addressId === $defaultShipping) { $addresses[$addressId]['default_shipping'] = '1'; } - if (null !== $addresses[$addressId]['street'] && !\is_array($addresses[$addressId]['street'])) { - $addresses[$addressId]['street'] = explode("\n", $addresses[$addressId]['street']); + foreach ($this->meta['general']['children'] as $attributeName => $attributeMeta) { + if ($attributeMeta['arguments']['data']['config']['dataType'] === Multiline::NAME + && isset($this->loadedData[$addressId][$attributeName]) + && !\is_array($this->loadedData[$addressId][$attributeName]) + ) { + $this->loadedData[$addressId][$attributeName] = explode( + "\n", + $this->loadedData[$addressId][$attributeName] + ); + } } } diff --git a/app/code/Magento/Ui/Component/Layout/Tabs.php b/app/code/Magento/Ui/Component/Layout/Tabs.php index 1c024f8a35eee..ea9664591b94f 100644 --- a/app/code/Magento/Ui/Component/Layout/Tabs.php +++ b/app/code/Magento/Ui/Component/Layout/Tabs.php @@ -88,7 +88,8 @@ protected function addChildren(array &$topNode, UiComponentInterface $component, $this->addWrappedBlock($childComponent, $childrenAreas); continue; } - if ($childComponent instanceof ComponentVisibilityInterface && false === $childComponent->canShow()) { + if ($childComponent instanceof ComponentVisibilityInterface + && false === $childComponent->isComponentVisible()) { continue; } From ed170bc1f9b4c7444605ce85edde8b262c7be8e4 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Wed, 5 Dec 2018 06:50:38 -0600 Subject: [PATCH 72/77] MAGETWO-96906: [MAGETWO-94346] PR stabilization --- app/code/Magento/Customer/Model/Customer/DataProvider.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Customer/Model/Customer/DataProvider.php b/app/code/Magento/Customer/Model/Customer/DataProvider.php index 283e18c201511..16739c6e61864 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProvider.php +++ b/app/code/Magento/Customer/Model/Customer/DataProvider.php @@ -34,6 +34,7 @@ * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * + * @deprecated \Magento\Customer\Model\Address\DataProvider is used instead * @api * @since 100.0.2 */ From d98c4d66ef280b6b063fbbf856388023a56ed2c0 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Wed, 5 Dec 2018 06:57:15 -0600 Subject: [PATCH 73/77] MAGETWO-96906: [MAGETWO-94346] PR stabilization --- app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index 25e0c7e99414a..adb420f983006 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -153,7 +153,7 @@ protected function saveDefaultFlags(array $addressIdList, array & $extractedCust /** * Reformat customer addresses data to be compatible with customer service interface * - * @deprecated addresses are save separately for now + * @deprecated addresses are saved separately for now * @param array $extractedCustomerData * @return array */ From d8bb0fd056228d4bd40c420db5fac78ec7777fc4 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Wed, 5 Dec 2018 07:00:31 -0600 Subject: [PATCH 74/77] MAGETWO-96906: [MAGETWO-94346] PR stabilization --- .../View/Element/ComponentVisibilityInterface.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Element/ComponentVisibilityInterface.php b/lib/internal/Magento/Framework/View/Element/ComponentVisibilityInterface.php index 8dd64b4674bf6..29d14553481d7 100644 --- a/lib/internal/Magento/Framework/View/Element/ComponentVisibilityInterface.php +++ b/lib/internal/Magento/Framework/View/Element/ComponentVisibilityInterface.php @@ -1,15 +1,13 @@ Date: Wed, 5 Dec 2018 17:36:54 +0200 Subject: [PATCH 75/77] MAGETWO-96906: [MAGETWO-94346] PR stabilization - Optimize component visibility check; --- app/code/Magento/Ui/Component/Layout/Tabs.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/Component/Layout/Tabs.php b/app/code/Magento/Ui/Component/Layout/Tabs.php index ea9664591b94f..0d176d7241653 100644 --- a/app/code/Magento/Ui/Component/Layout/Tabs.php +++ b/app/code/Magento/Ui/Component/Layout/Tabs.php @@ -88,8 +88,7 @@ protected function addChildren(array &$topNode, UiComponentInterface $component, $this->addWrappedBlock($childComponent, $childrenAreas); continue; } - if ($childComponent instanceof ComponentVisibilityInterface - && false === $childComponent->isComponentVisible()) { + if ($childComponent instanceof ComponentVisibilityInterface && !$childComponent->isComponentVisible()) { continue; } From 90359fe2d7392c9c2876d927df6e1fce7b8c5c5b Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Wed, 5 Dec 2018 13:02:03 -0600 Subject: [PATCH 76/77] MAGETWO-96906: [MAGETWO-94346] PR stabilization --- .../OpenEditCustomerFromAdminActionGroup.xml | 17 +++++++++++++ .../AdminCustomerAddressFiltersSection.xml | 24 +++++++++++++++++++ ...nCustomerAddressGridMainActionsSection.xml | 15 ++++++++++++ .../AdminCustomerAddressGridSection.xml | 17 +++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressFiltersSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressGridMainActionsSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressGridSection.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml index 5b3a7a70aa2e6..914cebe99ac5f 100755 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml @@ -20,6 +20,23 @@ + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressFiltersSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressFiltersSection.xml new file mode 100644 index 0000000000000..149d39e2d9bfa --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressFiltersSection.xml @@ -0,0 +1,24 @@ + + + + +
+ + + + + + + + + + + +
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressGridMainActionsSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressGridMainActionsSection.xml new file mode 100644 index 0000000000000..f226d49e3bf54 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressGridMainActionsSection.xml @@ -0,0 +1,15 @@ + + + + +
+ + +
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressGridSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressGridSection.xml new file mode 100644 index 0000000000000..fb153a7c102a5 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressGridSection.xml @@ -0,0 +1,17 @@ + + + + +
+ + + + +
+
From a1eb0cbb59319fa90ec02709a9a74d1f4ce18262 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Thu, 6 Dec 2018 12:32:30 +0200 Subject: [PATCH 77/77] MAGETWO-96906: [MAGETWO-94346] PR stabilization - Fix unit tests; --- .../Unit/Model/Address/DataProviderTest.php | 180 +++++++++++++++--- .../Test/Unit/Component/Form/FieldsetTest.php | 11 -- 2 files changed, 156 insertions(+), 35 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php index b1ac27802ff6d..4dafd305d619d 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php @@ -8,6 +8,7 @@ namespace Magento\Customer\Test\Unit\Model\Address; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\Address\DataProvider; use Magento\Customer\Model\AttributeMetadataResolver; use Magento\Customer\Model\FileUploaderDataResolver; use Magento\Customer\Model\ResourceModel\Address\CollectionFactory; @@ -17,12 +18,17 @@ use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Customer\Model\Address as AddressModel; +use Magento\Ui\Component\Form\Element\Multiline; +use Magento\Ui\Component\Form\Field; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DataProviderTest extends \PHPUnit\Framework\TestCase { + private const ATTRIBUTE_CODE = 'street'; + /** * @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject */ @@ -53,11 +59,6 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase */ private $context; - /** - * @var Type|\PHPUnit_Framework_MockObject_MockObject - */ - private $entityType; - /** * @var AddressModel|\PHPUnit_Framework_MockObject_MockObject */ @@ -74,7 +75,7 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase private $attributeMetadataResolver; /** - * @var \Magento\Customer\Model\Address\DataProvider + * @var DataProvider */ private $model; @@ -102,26 +103,68 @@ protected function setUp() $this->eavConfig = $this->getMockBuilder(Config::class) ->disableOriginalConstructor() ->getMock(); - $this->entityType = $this->getMockBuilder(Type::class) - ->disableOriginalConstructor() - ->getMock(); - $this->entityType->expects($this->once()) - ->method('getAttributeCollection') - ->willReturn([]); $this->eavConfig->expects($this->once()) ->method('getEntityType') - ->willReturn($this->entityType); + ->with('customer_address') + ->willReturn($this->getTypeAddressMock([])); $this->customer = $this->getMockForAbstractClass(CustomerInterface::class); $this->address = $this->getMockBuilder(AddressModel::class) ->disableOriginalConstructor() ->getMock(); - + $this->attributeMetadataResolver->expects($this->at(0)) + ->method('getAttributesMeta') + ->willReturn( + [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => Multiline::NAME, + 'formElement' => 'frontend_input', + 'options' => 'test-options', + 'visible' => null, + 'required' => 'is_required', + 'label' => __('Street'), + 'sortOrder' => 'sort_order', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + ], + ], + ], + ] + ); + $this->attributeMetadataResolver->expects($this->at(1)) + ->method('getAttributesMeta') + ->willReturn( + [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'visible' => null, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + 'prefer' => 'toggle', + 'valueMap' => [ + 'true' => 1, + 'false' => 0, + ], + ], + ], + ], + ] + ); $this->model = $objectManagerHelper->getObject( - \Magento\Customer\Model\Address\DataProvider::class, + DataProvider::class, [ - '', - '', - '', + 'name' => 'test-name', + 'primaryFieldName' => 'primary-field-name', + 'requestFieldName' => 'request-field-name', 'addressCollectionFactory' => $this->addressCollectionFactory, 'customerRepository' => $this->customerRepository, 'eavConfig' => $this->eavConfig, @@ -168,8 +211,10 @@ public function testGetDefaultData(): void public function testGetData(): void { $expectedData = [ - '3' => [ - 'parent_id' => "1", + '1' => [ + 'parent_id' => '1', + 'default_billing' => '1', + 'default_shipping' => '1', 'firstname' => 'John', 'lastname' => 'Doe', 'street' => [ @@ -197,16 +242,16 @@ public function testGetData(): void $this->address->expects($this->once()) ->method('getEntityId') - ->willReturn('3'); + ->willReturn('1'); $this->address->expects($this->once()) ->method('load') - ->with("3") + ->with('1') ->willReturnSelf(); $this->address->expects($this->once()) ->method('getData') ->willReturn([ - 'parent_id' => "1", - 'firstname' => "John", + 'parent_id' => '1', + 'firstname' => 'John', 'lastname' => 'Doe', 'street' => "42000 Ave W 55 Cedar City\nApt. 33" ]); @@ -216,4 +261,91 @@ public function testGetData(): void $this->assertEquals($expectedData, $this->model->getData()); } + + /** + * Get customer address type mock + * + * @param array $customerAttributes + * @return Type|\PHPUnit_Framework_MockObject_MockObject + */ + protected function getTypeAddressMock($customerAttributes = []) + { + $typeAddressMock = $this->getMockBuilder(Type::class) + ->disableOriginalConstructor() + ->getMock(); + $attributesCollection = !empty($customerAttributes) ? $customerAttributes : $this->getAttributeMock(); + foreach ($attributesCollection as $attribute) { + $attribute->expects($this->any()) + ->method('getEntityType') + ->willReturn($typeAddressMock); + } + + $typeAddressMock->expects($this->once()) + ->method('getAttributeCollection') + ->willReturn($attributesCollection); + + return $typeAddressMock; + } + + /** + * Get attribute mock + * + * @param array $options + * @return AbstractAttribute[]|\PHPUnit_Framework_MockObject_MockObject[] + */ + protected function getAttributeMock($options = []): array + { + $attributeMock = $this->getMockBuilder(AbstractAttribute::class) + ->setMethods( + [ + 'getAttributeCode', + 'getDataUsingMethod', + 'getFrontendInput', + 'getIsVisible', + 'getSource', + 'getIsUserDefined', + 'getUsedInForms', + 'getEntityType', + ] + ) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $attributeCode = self::ATTRIBUTE_CODE; + if (isset($options[self::ATTRIBUTE_CODE]['specific_code_prefix'])) { + $attributeCode .= $options[self::ATTRIBUTE_CODE]['specific_code_prefix']; + } + + $attributeMock->expects($this->exactly(2)) + ->method('getAttributeCode') + ->willReturn($attributeCode); + + $attributeBooleanMock = $this->getMockBuilder(AbstractAttribute::class) + ->setMethods( + [ + 'getAttributeCode', + 'getDataUsingMethod', + 'getFrontendInput', + 'getIsVisible', + 'getIsUserDefined', + 'getUsedInForms', + 'getSource', + 'getEntityType', + ] + ) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $booleanAttributeCode = 'test-code-boolean'; + if (isset($options['test-code-boolean']['specific_code_prefix'])) { + $booleanAttributeCode .= $options['test-code-boolean']['specific_code_prefix']; + } + + $attributeBooleanMock->expects($this->exactly(2)) + ->method('getAttributeCode') + ->willReturn($booleanAttributeCode); + + $mocks = [$attributeMock, $attributeBooleanMock]; + return $mocks; + } } diff --git a/app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php b/app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php index 4ad0b900762db..d243507580807 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php @@ -55,15 +55,4 @@ public function testGetComponentName() { $this->assertEquals(self::NAME, $this->fieldset->getComponentName()); } - - /** - * Run test for canShow() method - * - * @return void - * - */ - public function testCanShow() - { - $this->assertEquals(true, $this->fieldset->canShow()); - } }