diff --git a/app/code/Magento/BundleImportExport/Model/Export/RowCustomizer.php b/app/code/Magento/BundleImportExport/Model/Export/RowCustomizer.php index 5c8f89c8982d9d0e4cfbc55ca1d67ab15337aa31..227a7d0fc9f0c3b002daf25be16bd0912e70082b 100644 --- a/app/code/Magento/BundleImportExport/Model/Export/RowCustomizer.php +++ b/app/code/Magento/BundleImportExport/Model/Export/RowCustomizer.php @@ -10,6 +10,7 @@ use Magento\CatalogImportExport\Model\Export\RowCustomizerInterface; use Magento\CatalogImportExport\Model\Import\Product as ImportProductModel; use Magento\Bundle\Model\ResourceModel\Selection\Collection as SelectionCollection; use Magento\ImportExport\Controller\Adminhtml\Import; +use Magento\ImportExport\Model\Import as ImportModel; /** * Class RowCustomizer @@ -90,12 +91,13 @@ class RowCustomizer implements RowCustomizerInterface * Prepare data for export * * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection - * @param int $productIds + * @param int[] $productIds * @return $this */ public function prepareData($collection, $productIds) { - $collection->addAttributeToFilter( + $productCollection = clone $collection; + $productCollection->addAttributeToFilter( 'entity_id', ['in' => $productIds] )->addAttributeToFilter( @@ -103,7 +105,7 @@ class RowCustomizer implements RowCustomizerInterface ['eq' => \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE] ); - return $this->populateBundleData($collection); + return $this->populateBundleData($productCollection); } /** @@ -214,9 +216,9 @@ class RowCustomizer implements RowCustomizerInterface 'price_type' => $this->getPriceTypeValue($selection->getSelectionPriceType()) ]; $bundleData .= $optionValues - . ImportProductModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR + . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR . implode( - ImportProductModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, + ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, array_map( function ($value, $key) { return $key . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR . $value; @@ -240,9 +242,9 @@ class RowCustomizer implements RowCustomizerInterface protected function getFormattedOptionValues($option) { return 'name' . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR - . $option->getTitle() . ImportProductModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR + . $option->getTitle() . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR . 'type' . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR - . $option->getType() . ImportProductModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR + . $option->getType() . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR . 'required' . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR . $option->getRequired(); } @@ -290,7 +292,7 @@ class RowCustomizer implements RowCustomizerInterface { if (!empty($dataRow['additional_attributes'])) { $additionalAttributes = explode( - ImportProductModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, + ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $dataRow['additional_attributes'] ); $dataRow['additional_attributes'] = $this->getNotBundleAttributes($additionalAttributes); @@ -314,10 +316,10 @@ class RowCustomizer implements RowCustomizerInterface $cleanedAdditionalAttributes .= $attributeCode . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR . $attributeValue - . ImportProductModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR; + . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR; } } - return rtrim($cleanedAdditionalAttributes, ImportProductModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR); + return rtrim($cleanedAdditionalAttributes, ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR); } } diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index 5ba8ed055cd25afb032289d36d94856909e3ff07..30451fbbf94e7e0ed0bfb604bcf7ecda7f5efba9 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -5,6 +5,7 @@ */ namespace Magento\CatalogImportExport\Model\Export; +use Magento\ImportExport\Model\Import; use \Magento\Store\Model\Store; use \Magento\CatalogImportExport\Model\Import\Product as ImportProduct; @@ -638,7 +639,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity } $categories[] = $categoryPath; } - $dataRow[self::COL_CATEGORY] = implode(ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR, $categories); + $dataRow[self::COL_CATEGORY] = implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $categories); unset($rowCategories[$productId]); return true; @@ -672,7 +673,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity self::COL_ATTR_SET, self::COL_TYPE, self::COL_CATEGORY, - '_product_websites', + self::COL_PRODUCT_WEBSITES, ], $this->_getExportMainAttrCodes(), [self::COL_ADDITIONAL_ATTRIBUTES], @@ -683,7 +684,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity 'crosssell_skus', 'upsell_skus', ], - ['additional_images', 'additional_image_labels'] + ['additional_images', 'additional_image_labels', 'hide_from_product_page'] ); // have we merge custom options columns if ($customOptionsData) { @@ -901,7 +902,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity if (!empty($additionalAttributes)) { $data[$itemId][$storeId][self::COL_ADDITIONAL_ATTRIBUTES] = - implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $additionalAttributes); + implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $additionalAttributes); } else { unset($data[$itemId][$storeId][self::COL_ADDITIONAL_ATTRIBUTES]); } @@ -981,7 +982,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity protected function collectMultiselectValues($item, $attrCode, $storeId) { $attrValue = $item->getData($attrCode); - $optionIds = explode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $attrValue); + $optionIds = explode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $attrValue); $options = array_intersect_key( $this->_attributeValues[$attrCode], array_flip($optionIds) @@ -1040,21 +1041,28 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity foreach ($multiRawData['rowWebsites'][$productId] as $productWebsite) { $websiteCodes[] = $this->_websiteIdToCode[$productWebsite]; } - $dataRow['_product_websites'] = - implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $websiteCodes); + $dataRow[self::COL_PRODUCT_WEBSITES] = + implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $websiteCodes); $multiRawData['rowWebsites'][$productId] = []; } if (!empty($multiRawData['mediaGalery'][$productId])) { $additionalImages = []; $additionalImageLabels = []; + $additionalImageIsDisabled = []; foreach ($multiRawData['mediaGalery'][$productId] as $mediaItem) { $additionalImages[] = $mediaItem['_media_image']; $additionalImageLabels[] = $mediaItem['_media_label']; + + if ($mediaItem['_media_is_disabled'] == true) { + $additionalImageIsDisabled[] = $mediaItem['_media_image']; + } } $dataRow['additional_images'] = - implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $additionalImages); + implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $additionalImages); $dataRow['additional_image_labels'] = - implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $additionalImageLabels); + implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $additionalImageLabels); + $dataRow['hide_from_product_page'] = + implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $additionalImageIsDisabled); $multiRawData['mediaGalery'][$productId] = []; } foreach ($this->_linkTypeProvider->getLinkTypes() as $linkTypeName => $linkId) { @@ -1074,7 +1082,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity $multiRawData['linksRows'][$productId][$linkId] = []; asort($associations); $dataRow[$colPrefix . 'skus'] = - implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, array_keys($associations)); + implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, array_keys($associations)); } } $dataRow = $this->rowCustomizer->addData($dataRow, $productId); @@ -1085,7 +1093,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity foreach (array_keys($this->collectedMultiselectsData[$storeId][$productId]) as $attrKey) { if (!empty($this->collectedMultiselectsData[$storeId][$productId][$attrKey])) { $dataRow[$attrKey] = implode( - ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, + Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $this->collectedMultiselectsData[$storeId][$productId][$attrKey] ); } @@ -1161,7 +1169,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity $result[] = $key . ImportProduct::PAIR_NAME_VALUE_SEPARATOR . $value; } - return implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $result); + return implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $result); } /** diff --git a/app/code/Magento/CatalogImportExport/Model/Export/RowCustomizer/Composite.php b/app/code/Magento/CatalogImportExport/Model/Export/RowCustomizer/Composite.php index 1997d39b47efcb9b81c712a22180ab86d97d84a0..ab96bf9f3545f53accd86477dffe605b1cd0814a 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/RowCustomizer/Composite.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/RowCustomizer/Composite.php @@ -34,7 +34,7 @@ class Composite implements RowCustomizerInterface * Prepare data for export * * @param mixed $collection - * @param int $productIds + * @param int[] $productIds * @return mixed|void */ public function prepareData($collection, $productIds) diff --git a/app/code/Magento/CatalogImportExport/Model/Export/RowCustomizerInterface.php b/app/code/Magento/CatalogImportExport/Model/Export/RowCustomizerInterface.php index 6723dbc24afe1d86083ad3a759e359c309c956f8..240284ed32018edae5d3e653ada920d2a0523cb2 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/RowCustomizerInterface.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/RowCustomizerInterface.php @@ -14,7 +14,7 @@ interface RowCustomizerInterface * Prepare data for export * * @param mixed $collection - * @param int $productIds + * @param int[] $productIds * @return mixed */ public function prepareData($collection, $productIds); diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 24475736833f8cbcbda2528614e5f3d3573b2e53..066e0cf166ee184f43dbea1d6125c1a3393a73bf 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -13,6 +13,7 @@ use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as Va use Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface; use Magento\Framework\Model\ResourceModel\Db\ObjectRelationProcessor; use Magento\Framework\Stdlib\DateTime; +use Magento\ImportExport\Model\Import; use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError; use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; @@ -36,11 +37,6 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity */ const ATTRIBUTE_DELETE_BUNCH = 1000; - /** - * default delimiter for several values in one cell - */ - const DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR = ','; - /** * Pseudo multi line separator in one cell. * @@ -228,7 +224,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity ValidatorInterface::ERROR_SUPER_PRODUCTS_SKU_NOT_FOUND => 'Product with specified super products SKU not found', ValidatorInterface::ERROR_MEDIA_DATA_INCOMPLETE => 'Media data is incomplete', ValidatorInterface::ERROR_EXCEEDED_MAX_LENGTH => 'Attribute %s exceeded max length', - ValidatorInterface::ERROR_INVALID_ATTRIBUTE_TYPE => 'Value for \'%s\' attribute contains incorrect value, acceptable values are in %s format', + ValidatorInterface::ERROR_INVALID_ATTRIBUTE_TYPE => 'Value for \'%s\' attribute contains incorrect value', ValidatorInterface::ERROR_ABSENT_REQUIRED_ATTRIBUTE => 'Attribute %s is required', ValidatorInterface::ERROR_INVALID_ATTRIBUTE_OPTION => 'Value for \'%s\' attribute contains incorrect value, see acceptable values on settings specified for Admin', ValidatorInterface::ERROR_DUPLICATE_UNIQUE_ATTRIBUTE => 'Duplicated unique attribute', @@ -247,12 +243,11 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity protected $_fieldsMap = [ 'image' => 'base_image', 'image_label' => "base_image_label", - 'image' => 'base_image', - 'image_label' => 'base_image_label', 'thumbnail' => 'thumbnail_image', 'thumbnail_label' => 'thumbnail_image_label', self::COL_MEDIA_IMAGE => 'additional_images', '_media_image_label' => 'additional_image_labels', + '_media_is_disabled' => 'hide_from_product_page', Product::COL_STORE => 'store_view_code', Product::COL_ATTR_SET => 'attribute_set_code', Product::COL_TYPE => 'product_type', @@ -332,7 +327,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity self::COL_MEDIA_IMAGE, '_media_label', '_media_position', - '_media_is_disabled', + '_media_is_disabled' ]; /** @@ -711,26 +706,26 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity } /** - * Retrieve instance of product custom options import entity * - * @return \Magento\CatalogImportExport\Model\Import\Product\Option + * Multiple value separator getter. + * @return string */ - public function getOptionEntity() + public function getMultipleValueSeparator() { - return $this->_optionEntity; + if (!empty($this->_parameters[Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR])) { + return $this->_parameters[Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR]; + } + return Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR; } /** - * Multiple value separator getter. + * Retrieve instance of product custom options import entity * - * @return string + * @return \Magento\CatalogImportExport\Model\Import\Product\Option */ - public function getMultipleValueSeparator() + public function getOptionEntity() { - if (!empty($this->_parameters[\Magento\ImportExport\Model\Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR])) { - return $this->_parameters[\Magento\ImportExport\Model\Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR]; - } - return self::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR; + return $this->_optionEntity; } /** @@ -781,7 +776,10 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity */ public function deleteProductsForReplacement() { - $this->setParameters(array('behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_DELETE)); + $this->setParameters(array_merge( + $this->getParameters(), + ['behavior' => Import::BEHAVIOR_DELETE] + )); $this->_deleteProducts(); return $this; @@ -837,9 +835,9 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity protected function _importData() { $this->_validatedRows = null; - if (\Magento\ImportExport\Model\Import::BEHAVIOR_DELETE == $this->getBehavior()) { + if (Import::BEHAVIOR_DELETE == $this->getBehavior()) { $this->_deleteProducts(); - } elseif (\Magento\ImportExport\Model\Import::BEHAVIOR_REPLACE == $this->getBehavior()) { + } elseif (Import::BEHAVIOR_REPLACE == $this->getBehavior()) { $this->_replaceFlag = true; $this->_replaceProducts(); } else { @@ -859,7 +857,10 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $this->deleteProductsForReplacement(); $this->_oldSku = $this->skuProcessor->reloadOldSkus()->getOldSkus(); $this->_validatedRows = null; - $this->setParameters(array('behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND)); + $this->setParameters(array_merge( + $this->getParameters(), + ['behavior' => Import::BEHAVIOR_APPEND] + )); $this->_saveProductsData(); return $this; @@ -975,7 +976,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity static $lastSku = null; - if (\Magento\ImportExport\Model\Import::BEHAVIOR_DELETE == $this->getBehavior()) { + if (Import::BEHAVIOR_DELETE == $this->getBehavior()) { return $rowData; } @@ -1092,7 +1093,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity } } } - if (\Magento\ImportExport\Model\Import::BEHAVIOR_APPEND != $this->getBehavior() && $productIds) { + if (Import::BEHAVIOR_APPEND != $this->getBehavior() && $productIds) { $this->_connection->delete( $mainTable, $this->_connection->quoteInto('product_id IN (?)', array_unique($productIds)) @@ -1186,7 +1187,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $categoriesIn[] = ['product_id' => $productId, 'category_id' => $categoryId, 'position' => 1]; } } - if (\Magento\ImportExport\Model\Import::BEHAVIOR_APPEND != $this->getBehavior()) { + if (Import::BEHAVIOR_APPEND != $this->getBehavior()) { $this->_connection->delete( $tableName, $this->_connection->quoteInto('product_id IN (?)', $delProductId) @@ -1241,39 +1242,43 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity /** * Retrieving images from all columns and rows * - * @param $bunch + * @param array $bunch * @return array */ - protected function _getAllBunchImages($bunch) + protected function getBunchImages($bunch) { - $allImagesFromBunch = []; - foreach ($bunch as $rowData) { - $rowData = $this->_customFieldsMapping($rowData); - foreach ($this->_imagesArrayKeys as $image) { - if (empty($rowData[$image])) { + $images = []; + foreach ($bunch as $row) { + $row = $this->_customFieldsMapping($row); + foreach ($this->_imagesArrayKeys as $imageColumn) { + if (empty($row[$imageColumn])) { continue; } - $dispersionPath = - \Magento\Framework\File\Uploader::getDispretionPath($rowData[$image]); - $importImages = explode($this->getMultipleValueSeparator(), $rowData[$image]); - foreach ($importImages as $importImage) { - $imageSting = mb_strtolower( - $dispersionPath . '/' . preg_replace('/[^a-z0-9\._-]+/i', '', $importImage) - ); - $allImagesFromBunch[$importImage] = $imageSting; + + $rowImages = explode($this->getMultipleValueSeparator(), $row[$imageColumn]); + foreach ($rowImages as $rowImage) { + $destinationPath = str_replace('\\', '/', $rowImage); + $destinationPath = explode('/', $destinationPath); + $destinationPath = array_pop($destinationPath); + $destinationPath = preg_replace('/[^a-z0-9\._-]+/i', '', $destinationPath); + + $dispersion = \Magento\Framework\File\Uploader::getDispretionPath($destinationPath); + $destinationPath = mb_strtolower($dispersion . '/' . $destinationPath); + + $images[$rowImage] = $destinationPath; } } } - return $allImagesFromBunch; + return $images; } /** * Prepare all media files * - * @param $allImagesFromBunch + * @param array $images * @return array */ - protected function _prepareAllMediaFiles($allImagesFromBunch) + protected function getExistingImages($images) { static $productMediaGalleryTableName = null; static $resource = null; @@ -1292,22 +1297,54 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity ['entity_id' => 'mgvte.entity_id'] )->where( 'mg.value IN(?)', - $allImagesFromBunch + $images ); $allMedia = $this->_connection->fetchAll($select); - $result = array(); + $result = []; foreach ($allMedia as $image) { - $result[$image['value']] = []; + if (!isset($result[$image['value']])){ + $result[$image['value']] = []; + } foreach ($this->_oldSku as $sku => $oldSkuData) { - if ($oldSkuData['entity_id'] != $image['entity_id']) { - continue; + if ($oldSkuData['entity_id'] == $image['entity_id']) { + $result[$image['value']][$image['entity_id']] = $sku; } - $result[$image['value']][] = $sku; } } return $result; } + /** + * @param array $rowData + * @return array + */ + public function getImagesFromRow(array $rowData) + { + $images = []; + $labels = []; + foreach ($this->_imagesArrayKeys as $column) { + $images[$column] = []; + $labels[$column] = []; + if (!empty($rowData[$column])) { + $images[$column] = array_unique( + explode($this->getMultipleValueSeparator(), $rowData[$column]) + ); + } + + if (!empty($rowData[$column . '_label'])) { + $labels[$column] = explode($this->getMultipleValueSeparator(), $rowData[$column . '_label']); + } + + if (count($labels[$column]) > count($images[$column])) { + $labels[$column] = array_slice($labels[$column], 0, count($images[$column])); + } elseif (count($labels[$column]) < count($images[$column])) { + $labels[$column] = array_pad($labels[$column], count($images[$column]), ''); + } + } + + return [$images, $labels]; + } + /** * Gather and save information about product entities. * @@ -1319,8 +1356,6 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity */ protected function _saveProducts() { - /** @var $resource \Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceModel */ - $resource = $this->_resourceFactory->create(); $priceIsGlobal = $this->_catalogData->isPriceGlobal(); $productLimit = null; $productsQty = null; @@ -1333,11 +1368,11 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $this->categoriesCache = []; $tierPrices = []; $mediaGallery = []; - $uploadedGalleryFiles = []; + $uploadedImages = []; $previousType = null; $prevAttributeSet = null; - $allImagesFromBunch = $this->_getAllBunchImages($bunch); - $existingImages = $this->_prepareAllMediaFiles($allImagesFromBunch); + $bunchImages = $this->getBunchImages($bunch); + $existingImages = $this->getExistingImages($bunchImages); foreach ($bunch as $rowNum => $rowData) { if (!$this->validateRow($rowData, $rowNum)) { @@ -1387,7 +1422,9 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity } } - $this->websitesCache[$rowSku] = []; + if (!array_key_exists($rowSku, $this->websitesCache)) { + $this->websitesCache[$rowSku] = []; + } // 2. Product-to-Website phase if (!empty($rowData[self::COL_PRODUCT_WEBSITES])) { $websiteCodes = explode($this->getMultipleValueSeparator(), $rowData[self::COL_PRODUCT_WEBSITES]); @@ -1398,12 +1435,12 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity } // 3. Categories phase - $categoriesString = empty($rowData[self::COL_CATEGORY]) ? '' : $rowData[self::COL_CATEGORY]; - $this->categoriesCache[$rowSku] = []; - if (!empty($categoriesString)) { - foreach ($this->categoryProcessor->upsertCategories($categoriesString) as $categoryId) { - $this->categoriesCache[$rowSku][$categoryId] = true; - } + if (!array_key_exists($rowSku, $this->categoriesCache)) { + $this->categoriesCache[$rowSku] = []; + } + $categoryIds = $this->processRowCategories($rowData); + foreach ($categoryIds as $id) { + $this->categoriesCache[$rowSku][$id] = true; } // 4.1. Tier prices phase @@ -1424,95 +1461,52 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity } // 5. Media gallery phase - $mediaGalleryImages = array(); - $mediaGalleryLabels = array(); - if (!empty($rowData[self::COL_MEDIA_IMAGE])) { - $mediaGalleryImages = - explode($this->getMultipleValueSeparator(), $rowData[self::COL_MEDIA_IMAGE]); - if (isset($rowData['_media_image_label'])) { - $mediaGalleryLabels = - explode($this->getMultipleValueSeparator(), $rowData['_media_image_label']); - } else { - $mediaGalleryLabels = []; - } - if (count($mediaGalleryLabels) > count($mediaGalleryImages)) { - $mediaGalleryLabels = array_slice($mediaGalleryLabels, 0, count($mediaGalleryImages)); - } elseif (count($mediaGalleryLabels) < count($mediaGalleryImages)) { - $mediaGalleryLabels = array_pad($mediaGalleryLabels, count($mediaGalleryImages), ''); - } - } - - foreach ($this->_imagesArrayKeys as $imageCol) { - if (!empty($rowData[$imageCol]) - && ($imageCol != self::COL_MEDIA_IMAGE) - && !in_array($rowData[$imageCol], $mediaGalleryImages) - ) { - $mediaGalleryImages[] = $rowData[$imageCol]; - if (isset($mediaGalleryLabels)) { - $mediaGalleryLabels[] = isset($rowData[$imageCol . '_label']) - ? $rowData[$imageCol . '_label'] - : ''; - } else { - $mediaGalleryLabels[] = ''; - } - } + $disabledImages = []; + list($rowImages, $rowLabels) = $this->getImagesFromRow($rowData); + if (isset($rowData['_media_is_disabled'])) { + $disabledImages = array_flip( + explode($this->getMultipleValueSeparator(), $rowData['_media_is_disabled']) + ); } - $rowData[self::COL_MEDIA_IMAGE] = array(); - foreach ($mediaGalleryImages as $mediaImage) { - $imagePath = $allImagesFromBunch[$mediaImage]; - if (isset($existingImages[$imagePath]) && in_array($rowSku, $existingImages[$imagePath])) { - if (!array_key_exists($mediaImage, $uploadedGalleryFiles)) { - $uploadedFile = $this->_uploadMediaFiles( - trim($mediaImage), - true - ); + $rowData[self::COL_MEDIA_IMAGE] = []; + foreach ($rowImages as $column => $columnImages) { + foreach ($columnImages as $position => $columnImage) { + if (!isset($uploadedImages[$columnImage])) { + $uploadedFile = $this->uploadMediaFiles(trim($columnImage), true); if ($uploadedFile) { - $uploadedGalleryFiles[$mediaImage] = $uploadedFile; + $uploadedImages[$columnImage] = $uploadedFile; } else { - $this->addRowError(ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE, $rowNum, null, null, ProcessingError::ERROR_LEVEL_NOT_CRITICAL); + $this->addRowError( + ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE, + $rowNum, + null, + null, + ProcessingError::ERROR_LEVEL_NOT_CRITICAL + ); } + } else { + $uploadedFile = $uploadedImages[$columnImage]; } - } elseif (!isset($existingImages[$imagePath])) { - if (!array_key_exists($mediaImage, $uploadedGalleryFiles)) { - $uploadedFile = $this->_uploadMediaFiles( - trim($mediaImage), - true - ); - if ($uploadedFile) { - $uploadedGalleryFiles[$mediaImage] = $uploadedFile; - $newImagePath = $uploadedGalleryFiles[$mediaImage]; - $existingImages[$newImagePath][] = $rowSku; - } else { - $this->addRowError(ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE, $rowNum, null, null, ProcessingError::ERROR_LEVEL_NOT_CRITICAL); - } + + if ($uploadedFile && $column !== self::COL_MEDIA_IMAGE) { + $rowData[$column] = $uploadedFile; } - if (isset($uploadedGalleryFiles[$mediaImage])) { - $rowData[self::COL_MEDIA_IMAGE][] = $uploadedGalleryFiles[$mediaImage]; - if (!empty($rowData[self::COL_MEDIA_IMAGE]) && is_array($rowData[self::COL_MEDIA_IMAGE])) { - $position = array_search($mediaImage, $mediaGalleryImages); - foreach ($rowData[self::COL_MEDIA_IMAGE] as $mediaImage) { - $mediaGallery[$rowSku][] = [ - 'attribute_id' => $this->getMediaGalleryAttributeId(), - 'label' => isset($mediaGalleryLabels[$position]) ? $mediaGalleryLabels[$position] : '', - 'position' => $position, - 'disabled' => '', - 'value' => $mediaImage, - ]; - } + + $imageNotAssigned = !isset($existingImages[$uploadedFile]) + || !in_array($rowSku, $existingImages[$uploadedFile]); + + if ($uploadedFile && $imageNotAssigned) { + if ($column == self::COL_MEDIA_IMAGE) { + $rowData[$column][] = $uploadedFile; } - } - } - foreach ($this->_imagesArrayKeys as $imageCol) { - if (empty($rowData[$imageCol]) || ($imageCol == self::COL_MEDIA_IMAGE)) { - continue; - } - if (isset($existingImages[$imagePath]) - && !in_array($rowSku, $existingImages[$imagePath]) - && (($rowData[$imageCol] == $imagePath) || ($rowData[$imageCol] == $mediaImage)) - ) { - unset($rowData[$imageCol]); - } elseif (isset($uploadedGalleryFiles[$rowData[$imageCol]])) { - $rowData[$imageCol] = $uploadedGalleryFiles[$rowData[$imageCol]]; + $mediaGallery[$rowSku][] = [ + 'attribute_id' => $this->getMediaGalleryAttributeId(), + 'label' => isset($rowLabels[$column][$position]) ? $rowLabels[$column][$position] : '', + 'position' => $position + 1, + 'disabled' => isset($disabledImages[$columnImage]) ? '1' : '0', + 'value' => $uploadedFile, + ]; + $existingImages[$uploadedFile][] = $rowSku; } } } @@ -1547,7 +1541,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $this->taxClassProcessor->upsertTaxClass($rowData['tax_class_name'], $productTypeModel); } - if ($this->getBehavior() == \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND || + if ($this->getBehavior() == Import::BEHAVIOR_APPEND || empty($rowData[self::COL_SKU]) ) { $rowData = $productTypeModel->clearEmptyData($rowData); @@ -1592,7 +1586,9 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity } } foreach ($storeIds as $storeId) { - $attributes[$attrTable][$rowSku][$attrId][$storeId] = $attrValue; + if (!isset($attributes[$attrTable][$rowSku][$attrId][$storeId])) { + $attributes[$attrTable][$rowSku][$attrId][$storeId] = $attrValue; + } } // restore 'backend_model' to avoid 'default' setting $attribute->setBackendModel($backModel); @@ -1623,7 +1619,24 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity } /** - * @param $productSku + * @param array $rowData + * @return array + */ + protected function processRowCategories($rowData) + { + $categoriesString = empty($rowData[self::COL_CATEGORY]) ? '' : $rowData[self::COL_CATEGORY]; + $categoryIds = []; + if (!empty($categoriesString)) { + $categoryIds = $this->categoryProcessor->upsertCategories( + $categoriesString, + $this->getMultipleValueSeparator() + ); + } + return $categoryIds; + } + + /** + * @param string $productSku * @return array */ public function getProductWebsites($productSku) @@ -1632,7 +1645,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity } /** - * @param $productSku + * @param string $productSku * @return array */ public function getProductCategories($productSku) @@ -1641,7 +1654,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity } /** - * @param $storeCode + * @param string $storeCode * @return array|int|null|string */ public function getStoreIdByCode($storeCode) @@ -1678,7 +1691,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $tierPriceIn[] = $row; } } - if (\Magento\ImportExport\Model\Import::BEHAVIOR_APPEND != $this->getBehavior()) { + if (Import::BEHAVIOR_APPEND != $this->getBehavior()) { $this->_connection->delete( $tableName, $this->_connection->quoteInto('entity_id IN (?)', $delProductId) @@ -1709,8 +1722,8 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $DS = DIRECTORY_SEPARATOR; - if (!empty($this->_parameters[\Magento\ImportExport\Model\Import::FIELD_NAME_IMG_FILE_DIR])) { - $tmpPath = $this->_parameters[\Magento\ImportExport\Model\Import::FIELD_NAME_IMG_FILE_DIR]; + if (!empty($this->_parameters[Import::FIELD_NAME_IMG_FILE_DIR])) { + $tmpPath = $this->_parameters[Import::FIELD_NAME_IMG_FILE_DIR]; } else { $tmpPath = $dirAddon . $DS . $this->_mediaDirectory->getRelativePath('import'); } @@ -1733,6 +1746,15 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity return $this->_fileUploader; } + /** + * @return Uploader + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function getUploader() + { + return $this->_getUploader(); + } + /** * Uploading files into the "catalog/product" media folder. * Return a new file name if the same file is already exists. @@ -1740,7 +1762,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity * @param string $fileName * @return string */ - protected function _uploadMediaFiles($fileName, $renameFileOff = false) + protected function uploadMediaFiles($fileName, $renameFileOff = false) { try { $res = $this->_getUploader()->move($fileName, $renameFileOff); @@ -1766,22 +1788,15 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity static $mediaGalleryTableName = null; static $mediaValueTableName = null; static $mediaEntityToValueTableName = null; - static $productId = null; - if (!$mediaGalleryTableName) { - $mediaGalleryTableName = $this->_resourceFactory->create()->getTable( - 'catalog_product_entity_media_gallery' - ); - } - if (!$mediaValueTableName) { - $mediaValueTableName = $this->_resourceFactory->create()->getTable( - 'catalog_product_entity_media_gallery_value' - ); - } - if (!$mediaEntityToValueTableName) { - $mediaEntityToValueTableName = $this->_resourceFactory->create()->getTable( - 'catalog_product_entity_media_gallery_value_to_entity' - ); - } + $mediaGalleryTableName = $mediaGalleryTableName ?: $this->_resourceFactory->create()->getTable( + 'catalog_product_entity_media_gallery' + ); + $mediaValueTableName = $mediaValueTableName ?: $this->_resourceFactory->create()->getTable( + 'catalog_product_entity_media_gallery_value' + ); + $mediaEntityToValueTableName = $mediaEntityToValueTableName ?: $this->_resourceFactory->create()->getTable( + 'catalog_product_entity_media_gallery_value_to_entity' + ); $productIds = []; $imageNames = []; $multiInsertData = []; @@ -1790,7 +1805,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $productId = $this->skuProcessor->getNewSku($productSku)['entity_id']; $productIds[] = $productId; $insertedGalleryImgs = []; - if (\Magento\ImportExport\Model\Import::BEHAVIOR_APPEND != $this->getBehavior()) { + if (Import::BEHAVIOR_APPEND != $this->getBehavior()) { $this->_connection->delete( $mediaGalleryTableName, $this->_connection->quoteInto('entity_id IN (?)', $productId) @@ -1802,26 +1817,32 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity 'attribute_id' => $insertValue['attribute_id'], 'value' => $insertValue['value'], ]; - $valueToProductId[$insertValue['value']] = $productId; + $valueToProductId[$insertValue['value']][] = $productId; $imageNames[] = $insertValue['value']; $multiInsertData[] = $valueArr; $insertedGalleryImgs[] = $insertValue['value']; } } } - $this->_connection->insertOnDuplicate($mediaGalleryTableName, $multiInsertData, []); - $multiInsertData = []; - $dataForSkinnyTable = []; - $newMediaValues = $this->_connection->fetchAssoc( + $oldMediaValues = $this->_connection->fetchAssoc( $this->_connection->select()->from($mediaGalleryTableName, ['value_id', 'value']) ->where('value IN (?)', $imageNames) ); + $this->_connection->insertOnDuplicate($mediaGalleryTableName, $multiInsertData, []); + $multiInsertData = []; + $newMediaSelect = $this->_connection->select()->from($mediaGalleryTableName, ['value_id', 'value']) + ->where('value IN (?)', $imageNames); + if (array_keys($oldMediaValues)) { + $newMediaSelect->where('value_id NOT IN (?)', array_keys($oldMediaValues)); + } + + $newMediaValues = $this->_connection->fetchAssoc($newMediaSelect); foreach ($mediaGalleryData as $productSku => $mediaGalleryRows) { foreach ($mediaGalleryRows as $insertValue) { foreach ($newMediaValues as $value_id => $values) { if ($values['value'] == $insertValue['value']) { $insertValue['value_id'] = $value_id; - $insertValue['entity_id'] = $valueToProductId[$values['value']]; + $insertValue['entity_id'] = array_shift($valueToProductId[$values['value']]); unset($newMediaValues[$value_id]); break; } @@ -1844,7 +1865,11 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity } } try { - $this->_connection->insertOnDuplicate($mediaValueTableName, $multiInsertData, ['value_id']); + $this->_connection->insertOnDuplicate( + $mediaValueTableName, + $multiInsertData, + ['value_id', 'store_id', 'entity_id', 'label', 'position', 'disabled'] + ); $this->_connection->insertOnDuplicate($mediaEntityToValueTableName, $dataForSkinnyTable, ['value_id']); } catch (\Exception $e) { $this->_connection->delete( @@ -1881,7 +1906,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $websitesData[] = ['product_id' => $productId, 'website_id' => $websiteId]; } } - if (\Magento\ImportExport\Model\Import::BEHAVIOR_APPEND != $this->getBehavior()) { + if (Import::BEHAVIOR_APPEND != $this->getBehavior()) { $this->_connection->delete( $tableName, $this->_connection->quoteInto('product_id IN (?)', $delProductId) @@ -1945,12 +1970,14 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity } else { $row['qty'] = 0; } - $stockData[] = $row; + if (!isset($stockData[$rowData[self::COL_SKU]])) { + $stockData[$rowData[self::COL_SKU]] = $row; + } } // Insert rows if (!empty($stockData)) { - $this->_connection->insertOnDuplicate($entityTable, $stockData); + $this->_connection->insertOnDuplicate($entityTable, array_values($stockData)); } if ($productIdsToReindex) { @@ -2084,7 +2111,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $rowScope = $this->getRowScope($rowData); // BEHAVIOR_DELETE use specific validation logic - if (\Magento\ImportExport\Model\Import::BEHAVIOR_DELETE == $this->getBehavior()) { + if (Import::BEHAVIOR_DELETE == $this->getBehavior()) { if (self::SCOPE_DEFAULT == $rowScope && !isset($this->_oldSku[$rowData[self::COL_SKU]])) { $this->addRowError(ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE, $rowNum); return false; @@ -2233,7 +2260,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity private function _customFieldsMapping($rowData) { foreach ($this->_fieldsMap as $systemFieldName => $fileFieldName) { - if (isset($rowData[$fileFieldName])) { + if (array_key_exists($fileFieldName, $rowData)) { $rowData[$systemFieldName] = $rowData[$fileFieldName]; } } @@ -2241,10 +2268,12 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $rowData = $this->_parseAdditionalAttributes($rowData); $rowData = $this->_setStockUseConfigFieldsValues($rowData); - if (isset($rowData['status'])) { - if (($rowData['status'] == \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) || $rowData['status'] == 'yes') { + if (array_key_exists('status', $rowData) + && $rowData['status'] != \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED + ) { + if ($rowData['status'] == 'yes') { $rowData['status'] = \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED; - } else { + } elseif (!empty($rowData['status']) || $this->getRowScope($rowData) == self::SCOPE_DEFAULT) { $rowData['status'] = \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED; } } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php index 6d4ba7ee1833580f4dc503ac782566516a68f9bc..c442b1957954d29afb380354c947060b941cd742 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php @@ -7,11 +7,6 @@ namespace Magento\CatalogImportExport\Model\Import\Product; class CategoryProcessor { - /** - * Delimiter in import file between categories. - */ - const DELIMITER_CATEGORIES = '|'; - /** * Delimiter in category path. */ @@ -144,13 +139,14 @@ class CategoryProcessor * Returns IDs of categories by string path creating nonexistent ones. * * @param string $categoriesString + * @param string $categoriesSeparator * * @return array */ - public function upsertCategories($categoriesString) + public function upsertCategories($categoriesString, $categoriesSeparator) { $categoriesIds = []; - $categories = explode(self::DELIMITER_CATEGORIES, $categoriesString); + $categories = explode($categoriesSeparator, $categoriesString); foreach ($categories as $category) { $categoriesIds[] = $this->upsertCategory($category); diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index 89230e1da346389b6618d4db55ba1b48c28a2182..cee5e53e97978da309bf01c7605b575d961a6fb1 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -93,6 +93,26 @@ class Validator extends AbstractValidator implements RowValidatorInterface return $valid; } + /** + * @param string $attrCode + * @param array $attributeParams + * @param array $rowData + * @return bool + */ + public function isRequiredAttributeValid($attrCode, array $attributeParams, array $rowData) + { + $doCheck = false; + if ($attrCode == Product::COL_SKU) { + $doCheck = true; + } elseif ($attributeParams['is_required'] && $this->getRowScope($rowData) == Product::SCOPE_DEFAULT + && $this->context->getBehavior() != \Magento\ImportExport\Model\Import::BEHAVIOR_DELETE + ) { + $doCheck = true; + } + + return $doCheck ? isset($rowData[$attrCode]) && strlen(trim($rowData[$attrCode])) : true; + } + /** * @param string $attrCode * @param array $attrParams @@ -107,25 +127,20 @@ class Validator extends AbstractValidator implements RowValidatorInterface if (!empty($attrParams['apply_to']) && !in_array($rowData['product_type'], $attrParams['apply_to'])) { return true; } - if ($attrCode == Product::COL_SKU || $attrParams['is_required'] - && ($this->context->getBehavior() == \Magento\ImportExport\Model\Import::BEHAVIOR_REPLACE - || ($this->context->getBehavior() == \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND - && !isset($this->context->getOldSku()[$rowData[$attrCode]]))) - ) { - if (!isset($rowData[$attrCode]) || !strlen(trim($rowData[$attrCode]))) { - $valid = false; - $this->_addMessages( - [ - sprintf( - $this->context->retrieveMessageTemplate( - RowValidatorInterface::ERROR_VALUE_IS_REQUIRED - ), - $attrCode - ) - ] - ); - return $valid; - } + + if (!$this->isRequiredAttributeValid($attrCode, $attrParams, $rowData)) { + $valid = false; + $this->_addMessages( + [ + sprintf( + $this->context->retrieveMessageTemplate( + RowValidatorInterface::ERROR_VALUE_IS_REQUIRED + ), + $attrCode + ) + ] + ); + return $valid; } if (!strlen(trim($rowData[$attrCode]))) { @@ -146,7 +161,7 @@ class Validator extends AbstractValidator implements RowValidatorInterface $values = explode(Product::PSEUDO_MULTI_LINE_SEPARATOR, $rowData[$attrCode]); $valid = true; foreach ($values as $value) { - $valid = $valid || isset($attrParams['options'][strtolower($value)]); + $valid = $valid && isset($attrParams['options'][strtolower($value)]); } if (!$valid) { $this->_addMessages( @@ -227,6 +242,20 @@ class Validator extends AbstractValidator implements RowValidatorInterface return $returnValue; } + /** + * Obtain scope of the row from row data. + * + * @param array $rowData + * @return int + */ + public function getRowScope(array $rowData) + { + if (empty($rowData[Product::COL_STORE])) { + return Product::SCOPE_DEFAULT; + } + return Product::SCOPE_STORE; + } + /** * @param \Magento\CatalogImportExport\Model\Import\Product $context * @return $this diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php index dc0263e99d4f093bb65634c7f3eaeb9d12447caa..f22d910ccd45e6d386e7483a6b1373b75d9b4af7 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php @@ -223,7 +223,7 @@ class Uploader extends \Magento\MediaStorage\Model\File\Uploader && method_exists($params['object'], $params['method']) && is_callable([$params['object'], $params['method']]) ) { - $params['object']->{$params['method']}($filePath); + $params['object']->{$params['method']}($this->_directory->getAbsolutePath($filePath)); } } } diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/CategoryProcessorTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/CategoryProcessorTest.php index c6b5fd5570d265f89e344fa6eebf7cc1a0e47f20..3e281ba84afd0a769c29ef813e848250dc61c1bc 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/CategoryProcessorTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/CategoryProcessorTest.php @@ -110,7 +110,8 @@ class CategoryProcessorTest extends \PHPUnit_Framework_TestCase public function testUpsertCategories() { - $categoryIds = $this->categoryProcessor->upsertCategories(self::CHILD_CATEGORY_NAME); + $categoriesSeparator = ','; + $categoryIds = $this->categoryProcessor->upsertCategories(self::CHILD_CATEGORY_NAME, $categoriesSeparator); $this->assertArrayHasKey(self::CHILD_CATEGORY_ID, array_flip($categoryIds)); } diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php index 3541ccbb99338202ea5c1fdd73f1dea54d63bfab..b7fd1e3604b675d49510a4cdd94696e0e15c1904 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php @@ -5,8 +5,10 @@ */ namespace Magento\CatalogImportExport\Test\Unit\Model\Import\Product; +use Magento\CatalogImportExport\Model\Import\Product; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\CatalogImportExport\Model\Import\Product\Validator; +use Magento\ImportExport\Model\Import; class ValidatorTest extends \PHPUnit_Framework_TestCase { @@ -46,7 +48,7 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase false ); $this->context->expects($this->any())->method('retrieveProductTypeByName')->willReturn($entityTypeModel); - $this->context->expects($this->any())->method('retrieveMessageTemplate')->willReturn(''); + $this->context->expects($this->any())->method('retrieveMessageTemplate')->willReturn('error message'); $this->validatorOne = $this->getMock( 'Magento\CatalogImportExport\Model\Import\Product\Validator\Media', @@ -72,23 +74,123 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase $this->validator->init($this->context); } - public function testIsBooleanAttributeValid() + /** + * @param string $behavior + * @param array $attrParams + * @param array $rowData + * @param bool $isValid + * @param string $attrCode + * @dataProvider attributeValidationProvider + */ + public function testAttributeValidation($behavior, $attrParams, $rowData, $isValid, $attrCode = 'attribute_code') { - $this->context->expects($this->any())->method('getBehavior') - ->willReturn(\Magento\ImportExport\Model\Import::BEHAVIOR_REPLACE); + $this->context->expects($this->any())->method('getBehavior')->willReturn($behavior); $result = $this->validator->isAttributeValid( - 'boolean_attribute', + $attrCode, + $attrParams, + $rowData + ); + $this->assertEquals($isValid, $result); + if (!$isValid) { + $this->assertTrue($this->validator->hasMessages()); + } + } + + /** + * @return array + */ + public function attributeValidationProvider() + { + return [ + [ + 'any_behavior', + ['apply_to' => ['expected_product_type']], + ['product_type' => 'not_expected_product_type'], + true, //validation skipped in such case, so it means attribute is valid + 'any_attibute_code', + ], + [ + 'any_behavior', + [], + ['product_type' => 'any'], + false, + Product::COL_SKU + ], + [ + 'any_behavior', + ['type' => 'varchar'], + ['product_type' => 'any', 'sku' => 'sku_value'], + true, + Product::COL_SKU + ], + [ + 'any_behavior', + ['is_required' => true, 'type' => 'varchar'], + ['product_type' => 'any', 'attribute_code' => 'value'], + true + ], + [ + Import::BEHAVIOR_APPEND, + ['is_required' => true, 'type' => 'varchar'], + ['product_type' => 'any', 'attribute_code' => ''], + false + ], + [ + Import::BEHAVIOR_APPEND, + ['is_required' => true, 'type' => 'int'], + ['product_type' => 'any', 'attribute_code' => 'not-int'], + false + ], + [ + Import::BEHAVIOR_APPEND, + ['is_required' => true, 'type' => 'int'], + ['product_type' => 'any', 'attribute_code' => '1'], + true + ], + [ + Import::BEHAVIOR_APPEND, + ['is_required' => true, 'type' => 'boolean', 'options' => ['yes' => 0, 'no' => 1]], + ['product_type' => 'any', 'attribute_code' => 'some-value'], + false + ], + [ + Import::BEHAVIOR_APPEND, + ['is_required' => true, 'type' => 'boolean', 'options' => ['yes' => 0, 'no' => 1]], + ['product_type' => 'any', 'attribute_code' => 'Yes'], + true + ], [ - 'type' => 'boolean', - 'apply_to' => ['simple'], - 'is_required' => false + Import::BEHAVIOR_APPEND, + ['is_required' => true, 'type' => 'multiselect', 'options' => ['option 1' => 0, 'option 2' => 1]], + ['product_type' => 'any', 'attribute_code' => 'Option 1|Option 2|Option 3'], + false ], [ - 'product_type' => 'simple', - 'boolean_attribute' => 'Yes' + Import::BEHAVIOR_APPEND, + ['is_required' => true, 'type' => 'multiselect', 'options' => ['option 1' => 0, 'option 2' => 1]], + ['product_type' => 'any', 'attribute_code' => 'Option 1|Option 2'], + true + ], + [ + Import::BEHAVIOR_APPEND, + ['is_required' => true, 'type' => 'datetime'], + ['product_type' => 'any', 'attribute_code' => '1/1/15 12am'], + true + ], + [ + Import::BEHAVIOR_APPEND, + ['is_required' => true, 'type' => 'datetime'], + ['product_type' => 'any', 'attribute_code' => '1/1/15 13am'], + false + ], + [ + Import::BEHAVIOR_APPEND, + ['is_required' => true, 'type' => 'varchar', 'is_unique' => true], + ['product_type' => 'any', 'unique_attribute' => 'unique-value', Product::COL_SKU => 'sku-0'], + true, + 'unique_attribute' ] - ); - $this->assertTrue($result); + ]; } public function testIsValidCorrect() diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php index d5fbfaf8c9e8c693c690a26a6985b5525a166508..78580aee7c92428e19dcb8907a28e4f91cc32258 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php @@ -8,6 +8,7 @@ namespace Magento\CatalogImportExport\Test\Unit\Model\Import; use Magento\CatalogImportExport\Model\Import\Product; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Stdlib\DateTime; +use Magento\ImportExport\Model\Import; /** * Class ProductTest @@ -567,7 +568,7 @@ class ProductTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractI { $this->setPropertyValue($this->importProduct, '_parameters', null); $this->assertEquals( - \Magento\CatalogImportExport\Model\Import\Product::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, + Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $this->importProduct->getMultipleValueSeparator() ); } diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php index fa7a1b0b386988fac65e504512e8843c3e52d14c..855d800fcfdcbb9c6a72f13599560a591d9ea7bc 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php @@ -190,6 +190,7 @@ class AfterImportDataObserver implements ObserverInterface $product->setData($field, $rowData[$field]); } } + $this->categoryCache[$rowData['entity_id']] = $this->import->getProductCategories($rowData['sku']); $this->websiteCache[$rowData['entity_id']] = $this->import->getProductWebsites($rowData['sku']); foreach ($this->websiteCache[$rowData['entity_id']] as $websiteId) { @@ -197,11 +198,9 @@ class AfterImportDataObserver implements ObserverInterface $this->websitesToStoreIds[$websiteId] = $this->storeManager->getWebsite($websiteId)->getStoreIds(); } } - if (!empty($rowData[ImportProduct::COL_STORE]) - && ($storeId = $this->import->getStoreIdByCode($rowData[ImportProduct::COL_STORE])) - ) { - $product->setStoreId($storeId); - } + + $this->setStoreToProduct($product, $rowData); + if ($this->isGlobalScope($product->getStoreId())) { $this->populateGlobalProduct($product); } else { @@ -210,10 +209,26 @@ class AfterImportDataObserver implements ObserverInterface return $this; } + /** + * @param \Magento\Catalog\Model\Product $product + * @param array $rowData + * @return void + */ + protected function setStoreToProduct(\Magento\Catalog\Model\Product $product, array $rowData) + { + if (!empty($rowData[ImportProduct::COL_STORE]) + && ($storeId = $this->import->getStoreIdByCode($rowData[ImportProduct::COL_STORE])) + ) { + $product->setStoreId($storeId); + } elseif (!$product->hasData(\Magento\Catalog\Api\Data\ProductInterface::STORE_ID)) { + $product->setStoreId(Store::DEFAULT_STORE_ID); + } + } + /** * Add product to import * - * @param \Magento\CatalogImportExport\Model\Import\Product $product + * @param \Magento\Catalog\Model\Product $product * @param string $storeId * @return $this */ @@ -221,15 +236,15 @@ class AfterImportDataObserver implements ObserverInterface { if (!isset($this->products[$product->getId()])) { $this->products[$product->getId()] = []; - $this->products[$product->getId()][$storeId] = $product; } + $this->products[$product->getId()][$storeId] = $product; return $this; } /** * Populate global product * - * @param \Magento\CatalogImportExport\Model\Import\Product $product + * @param \Magento\Catalog\Model\Product $product * @return $this */ protected function populateGlobalProduct($product) diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/AfterImportDataObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/AfterImportDataObserverTest.php index 5c50b55ecc926f96e3d127cb46d73a1219a3a1a7..fc6636a16bd3fccdf34c8f5337d898f5dc6af465 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/AfterImportDataObserverTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/AfterImportDataObserverTest.php @@ -379,8 +379,7 @@ class AfterImportDataObserverTest extends \PHPUnit_Framework_TestCase /** * Test for afterImportData() - * Covers afterImportData() + protected methods used inside except related to generateUrls() ones. - * generateUrls will be covered separately. + * Covers afterImportData() + protected methods used inside * * @covers \Magento\CatalogUrlRewrite\Observer\AfterImportDataObserver::afterImportData * @covers \Magento\CatalogUrlRewrite\Observer\AfterImportDataObserver::_populateForUrlGeneration @@ -392,7 +391,7 @@ class AfterImportDataObserverTest extends \PHPUnit_Framework_TestCase */ public function testAfterImportData() { - $newSku = ['entity_id' => 'value']; + $newSku = [['entity_id' => 'value'], ['entity_id' => 'value3']]; $websiteId = 'websiteId value'; $productsCount = count($this->products); $websiteMock = $this->getMock( @@ -421,21 +420,24 @@ class AfterImportDataObserverTest extends \PHPUnit_Framework_TestCase [$this->products[0][ImportProduct::COL_SKU]], [$this->products[1][ImportProduct::COL_SKU]] ) - ->willReturn($newSku); + ->will($this->onConsecutiveCalls($newSku[0], $newSku[1])); $this->importProduct ->expects($this->exactly($productsCount)) ->method('getProductCategories') ->withConsecutive( [$this->products[0][ImportProduct::COL_SKU]], [$this->products[1][ImportProduct::COL_SKU]] - ); + )->willReturn([]); $getProductWebsitesCallsCount = $productsCount*2; $this->importProduct ->expects($this->exactly($getProductWebsitesCallsCount)) ->method('getProductWebsites') - ->willReturn([ - $newSku['entity_id'] => $websiteId, - ]); + ->willReturnOnConsecutiveCalls( + [$newSku[0]['entity_id'] => $websiteId], + [$newSku[0]['entity_id'] => $websiteId], + [$newSku[1]['entity_id'] => $websiteId], + [$newSku[1]['entity_id'] => $websiteId] + ); $map = [ [$this->products[0][ImportProduct::COL_STORE], $this->products[0][ImportProduct::COL_STORE]], [$this->products[1][ImportProduct::COL_STORE], $this->products[1][ImportProduct::COL_STORE]] @@ -460,11 +462,20 @@ class AfterImportDataObserverTest extends \PHPUnit_Framework_TestCase $product ->expects($this->exactly($productsCount)) ->method('setId') - ->with($newSku['entity_id']); + ->withConsecutive([$newSku[0]['entity_id']], [$newSku[1]['entity_id']]); $product ->expects($this->any()) ->method('getId') - ->willReturn($newSku['entity_id']); + ->willReturnOnConsecutiveCalls( + $newSku[0]['entity_id'], + $newSku[0]['entity_id'], + $newSku[0]['entity_id'], + $newSku[0]['entity_id'], + $newSku[0]['entity_id'], + $newSku[1]['entity_id'], + $newSku[1]['entity_id'], + $newSku[1]['entity_id'] + ); $product ->expects($this->exactly($productsCount)) ->method('getSku') @@ -480,9 +491,12 @@ class AfterImportDataObserverTest extends \PHPUnit_Framework_TestCase $this->products[1][ImportProduct::COL_STORE] )); $product - ->expects($this->once()) + ->expects($this->exactly($productsCount)) ->method('setStoreId') - ->with($this->products[1][ImportProduct::COL_STORE]); + ->withConsecutive( + [$this->products[0][ImportProduct::COL_STORE]], + [$this->products[1][ImportProduct::COL_STORE]] + ); $this->catalogProductFactory ->expects($this->exactly($productsCount)) ->method('create') @@ -497,32 +511,55 @@ class AfterImportDataObserverTest extends \PHPUnit_Framework_TestCase ], [ ' AND entity_id = ?)', - $newSku['entity_id'], + $newSku[0]['entity_id'], + ], + [ + '(store_id = ?', + $storeIds[0], + ], + [ + ' AND entity_id = ?)', + $newSku[1]['entity_id'], ] ); + $this->connection + ->expects($this->once()) + ->method('fetchAll') + ->willReturn([]); + $this->select->expects($this->any())->method('from')->willReturnSelf(); + $this->select->expects($this->any())->method('where')->willReturnSelf(); + + $this->urlFinder->expects($this->any())->method('findAllByData')->willReturn([]); + + $this->productUrlPathGenerator->expects($this->any())->method('getUrlPathWithSuffix') + ->willReturn('urlPathWithSuffix'); + $this->productUrlPathGenerator->expects($this->any())->method('getUrlPath') + ->willReturn('urlPath'); + $this->productUrlPathGenerator->expects($this->any())->method('getCanonicalUrlPath') + ->willReturn('canonicalUrlPath'); + + $this->urlRewrite->expects($this->any())->method('setStoreId')->willReturnSelf(); + $this->urlRewrite->expects($this->any())->method('setEntityId')->willReturnSelf(); + $this->urlRewrite->expects($this->any())->method('setEntityType')->willReturnSelf(); + $this->urlRewrite->expects($this->any())->method('setRequestPath')->willReturnSelf(); + $this->urlRewrite->expects($this->any())->method('setTargetPath')->willReturnSelf(); + $this->urlRewrite->expects($this->any())->method('getTargetPath')->willReturn('targetPath'); + $this->urlRewrite->expects($this->any())->method('getStoreId') + ->willReturnOnConsecutiveCalls(0, 'not global'); + + $this->urlRewriteFactory->expects($this->any())->method('create')->willReturn($this->urlRewrite); $productUrls = [ - 'url 1', - 'url 2', + 'targetPath-0' => $this->urlRewrite, + 'targetPath-not global' => $this->urlRewrite ]; - $importMock = $this->getImportMock([ - 'generateUrls', - 'canonicalUrlRewriteGenerate', - 'categoriesUrlRewriteGenerate', - 'currentUrlRewritesRegenerate', - 'cleanOverriddenUrlKey', - ]); - $importMock - ->expects($this->once()) - ->method('generateUrls') - ->willReturn($productUrls); $this->urlPersist ->expects($this->once()) ->method('replace') ->with($productUrls); - $importMock->execute($this->observer); + $this->import->execute($this->observer); } /** diff --git a/app/code/Magento/ConfigurableImportExport/Model/Export/RowCustomizer.php b/app/code/Magento/ConfigurableImportExport/Model/Export/RowCustomizer.php index 4405feba18426579b23073dcd3e89bfcb1851273..2dcde2898de2d426f6605dc2dd128b00bf72b538 100644 --- a/app/code/Magento/ConfigurableImportExport/Model/Export/RowCustomizer.php +++ b/app/code/Magento/ConfigurableImportExport/Model/Export/RowCustomizer.php @@ -7,6 +7,7 @@ namespace Magento\ConfigurableImportExport\Model\Export; use Magento\CatalogImportExport\Model\Export\RowCustomizerInterface; use \Magento\CatalogImportExport\Model\Import\Product as ImportProduct; +use Magento\ImportExport\Model\Import; class RowCustomizer implements RowCustomizerInterface { @@ -19,12 +20,13 @@ class RowCustomizer implements RowCustomizerInterface * Prepare configurable data for export * * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection - * @param int $productIds + * @param int[] $productIds * @return void */ public function prepareData($collection, $productIds) { - $collection->addAttributeToFilter( + $productCollection = clone $collection; + $productCollection->addAttributeToFilter( 'entity_id', ['in' => $productIds] )->addAttributeToFilter( @@ -32,7 +34,7 @@ class RowCustomizer implements RowCustomizerInterface ['eq' => \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE] ); - while ($product = $collection->fetchItem()) { + while ($product = $productCollection->fetchItem()) { $productAttributesOptions = $product->getTypeInstance()->getConfigurableOptions($product); foreach ($productAttributesOptions as $productAttributeOption) { @@ -51,11 +53,11 @@ class RowCustomizer implements RowCustomizerInterface foreach ($variations as $sku => $values) { $variations[$sku] = - 'sku=' . $sku . ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR - . implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $values); + 'sku=' . $sku . Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR + . implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $values); } $variations = implode(ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR, $variations); - $variationsLabels = implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $variationsLabels); + $variationsLabels = implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $variationsLabels); $this->configurableData[$product->getId()] = [ 'configurable_variations' => $variations, diff --git a/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Export/RowCustomizerTest.php b/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Export/RowCustomizerTest.php index 65732d3df1df550c4b987a87b4005efd3a5bf223..b067e19c381252aaf7f3fd5eb1c9f79497f1d96f 100644 --- a/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Export/RowCustomizerTest.php +++ b/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Export/RowCustomizerTest.php @@ -7,6 +7,7 @@ namespace Magento\ConfigurableImportExport\Test\Unit\Model\Export; use \Magento\CatalogImportExport\Model\Import\Product as ImportProduct; +use Magento\ImportExport\Model\Import; class RowCustomizerTest extends \PHPUnit_Framework_TestCase { @@ -256,17 +257,17 @@ class RowCustomizerTest extends \PHPUnit_Framework_TestCase return [ $this->initiatedProductId => [ 'configurable_variations' => implode(ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR, [ - '_sku_' => 'sku=_sku_' . ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR - . implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, [ + '_sku_' => 'sku=_sku_' . Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR + . implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, [ 'code_of_attribute=Option Title', 'code_of_attribute=Option Title', ]), - '_sku_2' => 'sku=_sku_2' . ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR - . implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, [ + '_sku_2' => 'sku=_sku_2' . Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR + . implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, [ 'code_of_attribute_2=Option Title 2', ]) ]), - 'configurable_variation_labels' => implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, [ + 'configurable_variation_labels' => implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, [ 'code_of_attribute' => 'code_of_attribute=Super attribute label', 'code_of_attribute_2' => 'code_of_attribute_2=Super attribute label 2', ]), @@ -291,6 +292,7 @@ class RowCustomizerTest extends \PHPUnit_Framework_TestCase /** * @param $object * @param $property + * @return mixed */ protected function getPropertyValue(&$object, $property) { diff --git a/app/code/Magento/GroupedImportExport/Model/Import/Product/Type/Grouped.php b/app/code/Magento/GroupedImportExport/Model/Import/Product/Type/Grouped.php index 6bccd71831032751861b547a66224fe88c8cf27f..3185df16bdb3bc95e55f46361d911a07e4f7bde7 100644 --- a/app/code/Magento/GroupedImportExport/Model/Import/Product/Type/Grouped.php +++ b/app/code/Magento/GroupedImportExport/Model/Import/Product/Type/Grouped.php @@ -8,6 +8,7 @@ namespace Magento\GroupedImportExport\Model\Import\Product\Type; use Magento\CatalogImportExport\Model\Import\Product; +use Magento\ImportExport\Model\Import; class Grouped extends \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType { @@ -76,7 +77,7 @@ class Grouped extends \Magento\CatalogImportExport\Model\Import\Product\Type\Abs if (!$this->_entityModel->isRowAllowedToImport($rowData, $rowNum) || empty($associatedSkusQty)) { continue; } - $associatedSkusAndQtyPairs = explode(Product::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $associatedSkusQty); + $associatedSkusAndQtyPairs = explode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $associatedSkusQty); $position = 0; foreach ($associatedSkusAndQtyPairs as $associatedSkuAndQty) { ++$position; diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php index 71f342b5b585770f34adcd495b10e90090280213..1a8f446b1de5d0c355c2c87ec3d5bc3bddc39869 100644 --- a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php +++ b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php @@ -5,6 +5,7 @@ */ namespace Magento\ImportExport\Block\Adminhtml\Import\Edit; +use Magento\ImportExport\Model\Import; use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; /** @@ -170,7 +171,7 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic 'required' => true, 'disabled' => true, 'class' => $behaviorCode, - 'value' => ',', + 'value' => Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, ] ); } diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Download.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Download.php index 240067a4f1ff2585dceb4e1806624e43d786efad..5982658bb5160d60d9c70a64dce7067dd4e1fada 100644 --- a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Download.php +++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Download.php @@ -5,6 +5,7 @@ */ namespace Magento\ImportExport\Controller\Adminhtml\Import; +use Magento\Framework\Component\ComponentRegistrar; use Magento\ImportExport\Controller\Adminhtml\Import as ImportController; use Magento\Framework\App\Filesystem\DirectoryList; @@ -26,9 +27,9 @@ class Download extends ImportController protected $readFactory; /** - * @var \Magento\Framework\Module\Dir\Reader + * @var \Magento\Framework\Component\ComponentRegistrar */ - protected $reader; + protected $componentRegistrar; /** * @var \Magento\Framework\App\Response\Http\FileFactory @@ -42,14 +43,14 @@ class Download extends ImportController * @param \Magento\Framework\App\Response\Http\FileFactory $fileFactory * @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory * @param \Magento\Framework\Filesystem\Directory\ReadFactory $readFactory - * @param \Magento\Framework\Module\Dir\Reader $reader + * @param ComponentRegistrar $componentRegistrar */ public function __construct( \Magento\Backend\App\Action\Context $context, \Magento\Framework\App\Response\Http\FileFactory $fileFactory, \Magento\Framework\Controller\Result\RawFactory $resultRawFactory, \Magento\Framework\Filesystem\Directory\ReadFactory $readFactory, - \Magento\Framework\Module\Dir\Reader $reader + \Magento\Framework\Component\ComponentRegistrar $componentRegistrar ) { parent::__construct( $context @@ -57,7 +58,7 @@ class Download extends ImportController $this->fileFactory = $fileFactory; $this->resultRawFactory = $resultRawFactory; $this->readFactory = $readFactory; - $this->reader = $reader; + $this->componentRegistrar = $componentRegistrar; } /** @@ -68,8 +69,8 @@ class Download extends ImportController public function executeInternal() { $fileName = $this->getRequest()->getParam('filename') . '.csv'; - $moduleDir = $this->reader->getModuleDir('', self::SAMPLE_FILES_MODULE); - $fileAbsolutePath = $moduleDir . '/' . $fileName; + $moduleDir = $this->componentRegistrar->getPath(ComponentRegistrar::MODULE, self::SAMPLE_FILES_MODULE); + $fileAbsolutePath = $moduleDir . '/Files/Sample/' . $fileName; $directoryRead = $this->readFactory->create($moduleDir); $filePath = $directoryRead->getRelativePath($fileAbsolutePath); diff --git a/app/code/Magento/ImportExport/Files/Sample/catalog_product.csv b/app/code/Magento/ImportExport/Files/Sample/catalog_product.csv index 488c1f0894b2615eceea828215b854b5e3526550..8d9eaaa5e9a41447dd57fd55f82f927ea13ff1ee 100644 --- a/app/code/Magento/ImportExport/Files/Sample/catalog_product.csv +++ b/app/code/Magento/ImportExport/Files/Sample/catalog_product.csv @@ -1,8 +1,7 @@ -sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,custom_options,configurable_variations,configurable_variation_prices,configurable_variation_labels,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values -24-WG085,,Sprite Yoga Strap,simple,Default Category/Gear|Default Category/Gear/Fitness Equipment,base,Sprite Yoga Strap 6 foot,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and urable under strain.</p><ul><li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,,1,Taxable Goods,"Catalog, Search",14,,,,sprite-yoga-strap-6-foot,Meta Title,"meta1, meta2, meta3",meta description,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,01.07.2015 15:38,01.07.2015 15:38,,,Block after Info Column,,,,,,,,,,,,,"has_options=0,required_options=0,size_strap=6 foot",100,0,1,0,0,1,1,1,0,1,1,,1,0,1,1,0,1,0,0,1,"24-WG086,24-WG087","24-WG086,24-WG087","24-WG086,24-WG087","/sample_data/l/u/luma-yoga-strap.jpg,/sample_data/l/u/luma-yoga-strap.jpg","Image,Image","name=Custom Yoga Option,type=drop_down,required=0,price=10.0000,price_type=fixed,sku=,option_title=Gold|name=Custom Yoga Option,type=drop_down,required=0,price=10.0000,price_type=fixed,sku=,option_title=Silver|name=Custom Yoga Option,type=drop_down,required=0,price=10.0000,price_type=fixed,sku=yoga3sku,option_title=Platinum",,,,,,,, -24-WG086,,Sprite Yoga Strap,simple,Default Category/Gear|Default Category/Gear/Fitness Equipment,base,Sprite Yoga Strap 8 foot,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>8' long x 1.0"" wide.<li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,,1,Taxable Goods,"Catalog, Search",17,,,,sprite-yoga-strap-8-foot,Meta Title,"meta1, meta2, meta4",meta description,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,01.07.2015 15:38,01.07.2015 15:38,,,Block after Info Column,,,,,,,,,,,,,"has_options=0,required_options=0,size_strap=8 foot",100,0,1,0,0,1,1,1,0,1,1,,1,0,1,1,0,1,0,0,1,"24-WG086,24-WG087","24-WG086,24-WG087","24-WG086,24-WG087","/sample_data/l/u/luma-yoga-strap.jpg,/sample_data/l/u/luma-yoga-strap.jpg","Image,Image",,,,,,,,, -24-WG087,,Sprite Yoga Strap,simple,Default Category/Gear|Default Category/Gear/Fitness Equipment,base,Sprite Yoga Strap 10 foot,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>10' long x 1.0"" wide.<li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,,1,Taxable Goods,"Catalog, Search",21,,,,sprite-yoga-strap-10-foot,Meta Title,"meta1, meta2, meta5",meta description,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,01.07.2015 15:39,01.07.2015 15:39,,,Block after Info Column,,,,,,,,,,,,,"has_options=0,required_options=0,size_strap=10 foot",100,0,1,0,0,1,1,1,0,1,1,,1,0,1,1,0,1,0,0,1,"24-WG086,24-WG087","24-WG086,24-WG087","24-WG086,24-WG087","/sample_data/l/u/luma-yoga-strap.jpg,/sample_data/l/u/luma-yoga-strap.jpg","Image,Image",,,,,,,,, -24-WG085_Group,,Gear,grouped,Default Category/Gear|Default Category/Gear/Fitness Equipment,base,Set of Sprite Yoga Straps,"<p>Great set of Sprite Yoga Straps for every stretch and hold you need. There are three straps in this set: 6', 8' and 10'.</p><ul><li> 100% soft and durable cotton.<li> Plastic cinch buckle is easy to use.<li> Choice of three natural colors made from phthalate and heavy metal free dyes.</ul>",,,1,,"Catalog, Search",,,,,set-of-sprite-yoga-straps,Meta Title,"meta1, meta2, meta6",meta description,/sample_data/l/u/luma-yoga-strap-set.jpg,Image Label,/sample_data/l/u/luma-yoga-strap-set.jpg,Image Label,/sample_data/l/u/luma-yoga-strap-set.jpg,Image Label,01.07.2015 15:39,01.07.2015 15:39,,,Block after Info Column,,,,,,,,,,,,,"has_options=0,required_options=0",0,0,1,0,0,1,1,1,0,1,1,,1,0,1,1,0,1,0,0,1,"24-WG086,24-WG087","24-WG086,24-WG087","24-WG086,24-WG087",/sample_data/l/u/luma-yoga-strap-set.jpg,"Image,Image",,,,,,,,, -24-WG085-configurable,,Sprite Yoga Strap,configurable,Default Category/Gear|Default Category/Gear/Fitness Equipment,base,Sprite Yoga Strap,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,,1,Taxable Goods,"Catalog, Search",14,,,,sprite-yoga-strap1,Meta Title,"meta1, meta2, meta7",meta description,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,01.07.2015 16:15,01.07.2015 16:15,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,required_options=1",0,0,1,0,0,1,1,1,0,1,1,,1,0,1,1,0,1,0,0,1,"24-WG086,24-WG087","24-WG086,24-WG087","24-WG086,24-WG087",/sample_data/l/u/luma-yoga-strap.jpg,"Image,Image",,"sku=24-WG086,size_strap=8 foot|sku=24-WG087,size_strap=10 foot|sku=24-WG085,size_strap=6 foot","name=size_strap,value=8 foot,price=3.0000,price_type=fixed|name=size_strap,value=10 foot,price=7.0000,price_type=fixed|name=size_strap,value=6 foot,price=,price_type=fixed",size_strap=Size Strap,,,,, -24-WG085-bundle-dynamic,,Sprite Yoga Strap,bundle,Default Category/Gear|Default Category/Gear/Fitness Equipment,base,Sprite Yoga Strap,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,,1,Taxable Goods,"Catalog, Search",14,,,,sprite-yoga-strap2,Meta Title,"meta1, meta2, meta8",meta description,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,01.07.2015 16:15,01.07.2015 16:15,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,required_options=1",0,0,1,0,0,1,1,1,0,1,1,,1,0,1,1,0,1,0,0,1,"24-WG086,24-WG087","24-WG086,24-WG087","24-WG086,24-WG087",/sample_data/l/u/luma-yoga-strap.jpg,"Image,Image",,,,,dynamic,dynamic,Price Range,fixed,"name=Bundle Option One1,type=dropdown,required=1,sku=bunsimplesku30,price=15; price_type=fixed, default_qty=1, is_defaul=0 | name=Bundle Option One1,type=dropdown; required=1, sku=bunsimplesku31,price=10, price_type=fixed, default_qty=1, is_defaul=1" -24-WG085-bundle-fixed,,Sprite Yoga Strap,bundle,Default Category/Gear|Default Category/Gear/Fitness Equipment,base,Sprite Yoga Strap,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,,1,Taxable Goods,"Catalog, Search",14,,,,sprite-yoga-strap3,Meta Title,"meta1, meta2, meta9",meta description,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,/sample_data/l/u/luma-yoga-strap.jpg,Image Label,01.07.2015 16:15,01.07.2015 16:15,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,required_options=1",0,0,1,0,0,1,1,1,0,1,1,,1,0,1,1,0,1,0,0,1,"24-WG086,24-WG087","24-WG086,24-WG087","24-WG086,24-WG087",/sample_data/l/u/luma-yoga-strap.jpg,"Image,Image",,,,,fixed,fixed,Price Range,fixed,"name=Yoga Strap,type=radiobutton,required=1,sku=24-WG086,default_qty=3,default=1 | name=Yoga Strap,type=radiobutton,required=1,sku=24-WG085,default_qty=3,default=0" +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,deferred_stock_update,use_config_deferred_stock_update,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus +24-WG085,,Default,simple,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,"Sprite Yoga Strap 6 foot","<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and urable under strain.</p><ul><li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,1.0000,1,"Taxable Goods","Catalog, Search",14.0000,,,,sprite-yoga-strap-6-foot,"Meta Title","meta1, meta2, meta3","meta description",/l/u/luma-yoga-strap.jpg,"Image Label",/l/u/luma-yoga-strap.jpg,"Image Label",/l/u/luma-yoga-strap.jpg,"Image Label","2015-10-25 03:34:19","2015-10-25 03:34:20",,,"Block after Info Column",,,,,,,,,,,"Use config",,"has_options=1,is_returnable=Use config,quantity_and_stock_status=In Stock,required_options=0",100.0000,0.0000,1,0,0,1,1.0000,0,0.0000,1,1,,1,0,1,1,0.0000,1,0,0,1,0,1,"24-WG087,24-WG086","24-WG087,24-WG086","24-WG087,24-WG086",/l/u/luma-yoga-strap.jpg,Image,,"name=Custom Yoga Option,type=drop_down,required=0,price=10.0000,price_type=fixed,sku=,option_title=Gold|name=Custom Yoga Option,type=drop_down,required=0,price=10.0000,price_type=fixed,sku=,option_title=Silver|name=Custom Yoga Option,type=drop_down,required=0,price=10.0000,price_type=fixed,sku=yoga3sku,option_title=Platinum",,,,,, +24-WG086,,Default,simple,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,"Sprite Yoga Strap 8 foot","<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>8' long x 1.0"" wide.<li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,1.0000,1,"Taxable Goods","Catalog, Search",17.0000,,,,sprite-yoga-strap-8-foot,"Meta Title","meta1, meta2, meta4","meta description",/l/u/luma-yoga-strap.jpg,"Image Label",/l/u/luma-yoga-strap.jpg,"Image Label",/l/u/luma-yoga-strap.jpg,"Image Label","2015-10-25 03:34:20","2015-10-25 03:34:20",,,"Block after Info Column",,,,,,,,,,,"Use config",,"has_options=0,is_returnable=Use config,quantity_and_stock_status=In Stock,required_options=0",100.0000,0.0000,1,0,0,1,1.0000,0,0.0000,1,1,,1,0,1,1,0.0000,1,0,0,1,0,1,24-WG087,24-WG087,24-WG087,/l/u/luma-yoga-strap.jpg,Image,,,,,,,, +24-WG087,,Default,simple,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,"Sprite Yoga Strap 10 foot","<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>10' long x 1.0"" wide.<li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,1.0000,1,"Taxable Goods","Catalog, Search",21.0000,,,,sprite-yoga-strap-10-foot,"Meta Title","meta1, meta2, meta5","meta description",/l/u/luma-yoga-strap.jpg,"Image Label",/l/u/luma-yoga-strap.jpg,"Image Label",/l/u/luma-yoga-strap.jpg,"Image Label","2015-10-25 03:34:20","2015-10-25 03:34:20",,,"Block after Info Column",,,,,,,,,,,"Use config",,"has_options=0,is_returnable=Use config,quantity_and_stock_status=In Stock,required_options=0",100.0000,0.0000,1,0,0,1,1.0000,0,0.0000,1,1,,1,0,1,1,0.0000,1,0,0,1,0,1,24-WG086,24-WG086,24-WG086,/l/u/luma-yoga-strap.jpg,Image,,,,,,,, +24-WG085_Group,,Default,grouped,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,"Set of Sprite Yoga Straps","<p>Great set of Sprite Yoga Straps for every stretch and hold you need. There are three straps in this set: 6', 8' and 10'.</p><ul><li>100% soft and durable cotton.</li><li>Plastic cinch buckle is easy to use.</li><li>Choice of three natural colors made from phthalate and heavy metal free dyes.</li></ul>",,,1,,"Catalog, Search",,,,,set-of-sprite-yoga-straps,"Meta Title","meta1, meta2, meta6","meta description",/l/u/luma-yoga-strap-set.jpg,Image,/l/u/luma-yoga-strap-set.jpg,Image,/l/u/luma-yoga-strap-set.jpg,Image,"2015-10-25 03:34:20","2015-10-25 03:36:31",,,"Block after Info Column",,,,,,,,,,,,,"has_options=0,is_returnable=Use config,quantity_and_stock_status=In Stock,required_options=0",0.0000,0.0000,1,0,0,1,1.0000,0,0.0000,1,1,,1,1,1,1,0.0000,1,0,0,1,0,1,"24-WG087,24-WG086","24-WG087,24-WG086","24-WG087,24-WG086",/l/u/luma-yoga-strap-set.jpg,Image,,,,,,,,"24-WG085=5.0000,24-WG086=5.0000" +24-WG085-bundle-dynamic,,Default,bundle,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,"Sprite Yoga Strap Dynamic Bundle","<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,1.0000,1,"Taxable Goods","Catalog, Search",14.0000,,,,sprite-yoga-strap2,"Meta Title","meta1, meta2, meta8","meta description",/l/u/luma-yoga-strap.jpg,,/l/u/luma-yoga-strap.jpg,,/l/u/luma-yoga-strap.jpg,,"2015-10-25 03:34:20","2015-10-25 03:34:20",,,"Block after Info Column",,,,,,,,,,,"Use config",,"has_options=1,is_returnable=Use config,quantity_and_stock_status=In Stock,required_options=0",0.0000,0.0000,1,0,0,1,1.0000,0,0.0000,1,1,,1,0,1,1,0.0000,1,0,0,1,0,1,"24-WG087,24-WG086","24-WG087,24-WG086","24-WG087,24-WG086",/l/u/luma-yoga-strap.jpg,Image,,,dynamic,dynamic,"Price range",fixed,"name=Bundle Option One1,type=select,required=1,sku=24-WG085,price=15.0000,default=0,default_qty=1.0000,price_type=fixed|name=Bundle Option One1,type=select,required=1,sku=24-WG086,price=10.0000,default=1,default_qty=1.0000,price_type=fixed", +24-WG085-bundle-fixed,,Default,bundle,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,"Sprite Yoga Strap Fixed Bundle","<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,1.0000,1,"Taxable Goods","Catalog, Search",14.0000,,,,sprite-yoga-strap3,"Meta Title","meta1, meta2, meta9","meta description",/l/u/luma-yoga-strap.jpg,,/l/u/luma-yoga-strap.jpg,,/l/u/luma-yoga-strap.jpg,,"2015-10-25 03:34:20","2015-10-25 03:34:20",,,"Block after Info Column",,,,,,,,,,,"Use config",,"has_options=1,is_returnable=Use config,quantity_and_stock_status=In Stock,required_options=0",0.0000,0.0000,1,0,0,1,1.0000,0,0.0000,1,1,,1,0,1,1,0.0000,1,0,0,1,0,1,"24-WG087,24-WG086","24-WG087,24-WG086","24-WG087,24-WG086",/l/u/luma-yoga-strap.jpg,Image,,,fixed,fixed,"Price range",fixed,"name=Yoga Strap,type=radio,required=1,sku=24-WG086,price=0.0000,default=1,default_qty=3.0000,price_type=percent|name=Yoga Strap,type=radio,required=1,sku=24-WG085,price=0.0000,default=0,default_qty=3.0000,price_type=percent", diff --git a/app/code/Magento/ImportExport/Model/Import.php b/app/code/Magento/ImportExport/Model/Import.php index 3742d5e54f3e3120ce59ab2cfd23c722dd3d9d7c..1d9984df7944d48824226beed2fb1973f5e3d173 100644 --- a/app/code/Magento/ImportExport/Model/Import.php +++ b/app/code/Magento/ImportExport/Model/Import.php @@ -80,6 +80,11 @@ class Import extends \Magento\ImportExport\Model\AbstractModel /**#@-*/ + /** + * default delimiter for several values in one cell as default for FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR + */ + const DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR = ','; + /**#@+ * Import constants */ diff --git a/app/code/Magento/ImportExport/Model/Import/AbstractEntity.php b/app/code/Magento/ImportExport/Model/Import/AbstractEntity.php index 085c9261e341229fb19bebc078874f87e9dff561..5edac8699319617b7fa169e5c51ce86dc25139d5 100644 --- a/app/code/Magento/ImportExport/Model/Import/AbstractEntity.php +++ b/app/code/Magento/ImportExport/Model/Import/AbstractEntity.php @@ -69,7 +69,7 @@ abstract class AbstractEntity self::ERROR_CODE_COLUMNS_NUMBER => "Number of columns does not correspond to the number of rows in the header", self::ERROR_EXCEEDED_MAX_LENGTH => 'Attribute %s exceeded max length', self::ERROR_INVALID_ATTRIBUTE_TYPE => - 'Value for \'%s\' attribute contains incorrect value, acceptable values are in %s format', + 'Value for \'%s\' attribute contains incorrect value', self::ERROR_INVALID_ATTRIBUTE_OPTION => "Value for %s attribute contains incorrect value, see acceptable values on settings specified for Admin", ]; diff --git a/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregator.php b/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregator.php index acb19a9847212e0459c5c7a937044ab3fb3bbc8a..e6df28446fbf0e74026d2c8d120e06ed78dd20a3 100644 --- a/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregator.php +++ b/app/code/Magento/ImportExport/Model/Import/ErrorProcessing/ProcessingErrorAggregator.php @@ -81,7 +81,9 @@ class ProcessingErrorAggregator implements ProcessingErrorAggregatorInterface return $this; } $this->processErrorStatistics($errorLevel); - $this->processInvalidRow($rowNumber); + if ($errorLevel == ProcessingError::ERROR_LEVEL_CRITICAL) { + $this->processInvalidRow($rowNumber); + } $errorMessage = $this->getErrorMessage($errorCode, $errorMessage, $columnName); /** @var ProcessingError $newError */ diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Import/ErrorProcessing/ProcessingErrorAggregatorTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Import/ErrorProcessing/ProcessingErrorAggregatorTest.php index 63b82e3693d2d7a1ee524f5aaa33c77a9e7950ae..090370a728a68b97d6b64499fe5569d7ccf5fe8c 100644 --- a/app/code/Magento/ImportExport/Test/Unit/Model/Import/ErrorProcessing/ProcessingErrorAggregatorTest.php +++ b/app/code/Magento/ImportExport/Test/Unit/Model/Import/ErrorProcessing/ProcessingErrorAggregatorTest.php @@ -5,6 +5,8 @@ */ namespace Magento\ImportExport\Test\Unit\Model\Import\ErrorProcessing; +use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError; + class ProcessingErrorAggregatorTest extends \PHPUnit_Framework_TestCase { /** @@ -124,22 +126,26 @@ class ProcessingErrorAggregatorTest extends \PHPUnit_Framework_TestCase /** * Test for method isRowInvalid. Expected true result. + * @dataProvider isRowInvalidDataProvider */ - public function testIsRowInvalidTrue() + public function testIsRowInvalid($errorLevel, $rowNumber, $isValid) { - $this->model->addError('systemException', 'critical', 7, 'Some column name', 'Message', 'Description'); - $result = $this->model->isRowInvalid(7); - $this->assertTrue($result); + $this->model->addError('systemException', $errorLevel, $rowNumber, 'Column name', 'Message', 'Description'); + $result = $this->model->isRowInvalid($rowNumber); + $this->assertEquals($isValid, $result); } /** - * Test for method isRowInvalid. Expected false result. + * @return array */ - public function testIsRowInvalidFalse() + public function isRowInvalidDataProvider() { - $this->model->addError('systemException'); - $result = $this->model->isRowInvalid(8); - $this->assertFalse($result); + return [ + [ProcessingError::ERROR_LEVEL_CRITICAL, 7, true], + [ProcessingError::ERROR_LEVEL_NOT_CRITICAL, 8, false], + [ProcessingError::ERROR_LEVEL_NOTICE, 9, false], + [ProcessingError::ERROR_LEVEL_WARNING, 10, false] + ]; } /** diff --git a/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/after.phtml b/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/after.phtml index 73eacb73e7869479b1e1c88bf37ae481d5313c4c..e363b6fe0d017d628ed7019044dd8e0fbbc23e56 100644 --- a/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/after.phtml +++ b/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/after.phtml @@ -14,7 +14,7 @@ <div id="import_validation_messages" class="fieldset"><!-- --></div> </div> <script> -require(['jquery', 'prototype'], function(jQuery){ +require(['jquery', 'Magento_Ui/js/modal/alert', 'prototype'], function(jQuery){ //<![CDATA[ varienImport.resetSelectIndex('entity'); // forced resetting entity selector after page refresh //]]> diff --git a/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php b/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php index 667da81dd04e1494ffb875185b796fc6880a74f9..022824faafbd41faf61b1539f2939ee0ba0277bd 100644 --- a/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php +++ b/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php @@ -66,8 +66,9 @@ class BuiltinPlugin ) { $result = $proceed($response); $usePlugin = $this->registry->registry('use_page_cache_plugin'); - if (!$this->config->isEnabled() || $this->config->getType() != \Magento\PageCache\Model\Config::BUILT_IN - || !$usePlugin) { + if (!$usePlugin || !$this->config->isEnabled() + || $this->config->getType() != \Magento\PageCache\Model\Config::BUILT_IN + ) { return $result; } @@ -76,6 +77,16 @@ class BuiltinPlugin $response->setHeader('X-Magento-Cache-Control', $cacheControl); $response->setHeader('X-Magento-Cache-Debug', 'MISS', true); } + + $tagsHeader = $response->getHeader('X-Magento-Tags'); + $tags = []; + if ($tagsHeader) { + $tags = explode(',', $tagsHeader->getFieldValue()); + $response->clearHeader('X-Magento-Tags'); + } + $tags = array_unique(array_merge($tags, [\Magento\PageCache\Model\Cache\Type::CACHE_TAG])); + $response->setHeader('X-Magento-Tags', implode(',', $tags)); + $this->kernel->process($response); return $result; } diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php index a3542bdc708cf1c7d7dcbfc9fb8d91c6bb626f41..2ae5fa1f9f1e54491c0f00648bab3ed303d5cded 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php @@ -11,83 +11,133 @@ namespace Magento\PageCache\Test\Unit\Model\Controller\Result; class BuiltinPluginTest extends \PHPUnit_Framework_TestCase { /** - * @param bool $usePlugin - * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $getHeaderCount - * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $setCacheControlHeaderCount - * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $setCacheDebugHeaderCount - * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $getModeCount - * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $processCount - * @dataProvider dataProvider + * @var \Magento\PageCache\Model\Controller\Result\BuiltinPlugin */ - public function testAroundResult( - $usePlugin, $getHeaderCount, $setCacheControlHeaderCount, $setCacheDebugHeaderCount, $getModeCount, - $processCount - ) { - $cacheControl = 'test'; - - $header = $this->getMockBuilder('Zend\Http\Header\HeaderInterface') - ->getMockForAbstractClass(); - $header->expects($this->any())->method('getFieldValue') - ->willReturn($cacheControl); - - $response = $this->getMock('Magento\Framework\App\Response\Http', [], [], '', false); - $response->expects($getHeaderCount) - ->method('getHeader') - ->with('Cache-Control') - ->willReturn($header); - $response->expects($setCacheControlHeaderCount)->method('setHeader') - ->with('X-Magento-Cache-Control', $cacheControl); - $response->expects($setCacheDebugHeaderCount)->method('setHeader') - ->with('X-Magento-Cache-Debug', 'MISS', true); - - /** @var \Magento\Framework\Controller\ResultInterface $result */ + protected $plugin; + + /** + * @var \Magento\Framework\Controller\ResultInterface + */ + protected $subject; + + /** + * @var \Closure + */ + protected $closure; + + /** + * @var \Magento\Framework\App\Response\Http|\PHPUnit_Framework_MockObject_MockObject + */ + protected $response; + + /** + * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject + */ + protected $registry; + + /** + * @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject + */ + protected $state; + + /** + * @var \Zend\Http\Header\HeaderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $header; + + /** + * @var \Magento\Framework\App\PageCache\Kernel|\PHPUnit_Framework_MockObject_MockObject + */ + protected $kernel; + + protected function setUp() + { $result = $this->getMock('Magento\Framework\Controller\ResultInterface', [], [], '', false); - $closure = function () use ($result) { + $this->closure = function() use ($result) { return $result; }; - /** @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject $registry */ - $registry = $this->getMock('Magento\Framework\Registry', [], [], '', false); - $registry->expects($this->once())->method('registry')->with('use_page_cache_plugin') - ->will($this->returnValue($usePlugin)); - - /** @var \Magento\PageCache\Model\Config|\PHPUnit_Framework_MockObject_MockObject $config */ - $config = $this->getMock('Magento\PageCache\Model\Config', [], [], '', false); - $config->expects($this->once())->method('isEnabled')->will($this->returnValue(true)); - $config->expects($this->once())->method('getType') - ->will($this->returnValue(\Magento\PageCache\Model\Config::BUILT_IN)); + $this->header = $this->getMock('Zend\Http\Header\HeaderInterface', [], [], '', false); + $this->subject = $this->getMock('Magento\Framework\Controller\ResultInterface', [], [], '', false); + $this->response = $this->getMock( + 'Magento\Framework\App\Response\Http', + ['getHeader', 'clearHeader', 'setHeader'], + [], + '', + false + ); + $this->response->expects($this->any())->method('getHeader')->willReturnMap( + [ + ['X-Magento-Tags', $this->header], + ['Cache-Control', $this->header] + ] + ); - /** @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject $state */ - $state = $this->getMock('Magento\Framework\App\State', [], [], '', false); - $state->expects($getModeCount)->method('getMode') - ->will($this->returnValue(\Magento\Framework\App\State::MODE_DEVELOPER)); + $this->registry = $this->getMock('Magento\Framework\Registry', [], [], '', false); - $kernel = $this->getMock('Magento\Framework\App\PageCache\Kernel', [], [], '', false); - $kernel->expects($processCount)->method('process')->with($response); + $config = $this->getMock('Magento\PageCache\Model\Config', ['isEnabled', 'getType'], [], '', false); + $config->expects($this->any())->method('isEnabled')->willReturn(true); + $config->expects($this->any())->method('getType')->willReturn(\Magento\PageCache\Model\Config::BUILT_IN); - $subject = $this->getMock('Magento\Framework\Controller\ResultInterface', [], [], '', false); + $this->kernel = $this->getMock('Magento\Framework\App\PageCache\Kernel', [], [], '', false); - /** @var \Magento\PageCache\Model\Controller\Result\BuiltinPlugin $plugin */ - $plugin = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject( + $this->state = $this->getMock('Magento\Framework\App\State', [], [], '', false); + $this->plugin = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject( 'Magento\PageCache\Model\Controller\Result\BuiltinPlugin', [ - 'registry' => $registry, + 'registry' => $this->registry, 'config' => $config, - 'kernel' => $kernel, - 'state' => $state + 'kernel' => $this->kernel, + 'state' => $this->state ] ); - $this->assertSame($result, $plugin->aroundRenderResult($subject, $closure, $response)); } - /** - * @return array - */ - public function dataProvider() + public function testAroundResultWithoutPlugin() { - return [ - [true, $this->once(), $this->at(1), $this->at(2), $this->once(), $this->once()], - [false, $this->never(), $this->never(), $this->never(), $this->never(), $this->never()] - ]; + $this->registry->expects($this->once())->method('registry')->with('use_page_cache_plugin')->willReturn(false); + $this->kernel->expects($this->never())->method('process')->with($this->response); + $this->assertSame( + call_user_func($this->closure), + $this->plugin->aroundRenderResult($this->subject, $this->closure, $this->response) + ); + } + + public function testAroundResultWithPlugin() + { + $this->registry->expects($this->once())->method('registry')->with('use_page_cache_plugin')->willReturn(true); + $this->state->expects($this->once())->method('getMode')->willReturn(null); + $this->header->expects($this->any())->method('getFieldValue')->willReturn('tag,tag'); + $this->response->expects($this->once())->method('clearHeader')->with('X-Magento-Tags'); + $this->response->expects($this->once())->method('setHeader')->with( + 'X-Magento-Tags', + 'tag,' . \Magento\PageCache\Model\Cache\Type::CACHE_TAG + ); + $this->kernel->expects($this->once())->method('process')->with($this->response); + $result = call_user_func($this->closure); + $this->assertSame($result, $this->plugin->aroundRenderResult($this->subject, $this->closure, $this->response)); + } + + public function testAroundResultWithPluginDeveloperMode() + { + $this->registry->expects($this->once())->method('registry')->with('use_page_cache_plugin')->willReturn(true); + $this->state->expects($this->once())->method('getMode') + ->willReturn(\Magento\Framework\App\State::MODE_DEVELOPER); + + $this->header->expects($this->any())->method('getFieldValue')->willReturnOnConsecutiveCalls('test', 'tag,tag2'); + + $this->response->expects($this->any())->method('setHeader')->withConsecutive( + ['X-Magento-Cache-Control', 'test'], + ['X-Magento-Cache-Debug', 'MISS', true], + ['X-Magento-Tags', 'tag,tag2,' . \Magento\PageCache\Model\Cache\Type::CACHE_TAG] + ); + + $this->response->expects($this->once())->method('clearHeader')->with('X-Magento-Tags'); + $this->registry->expects($this->once())->method('registry')->with('use_page_cache_plugin') + ->will($this->returnValue(true)); + $this->kernel->expects($this->once())->method('process')->with($this->response); + + $result = call_user_func($this->closure); + $this->assertSame($result, $this->plugin->aroundRenderResult($this->subject, $this->closure, $this->response)); } } diff --git a/app/code/Magento/Reports/Model/ResourceModel/Review/Product/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Review/Product/Collection.php index 982219690499708d18323152f87485215b55f02d..662047abeca66f983f40e48debae4209e72fe2f9 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Review/Product/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Review/Product/Collection.php @@ -58,8 +58,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection $this->getConnection()->quoteInto('table_rating.store_id > ?', 0), ]; - $percentField = $this->getConnection()->quoteIdentifier('table_rating.percent'); - $sumPercentField = new \Zend_Db_Expr("SUM({$percentField})"); + $sumPercentField = new \Zend_Db_Expr("SUM(table_rating.percent)"); $sumPercentApproved = new \Zend_Db_Expr('SUM(table_rating.percent_approved)'); $countRatingId = new \Zend_Db_Expr('COUNT(table_rating.rating_id)'); @@ -67,8 +66,8 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection ['table_rating' => $this->getTable('rating_option_vote_aggregated')], implode(' AND ', $joinCondition), [ - 'avg_rating' => sprintf('%s/%s', $sumPercentField, $countRatingId), - 'avg_rating_approved' => sprintf('%s/%s', $sumPercentApproved, $countRatingId) + 'avg_rating' => new \Zend_Db_Expr(sprintf('%s/%s', $sumPercentField, $countRatingId)), + 'avg_rating_approved' => new \Zend_Db_Expr(sprintf('%s/%s', $sumPercentApproved, $countRatingId)) ] ); diff --git a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/js.phtml b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/js.phtml index 0f911d563f714492a0700f50cdbb1746683f3fbe..c407178e0be985ed528df155a1259b527d584b27 100644 --- a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/js.phtml +++ b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/js.phtml @@ -15,248 +15,260 @@ require([ 'Magento_Ui/js/modal/prompt', "collapsable", "prototype" -], function(jQuery, rg, alert, prompt){ - -function toggleApplyVisibility(select) { - if ($(select).value == 1) { - $(select).next('select').removeClassName('no-display'); - $(select).next('select').removeClassName('ignore-validate'); - - } else { - $(select).next('select').addClassName('no-display'); - $(select).next('select').addClassName('ignore-validate'); - var options = $(select).next('select').options; - for( var i=0; i < options.length; i++) { - options[i].selected = false; - } - } -} -function getFrontTab() { - if ($('product_attribute_tabs_front')) { - return $('product_attribute_tabs_front').up('li'); - } else { - return $('front_fieldset-wrapper'); - } -} +], function (jQuery, rg, alert, prompt) { + var frontendInput = $('frontend_input'); -function checkOptionsPanelVisibility(){ - if($('manage-options-panel')){ - var panel = $('manage-options-panel').up('.fieldset'); + function toggleApplyVisibility (select) { + if ($(select).value == 1) { + $(select).next('select').removeClassName('no-display'); + $(select).next('select').removeClassName('ignore-validate'); - if($('frontend_input') && ($('frontend_input').value=='select' || $('frontend_input').value=='multiselect')){ - panel.show(); - rg.get('manage-options-panel', function() { - jQuery('#manage-options-panel').trigger('render'); - }); - } - else { - panel.hide(); - } - } - if($('swatch-visual-options-panel')){ - var panel = $('swatch-visual-options-panel').up('.fieldset'); - - if($('frontend_input') && $('frontend_input').value=='swatch_visual') { - panel.show(); - rg.get('swatch-visual-options-panel', function() { - jQuery('#swatch-visual-options-panel').trigger('render'); - }); - } - else { - panel.hide(); + } else { + $(select).next('select').addClassName('no-display'); + $(select).next('select').addClassName('ignore-validate'); + var options = $(select).next('select').options; + for( var i=0; i < options.length; i++) { + options[i].selected = false; + } } } - if($('swatch-text-options-panel')){ - var panel = $('swatch-text-options-panel').up('.fieldset'); - - if($('frontend_input') && $('frontend_input').value=='swatch_text') { - panel.show(); - rg.get('swatch-text-options-panel', function() { - jQuery('#swatch-text-options-panel').trigger('render'); - }); - } - else { - panel.hide(); + function getFrontTab () { + var tabsFront = $('product_attribute_tabs_front'); + if (tabsFront) { + return tabsFront.up('li'); + } else { + return $('front_fieldset-wrapper'); } } -} - -function bindAttributeInputType() -{ - checkOptionsPanelVisibility(); - switchDefaultValueField(); - if($('frontend_input') && ($('frontend_input').value=='select' || $('frontend_input').value=='multiselect' || $('frontend_input').value=='price' - || $('frontend_input').value=='swatch_text' || $('frontend_input').value=='swatch_visual')){ - if($('is_filterable') && !$('is_filterable').getAttribute('readonly')){ - $('is_filterable').disabled = false; + + function checkOptionsPanelVisibility () { + var optionsPanel = $('manage-options-panel'), + visualOptionsPanel = $('swatch-visual-options-panel'), + textOptionsPanel = $('swatch-text-options-panel'); + if (optionsPanel) { + var panel = optionsPanel.up('.fieldset'); + + if (frontendInput && (frontendInput.value=='select' || frontendInput.value=='multiselect')) { + panel.show(); + rg.get('manage-options-panel', function () { + jQuery('#manage-options-panel').trigger('render'); + }); + } else { + panel.hide(); + } } - if($('is_filterable_in_search') && !$('is_filterable_in_search').getAttribute('readonly')){ - $('is_filterable_in_search').disabled = false; + if (visualOptionsPanel) { + var visualPanel = visualOptionsPanel.up('.fieldset'); + + if (frontendInput && frontendInput.value=='swatch_visual') { + visualPanel.show(); + rg.get('swatch-visual-options-panel', function () { + jQuery('#swatch-visual-options-panel').trigger('render'); + }); + } else { + visualPanel.hide(); + } } - if($('backend_type') && $('backend_type').options){ - for(var i=0;i<$('backend_type').options.length;i++){ - if($('backend_type').options[i].value=='int') $('backend_type').selectedIndex = i; + if (textOptionsPanel) { + var textPanel = textOptionsPanel.up('.fieldset'); + + if (frontendInput && frontendInput.value=='swatch_text') { + textPanel.show(); + rg.get('swatch-text-options-panel', function () { + jQuery('#swatch-text-options-panel').trigger('render'); + }); + } else { + textPanel.hide(); } } } - else { - if($('is_filterable')){ - $('is_filterable').selectedIndex=0; - $('is_filterable').disabled = true; - } - if($('is_filterable_in_search')){ - $('is_filterable_in_search').disabled = true; + + function bindAttributeInputType () { + var isFilterable = $('is_filterable'), + isFilterableInSearch = $('is_filterable_in_search'), + backendType = $('backend_type'), + usedForSortBy = $('used_for_sort_by'), + frontendClass = $('frontend_class'), + selectFields = ['select', 'multiselect', 'price', 'swatch_text', 'swatch_visual']; + + checkOptionsPanelVisibility(); + switchDefaultValueField(); + if (frontendInput + && jQuery.inArray(frontendInput.value, selectFields) >= 0 + ) { + if (isFilterable && !isFilterable.getAttribute('readonly')) { + isFilterable.disabled = false; + } + if (isFilterableInSearch && !isFilterableInSearch.getAttribute('readonly')) { + isFilterableInSearch.disabled = false; + } + if (backendType && backendType.options) { + for (var i=0; i<backendType.options.length; i++) { + if (backendType.options[i].value=='int') { + backendType.selectedIndex = i; + } + } + } + } else { + if (isFilterable) { + isFilterable.selectedIndex=0; + isFilterable.disabled = true; + } + if (isFilterableInSearch) { + isFilterableInSearch.disabled = true; + } } - } - if ($('frontend_input') && !$('frontend_input').disabled - && ($('frontend_input').value=='swatch_text' || $('frontend_input').value=='swatch_visual') - ) { - $('used_in_product_listing').value = 1; - $('is_visible_on_front').value = 1; - $('update_product_preview_image').value = 1; - } + if (frontendInput && !frontendInput.disabled + && (frontendInput.value=='swatch_text' || frontendInput.value=='swatch_visual') + ) { + $('used_in_product_listing').value = 1; + $('is_visible_on_front').value = 1; + $('update_product_preview_image').value = 1; + } - if ($('frontend_input') && ($('frontend_input').value=='multiselect' - || $('frontend_input').value=='gallery' - || $('frontend_input').value=='textarea')) { - if ($('used_for_sort_by')) { - $('used_for_sort_by').disabled = true; + if (frontendInput && (frontendInput.value=='multiselect' + || frontendInput.value=='gallery' + || frontendInput.value=='textarea')) { + if (usedForSortBy) { + usedForSortBy.disabled = true; + } + } else { + if (usedForSortBy && !usedForSortBy.getAttribute('readonly')) { + usedForSortBy.disabled = false; + } } - } - else { - if ($('used_for_sort_by') && !$('used_for_sort_by').getAttribute('readonly')) { - $('used_for_sort_by').disabled = false; + + if (jQuery('#frontend_input').val() == 'swatch_text') { + jQuery('.swatch-text-field-0').addClass('required-option'); + } else { + jQuery('.swatch-text-field-0').removeClass('required-option'); } - } - if (jQuery('#frontend_input').val() == 'swatch_text') { - jQuery('.swatch-text-field-0').addClass('required-option'); - } else { - jQuery('.swatch-text-field-0').removeClass('required-option'); - } + setRowVisibility('is_wysiwyg_enabled', false); + setRowVisibility('is_html_allowed_on_front', false); - setRowVisibility('is_wysiwyg_enabled', false); - setRowVisibility('is_html_allowed_on_front', false); + switch (frontendInput.value) { + case 'textarea': + setRowVisibility('is_wysiwyg_enabled', true); + if ($('is_wysiwyg_enabled').value == '0') { + setRowVisibility('is_html_allowed_on_front', true); + $('is_html_allowed_on_front').disabled = false; + } + frontendClass.value = ''; + frontendClass.disabled = true; + break; + case 'text': + setRowVisibility('is_html_allowed_on_front', true); + $('is_html_allowed_on_front').disabled = false; - switch ($('frontend_input').value) { - case 'textarea': - setRowVisibility('is_wysiwyg_enabled', true); - if($('is_wysiwyg_enabled').value == '0'){ + if (!frontendClass.getAttribute('readonly')) { + frontendClass.disabled = false; + } + break; + case 'select': + case 'multiselect': setRowVisibility('is_html_allowed_on_front', true); $('is_html_allowed_on_front').disabled = false; - } - $('frontend_class').value = ''; - $('frontend_class').disabled = true; - break; - case 'text': - setRowVisibility('is_html_allowed_on_front', true); - $('is_html_allowed_on_front').disabled = false; + frontendClass.value = ''; + frontendClass.disabled = true; + break; + default: + frontendClass.value = ''; + frontendClass.disabled = true; + } - if (!$('frontend_class').getAttribute('readonly')) { - $('frontend_class').disabled = false; - } - break; - case 'select': - case 'multiselect': - setRowVisibility('is_html_allowed_on_front', true); - $('is_html_allowed_on_front').disabled = false; - $('frontend_class').value = ''; - $('frontend_class').disabled = true; - break; - default: - $('frontend_class').value = ''; - $('frontend_class').disabled = true; + switchIsFilterable(); } - switchIsFilterable(); -} - -function switchIsFilterable() -{ - if ($('is_filterable')) { - if ($('is_filterable').selectedIndex == 0) { - $('position').disabled = true; - } else { - if (!$('position').getAttribute('readonly')){ - $('position').disabled = false; + function switchIsFilterable () { + var isFilterable = $('is_filterable'), + position = $('position'); + if (isFilterable) { + if (isFilterable.selectedIndex == 0) { + position.disabled = true; + } else { + if (!position.getAttribute('readonly')) { + position.disabled = false; + } } } } -} - -function switchDefaultValueField() -{ - if (!$('frontend_input')) { - return; - } - var currentValue = $('frontend_input').value; - - var defaultValueTextVisibility = false; - var defaultValueTextareaVisibility = false; - var defaultValueDateVisibility = false; - var defaultValueYesnoVisibility = false; - var scopeVisibility = true; - - /* swatch attributes */ - var useProductImageForSwatch = false; - var defaultValueUpdateImage = false; - - - switch (currentValue) { - case 'select': - optionDefaultInputType = 'radio'; - break; - - case 'multiselect': - optionDefaultInputType = 'checkbox'; - break; - - case 'date': - defaultValueDateVisibility = true; - break; + function switchDefaultValueField () { + if (!frontendInput) { + return; + } - case 'boolean': - defaultValueYesnoVisibility = true; - break; + var currentValue = frontendInput.value; + + var defaultValueTextVisibility = false; + var defaultValueTextareaVisibility = false; + var defaultValueDateVisibility = false; + var defaultValueYesnoVisibility = false; + var scopeVisibility = true; + + /* swatch attributes */ + var useProductImageForSwatch = false; + var defaultValueUpdateImage = false; + + var optionDefaultInputType = ''; + + + switch (currentValue) { + case 'select': + optionDefaultInputType = 'radio'; + break; + + case 'multiselect': + optionDefaultInputType = 'checkbox'; + break; + + case 'date': + defaultValueDateVisibility = true; + break; + + case 'boolean': + defaultValueYesnoVisibility = true; + break; + + case 'textarea': + defaultValueTextareaVisibility = true; + break; + + case 'media_image': + defaultValueTextVisibility = false; + break; + case 'price': + scopeVisibility = false; + break; + case 'swatch_visual': + useProductImageForSwatch = true; + defaultValueUpdateImage = true; + defaultValueTextVisibility = false; + break; + case 'swatch_text': + useProductImageForSwatch = false; + defaultValueUpdateImage = true; + defaultValueTextVisibility = false; + break; + default: + defaultValueTextVisibility = true; + break; + } - case 'textarea': - defaultValueTextareaVisibility = true; - break; + switch (currentValue) { + case 'media_image': + getFrontTab().hide(); - case 'media_image': - defaultValueTextVisibility = false; + setRowVisibility('is_required', false); + setRowVisibility('is_unique', false); + setRowVisibility('frontend_class', false); break; - case 'price': - scopeVisibility = false; - case 'swatch_visual': - useProductImageForSwatch = true; - defaultValueUpdateImage = true; - defaultValueTextVisibility = false; - break; - case 'swatch_text': - useProductImageForSwatch = false; - defaultValueUpdateImage = true; - defaultValueTextVisibility = false; - break; - default: - defaultValueTextVisibility = true; - break; - } - - switch (currentValue) { - case 'media_image': - getFrontTab().hide(); - - setRowVisibility('is_required', false); - setRowVisibility('is_unique', false); - setRowVisibility('frontend_class', false); - break; - - <?php foreach ($this->helper('Magento\Catalog\Helper\Data')->getAttributeHiddenFields() as $type => $fields): ?> + <?php $hiddenFields = $block->helper('Magento\Catalog\Helper\Data')->getAttributeHiddenFields() ?> + <?php foreach ($hiddenFields as $type => $fields): ?> <?php if (in_array($type, array('swatch_visual', 'swatch_text'))) continue ?> - case '<?php /* @escapeNotVerified */ echo $type; ?>': + case '<?php echo $block->escapeHtml($type); ?>': <?php foreach ($fields as $one): ?> <?php if ($one == '_front_fieldset'): ?> getFrontTab().hide(); @@ -268,145 +280,141 @@ function switchDefaultValueField() <?php elseif ($one == '_scope'): ?> scopeVisibility = false; <?php else: ?> - setRowVisibility('<?php /* @escapeNotVerified */ echo $one; ?>', false); + setRowVisibility('<?php echo $block->escapeHtml($one); ?>', false); <?php endif; ?> <?php endforeach; ?> break; <?php endforeach; ?> - default: - getFrontTab().show(); + default: + getFrontTab().show(); - showDefaultRows(); - break; - } + showDefaultRows(); + break; + } - setRowVisibility('default_value_text', defaultValueTextVisibility); - setRowVisibility('default_value_textarea', defaultValueTextareaVisibility); - setRowVisibility('default_value_date', defaultValueDateVisibility); - setRowVisibility('default_value_yesno', defaultValueYesnoVisibility); - setRowVisibility('is_global', scopeVisibility); + setRowVisibility('default_value_text', defaultValueTextVisibility); + setRowVisibility('default_value_textarea', defaultValueTextareaVisibility); + setRowVisibility('default_value_date', defaultValueDateVisibility); + setRowVisibility('default_value_yesno', defaultValueYesnoVisibility); + setRowVisibility('is_global', scopeVisibility); - /* swatch attributes */ - setRowVisibility('use_product_image_for_swatch', useProductImageForSwatch); - setRowVisibility('update_product_preview_image', defaultValueUpdateImage); + /* swatch attributes */ + setRowVisibility('use_product_image_for_swatch', useProductImageForSwatch); + setRowVisibility('update_product_preview_image', defaultValueUpdateImage); - var elems = document.getElementsByName('default[]'); - for (var i = 0; i < elems.length; i++) { - elems[i].type = optionDefaultInputType; - } -} - -function showDefaultRows() -{ - setRowVisibility('is_required', true); - setRowVisibility('is_unique', true); - setRowVisibility('frontend_class', true); -} - -function setRowVisibility(id, isVisible) -{ - if ($(id)) { - var td = $(id).parentNode; - var tr = $(td.parentNode); - - if (isVisible) { - tr.show(); - } else { - tr.blur(); - tr.hide(); + var elems = document.getElementsByName('default[]'); + for (var i = 0; i < elems.length; i++) { + elems[i].type = optionDefaultInputType; } } -} + function showDefaultRows () { + setRowVisibility('is_required', true); + setRowVisibility('is_unique', true); + setRowVisibility('frontend_class', true); + } -function updateRequriedOptions() -{ - if ($F('frontend_input')=='select' && $F('is_required')==1) { - $('option-count-check').addClassName('required-options-count'); - } else { - $('option-count-check').removeClassName('required-options-count'); + function setRowVisibility (id, isVisible) { + if ($(id)) { + var td = $(id).parentNode; + var tr = $(td.parentNode); + + if (isVisible) { + tr.show(); + } else { + tr.blur(); + tr.hide(); + } + } } -} -function saveAttributeInNewSet(promptMessage) -{ - var newAttributeSetName; - prompt({ - content: promptMessage, - actions: { - confirm: function(val) { - newAttributeSetName = val; + function updateRequriedOptions () { + if ($F('frontend_input')=='select' && $F('is_required')==1) { + $('option-count-check').addClassName('required-options-count'); + } else { + $('option-count-check').removeClassName('required-options-count'); + } + } - if (!newAttributeSetName) { - return; - } + function saveAttributeInNewSet (promptMessage) { + var newAttributeSetName; - var rules = ['required-entry', 'validate-no-html-tags']; - for (var i = 0; i < rules.length; i++) { - if (!jQuery.validator.methods[rules[i]](newAttributeSetName)) { - alert({ - content: jQuery.validator.messages[rules[i]] - }); + prompt({ + content: promptMessage, + actions: { + confirm: function (val) { + newAttributeSetName = val; + if (!newAttributeSetName) { return; } - } - var newAttributeSetNameInputId = 'new_attribute_set_name'; - - if ($(newAttributeSetNameInputId)) { - $(newAttributeSetNameInputId).value = newAttributeSetName; - } else { - $('edit_form').insert({ - top: new Element('input', { - type: 'hidden', - id: newAttributeSetNameInputId, - name: 'new_attribute_set_name', - value: newAttributeSetName - }) - }); + var rules = ['required-entry', 'validate-no-html-tags']; + for (var i = 0; i < rules.length; i++) { + if (!jQuery.validator.methods[rules[i]](newAttributeSetName)) { + alert({ + content: jQuery.validator.messages[rules[i]] + }); + + return; + } + } + + var newAttributeSetNameInputId = 'new_attribute_set_name'; + + if ($(newAttributeSetNameInputId)) { + $(newAttributeSetNameInputId).value = newAttributeSetName; + } else { + $('edit_form').insert({ + top: new Element('input', { + type: 'hidden', + id: newAttributeSetNameInputId, + name: 'new_attribute_set_name', + value: newAttributeSetName + }) + }); + } + // Temporary solution will replaced after refactoring of attributes functionality + jQuery('#edit_form').triggerHandler('save'); } - // Temporary solution will replaced after refactoring of attributes functionality - jQuery('#edit_form').triggerHandler('save'); } - } + }); + } + + if (frontendInput) { + Event.observe(frontendInput, 'change', updateRequriedOptions); + Event.observe(frontendInput, 'change', bindAttributeInputType); + } + + if ($('is_filterable')) { + Event.observe($('is_filterable'), 'change', switchIsFilterable); + } + + if ($('is_required')) { + Event.observe($('is_required'), 'change', updateRequriedOptions); + } + + jQuery(function ($) { + bindAttributeInputType(); + // @todo: refactor collapsable component + $('.attribute-popup .collapse, [data-role="advanced_fieldset-content"]') + .collapsable() + .collapse('hide'); }); -} - -if($('frontend_input')){ - Event.observe($('frontend_input'), 'change', updateRequriedOptions); - Event.observe($('frontend_input'), 'change', bindAttributeInputType); -} - -if ($('is_filterable')) { - Event.observe($('is_filterable'), 'change', switchIsFilterable); -} - -if ($('is_required')) { - Event.observe($('is_required'), 'change', updateRequriedOptions); -} - -jQuery(function($) { - bindAttributeInputType(); - // @todo: refactor collapsable component - $('.attribute-popup .collapse, [data-role="advanced_fieldset-content"]') - .collapsable() - .collapse('hide'); -}); -window.saveAttributeInNewSet = saveAttributeInNewSet; -window.updateRequriedOptions = updateRequriedOptions; -window.setRowVisibility = setRowVisibility; -window.showDefaultRows = showDefaultRows; -window.switchDefaultValueField = switchDefaultValueField; -window.switchIsFilterable = switchIsFilterable; -window.switchIsFilterable = switchIsFilterable; -window.bindAttributeInputType = bindAttributeInputType; -window.checkOptionsPanelVisibility = checkOptionsPanelVisibility; -window.getFrontTab = getFrontTab; -window.toggleApplyVisibility = toggleApplyVisibility; + window.saveAttributeInNewSet = saveAttributeInNewSet; + window.updateRequriedOptions = updateRequriedOptions; + window.setRowVisibility = setRowVisibility; + window.showDefaultRows = showDefaultRows; + window.switchDefaultValueField = switchDefaultValueField; + window.switchIsFilterable = switchIsFilterable; + window.switchIsFilterable = switchIsFilterable; + window.bindAttributeInputType = bindAttributeInputType; + window.checkOptionsPanelVisibility = checkOptionsPanelVisibility; + window.getFrontTab = getFrontTab; + window.toggleApplyVisibility = toggleApplyVisibility; }); </script> diff --git a/dev/tests/integration/etc/di/preferences/ce.php b/dev/tests/integration/etc/di/preferences/ce.php index fdf00fa6d59b02c3529babbe44abc54722a3c109..674ebe49afca3f053fe8664ffa14c9c09881aefe 100644 --- a/dev/tests/integration/etc/di/preferences/ce.php +++ b/dev/tests/integration/etc/di/preferences/ce.php @@ -19,4 +19,5 @@ return [ 'Magento\Framework\View\LayoutInterface' => 'Magento\TestFramework\View\Layout', 'Magento\Framework\App\ResourceConnection\ConnectionAdapterInterface' => 'Magento\TestFramework\Db\ConnectionAdapter', + 'Magento\Framework\Filesystem\DriverInterface' => 'Magento\Framework\Filesystem\Driver\File' ]; diff --git a/dev/tests/integration/framework/Magento/TestFramework/Application.php b/dev/tests/integration/framework/Magento/TestFramework/Application.php index 523c4de04e492b3a64d26133a1e4d66ba16122ff..966451fb35855c784a9533829fd319e7ad659082 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Application.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Application.php @@ -441,6 +441,7 @@ class Application $dirs = \Magento\Framework\App\Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS; $this->_ensureDirExists($this->installDir); $this->_ensureDirExists($this->_configDir); + $this->_ensureDirExists($this->_initParams[$dirs][DirectoryList::PUB][DirectoryList::PATH]); $this->_ensureDirExists($this->_initParams[$dirs][DirectoryList::MEDIA][DirectoryList::PATH]); $this->_ensureDirExists($this->_initParams[$dirs][DirectoryList::STATIC_VIEW][DirectoryList::PATH]); $this->_ensureDirExists($this->_initParams[$dirs][DirectoryList::VAR_DIR][DirectoryList::PATH]); @@ -624,14 +625,15 @@ class Application $customDirs = [ DirectoryList::CONFIG => [$path => "{$this->installDir}/etc"], DirectoryList::VAR_DIR => [$path => $var], - DirectoryList::MEDIA => [$path => "{$this->installDir}/media"], - DirectoryList::STATIC_VIEW => [$path => "{$this->installDir}/pub_static"], + DirectoryList::MEDIA => [$path => "{$this->installDir}/pub/media"], + DirectoryList::STATIC_VIEW => [$path => "{$this->installDir}/pub/static"], DirectoryList::GENERATION => [$path => "{$var}/generation"], DirectoryList::CACHE => [$path => "{$var}/cache"], DirectoryList::LOG => [$path => "{$var}/log"], DirectoryList::SESSION => [$path => "{$var}/session"], DirectoryList::TMP => [$path => "{$var}/tmp"], DirectoryList::UPLOAD => [$path => "{$var}/upload"], + DirectoryList::PUB => [$path => "{$this->installDir}/pub"], ]; return $customDirs; } diff --git a/dev/tests/integration/testsuite/Magento/BundleImportExport/Model/Export/RowCustomizerTest.php b/dev/tests/integration/testsuite/Magento/BundleImportExport/Model/Export/RowCustomizerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c5adb5a6bd03988ab693459abb3c4991bd93fb75 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/BundleImportExport/Model/Export/RowCustomizerTest.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\BundleImportExport\Model\Export; + +/** + * @magentoAppArea adminhtml + */ +class RowCustomizerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\BundleImportExport\Model\Export\RowCustomizer + */ + private $model; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->model = $this->objectManager->create( + 'Magento\BundleImportExport\Model\Export\RowCustomizer' + ); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/product.php + */ + public function testPrepareData() + { + $collection = $this->objectManager->get('Magento\Catalog\Model\ResourceModel\Product\Collection'); + $select = (string)$collection->getSelect(); + $this->model->prepareData($collection, [1, 2, 3, 4]); + $this->assertEquals($select, (string)$collection->getSelect()); + $result = $this->model->addData([], 3); + $this->assertArrayHasKey('bundle_price_type', $result); + $this->assertArrayHasKey('bundle_sku_type', $result); + $this->assertArrayHasKey('bundle_price_view', $result); + $this->assertArrayHasKey('bundle_weight_type', $result); + $this->assertArrayHasKey('bundle_values', $result); + $this->assertContains('sku=simple,', $result['bundle_values']); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php index b87c30697f2c4b1eb4e7711f5d474e4f84258499..8a48ef56be186487d0720dc91a86fdafddcf7664 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php @@ -32,6 +32,7 @@ $attribute->setData( 'used_for_sort_by' => 0, 'frontend_label' => ['Multiselect Attribute'], 'backend_type' => 'varchar', + 'backend_model' => 'Magento\Eav\Model\Entity\Attribute\Backend\ArrayBackend', 'option' => [ 'value' => [ 'option_1' => ['Option 1'], diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php new file mode 100644 index 0000000000000000000000000000000000000000..604e317213c9d76357e66200e7b976221abcb9e0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php @@ -0,0 +1,174 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogImportExport\Model\Export; + +/** + * @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_reindex_schedule.php + */ +class ProductTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\CatalogImportExport\Model\Export\Product + */ + protected $_model; + + /** + * Stock item attributes which must be exported + * + * @var array + */ + public static $stockItemAttributes = [ + 'qty', + 'min_qty', + 'use_config_min_qty', + 'is_qty_decimal', + 'backorders', + 'use_config_backorders', + 'min_sale_qty', + 'use_config_min_sale_qty', + 'max_sale_qty', + 'use_config_max_sale_qty', + 'is_in_stock', + 'notify_stock_qty', + 'use_config_notify_stock_qty', + 'manage_stock', + 'use_config_manage_stock', + 'use_config_qty_increments', + 'qty_increments', + 'use_config_enable_qty_inc', + 'enable_qty_increments', + 'is_decimal_divided', + ]; + + protected function setUp() + { + parent::setUp(); + + $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\CatalogImportExport\Model\Export\Product' + ); + } + + /** + * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_data.php + */ + public function testExport() + { + $this->_model->setWriter( + \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\ImportExport\Model\Export\Adapter\Csv' + ) + ); + $this->assertNotEmpty($this->_model->export()); + } + + /** + * Verify that all stock item attribute values are exported (aren't equal to empty string) + * + * @covers \Magento\CatalogImportExport\Model\Export\Product::export + * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_data.php + */ + public function testExportStockItemAttributesAreFilled() + { + $fileWrite = $this->getMock('Magento\Framework\Filesystem\File\Write', [], [], '', false); + $directoryMock = $this->getMock('Magento\Framework\Filesystem\Directory\Write', [], [], '', false); + $directoryMock->expects($this->any())->method('getParentDirectory')->will($this->returnValue('some#path')); + $directoryMock->expects($this->any())->method('isWritable')->will($this->returnValue(true)); + $directoryMock->expects($this->any())->method('isFile')->will($this->returnValue(true)); + $directoryMock->expects( + $this->any() + )->method( + 'readFile' + )->will( + $this->returnValue('some string read from file') + ); + $directoryMock->expects($this->once())->method('openFile')->will($this->returnValue($fileWrite)); + + $filesystemMock = $this->getMock('Magento\Framework\Filesystem', [], [], '', false); + $filesystemMock->expects($this->once())->method('getDirectoryWrite')->will($this->returnValue($directoryMock)); + + $exportAdapter = new \Magento\ImportExport\Model\Export\Adapter\Csv($filesystemMock); + + $this->_model->setWriter($exportAdapter)->export(); + } + + /** + * Verify header columns (that stock item attributes column headers are present) + * + * @param array $headerColumns + */ + public function verifyHeaderColumns(array $headerColumns) + { + foreach (self::$stockItemAttributes as $stockItemAttribute) { + $this->assertContains( + $stockItemAttribute, + $headerColumns, + "Stock item attribute {$stockItemAttribute} is absent among header columns" + ); + } + } + + /** + * Verify row data (stock item attribute values) + * + * @param array $rowData + */ + public function verifyRow(array $rowData) + { + foreach (self::$stockItemAttributes as $stockItemAttribute) { + $this->assertNotSame( + '', + $rowData[$stockItemAttribute], + "Stock item attribute {$stockItemAttribute} value is empty string" + ); + } + } + + /** + * Verifies if exception processing works properly + * + * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_data.php + */ + public function testExceptionInGetExportData() + { + $exception = new \Exception('Error'); + + $rowCustomizerMock = $this->getMockBuilder('Magento\CatalogImportExport\Model\Export\RowCustomizerInterface') + ->disableOriginalConstructor() + ->getMock(); + + $loggerMock = $this->getMockBuilder('\Psr\Log\LoggerInterface')->getMock(); + + $directoryMock = $this->getMock('Magento\Framework\Filesystem\Directory\Write', [], [], '', false); + $directoryMock->expects($this->any())->method('getParentDirectory')->will($this->returnValue('some#path')); + $directoryMock->expects($this->any())->method('isWritable')->will($this->returnValue(true)); + + $filesystemMock = $this->getMock('Magento\Framework\Filesystem', [], [], '', false); + $filesystemMock->expects($this->once())->method('getDirectoryWrite')->will($this->returnValue($directoryMock)); + + $exportAdapter = new \Magento\ImportExport\Model\Export\Adapter\Csv($filesystemMock); + + $rowCustomizerMock->expects($this->once())->method('prepareData')->willThrowException($exception); + $loggerMock->expects($this->once())->method('critical')->with($exception); + + $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + '\Magento\Catalog\Model\ResourceModel\Product\Collection' + ); + + /** @var \Magento\CatalogImportExport\Model\Export\Product $model */ + $model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\CatalogImportExport\Model\Export\Product', + [ + 'rowCustomizer' => $rowCustomizerMock, + 'logger' => $loggerMock, + 'collection' => $collection + ] + ); + + $data = $model->setWriter($exportAdapter)->export(); + $this->assertEmpty($data); + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e8205523ec8d5f8a3a6e42b0133a6b2272c2f4e8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -0,0 +1,821 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile + +/** + * Test class for \Magento\CatalogImportExport\Model\Import\Product + * + * The "CouplingBetweenObjects" warning is caused by tremendous complexity of the original class + * + */ +namespace Magento\CatalogImportExport\Model\Import; + +use Magento\Framework\App\Bootstrap; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\ImportExport\Model\Import; + +/** + * Class ProductTest + * + * @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_reindex_schedule.php + */ +class ProductTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\CatalogImportExport\Model\Import\Product + */ + protected $_model; + + /** + * @var \Magento\CatalogImportExport\Model\Import\Uploader + */ + protected $_uploader; + + /** + * @var \Magento\CatalogImportExport\Model\Import\UploaderFactory + */ + protected $_uploaderFactory; + + /** + * @var \Magento\CatalogInventory\Model\Spi\StockStateProviderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $_stockStateProvider; + + protected function setUp() + { + $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\CatalogImportExport\Model\Import\Product' + ); + } + + /** + * Options for assertion + * + * @var array + */ + protected $_assertOptions = [ + 'is_require' => '_custom_option_is_required', + 'price' => '_custom_option_price', + 'sku' => '_custom_option_sku', + 'sort_order' => '_custom_option_sort_order', + ]; + + /** + * Option values for assertion + * + * @var array + */ + protected $_assertOptionValues = ['title', 'price', 'sku']; + + /** + * Test if visibility properly saved after import + * + * @magentoDataFixture Magento/Catalog/_files/multiple_products.php + * @magentoAppIsolation enabled + */ + public function testSaveProductsVisibility() + { + $existingProductIds = [10, 11, 12]; + $productsBeforeImport = []; + foreach ($existingProductIds as $productId) { + $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Catalog\Model\Product' + ); + $product->load($productId); + $productsBeforeImport[] = $product; + } + + $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create('Magento\Framework\Filesystem'); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + + $source = new \Magento\ImportExport\Model\Import\Source\Csv( + __DIR__ . '/_files/products_to_import.csv', + $directory + ); + $errors = $this->_model->setParameters( + ['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, 'entity' => 'catalog_product'] + )->setSource( + $source + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 0); + + $this->_model->importData(); + + /** @var $productBeforeImport \Magento\Catalog\Model\Product */ + foreach ($productsBeforeImport as $productBeforeImport) { + /** @var $productAfterImport \Magento\Catalog\Model\Product */ + $productAfterImport = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Catalog\Model\Product' + ); + $productAfterImport->load($productBeforeImport->getId()); + + $this->assertEquals($productBeforeImport->getVisibility(), $productAfterImport->getVisibility()); + unset($productAfterImport); + } + + unset($productsBeforeImport, $product); + } + + /** + * Test if stock item quantity properly saved after import + * + * @magentoDataFixture Magento/Catalog/_files/multiple_products.php + * @magentoAppIsolation enabled + */ + public function testSaveStockItemQty() + { + $existingProductIds = [10, 11, 12]; + $stockItems = []; + foreach ($existingProductIds as $productId) { + /** @var $stockRegistry \Magento\CatalogInventory\Model\StockRegistry */ + $stockRegistry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\CatalogInventory\Model\StockRegistry' + ); + + $stockItem = $stockRegistry->getStockItem($productId, 1); + $stockItems[$productId] = $stockItem; + } + + $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create('Magento\Framework\Filesystem'); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = new \Magento\ImportExport\Model\Import\Source\Csv( + __DIR__ . '/_files/products_to_import.csv', + $directory + ); + $errors = $this->_model->setParameters( + ['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, 'entity' => 'catalog_product'] + )->setSource( + $source + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 0); + + $this->_model->importData(); + + /** @var $stockItmBeforeImport \Magento\CatalogInventory\Model\Stock\Item */ + foreach ($stockItems as $productId => $stockItmBeforeImport) { + /** @var $stockRegistry \Magento\CatalogInventory\Model\StockRegistry */ + $stockRegistry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\CatalogInventory\Model\StockRegistry' + ); + + $stockItemAfterImport = $stockRegistry->getStockItem($productId, 1); + + $this->assertEquals($stockItmBeforeImport->getQty(), $stockItemAfterImport->getQty()); + $this->assertEquals(1, $stockItemAfterImport->getIsInStock()); + unset($stockItemAfterImport); + } + + unset($stockItems, $stockItem); + } + + /** + * Test if stock state properly changed after import + * + * @magentoDataFixture Magento/Catalog/_files/multiple_products.php + * @magentoAppIsolation enabled + */ + public function testStockState() + { + $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create('Magento\Framework\Filesystem'); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = new \Magento\ImportExport\Model\Import\Source\Csv( + __DIR__ . '/_files/products_to_import_with_qty.csv', + $directory + ); + + $errors = $this->_model->setParameters( + ['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, 'entity' => 'catalog_product'] + )->setSource( + $source + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 0); + $this->_model->importData(); + } + + /** + * Tests adding of custom options with existing and new product + * + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @dataProvider getBehaviorDataProvider + * @param string $importFile + * @param string $sku + * @magentoAppIsolation enabled + */ + public function testSaveCustomOptions($importFile, $sku) + { + $pathToFile = __DIR__ . '/_files/' . $importFile; + $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create('Magento\Framework\Filesystem'); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + + $source = new \Magento\ImportExport\Model\Import\Source\Csv($pathToFile, $directory); + $errors = $this->_model->setParameters( + ['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, 'entity' => 'catalog_product'] + )->setSource( + $source + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 0); + $this->_model->importData(); + + /** @var \Magento\Catalog\Model\Product $productModel */ + $productModel = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Catalog\Model\Product' + ); + $product = $productModel->loadByAttribute('sku', $sku); + + $this->assertInstanceOf('Magento\Catalog\Model\Product', $product); + $options = $product->getProductOptionsCollection(); + + $expectedData = $this->getExpectedOptionsData($pathToFile); + $expectedData = $this->mergeWithExistingData($expectedData, $options); + $actualData = $this->getActualOptionsData($options); + + // assert of equal type+titles + $expectedOptions = $expectedData['options']; + // we need to save key values + $actualOptions = $actualData['options']; + sort($expectedOptions); + sort($actualOptions); + $this->assertEquals($expectedOptions, $actualOptions); + + // assert of options data + $this->assertCount(count($expectedData['data']), $actualData['data']); + $this->assertCount(count($expectedData['values']), $actualData['values']); + foreach ($expectedData['options'] as $expectedId => $expectedOption) { + $elementExist = false; + // find value in actual options and values + foreach ($actualData['options'] as $actualId => $actualOption) { + if ($actualOption == $expectedOption) { + $elementExist = true; + $this->assertEquals($expectedData['data'][$expectedId], $actualData['data'][$actualId]); + if (array_key_exists($expectedId, $expectedData['values'])) { + $this->assertEquals($expectedData['values'][$expectedId], $actualData['values'][$actualId]); + } + unset($actualData['options'][$actualId]); + // remove value in case of duplicating key values + break; + } + } + $this->assertTrue($elementExist, 'Element must exist.'); + } + } + + /** + * Data provider for test 'testSaveCustomOptionsDuplicate' + * + * @return array + */ + public function getBehaviorDataProvider() + { + return [ + 'Append behavior with existing product' => [ + '$importFile' => 'product_with_custom_options.csv', + '$sku' => 'simple', + ], + 'Append behavior with new product' => [ + '$importFile' => 'product_with_custom_options_new.csv', + '$sku' => 'simple_new', + ] + ]; + } + + /** + * Test if datetime properly saved after import + * + * @magentoDataFixture Magento/Catalog/_files/multiple_products.php + * @magentoAppIsolation enabled + */ + public function testSaveDatetimeAttribute() + { + $existingProductIds = [10, 11, 12]; + $productsBeforeImport = []; + foreach ($existingProductIds as $productId) { + $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Catalog\Model\Product' + ); + $product->load($productId); + $productsBeforeImport[$product->getSku()] = $product; + } + + $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create('Magento\Framework\Filesystem'); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + + $source = new \Magento\ImportExport\Model\Import\Source\Csv( + __DIR__ . '/_files/products_to_import_with_datetime.csv', + $directory + ); + $errors = $this->_model->setParameters( + ['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, 'entity' => 'catalog_product'] + )->setSource( + $source + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 0); + + $this->_model->importData(); + + $source->rewind(); + foreach ($source as $row) { + /** @var $productAfterImport \Magento\Catalog\Model\Product */ + $productBeforeImport = $productsBeforeImport[$row['sku']]; + + /** @var $productAfterImport \Magento\Catalog\Model\Product */ + $productAfterImport = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Catalog\Model\Product' + ); + $productAfterImport->load($productBeforeImport->getId()); + $this->assertEquals( + @strtotime($row['news_from_date']), + @strtotime($productAfterImport->getNewsFromDate()) + ); + unset($productAfterImport); + } + unset($productsBeforeImport, $product); + } + + /** + * Returns expected product data: current id, options, options data and option values + * + * @param string $pathToFile + * @return array + */ + protected function getExpectedOptionsData($pathToFile) + { + $productData = $this->csvToArray(file_get_contents($pathToFile)); + $expectedOptionId = 0; + $expectedOptions = []; + // array of type and title types, key is element ID + $expectedData = []; + // array of option data + $expectedValues = []; + // array of option values data + foreach ($productData['data'] as $data) { + if (!empty($data['_custom_option_type']) && !empty($data['_custom_option_title'])) { + $lastOptionKey = $data['_custom_option_type'] . '|' . $data['_custom_option_title']; + $expectedOptionId++; + $expectedOptions[$expectedOptionId] = $lastOptionKey; + $expectedData[$expectedOptionId] = []; + foreach ($this->_assertOptions as $assertKey => $assertFieldName) { + if (array_key_exists($assertFieldName, $data)) { + $expectedData[$expectedOptionId][$assertKey] = $data[$assertFieldName]; + } + } + } + if (!empty($data['_custom_option_row_title']) && empty($data['_custom_option_store'])) { + $optionData = []; + foreach ($this->_assertOptionValues as $assertKey) { + $valueKey = \Magento\CatalogImportExport\Model\Import\Product\Option::COLUMN_PREFIX . + 'row_' . + $assertKey; + $optionData[$assertKey] = $data[$valueKey]; + } + $expectedValues[$expectedOptionId][] = $optionData; + } + } + + return [ + 'id' => $expectedOptionId, + 'options' => $expectedOptions, + 'data' => $expectedData, + 'values' => $expectedValues + ]; + } + + /** + * Updates expected options data array with existing unique options data + * + * @param array $expected + * @param \Magento\Catalog\Model\ResourceModel\Product\Option\Collection $options + * @return array + */ + protected function mergeWithExistingData( + array $expected, + \Magento\Catalog\Model\ResourceModel\Product\Option\Collection $options + ) { + $expectedOptionId = $expected['id']; + $expectedOptions = $expected['options']; + $expectedData = $expected['data']; + $expectedValues = $expected['values']; + foreach ($options->getItems() as $option) { + $optionKey = $option->getType() . '|' . $option->getTitle(); + if (!in_array($optionKey, $expectedOptions)) { + $expectedOptionId++; + $expectedOptions[$expectedOptionId] = $optionKey; + $expectedData[$expectedOptionId] = $this->getOptionData($option); + if ($optionValues = $this->getOptionValues($option)) { + $expectedValues[$expectedOptionId] = $optionValues; + } + } + } + + return [ + 'id' => $expectedOptionId, + 'options' => $expectedOptions, + 'data' => $expectedData, + 'values' => $expectedValues + ]; + } + + /** + * Returns actual product data: current id, options, options data and option values + * + * @param \Magento\Catalog\Model\ResourceModel\Product\Option\Collection $options + * @return array + */ + protected function getActualOptionsData(\Magento\Catalog\Model\ResourceModel\Product\Option\Collection $options) + { + $actualOptionId = 0; + $actualOptions = []; + // array of type and title types, key is element ID + $actualData = []; + // array of option data + $actualValues = []; + // array of option values data + /** @var $option \Magento\Catalog\Model\Product\Option */ + foreach ($options->getItems() as $option) { + $lastOptionKey = $option->getType() . '|' . $option->getTitle(); + $actualOptionId++; + if (!in_array($lastOptionKey, $actualOptions)) { + $actualOptions[$actualOptionId] = $lastOptionKey; + $actualData[$actualOptionId] = $this->getOptionData($option); + if ($optionValues = $this->getOptionValues($option)) { + $actualValues[$actualOptionId] = $optionValues; + } + } + } + return [ + 'id' => $actualOptionId, + 'options' => $actualOptions, + 'data' => $actualData, + 'values' => $actualValues + ]; + } + + /** + * Retrieve option data + * + * @param \Magento\Catalog\Model\Product\Option $option + * @return array + */ + protected function getOptionData(\Magento\Catalog\Model\Product\Option $option) + { + $result = []; + foreach (array_keys($this->_assertOptions) as $assertKey) { + $result[$assertKey] = $option->getData($assertKey); + } + return $result; + } + + /** + * Retrieve option values or false for options which has no values + * + * @param \Magento\Catalog\Model\Product\Option $option + * @return array|bool + */ + protected function getOptionValues(\Magento\Catalog\Model\Product\Option $option) + { + $values = $option->getValues(); + if (!empty($values)) { + $result = []; + /** @var $value \Magento\Catalog\Model\Product\Option\Value */ + foreach ($values as $value) { + $optionData = []; + foreach ($this->_assertOptionValues as $assertKey) { + if ($value->hasData($assertKey)) { + $optionData[$assertKey] = $value->getData($assertKey); + } + } + $result[] = $optionData; + } + return $result; + } + + return false; + } + + /** + * @magentoDataIsolation enabled + * @magentoDataFixture mediaImportImageFixture + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testSaveMediaImage() + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create('Magento\Framework\Filesystem'); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + + $source = new \Magento\ImportExport\Model\Import\Source\Csv( + __DIR__ . '/_files/import_media.csv', + $directory + ); + $this->_model->setParameters( + [ + 'behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, + 'entity' => 'catalog_product', + 'import_images_file_dir' => 'pub/media/import' + ] + ); + $appParams = \Magento\TestFramework\Helper\Bootstrap::getInstance()->getBootstrap()->getApplication()->getInitParams()[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS]; + $uploader = $this->_model->getUploader(); + + $destDir = $directory->getRelativePath($appParams[DirectoryList::MEDIA][DirectoryList::PATH] . '/catalog/product'); + $tmpDir = $directory->getRelativePath($appParams[DirectoryList::MEDIA][DirectoryList::PATH] . '/import'); + + $directory->create($destDir); + $this->assertTrue($uploader->setDestDir($destDir)); + $this->assertTrue($uploader->setTmpDir($tmpDir)); + $errors = $this->_model->setSource( + $source + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 0); + $this->_model->importData(); + + $resource = $objectManager->get('Magento\Catalog\Model\ResourceModel\Product'); + $productId = $resource->getIdBySku('simple_new'); + + $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Catalog\Model\Product' + ); + $product->load($productId); + $gallery = $product->getMediaGalleryImages(); + $this->assertInstanceOf('Magento\Framework\Data\Collection', $gallery); + $items = $gallery->getItems(); + $this->assertCount(1, $items); + $item = array_pop($items); + $this->assertInstanceOf('Magento\Framework\DataObject', $item); + $this->assertEquals('/m/a/magento_image.jpg', $item->getFile()); + $this->assertEquals('Image Label', $item->getLabel()); + } + + /** + * Copy a fixture image into media import directory + */ + public static function mediaImportImageFixture() + { + /** @var \Magento\Framework\Filesystem\Directory\Write $mediaDirectory */ + $mediaDirectory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + 'Magento\Framework\Filesystem' + )->getDirectoryWrite( + DirectoryList::MEDIA + ); + $mediaDirectory->create('import'); + $dirPath = $mediaDirectory->getAbsolutePath('import'); + copy(__DIR__ . '/../../../../Magento/Catalog/_files/magento_image.jpg', "{$dirPath}/magento_image.jpg"); + } + + /** + * Cleanup media import and catalog directories + */ + public static function mediaImportImageFixtureRollback() + { + /** @var \Magento\Framework\Filesystem\Directory\Write $mediaDirectory */ + $mediaDirectory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + 'Magento\Framework\Filesystem' + )->getDirectoryWrite( + DirectoryList::MEDIA + ); + $mediaDirectory->delete('import'); + $mediaDirectory->delete('catalog'); + } + + /** + * Export CSV string to array + * + * @param string $content + * @param mixed $entityId + * @return array + */ + protected function csvToArray($content, $entityId = null) + { + $data = ['header' => [], 'data' => []]; + + $lines = str_getcsv($content, "\n"); + foreach ($lines as $index => $line) { + if ($index == 0) { + $data['header'] = str_getcsv($line); + } else { + $row = array_combine($data['header'], str_getcsv($line)); + if (!is_null($entityId) && !empty($row[$entityId])) { + $data['data'][$row[$entityId]] = $row; + } else { + $data['data'][] = $row; + } + } + } + return $data; + } + + /** + * Tests that no products imported if source file contains errors + * + * In this case, the second product data has an invalid attribute set. + * + * @magentoDbIsolation enabled + */ + public function testInvalidSkuLink() + { + // import data from CSV file + $pathToFile = __DIR__ . '/_files/products_to_import_invalid_attribute_set.csv'; + $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Framework\Filesystem' + ); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = new \Magento\ImportExport\Model\Import\Source\Csv($pathToFile, $directory); + $errors = $this->_model->setParameters( + [ + 'behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, + 'entity' => 'catalog_product' + ] + )->setSource( + $source + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 1); + $this->assertEquals( + 'Invalid value for Attribute Set column (set doesn\'t exist?)', + $errors->getErrorByRowNumber(1)[0]->getErrorMessage() + ); + $this->_model->importData(); + + $productCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Catalog\Model\ResourceModel\Product\Collection' + ); + + $products = []; + /** @var $product \Magento\Catalog\Model\Product */ + foreach ($productCollection as $product) { + $products[$product->getSku()] = $product; + } + $this->assertArrayNotHasKey("simple1", $products, "Simple Product should not have been imported"); + $this->assertArrayNotHasKey("simple3", $products, "Simple Product 3 should not have been imported"); + $this->assertArrayNotHasKey("simple2", $products, "Simple Product2 should not have been imported"); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/products_with_multiselect_attribute.php + * @magentoAppIsolation enabled + */ + public function testValidateInvalidMultiselectValues() + { + $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Framework\Filesystem' + ); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = new \Magento\ImportExport\Model\Import\Source\Csv( + __DIR__ . '/_files/products_with_invalid_multiselect_values.csv', + $directory + ); + $errors = $this->_model->setParameters( + ['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, 'entity' => 'catalog_product'] + )->setSource( + $source + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 1); + $this->assertEquals( + "Value for 'multiselect_attribute' attribute contains incorrect value, " + ."see acceptable values on settings specified for Admin", + $errors->getErrorByRowNumber(1)[0]->getErrorMessage() + ); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/categories.php + * @magentoDataFixture Magento/Store/_files/website.php + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php + * @magentoAppIsolation enabled + */ + public function testProductsWithMultipleStores() + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + + $filesystem = $objectManager->create('Magento\Framework\Filesystem'); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + + $source = new \Magento\ImportExport\Model\Import\Source\Csv( + __DIR__ . '/_files/products_multiple_stores.csv', + $directory + ); + $errors = $this->_model->setParameters( + ['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, 'entity' => 'catalog_product'] + )->setSource( + $source + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 0); + + $this->_model->importData(); + + /** @var \Magento\Catalog\Model\Product $product */ + $product = $objectManager->create('Magento\Catalog\Model\Product'); + $id = $product->getIdBySku('Configurable 03'); + $product->load($id); + $this->assertEquals('1', $product->getHasOptions()); + + $objectManager->get('Magento\Store\Model\StoreManagerInterface')->setCurrentStore('fixturestore'); + + /** @var \Magento\Catalog\Model\Product $simpleProduct */ + $simpleProduct = $objectManager->create('Magento\Catalog\Model\Product'); + $id = $simpleProduct->getIdBySku('Configurable 03-Option 1'); + $simpleProduct->load($id); + $this->assertTrue(count($simpleProduct->getWebsiteIds()) == 2); + $this->assertEquals('Option Label', $simpleProduct->getAttributeText('attribute_with_option')); + } + + /** + * @magentoDbIsolation enabled + */ + public function testProductWithInvalidWeight() + { + // import data from CSV file + $pathToFile = __DIR__ . '/_files/product_to_import_invalid_weight.csv'; + $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Framework\Filesystem' + ); + + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = new \Magento\ImportExport\Model\Import\Source\Csv($pathToFile, $directory); + $errors = $this->_model->setSource( + $source + )->setParameters( + ['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND] + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 1); + $this->assertEquals( + "Value for 'weight' attribute contains incorrect value", + $errors->getErrorByRowNumber(1)[0]->getErrorMessage() + ); + } + + /** + * @magentoAppArea adminhtml + * @dataProvider categoryTestDataProvider + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ + public function testProductCategories($fixture, $separator) + { + // import data from CSV file + $pathToFile = __DIR__ . '/_files/' . $fixture; + $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Framework\Filesystem' + ); + + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = new \Magento\ImportExport\Model\Import\Source\Csv($pathToFile, $directory); + $errors = $this->_model->setSource( + $source + )->setParameters( + [ + 'behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, + 'entity' => 'catalog_product', + Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR => $separator + ] + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 0); + $this->_model->importData(); + + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $resource = $objectManager->get('Magento\Catalog\Model\ResourceModel\Product'); + $productId = $resource->getIdBySku('simple1'); + $this->assertTrue(is_numeric($productId)); + /** @var \Magento\Catalog\Model\Product $product */ + $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Catalog\Model\Product' + ); + $product->load($productId); + $this->assertFalse($product->isObjectNew()); + $categories = $product->getCategoryIds(); + $this->assertTrue(count($categories) == 2); + } + + /** + * @return array + */ + public function categoryTestDataProvider() + { + return [ + ['import_new_categories_default_separator.csv', ','], + ['import_new_categories_custom_separator.csv', '|'] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media.csv new file mode 100644 index 0000000000000000000000000000000000000000..608b7569826e7614b3454054d596077f6e3038d1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media.csv @@ -0,0 +1,2 @@ +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus +simple_new,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product ,magento_image.jpg,,magento_image.jpg,,magento_image.jpg,,10/20/15 07:05,10/20/15 07:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,1,1,1,10000,1,1,1,1,1,0,1,1,0,0,0,1,,,,magento_image.jpg,Image Label,,,,,,,, \ No newline at end of file diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_new_categories_custom_separator.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_new_categories_custom_separator.csv new file mode 100644 index 0000000000000000000000000000000000000000..c0deb9b289fd33ec9dbb2314f4494fd24d13dd65 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_new_categories_custom_separator.csv @@ -0,0 +1,2 @@ +sku,product_type,store_view_code,name,price,attribute_set_code,categories,product_websites +simple1,simple,,"simple 2",25,Default,"Default Category/Category 1|Default Category/Category 2",base diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_new_categories_default_separator.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_new_categories_default_separator.csv new file mode 100644 index 0000000000000000000000000000000000000000..9bf687dec4632f79a42b3859e0297c21f7984030 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_new_categories_default_separator.csv @@ -0,0 +1,2 @@ +sku,product_type,store_view_code,name,price,attribute_set_code,categories,product_websites,url_key +simple1,simple,,"simple 1",25,Default,"Default Category/Category 1,Default Category/Category 2",base,simple1-ds diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/product_with_custom_options_new.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/product_with_custom_options_new.csv index 5211dd576ae6a16a71cd9f3e03a8b9b6b000db78..9c4e884646335f655a6995dff184af3091b116ab 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/product_with_custom_options_new.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/product_with_custom_options_new.csv @@ -1,2 +1,2 @@ -sku,store_view_code,attribute_set_code,product_type,categories,product_websites,color,cost,country_of_manufacture,created_at,custom_design,custom_design_from,custom_design_to,custom_layout_update,description,gallery,gift_message_available,has_options,image,image_label,manufacturer,media_gallery,meta_description,meta_keyword,meta_title,minimal_price,msrp,msrp_display_actual_price_type,msrp_enabled,name,news_from_date,news_to_date,options_container,page_layout,price,required_options,short_description,small_image,small_image_label,special_from_date,special_price,special_to_date,product_online,tax_class_name,thumbnail,thumbnail_label,updated_at,url_key,url_path,visibility,weight,qty,min_qty,use_config_min_qty,is_qty_decimal,backorders,use_config_backorders,min_sale_qty,use_config_min_sale_qty,max_sale_qty,use_config_max_sale_qty,is_in_stock,notify_stock_qty,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,_related_sku,_related_position,_crosssell_sku,_crosssell_position,_upsell_sku,_upsell_position,_associated_sku,_associated_default_qty,_associated_position,_tier_price_website,_tier_price_customer_group,_tier_price_qty,_tier_price_price,_group_price_website,_group_price_customer_group,_group_price_price,_media_attribute_id,_media_image,_media_label,_media_position,_media_is_disabled,custom_options -simple_new,,Default,simple,,,base,,,,,,,,,,,1,,,,,,,,,,,,New Product,,,Block after Info Column,,10,1,,,,,,,1,Taxable Goods,,,2012-07-13 12:04:17,new-product,new-product.html,"Catalog, Search",,100,0,1,0,0,1,1,1,0,1,1,,1,0,1,1,0,1,0,0,,,,,,,,,,,,,,,,,,,,,,"name=Test Field Title,type=field,required=1;sku=1-text,price=0,price_type=fixed|name=Test Date and Time Title,type=date_time,required=1,price=2,option_title=custom option 1,sku=2-date|name=New Select,type=drop_down,required=1,price=3,option_title=Option 1,sku=3-1-select|name=New Select,type=drop_down,required=1,price=3,option_title=Option 2,sku=3-2-select|name=New Radio,type=radio,required=1,price=3,option_title=Option 1,sku=4-1-radio|name=New Radio,type=radio,required=1,price=3,option_title=Option 2,sku=4-2-radio" +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus +simple_new,,Default,simple,,base,"New Product",,,,1,"Taxable Goods","Catalog, Search",10.0000,,,,new-product,"New Product","New Product","New Product ",,,,,,,"2015-10-20 07:05:38","2015-10-20 07:05:38",,,"Block after Info Column",,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100.0000,0.0000,1,0,0,1,1.0000,1,10000.0000,1,1,1.0000,1,1,0,1,1.0000,0,0,0,1,,,,,,,"name=New Radio,type=radio,required=1,price=3.0000,price_type=fixed,sku=4-1-radio,option_title=Option 1|name=New Radio,type=radio,required=1,price=3.0000,price_type=fixed,sku=4-2-radio,option_title=Option 2|name=New Select,type=drop_down,required=1,price=3.0000,price_type=fixed,sku=3-1-select,option_title=Option 1|name=New Select,type=drop_down,required=1,price=3.0000,price_type=fixed,sku=3-2-select,option_title=Option2|name=Test Date and Time Title,type=date_time,required=1,price=2.0000,price_type=fixed,sku=2-date|name=Test Field Title,type=field,required=1,price=0.0000,price_type=fixed,sku=1-text",,,,,, diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_multiple_stores.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_multiple_stores.csv index 10299936a27ea2d3283e4d9b49ca93c9ea8be71f..d7876f612233ccdcaf5c37fe78aded58edc75ad9 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_multiple_stores.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_multiple_stores.csv @@ -1,5 +1,5 @@ -sku,store_view_code,attribute_set_code,product_type,categories,product_websites,test_configurable,cost,country_of_manufacture,created_at,custom_design,custom_design_from,custom_design_to,custom_layout_update,description,gallery,gift_message_available,gift_wrapping_available,gift_wrapping_price,has_options,image,image_label,is_returnable,manufacturer,meta_description,meta_keyword,meta_title,minimal_price,msrp,msrp_display_actual_price_type,msrp_enabled,name,news_from_date,news_to_date,options_container,page_layout,price,quantity_and_stock_status,related_tgtr_position_behavior,related_tgtr_position_limit,required_options,short_description,attribute_with_option,small_image,small_image_label,special_from_date,special_price,special_to_date,product_online,tax_class_name,thumbnail,thumbnail_label,updated_at,upsell_tgtr_position_behavior,upsell_tgtr_position_limit,url_key,visibility,weight,qty,min_qty,use_config_min_qty,is_qty_decimal,backorders,use_config_backorders,min_sale_qty,use_config_min_sale_qty,max_sale_qty,use_config_max_sale_qty,is_in_stock,notify_stock_qty,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,_related_sku,_related_position,_crosssell_sku,_crosssell_position,_upsell_sku,_upsell_position,configurable_variations,configurable_variation_prices,configurable_variation_labels -Configurable 03-option_0,,Default,virtual,Default Category/Category 1/Category 1.1,base,Option 1,,,2014-06-13 07:34:02,,,,,,,,,,0,,,Use config,,Configurable 03 ,Configurable 03,Configurable 03,,,Use config,Use config,Configurable 03-option_0,,,Block after Info Column,,10,In Stock,,,0,,Option Label,,,,,,1,Taxable Goods,,,2014-06-13 07:35:59,,,configurable-03-option11,Search,,99999,0,1,0,0,1,1,1,0,1,1,,1,1,1,1,0,1,0,0,,,,,,,,, -Configurable 03-option_0,fixturestore,Default,virtual,Default Category/Category 1/Category 1.1,base,Option 1,,,2014-06-13 07:34:02,,,,,,,,,,,,,,,,,,,,,,Configurable 03-option_0,,,Block after Info Column,,10,,,,0,,Option Label,,,,,,,,,,,,,,Search,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Configurable 03-option_1,,Default,virtual,Default Category/Category 1/Category 1.1,base,Option 2,,,2014-06-13 07:34:05,,,,,,,,,,0,,,Use config,,Configurable 03 ,Configurable 03,Configurable 03,,,Use config,Use config,Configurable 03-option_1,,,Block after Info Column,,10,In Stock,,,0,,,,,,,,1,Taxable Goods,,,2014-06-13 07:34:05,,,configurable-03-option12,Search,,99999,0,1,0,0,1,1,1,0,1,1,,1,1,1,1,0,1,0,0,,,,,,,,, -Configurable 03,,Default,configurable,Default Category/Category 1/Category 1.1,base,,,,2014-06-13 07:34:07,,,,,,,,,,1,,,Use config,,Configurable 03 ,Configurable 03,Configurable 03,,,Use config,Use config,Configurable 03,,,Block after Info Column,,10,In Stock,,,1,,,,,,,,1,Taxable Goods,,,2014-06-13 07:36:32,,,Configurable-03-11,"Catalog, Search",,0,0,1,0,0,1,1,1,0,1,1,,1,1,1,1,0,1,0,0,,,,,,,"sku=Configurable 03-option_0,test_configurable=Option 1,display=1|sku=Configurable 03-option_1,test_configurable=Option 2,display=1","name=test_configurable,value=Option 1,price=1|name=test_configurable,value=Option 2,price=2", +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,configurable_variations,configurable_variation_labels,associated_skus +"Configurable 03-Option 1",,Default,simple,"Default Category/Category 1/Category 1.1","base,test","Configurable 03-Option 1",,,,1,"Taxable Goods","Not Visible Individually",10.0000,,,,configurable-03-option-1,"Configurable 03","Configurable 03","Configurable 03 ",,,,,,,"2015-10-23 00:35:03","2015-10-23 01:00:25",,,"Block after Info Column",,,,,,,,,,,"Use config",,"attribute_with_option=Option Label,has_options=0,quantity_and_stock_status=In Stock,required_options=0,test_configurable=Option 1",99999.0000,0.0000,0,0,0,1,1.0000,0,0.0000,0,1,,1,1,0,0,1.0000,0,0,0,1,,,,,,,,,,,,,, +"Configurable 03-Option 1",fixturestore,Default,"simple",,,"Configurable 03-Option 1 fixturestore",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +"Configurable 03-Option 2",,Default,simple,"Default Category/Category 1/Category 1.1","base,test","Configurable 03-Option 2",,,,1,"Taxable Goods","Not Visible Individually",10.0000,,,,configurable-03-option-2,"Configurable 03","Configurable 03","Configurable 03 ",,,,,,,"2015-10-23 00:35:03","2015-10-23 00:35:03",,,"Block after Info Column",,,,,,,,,,,"Use config",,"has_options=0,quantity_and_stock_status=In Stock,required_options=0,test_configurable=Option 2",99999.0000,0.0000,0,0,0,1,1.0000,0,0.0000,0,1,,1,1,0,0,1.0000,0,0,0,1,,,,,,,,,,,,,, +"Configurable 03",,Default,configurable,"Default Category/Category 1/Category 1.1","base,test","Configurable 03",,,,1,"Taxable Goods","Catalog, Search",10.0000,,,,configurable-03,"Configurable 03","Configurable 03","Configurable 03 ",,,,,,,"2015-10-23 00:35:03","2015-10-23 00:35:03",,,"Block after Info Column",,,,,,,,,,,"Use config",,"has_options=1,quantity_and_stock_status=In Stock,required_options=0",0.0000,0.0000,0,0,0,1,1.0000,0,0.0000,0,1,,1,0,0,0,1.0000,0,0,0,1,,,,,,,,,,,,"sku=Configurable 03-Option 1,test_configurable=Option 1|sku=Configurable 03-Option 2,test_configurable=Option 2",test_configurable=test_configurable, diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import.csv index d8d879193d82e4526a46ee09b9226a5885bd2e45..a107fdff250ec48bcd00ca179f56225df2fbe2c3 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import.csv @@ -1,5 +1,4 @@ -sku,store_view_code,name,price -simple1,,"simple 1",25 -simple1,German,"simple 1 German", -simple2,,"simple 2",34 -simple3,,"simple 3",58 +sku,product_type,store_view_code,name,price,attribute_set_code +simple1,simple,,"simple 1",25,Default +simple2,simple,,"simple 2",34,Default +simple3,simple,,"simple 3",58,Default diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_invalid_attribute_set.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_invalid_attribute_set.csv index ae16f0735a163d9138cf7124cd5b88d9e078bed6..59b8b7b4895603491648cb1f98beb98bb3c19dc6 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_invalid_attribute_set.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_invalid_attribute_set.csv @@ -1,4 +1,4 @@ -sku,price,name,product_type,_attribute_set,_upsell_sku,description +sku,price,name,product_type,attribute_set_code,_upsell_sku,description simple1,25,"Simple Product",simple,Default,,description -simple2,NULL,"Simple Product2",invalid attribute set,Default,,HiThere +simple2,10,"Simple Product2",simple,invalid attribute set,,description simple3,58,"Simple Product 3",simple,Default,simple2,description diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_datetime.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_datetime.csv index a45a871fab6918889074f01fbeafa142f727eab0..ae7e27dbd95c0984548142c93c5d4e35afa0cc20 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_datetime.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_datetime.csv @@ -1,4 +1,4 @@ sku,news_from_date -simple1,"1/1/2015 20:00 pm" -simple2,"10/8/2012 23:58 pm" -simple3,"12/31/1998 17:30 pm" +simple1,"1/1/2015 20:00" +simple2,"10/8/2012 23:58" +simple3,"12/31/1998 17:30" diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_qty.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_qty.csv index 7872a16727763867dcf4d28723dd26a5d970f6c3..3abf9cecb6ff1e1e34256d25b1eacb8959734575 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_qty.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_qty.csv @@ -1,2 +1,2 @@ -sku,store_view_code,name,price,qty,product_type,attribute_set_code,manage_stock -simple1,,"simple 1",25,0.0000,simple,Default,1 +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus +simple_new,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product ,,,,,,,10/20/15 07:05,10/20/15 07:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,1,1,1,10000,1,1,1,1,1,0,1,1,0,0,0,1,,,,,,,,,,,,, diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_with_invalid_multiselect_values.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_with_invalid_multiselect_values.csv index b6da8449c9ccd80e53d52aab642c3e7f51fb5184..a7cc848ea4bcfed1f7ed9c8f67de728f49aa40fe 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_with_invalid_multiselect_values.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_with_invalid_multiselect_values.csv @@ -1,3 +1,3 @@ -sku,store_view_code,product_type,name,price,multiselect_attribute -simple_ms_1,,simple,"With Multiselect 1",10,Option 1 -simple_ms_2,,simple,"With Multiselect 2",10,"Option 5,Option 2,Option 3" +sku,store_view_code,product_type,name,price,additional_attributes +simple_ms_1,,simple,"With Multiselect 1",10,"multiselect_attribute=Option 1" +simple_ms_2,,simple,"With Multiselect 2",10,"multiselect_attribute=Option 5|Option 2|Option 3" diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableImportExport/Model/Export/RowCustomizerTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableImportExport/Model/Export/RowCustomizerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9dc933734fd5ca8447851bb1097246738318867b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableImportExport/Model/Export/RowCustomizerTest.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableImportExport\Model\Export; + +/** + * @magentoAppArea adminhtml + */ +class RowCustomizerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\ConfigurableImportExport\Model\Export\RowCustomizer + */ + private $model; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->model = $this->objectManager->create( + 'Magento\ConfigurableImportExport\Model\Export\RowCustomizer' + ); + } + + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ + public function testPrepareData() + { + $collection = $this->objectManager->get('Magento\Catalog\Model\ResourceModel\Product\Collection'); + $select = (string)$collection->getSelect(); + $this->model->prepareData($collection, [1, 2, 3, 4]); + $this->assertEquals($select, (string)$collection->getSelect()); + $result = $this->model->addData([], 1); + $this->assertArrayHasKey('configurable_variations', $result); + $this->assertArrayHasKey('configurable_variation_labels', $result); + $this->assertEquals( + 'sku=simple_10,test_configurable=Option 1|sku=simple_20,test_configurable=Option 2', + $result['configurable_variations'] + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Reports/Model/ResourceModel/Review/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Reports/Model/ResourceModel/Review/Product/CollectionTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d8c0eddd7ec592ed9902c641b9b8e48f111aa582 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Reports/Model/ResourceModel/Review/Product/CollectionTest.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Reports\Model\ResourceModel\Review\Product; + +/** + * @magentoAppArea adminhtml + */ +class CollectionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Reports\Model\ResourceModel\Review\Product\Collection + */ + private $_collection; + + protected function setUp() + { + $this->_collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Reports\Model\ResourceModel\Review\Product\Collection' + ); + } + + public function testGetSelect() + { + $select = (string)$this->_collection->getSelect(); + $search = '/SUM\(table_rating.percent\)\/COUNT\(table_rating.rating_id\) AS `avg_rating`' + . '[\s\S]+SUM\(table_rating.percent_approved\)\/COUNT\(table_rating.rating_id\) AS `avg_rating_approved`' + . '[\s\S]+LEFT JOIN `.*rating_option_vote_aggregated` AS `table_rating`/'; + + $this->assertRegExp($search, $select); + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_constants.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_constants.php index 1a0a6503809d5e2717bac49bcf46d39e750a23da..a6c1b3688018ef9e080faa7423bc192c90f7b659 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_constants.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_constants.php @@ -945,4 +945,9 @@ return [ 'THEMES', 'Magento\Framework\App\Filesystem\DirectoryList' ], + [ + 'DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR', + 'Magento\CatalogImportExport\Model\Import\Product', + 'Magento\ImportExport\Model\Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR' + ] ];