diff --git a/app/code/Magento/AdvancedPricingImportExport/LICENSE.txt b/app/code/Magento/AdvancedPricingImportExport/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..2b7359b7dfcb4f712444e12973367eedd1363b6e --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/AdvancedPricingImportExport/LICENSE_AFL.txt b/app/code/Magento/AdvancedPricingImportExport/LICENSE_AFL.txt new file mode 100644 index 0000000000000000000000000000000000000000..7232b53a673ea1a1a51bb63c41fae9baf44e5a59 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +Academic Free License ("AFL") v. 3.0 + +This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Academic Free License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright � 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php new file mode 100644 index 0000000000000000000000000000000000000000..7acf70d675ef277f41c28f1390bfaf541bac4f25 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php @@ -0,0 +1,402 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AdvancedPricingImportExport\Model\Import; + +use Magento\CatalogImportExport\Model\Import\Product as ImportProduct; +use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as ValidatorInterface; + +/** + * Class AdvancedPricing + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class AdvancedPricing extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity +{ + const VALUE_ALL_GROUPS = 'ALL GROUPS'; + + const VALUE_ALL_WEBSITES = 'All Websites'; + + const COL_SKU = 'sku'; + + const COL_TIER_PRICE_WEBSITE = 'tier_price_website'; + + const COL_TIER_PRICE_CUSTOMER_GROUP = 'tier_price_customer_group'; + + const COL_TIER_PRICE_QTY = 'tier_price_qty'; + + const COL_TIER_PRICE = 'tier_price'; + + const COL_GROUP_PRICE_WEBSITE = 'group_price_website'; + + const COL_GROUP_PRICE_CUSTOMER_GROUP = 'group_price_customer_group'; + + const COL_GROUP_PRICE = 'group_price'; + + const TABLE_TIER_PRICE = 'catalog_product_entity_tier_price'; + + const TABLE_GROUPED_PRICE = 'catalog_product_entity_group_price'; + + const DEFAULT_ALL_GROUPS_GROUPED_PRICE_VALUE = '0'; + + /** + * Validation failure message template definitions + * + * @var array + */ + protected $_messageTemplates = [ + ValidatorInterface::ERROR_INVALID_WEBSITE => 'Invalid value in Website column (website does not exists?)', + ValidatorInterface::ERROR_SKU_IS_EMPTY => 'SKU is empty', + ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE => 'Product with specified SKU not found', + ValidatorInterface::ERROR_INVALID_TIER_PRICE_QTY => 'Tier Price data price or quantity value is invalid', + ValidatorInterface::ERROR_INVALID_TIER_PRICE_SITE => 'Tier Price data website is invalid', + ValidatorInterface::ERROR_INVALID_TIER_PRICE_GROUP => 'Tier Price customer group is invalid', + ValidatorInterface::ERROR_TIER_DATA_INCOMPLETE => 'Tier Price data is incomplete', + ValidatorInterface::ERROR_INVALID_GROUP_PRICE_SITE => 'Group Price data website is invalid', + ValidatorInterface::ERROR_INVALID_GROUP_PRICE_GROUP => 'Group Price customer group is invalid', + ValidatorInterface::ERROR_GROUP_PRICE_DATA_INCOMPLETE => 'Group Price data is incomplete', + ]; + + /** + * @var \Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceFactory + */ + protected $_resourceFactory; + + /** + * @var \Magento\Catalog\Helper\Data + */ + protected $_catalogData; + + /** + * @var \Magento\Catalog\Model\Product + */ + protected $_productModel; + + /** + * @var \Magento\CatalogImportExport\Model\Import\Product\StoreResolver + */ + protected $_storeResolver; + + /** + * @var ImportProduct + */ + protected $_importProduct; + + /** + * @var AdvancedPricing\Validator + */ + protected $_validator; + + /** + * @var array + */ + protected $_cachedSkuToDelete; + + /** + * @var array + */ + protected $_oldSkus; + + /** + * @var AdvancedPricing\Validator\Website + */ + protected $websiteValidator; + + /** + * @var AdvancedPricing\Validator\GroupPrice + */ + protected $groupPriceValidator; + + /** + * Permanent entity columns. + * + * @var string[] + */ + protected $_permanentAttributes = [self::COL_SKU]; + + /** + * @param \Magento\Framework\Json\Helper\Data $jsonHelper + * @param \Magento\ImportExport\Helper\Data $importExportData + * @param \Magento\ImportExport\Model\Resource\Helper $resourceHelper + * @param \Magento\ImportExport\Model\Resource\Import\Data $importData + * @param \Magento\Framework\App\Resource $resource + * @param \Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceFactory $resourceFactory + * @param \Magento\Catalog\Model\Product $productModel + * @param \Magento\Catalog\Helper\Data $catalogData + * @param ImportProduct\StoreResolver $storeResolver + * @param ImportProduct $importProduct + * @param AdvancedPricing\Validator $validator + * @param AdvancedPricing\Validator\Website $websiteValidator + * @param AdvancedPricing\Validator\GroupPrice $groupPriceValidator + */ + public function __construct( + \Magento\Framework\Json\Helper\Data $jsonHelper, + \Magento\ImportExport\Helper\Data $importExportData, + \Magento\ImportExport\Model\Resource\Helper $resourceHelper, + \Magento\ImportExport\Model\Resource\Import\Data $importData, + \Magento\Framework\App\Resource $resource, + \Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceFactory $resourceFactory, + \Magento\Catalog\Model\Product $productModel, + \Magento\Catalog\Helper\Data $catalogData, + \Magento\CatalogImportExport\Model\Import\Product\StoreResolver $storeResolver, + ImportProduct $importProduct, + AdvancedPricing\Validator $validator, + AdvancedPricing\Validator\Website $websiteValidator, + AdvancedPricing\Validator\GroupPrice $groupPriceValidator + ) { + $this->jsonHelper = $jsonHelper; + $this->_importExportData = $importExportData; + $this->_resourceHelper = $resourceHelper; + $this->_dataSourceModel = $importData; + $this->_connection = $resource->getConnection('write'); + $this->_resourceFactory = $resourceFactory; + $this->_productModel = $productModel; + $this->_catalogData = $catalogData; + $this->_storeResolver = $storeResolver; + $this->_importProduct = $importProduct; + $this->_validator = $validator; + $this->_oldSkus = $this->retrieveOldSkus(); + $this->websiteValidator = $websiteValidator; + $this->groupPriceValidator = $groupPriceValidator; + } + + /** + * Entity type code getter. + * + * @return string + */ + public function getEntityTypeCode() + { + return 'advanced_pricing'; + } + + /** + * Row validation. + * + * @param array $rowData + * @param int $rowNum + * @return bool + */ + public function validateRow(array $rowData, $rowNum) + { + $sku = false; + if (isset($this->_validatedRows[$rowNum])) { + return !isset($this->_invalidRows[$rowNum]); + } + $this->_validatedRows[$rowNum] = true; + // BEHAVIOR_DELETE use specific validation logic + if (\Magento\ImportExport\Model\Import::BEHAVIOR_DELETE == $this->getBehavior()) { + if (!isset($rowData[self::COL_SKU])) { + $this->addRowError(ValidatorInterface::ERROR_SKU_IS_EMPTY, $rowNum); + return false; + } + return true; + } + if (!$this->_validator->isValid($rowData)) { + foreach ($this->_validator->getMessages() as $message) { + $this->addRowError($message, $rowNum); + } + } + if (isset($rowData[self::COL_SKU])) { + $sku = $rowData[self::COL_SKU]; + } + if (false === $sku) { + $this->addRowError(ValidatorInterface::ERROR_ROW_IS_ORPHAN, $rowNum); + } + return !isset($this->_invalidRows[$rowNum]); + } + + /** + * Create Advanced price data from raw data. + * + * @throws \Exception + * @return bool Result of operation. + */ + protected function _importData() + { + if (\Magento\ImportExport\Model\Import::BEHAVIOR_DELETE == $this->getBehavior()) { + $this->deleteAdvancedPricing(); + } elseif (\Magento\ImportExport\Model\Import::BEHAVIOR_REPLACE == $this->getBehavior()) { + $this->replaceAdvancedPricing(); + } elseif (\Magento\ImportExport\Model\Import::BEHAVIOR_APPEND == $this->getBehavior()) { + $this->saveAdvancedPricing(); + } + + return true; + } + + /** + * Save advanced pricing + * + * @return void + */ + public function saveAdvancedPricing() + { + while ($bunch = $this->_dataSourceModel->getNextBunch()) { + $tierPrices = []; + $groupPrices = []; + foreach ($bunch as $rowNum => $rowData) { + if (!$this->validateRow($rowData, $rowNum)) { + $this->addRowError(ValidatorInterface::ERROR_SKU_IS_EMPTY, $rowNum); + continue; + } + $rowSku = $rowData[self::COL_SKU]; + if (!empty($rowData[self::COL_TIER_PRICE_WEBSITE])) { + $tierPrices[$rowSku][] = [ + 'all_groups' => $rowData[self::COL_TIER_PRICE_CUSTOMER_GROUP] == self::VALUE_ALL_GROUPS, + 'customer_group_id' => $this->getCustomerGroupId( + $rowData[self::COL_TIER_PRICE_CUSTOMER_GROUP] + ), + 'qty' => $rowData[self::COL_TIER_PRICE_QTY], + 'value' => $rowData[self::COL_TIER_PRICE], + 'website_id' => $this->getWebsiteId($rowData[self::COL_TIER_PRICE_WEBSITE]) + ]; + } + if (!empty($rowData[self::COL_GROUP_PRICE_WEBSITE])) { + $groupPrices[$rowSku][] = [ + 'all_groups' => self::DEFAULT_ALL_GROUPS_GROUPED_PRICE_VALUE, + 'customer_group_id' => $this->getCustomerGroupId( + $rowData[self::COL_GROUP_PRICE_CUSTOMER_GROUP] + ), + 'value' => $rowData[self::COL_GROUP_PRICE], + 'website_id' => $this->getWebSiteId($rowData[self::COL_GROUP_PRICE_WEBSITE]) + ]; + } + } + $this->saveProductPrices($tierPrices, self::TABLE_TIER_PRICE) + ->saveProductPrices($groupPrices, self::TABLE_GROUPED_PRICE); + } + } + + /** + * Deletes Advanced price data from raw data. + * + * @return void + */ + public function deleteAdvancedPricing() + { + $this->_cachedSkuToDelete = null; + $listSku = []; + while ($bunch = $this->_dataSourceModel->getNextBunch()) { + foreach ($bunch as $rowNum => $rowData) { + if ($this->validateRow($rowData, $rowNum)) { + $rowSku = $rowData[self::COL_SKU]; + $listSku[] = $rowSku; + } + } + } + if ($listSku) { + $this->deleteProductTierAndGroupPrices(array_unique($listSku), self::TABLE_GROUPED_PRICE) + ->deleteProductTierAndGroupPrices(array_unique($listSku), self::TABLE_TIER_PRICE); + } + } + + /** + * Replace advanced pricing + * + * @return bool + */ + public function replaceAdvancedPricing() + { + } + + /** + * Save product prices. + * + * @param array $priceData + * @param string $table + * @return $this + */ + protected function saveProductPrices(array $priceData, $table) + { + if ($priceData) { + $tableName = $this->_resourceFactory->create()->getTable($table); + $priceIn = []; + foreach ($priceData as $sku => $priceRows) { + $productId = $this->_oldSkus[$sku]; + foreach ($priceRows as $row) { + $row['entity_id'] = $productId; + $priceIn[] = $row; + } + } + if ($priceIn) { + $this->_connection->insertOnDuplicate($tableName, $priceIn, ['value']); + } + } + return $this; + } + + /** + * Deletes tier prices and group prices. + * + * @param array $listSku + * @param string $tableName + * @return $this + */ + protected function deleteProductTierAndGroupPrices(array $listSku, $tableName) + { + if ($tableName && $listSku) { + if (!$this->_cachedSkuToDelete) { + $this->_cachedSkuToDelete = $this->_connection->fetchCol( + $this->_connection->select() + ->from($this->_connection->getTableName('catalog_product_entity'), 'entity_id') + ->where('sku IN (?)', $listSku) + ); + } + if ($this->_cachedSkuToDelete) { + $this->_connection->delete( + $tableName, + $this->_connection->quoteInto('entity_id IN (?)', $this->_cachedSkuToDelete) + ); + } else { + $this->addRowError(ValidatorInterface::ERROR_SKU_IS_EMPTY, 0); + return false; + } + } + return $this; + } + + /** + * Get website id by code + * + * @param string $websiteCode + * @return array|int|string + */ + protected function getWebSiteId($websiteCode) + { + $result = $websiteCode == $this->websiteValidator->getAllWebsitesValue() || + $this->_catalogData->isPriceGlobal() ? 0 : $this->_storeResolver->getWebsiteCodeToId($websiteCode); + return $result; + } + + /** + * Get customer group id + * + * @param string $customerGroup + * @return int + */ + protected function getCustomerGroupId($customerGroup) + { + $customerGroups = $this->groupPriceValidator->getCustomerGroups(); + return $customerGroup == self::VALUE_ALL_GROUPS ? 0 : $customerGroups[$customerGroup]; + } + + /** + * Retrieve product skus + * + * @return array + */ + protected function retrieveOldSkus() + { + $oldSkus = $this->_connection->fetchPairs( + $this->_connection->select()->from( + $this->_connection->getTableName('catalog_product_entity'), + ['sku', 'entity_id'] + ) + ); + return $oldSkus; + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator.php b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator.php new file mode 100644 index 0000000000000000000000000000000000000000..71124546a93db5803e6c11600efaec218a51c55c --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing; + +use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface; +use \Magento\Framework\Validator\AbstractValidator; + +class Validator extends AbstractValidator implements RowValidatorInterface +{ + /** + * @var RowValidatorInterface[]|AbstractValidator[] + */ + protected $validators = []; + + /** + * @param RowValidatorInterface[] $validators + */ + public function __construct($validators = []) + { + $this->validators = $validators; + } + + /** + * Check value is valid + * + * @param array $value + * @return bool + */ + public function isValid($value) + { + $returnValue = true; + $this->_clearMessages(); + foreach ($this->validators as $validator) { + if (!$validator->isValid($value)) { + $returnValue = false; + $this->_addMessages($validator->getMessages()); + } + } + return $returnValue; + } + + /** + * Init validators + * + * @return void + */ + public function init() + { + foreach ($this->validators as $validator) { + $validator->init(); + } + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/GroupPrice.php b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/GroupPrice.php new file mode 100644 index 0000000000000000000000000000000000000000..3d62cdc330c73019d5a3fae6957afedb29e0dcb7 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/GroupPrice.php @@ -0,0 +1,127 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator; + +use Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing; + +class GroupPrice extends \Magento\CatalogImportExport\Model\Import\Product\Validator\AbstractPrice +{ + /** + * @var \Magento\CatalogImportExport\Model\Import\Product\StoreResolver + */ + protected $storeResolver; + + /** + * @var array + */ + private $_groupPriceColumns = [ + AdvancedPricing::COL_GROUP_PRICE_WEBSITE, + AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP, + AdvancedPricing::COL_GROUP_PRICE + ]; + + /** + * @param \Magento\Customer\Api\GroupRepositoryInterface $groupRepository + * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder + * @param \Magento\CatalogImportExport\Model\Import\Product\StoreResolver $storeResolver + */ + public function __construct( + \Magento\Customer\Api\GroupRepositoryInterface $groupRepository, + \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder, + \Magento\CatalogImportExport\Model\Import\Product\StoreResolver $storeResolver + ) { + $this->storeResolver = $storeResolver; + parent::__construct($groupRepository, $searchCriteriaBuilder); + } + + /** + * Call parent init() + * + * @return $this + */ + public function init() + { + foreach ($this->groupRepository->getList($this->searchCriteriaBuilder->create())->getItems() as $group) { + $this->customerGroups[$group->getCode()] = $group->getId(); + } + } + + /** + * Validate value + * + * @param array $value + * @return bool + */ + public function isValid($value) + { + $this->_clearMessages(); + if (!$this->customerGroups) { + $this->init(); + } + if ($this->isValidValueAndLength($value)) { + if (!isset($value[AdvancedPricing::COL_GROUP_PRICE_WEBSITE]) + || !isset($value[AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP]) + || $this->hasEmptyColumns($value)) { + $this->_addMessages([self::ERROR_GROUP_PRICE_DATA_INCOMPLETE]); + return false; + } elseif ( + $value[AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP] == AdvancedPricing::VALUE_ALL_GROUPS + || !isset($this->customerGroups[$value[AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP]]) + ) { + $this->_addMessages([self::ERROR_INVALID_GROUP_PRICE_GROUP]); + return false; + } + } + return true; + } + + /** + * Get existing customers groups + * + * @return array + */ + public function getCustomerGroups() + { + if (!$this->customerGroups) { + $this->init(); + } + return $this->customerGroups; + } + + /** + * Check if at list one value and length are valid + * + * @param array $value + * @return bool + */ + protected function isValidValueAndLength(array $value) + { + $isValid = false; + foreach ($this->_groupPriceColumns as $column) { + if (isset($value[$column]) && strlen($value[$column])) { + $isValid = true; + } + } + return $isValid; + } + + /** + * Check if value has empty columns + * + * @param array $value + * @return bool + */ + protected function hasEmptyColumns(array $value) + { + $hasEmptyValues = false; + foreach ($this->_groupPriceColumns as $column) { + if (!strlen($value[$column])) { + $hasEmptyValues = true; + } + } + return $hasEmptyValues; + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/TierPrice.php b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/TierPrice.php new file mode 100644 index 0000000000000000000000000000000000000000..a54157c4895b8a368335da0f1ff752799daa76a5 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/TierPrice.php @@ -0,0 +1,122 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator; + +use Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing; + +class TierPrice extends \Magento\CatalogImportExport\Model\Import\Product\Validator\AbstractPrice +{ + /** + * @var \Magento\CatalogImportExport\Model\Import\Product\StoreResolver + */ + protected $storeResolver; + + /** + * @var array + */ + private $_tierPriceColumns = [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE, + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP, + AdvancedPricing::COL_TIER_PRICE_QTY, + AdvancedPricing::COL_TIER_PRICE + ]; + + /** + * @param \Magento\Customer\Api\GroupRepositoryInterface $groupRepository + * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder + * @param \Magento\CatalogImportExport\Model\Import\Product\StoreResolver $storeResolver + */ + public function __construct( + \Magento\Customer\Api\GroupRepositoryInterface $groupRepository, + \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder, + \Magento\CatalogImportExport\Model\Import\Product\StoreResolver $storeResolver + ) { + $this->storeResolver = $storeResolver; + parent::__construct($groupRepository, $searchCriteriaBuilder); + } + + /** + * Call parent init() + * + * @return $this + */ + public function init() + { + foreach ($this->groupRepository->getList($this->searchCriteriaBuilder->create())->getItems() as $group) { + $this->customerGroups[$group->getCode()] = $group->getId(); + } + } + + /** + * Validation + * + * @param mixed $value + * @return bool + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + public function isValid($value) + { + $this->_clearMessages(); + if (!$this->customerGroups) { + $this->init(); + } + if ($this->isValidValueAndLength($value)) { + if (!isset($value[AdvancedPricing::COL_TIER_PRICE_WEBSITE]) + || !isset($value[AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP]) + || !isset($value[AdvancedPricing::COL_TIER_PRICE_QTY]) + || !isset($value[AdvancedPricing::COL_TIER_PRICE]) + || $this->hasEmptyColumns($value) + ) { + $this->_addMessages([self::ERROR_TIER_DATA_INCOMPLETE]); + return false; + } elseif ($value[AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP] != AdvancedPricing::VALUE_ALL_GROUPS + && !isset($this->customerGroups[$value[AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP]]) + ) { + $this->_addMessages([self::ERROR_INVALID_TIER_PRICE_GROUP]); + return false; + } elseif ($value[AdvancedPricing::COL_TIER_PRICE_QTY] <= 0 + || $value[AdvancedPricing::COL_TIER_PRICE] <= 0) { + $this->_addMessages([self::ERROR_INVALID_TIER_PRICE_QTY]); + return false; + } + } + return true; + } + + /** + * Check if at list one value and length are valid + * + * @param array $value + * @return bool + */ + protected function isValidValueAndLength(array $value) + { + $isValid = false; + foreach ($this->_tierPriceColumns as $column) { + if (isset($value[$column]) && strlen($value[$column])) { + $isValid = true; + } + } + return $isValid; + } + + /** + * Check if value has empty columns + * + * @param array $value + * @return bool + */ + protected function hasEmptyColumns(array $value) + { + $hasEmptyValues = false; + foreach ($this->_tierPriceColumns as $column) { + if (!strlen($value[$column])) { + $hasEmptyValues = true; + } + } + return $hasEmptyValues; + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/Website.php b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/Website.php new file mode 100644 index 0000000000000000000000000000000000000000..bbfb97222f6a3f69cca941b68b679af479c412f9 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/Website.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator; + +use Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing; +use \Magento\Framework\Validator\AbstractValidator; +use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface; + +class Website extends AbstractValidator implements RowValidatorInterface +{ + /** + * @var \Magento\CatalogImportExport\Model\Import\Product\StoreResolver + */ + protected $storeResolver; + + /** + * @var \Magento\Store\Model\Website + */ + protected $websiteModel; + + /** + * @param \Magento\CatalogImportExport\Model\Import\Product\StoreResolver $storeResolver + * @param \Magento\Store\Model\Website $websiteModel + */ + public function __construct( + \Magento\CatalogImportExport\Model\Import\Product\StoreResolver $storeResolver, + \Magento\Store\Model\Website $websiteModel + ) { + $this->storeResolver = $storeResolver; + $this->websiteModel = $websiteModel; + } + + /** + * Initialize validator + * + * @return $this + */ + public function init() + { + return $this; + } + + /** + * Validate value + * + * @param mixed $value + * @return bool + */ + public function isValid($value) + { + $this->_clearMessages(); + if ($value[AdvancedPricing::COL_TIER_PRICE_WEBSITE] != $this->getAllWebsitesValue() && + $value[AdvancedPricing::COL_GROUP_PRICE_WEBSITE] != $this->getAllWebsitesValue()) { + if ((!empty($value[AdvancedPricing::COL_TIER_PRICE_WEBSITE]) + && !$this->storeResolver->getWebsiteCodeToId($value[AdvancedPricing::COL_TIER_PRICE_WEBSITE])) + || ((!empty($value[AdvancedPricing::COL_GROUP_PRICE_WEBSITE])) + && !$this->storeResolver->getWebsiteCodeToId($value[AdvancedPricing::COL_GROUP_PRICE_WEBSITE])) + ) { + $this->_addMessages([self::ERROR_INVALID_WEBSITE]); + return false; + } + } + return true; + } + + /** + * Get all websites value with currency code + * + * @return string + */ + public function getAllWebsitesValue() + { + return AdvancedPricing::VALUE_ALL_WEBSITES . ' ['.$this->websiteModel->getBaseCurrency()->getCurrencyCode().']'; + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Indexer/Product/Price/Plugin/Import.php b/app/code/Magento/AdvancedPricingImportExport/Model/Indexer/Product/Price/Plugin/Import.php new file mode 100644 index 0000000000000000000000000000000000000000..d1b94795ecb3283a7af902fd6561c65aa3332db9 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Indexer/Product/Price/Plugin/Import.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AdvancedPricingImportExport\Model\Indexer\Product\Price\Plugin; + +use Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing; + +class Import extends \Magento\Catalog\Model\Indexer\Product\Price\Plugin\AbstractPlugin +{ + /** + * After import handler + * + * @param AdvancedPricing $subject + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterSaveAdvancedPricing(AdvancedPricing $subject) + { + if (!$this->getPriceIndexer()->isScheduled()) { + $this->invalidateIndexer(); + } + } + + /** + * After delete handler + * + * @param AdvancedPricing $subject + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterDeleteAdvancedPricing(AdvancedPricing $subject) + { + if (!$this->getPriceIndexer()->isScheduled()) { + $this->invalidateIndexer(); + } + } + + /** + * Get price indexer + * + * @return \Magento\Indexer\Model\IndexerInterface + */ + protected function getPriceIndexer() + { + return $this->indexerRegistry->get(\Magento\Catalog\Model\Indexer\Product\Price\Processor::INDEXER_ID); + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/README.md b/app/code/Magento/AdvancedPricingImportExport/README.md new file mode 100644 index 0000000000000000000000000000000000000000..581a3a91c8a1a1755e6dc93848a30d09839e823b --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/README.md @@ -0,0 +1 @@ +The Magento_AdvancedPricingImportExport module handles the import and export of the advanced pricing. \ No newline at end of file diff --git a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/GroupPriceTest.php b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/GroupPriceTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a3ec05281836b40719c7b3b7f1ea0ac85bbd7022 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/GroupPriceTest.php @@ -0,0 +1,335 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdvancedPricingImportExport\Test\Unit\Model\Import\AdvancedPricing\Validator; + +use \Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing as AdvancedPricing; + +/** + * @SuppressWarnings(PHPMD) + */ +class GroupPriceTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @var \Magento\Customer\Api\GroupRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $groupRepository; + + /** + * @var \Magento\Framework\Api\SearchCriteriaBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + protected $searchCriteriaBuilder; + + /** + * @var \Magento\CatalogImportExport\Model\Import\Product\StoreResolver|\PHPUnit_Framework_MockObject_MockObject + */ + protected $storeResolver; + + /** + * @var AdvancedPricing\Validator\GroupPrice|\PHPUnit_Framework_MockObject_MockObject + */ + protected $groupPrice; + + public function setUp() + { + $this->groupRepository = $this->getMockBuilder('\Magento\Customer\Api\GroupRepositoryInterface') + ->disableOriginalConstructor() + ->setMethods(['getList']) + ->getMockForAbstractClass(); + $this->searchCriteriaBuilder = $this->getMock( + '\Magento\Framework\Api\SearchCriteriaBuilder', + [], + [], + '', + false + ); + $this->storeResolver = $this->getMock( + '\Magento\CatalogImportExport\Model\Import\Product\StoreResolver', + [], + [], + '', + false + ); + + $this->groupPrice = $this->getMock( + 'Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\GroupPrice', + ['isValidValueAndLength', 'hasEmptyColumns', '_addMessages'], + [ + $this->groupRepository, + $this->searchCriteriaBuilder, + $this->storeResolver, + ], + '' + ); + } + + public function testInitInternalCalls() + { + $searchCriteria = $this->getMock('Magento\Framework\Api\SearchCriteria', [], [], '', false); + $this->searchCriteriaBuilder->expects($this->once())->method('create')->willReturn($searchCriteria); + $groupSearchResult = $this->getMockForAbstractClass( + '\Magento\Customer\Api\Data\GroupSearchResultsInterface', + [], + '', + false + ); + $this->groupRepository + ->expects($this->once()) + ->method('getList') + ->with($searchCriteria) + ->willReturn($groupSearchResult); + + $groupTest = $this->getMockBuilder('\Magento\Customer\Api\Data\GroupInterface') + ->disableOriginalConstructor() + ->setMethods(['getCode', 'getId']) + ->getMockForAbstractClass(); + $groupTest->expects($this->once())->method('getCode'); + $groupTest->expects($this->once())->method('getId'); + $groups = [$groupTest]; + $groupSearchResult->expects($this->once())->method('getItems')->willReturn($groups); + + $this->groupPrice->init(); + } + + public function testInitAddToCustomerGroups() + { + $searchCriteria = $this->getMock('Magento\Framework\Api\SearchCriteria', [], [], '', false); + $this->searchCriteriaBuilder->expects($this->once())->method('create')->willReturn($searchCriteria); + $groupSearchResult = $this->getMockForAbstractClass( + '\Magento\Customer\Api\Data\GroupSearchResultsInterface', + [], + '', + false + ); + $this->groupRepository + ->expects($this->once()) + ->method('getList') + ->with($searchCriteria) + ->willReturn($groupSearchResult); + + $groupTest = $this->getMockBuilder('\Magento\Customer\Api\Data\GroupInterface') + ->disableOriginalConstructor() + ->setMethods(['getCode', 'getId']) + ->getMockForAbstractClass(); + + $expectedCode = 'code'; + $expectedId = 'id'; + $expectedCustomerGroups = [ + $expectedCode => $expectedId, + ]; + $groupTest->expects($this->once())->method('getCode')->willReturn($expectedCode); + $groupTest->expects($this->once())->method('getId')->willReturn($expectedId); + $groups = [$groupTest]; + $groupSearchResult->expects($this->once())->method('getItems')->willReturn($groups); + + $this->groupPrice->init(); + $this->assertEquals($expectedCustomerGroups, $this->getPropertyValue($this->groupPrice, 'customerGroups')); + } + + public function testIsValidInitCall() + { + $groupPrice = $this->groupPrice = $this->getMock( + 'Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\GroupPrice', + ['init', '_clearMessages'], + [ + $this->groupRepository, + $this->searchCriteriaBuilder, + $this->storeResolver, + ], + '' + ); + $groupPrice->expects($this->once())->method('_clearMessages'); + $this->setPropertyValue($groupPrice, 'customerGroups', false); + $groupPrice->expects($this->once())->method('init'); + + $groupPrice->isValid([]); + } + + /** + * @dataProvider isValidResultFalseDataProvider + * + * @param array $value + * @param array $hasEmptyColumns + * @param array $customerGroups + */ + public function testIsValidResultFalse($value, $hasEmptyColumns, $customerGroups) + { + $this->groupPrice->expects($this->once())->method('isValidValueAndLength')->willReturn(true); + $this->groupPrice->expects($this->any())->method('hasEmptyColumns')->willReturn($hasEmptyColumns); + $this->setPropertyValue($this->groupPrice, 'customerGroups', $customerGroups); + + $result = $this->groupPrice->isValid($value); + $this->assertFalse($result); + } + + public function testIsValidResultTrue() + { + $this->groupPrice->expects($this->once())->method('isValidValueAndLength')->willReturn(false); + $this->setPropertyValue($this->groupPrice, 'customerGroups', true); + + $result = $this->groupPrice->isValid([]); + $this->assertTrue($result); + } + + /** + * @dataProvider isValidAddMessagesCallDataProvider + * + * @param array $value + * @param bool $hasEmptyColumns + * @param array $customerGroups + * @param array $expectedMessages + */ + public function testIsValidAddMessagesCall($value, $hasEmptyColumns, $customerGroups, $expectedMessages) + { + $this->groupPrice->expects($this->once())->method('isValidValueAndLength')->willReturn(true); + $this->groupPrice->expects($this->any())->method('hasEmptyColumns')->willReturn($hasEmptyColumns); + $this->setPropertyValue($this->groupPrice, 'customerGroups', $customerGroups); + + $this->groupPrice->expects($this->once())->method('_addMessages')->with($expectedMessages); + $this->groupPrice->isValid($value); + } + + public function testGetCustomerGroupsInitCall() + { + $groupPrice = $this->groupPrice = $this->getMock( + 'Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\GroupPrice', + ['init'], + [ + $this->groupRepository, + $this->searchCriteriaBuilder, + $this->storeResolver, + ], + '' + ); + $this->setPropertyValue($groupPrice, 'customerGroups', false); + $groupPrice->expects($this->once())->method('init'); + + $groupPrice->getCustomerGroups(); + } + + public function isValidResultFalseDataProvider() + { + return [ + // First if condition cases. + [ + '$value' => [ + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => null, + AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP => 'value', + ], + '$hasEmptyColumns' => null, + '$customerGroups' => [ + 'value' => 'value' + ], + ], + [ + '$value' => [ + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'value', + AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP => null, + ], + '$hasEmptyColumns' => null, + '$customerGroups' => [ + 'value' + ], + ], + [ + '$value' => [ + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'value', + AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP => 'value', + ], + '$hasEmptyColumns' => true, + '$customerGroups' => [ + 'value' => 'value' + ], + ], + // Second if condition cases. + [ + '$value' => [ + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'value', + AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP => AdvancedPricing::VALUE_ALL_GROUPS, + ], + '$hasEmptyColumns' => false, + '$customerGroups' => [ + 'group price customer value' => 'value' + ], + ], + [ + '$value' => [ + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'value', + AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP => 'group price customer value', + ], + '$hasEmptyColumns' => false, + '$customerGroups' => [ + 'group price customer value' => null + ], + ], + ]; + } + + public function isValidAddMessagesCallDataProvider() + { + return [ + // First if condition cases. + [ + '$value' => [ + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => null, + AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP => 'value', + AdvancedPricing::VALUE_ALL_GROUPS => 'value', + ], + '$hasEmptyColumns' => null, + '$customerGroups' => [ + 'value' => 'value' + ], + '$expectedMessages' => [AdvancedPricing\Validator::ERROR_GROUP_PRICE_DATA_INCOMPLETE], + ], + // Second if condition cases. + [ + '$value' => [ + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'value', + AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP => 'value', + AdvancedPricing::VALUE_ALL_GROUPS => 'not ALL GROUPS', + ], + '$hasEmptyColumns' => false, + '$customerGroups' => [ + 'value' => null + ], + '$expectedMessages' => [AdvancedPricing\Validator::ERROR_INVALID_GROUP_PRICE_GROUP], + ], + ]; + } + + /** + * Get any object property value. + * + * @param $object + * @param $property + */ + protected function getPropertyValue($object, $property) + { + $reflection = new \ReflectionClass(get_class($object)); + $reflectionProperty = $reflection->getProperty($property); + $reflectionProperty->setAccessible(true); + + return $reflectionProperty->getValue($object); + } + + /** + * Set object property value. + * + * @param $object + * @param $property + * @param $value + */ + protected function setPropertyValue(&$object, $property, $value) + { + $reflection = new \ReflectionClass(get_class($object)); + $reflectionProperty = $reflection->getProperty($property); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($object, $value); + + return $object; + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/TierPriceTest.php b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/TierPriceTest.php new file mode 100644 index 0000000000000000000000000000000000000000..313c038c2c900859632c964d8fa1d212eb108a82 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/TierPriceTest.php @@ -0,0 +1,381 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdvancedPricingImportExport\Test\Unit\Model\Import\AdvancedPricing\Validator; + +use \Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing as AdvancedPricing; + +/** + * @SuppressWarnings(PHPMD) + */ +class TierPriceTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Customer\Api\GroupRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $groupRepository; + + /** + * @var \Magento\Framework\Api\SearchCriteriaBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + protected $searchCriteriaBuilder; + + /** + * @var \Magento\CatalogImportExport\Model\Import\Product\StoreResolver|\PHPUnit_Framework_MockObject_MockObject + */ + protected $storeResolver; + + /** + * @var AdvancedPricing\Validator\TierPrice|\PHPUnit_Framework_MockObject_MockObject + */ + protected $tierPrice; + + + public function setUp() + { + $this->groupRepository = $this->getMockBuilder('\Magento\Customer\Api\GroupRepositoryInterface') + ->disableOriginalConstructor() + ->setMethods(['getList']) + ->getMockForAbstractClass(); + + $this->searchCriteriaBuilder = $this->getMock( + '\Magento\Framework\Api\SearchCriteriaBuilder', + [], + [], + '', + false + ); + $this->storeResolver = $this->getMock( + '\Magento\CatalogImportExport\Model\Import\Product\StoreResolver', + [], + [], + '', + false + ); + + $this->tierPrice = $this->getMock( + '\Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\TierPrice', + ['isValidValueAndLength', 'hasEmptyColumns', '_addMessages'], + [ + $this->groupRepository, + $this->searchCriteriaBuilder, + $this->storeResolver, + ], + '' + ); + } + + public function testInitInternalCalls() + { + $searchCriteria = $this->getMock('Magento\Framework\Api\SearchCriteria', [], [], '', false); + $this->searchCriteriaBuilder->expects($this->once())->method('create')->willReturn($searchCriteria); + $groupSearchResult = $this->getMockForAbstractClass( + '\Magento\Customer\Api\Data\GroupSearchResultsInterface', + [], + '', + false + ); + $this->groupRepository + ->expects($this->once()) + ->method('getList') + ->with($searchCriteria) + ->willReturn($groupSearchResult); + + $groupTest = $this->getMockBuilder('\Magento\Customer\Api\Data\GroupInterface') + ->disableOriginalConstructor() + ->setMethods(['getCode', 'getId']) + ->getMockForAbstractClass(); + $groupTest->expects($this->once())->method('getCode'); + $groupTest->expects($this->once())->method('getId'); + $groups = [$groupTest]; + $groupSearchResult->expects($this->once())->method('getItems')->willReturn($groups); + + $this->tierPrice->init(); + } + + public function testInitAddToCustomerGroups() + { + $searchCriteria = $this->getMock('Magento\Framework\Api\SearchCriteria', [], [], '', false); + $this->searchCriteriaBuilder->expects($this->once())->method('create')->willReturn($searchCriteria); + $groupSearchResult = $this->getMockForAbstractClass( + '\Magento\Customer\Api\Data\GroupSearchResultsInterface', + [], + '', + false + ); + $this->groupRepository + ->expects($this->once()) + ->method('getList') + ->with($searchCriteria) + ->willReturn($groupSearchResult); + + $groupTest = $this->getMockBuilder('\Magento\Customer\Api\Data\GroupInterface') + ->disableOriginalConstructor() + ->setMethods(['getCode', 'getId']) + ->getMockForAbstractClass(); + + $expectedCode = 'code'; + $expectedId = 'id'; + $expectedCustomerGroups = [ + $expectedCode => $expectedId, + ]; + $groupTest->expects($this->once())->method('getCode')->willReturn($expectedCode); + $groupTest->expects($this->once())->method('getId')->willReturn($expectedId); + $groups = [$groupTest]; + $groupSearchResult->expects($this->once())->method('getItems')->willReturn($groups); + + $this->tierPrice->init(); + $this->assertEquals($expectedCustomerGroups, $this->getPropertyValue($this->tierPrice, 'customerGroups')); + } + + public function testIsValidInitCall() + { + $tierPrice = $this->tierPrice = $this->getMock( + 'Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\GroupPrice', + ['init', '_clearMessages'], + [ + $this->groupRepository, + $this->searchCriteriaBuilder, + $this->storeResolver, + ], + '' + ); + $tierPrice->expects($this->once())->method('_clearMessages'); + $this->setPropertyValue($tierPrice, 'customerGroups', false); + $tierPrice->expects($this->once())->method('init'); + + $tierPrice->isValid([]); + } + + /** + * @dataProvider isValidResultFalseDataProvider + * + * @param array $value + * @param bool $hasEmptyColumns + * @param array $customerGroups + */ + public function testIsValidResultFalse($value, $hasEmptyColumns, $customerGroups) + { + $this->tierPrice->expects($this->once())->method('isValidValueAndLength')->willReturn(true); + $this->tierPrice->expects($this->any())->method('hasEmptyColumns')->willReturn($hasEmptyColumns); + $this->setPropertyValue($this->tierPrice, 'customerGroups', $customerGroups); + + $result = $this->tierPrice->isValid($value); + $this->assertFalse($result); + } + + public function testIsValidResultTrue() + { + $this->tierPrice->expects($this->once())->method('isValidValueAndLength')->willReturn(false); + $this->setPropertyValue($this->tierPrice, 'customerGroups', true); + + $result = $this->tierPrice->isValid([]); + $this->assertTrue($result); + } + + /** + * @dataProvider isValidAddMessagesCallDataProvider + * + * @param array $value + * @param bool $hasEmptyColumns + * @param array $customerGroups + * @param array $expectedMessages + */ + public function testIsValidAddMessagesCall($value, $hasEmptyColumns, $customerGroups, $expectedMessages) + { + $this->tierPrice->expects($this->once())->method('isValidValueAndLength')->willReturn(true); + $this->tierPrice->expects($this->any())->method('hasEmptyColumns')->willReturn($hasEmptyColumns); + $this->setPropertyValue($this->tierPrice, 'customerGroups', $customerGroups); + + $this->tierPrice->expects($this->once())->method('_addMessages')->with($expectedMessages); + $this->tierPrice->isValid($value); + } + + public function isValidResultFalseDataProvider() + { + return [ + // First if condition cases. + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => null, + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'value', + AdvancedPricing::COL_TIER_PRICE_QTY => 1000, + AdvancedPricing::COL_TIER_PRICE => 1000, + ], + '$hasEmptyColumns' => null, + '$customerGroups' => [ + 'value' => 'value' + ], + ], + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'value', + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => null, + AdvancedPricing::COL_TIER_PRICE_QTY => 1000, + AdvancedPricing::COL_TIER_PRICE => 1000, + ], + '$hasEmptyColumns' => null, + '$customerGroups' => [ + 'value' => 'value' + ], + ], + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'value', + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'value', + AdvancedPricing::COL_TIER_PRICE_QTY => null, + AdvancedPricing::COL_TIER_PRICE => 1000, + ], + '$hasEmptyColumns' => null, + '$customerGroups' => [ + 'value' => 'value' + ], + ], + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'value', + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'value', + AdvancedPricing::COL_TIER_PRICE_QTY => 1000, + AdvancedPricing::COL_TIER_PRICE => null, + ], + '$hasEmptyColumns' => null, + '$customerGroups' => [ + 'value' => 'value' + ], + ], + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'value', + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'value', + AdvancedPricing::COL_TIER_PRICE_QTY => 1000, + AdvancedPricing::COL_TIER_PRICE => 1000, + ], + '$hasEmptyColumns' => true, + '$customerGroups' => [ + 'value' => 'value' + ], + ], + // Second if condition cases. + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'value', + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'not ALL GROUPS', + AdvancedPricing::COL_TIER_PRICE_QTY => 1000, + AdvancedPricing::COL_TIER_PRICE => 1000, + ], + '$hasEmptyColumns' => null, + '$customerGroups' => [ + 'value' => 'value' + ], + ], + // Third if condition cases. + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'value', + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'value', + AdvancedPricing::COL_TIER_PRICE_QTY => -1000, + AdvancedPricing::COL_TIER_PRICE => 1000, + ], + '$hasEmptyColumns' => null, + '$customerGroups' => [ + 'value' => 'value' + ], + ], + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'value', + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'value', + AdvancedPricing::COL_TIER_PRICE_QTY => 1000, + AdvancedPricing::COL_TIER_PRICE => -1000, + ], + '$hasEmptyColumns' => null, + '$customerGroups' => [ + 'value' => 'value' + ], + ], + ]; + } + + public function isValidAddMessagesCallDataProvider() + { + return [ + // First if condition cases. + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => null, + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'value', + AdvancedPricing::COL_TIER_PRICE_QTY => 1000, + AdvancedPricing::COL_TIER_PRICE => 1000, + ], + '$hasEmptyColumns' => null, + '$customerGroups' => [ + 'value' => 'value' + ], + '$expectedMessages' => [AdvancedPricing\Validator::ERROR_TIER_DATA_INCOMPLETE], + ], + // Second if condition cases. + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'value', + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'not ALL GROUPS', + AdvancedPricing::COL_TIER_PRICE_QTY => 1000, + AdvancedPricing::COL_TIER_PRICE => 1000, + ], + '$hasEmptyColumns' => null, + '$customerGroups' => [ + 'value' => 'value' + ], + '$expectedMessages' => [AdvancedPricing\Validator::ERROR_INVALID_TIER_PRICE_GROUP], + ], + // Third if condition cases. + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'value', + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'value', + AdvancedPricing::COL_TIER_PRICE_QTY => -1000, + AdvancedPricing::COL_TIER_PRICE => 1000, + ], + '$hasEmptyColumns' => null, + '$customerGroups' => [ + 'value' => 'value' + ], + '$expectedMessages' => [AdvancedPricing\Validator::ERROR_INVALID_TIER_PRICE_QTY], + ], + ]; + } + + /** + * Get any object property value. + * + * @param object $object + * @param string $property + */ + protected function getPropertyValue($object, $property) + { + $reflection = new \ReflectionClass(get_class($object)); + $reflectionProperty = $reflection->getProperty($property); + $reflectionProperty->setAccessible(true); + + return $reflectionProperty->getValue($object); + } + + /** + * Set object property value. + * + * @param object $object + * @param string $property + * @param mixed $value + */ + protected function setPropertyValue(&$object, $property, $value) + { + $reflection = new \ReflectionClass(get_class($object)); + $reflectionProperty = $reflection->getProperty($property); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($object, $value); + + return $object; + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/WebsiteTest.php b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/WebsiteTest.php new file mode 100644 index 0000000000000000000000000000000000000000..54045bbec6aa708f3bb868bd0b21ad304578acb9 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/WebsiteTest.php @@ -0,0 +1,223 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdvancedPricingImportExport\Test\Unit\Model\Import\AdvancedPricing\Validator; + +use \Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing as AdvancedPricing; + +class WebsiteTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Store\Model\WebSite|\PHPUnit_Framework_MockObject_MockObject + */ + protected $webSiteModel; + + /** + * @var \Magento\CatalogImportExport\Model\Import\Product\StoreResolver|\PHPUnit_Framework_MockObject_MockObject + */ + protected $storeResolver; + + /** + * @var AdvancedPricing\Validator\Website|\PHPUnit_Framework_MockObject_MockObject + */ + protected $website; + + public function setUp() + { + $this->webSiteModel = $this->getMock( + '\Magento\Store\Model\WebSite', + ['getBaseCurrency'], + [], + '', + false + ); + $this->storeResolver = $this->getMock( + '\Magento\CatalogImportExport\Model\Import\Product\StoreResolver', + ['getWebsiteCodeToId'], + [], + '', + false + ); + + $this->website = $this->getMock( + '\Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\Website', + ['getAllWebsitesValue', '_clearMessages', '_addMessages'], + [ + $this->storeResolver, + $this->webSiteModel, + ], + '' + ); + } + + public function testInit() + { + $result = $this->website->init(); + + $this->assertEquals($this->website, $result); + } + + /** + * @dataProvider isValidReturnDataProvider + * + * @param array $value + * @param string $allWebsitesValue + * @param string $colTierPriceWebsite + * @param string $colGroupPriceWebsite + * @param bool $expectedResult + */ + public function testIsValidReturn( + $value, + $allWebsites, + $colTierPriceWebsite, + $colGroupPriceWebsite, + $expectedResult + ) { + $this->website->expects($this->once())->method('_clearMessages'); + $this->website->expects($this->atLeastOnce())->method('getAllWebsitesValue')->willReturn($allWebsites); + $this->storeResolver->method('getWebsiteCodeToId')->willReturnMap([ + [$value[AdvancedPricing::COL_TIER_PRICE_WEBSITE], $colTierPriceWebsite], + [$value[AdvancedPricing::COL_GROUP_PRICE_WEBSITE], $colGroupPriceWebsite], + ]); + + $result = $this->website->isValid($value); + $this->assertEquals($expectedResult, $result); + } + + public function testIsValidReturnAddMessagesCall() + { + $value = [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'tier value', + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'group value', + ]; + $allWebsitesValue = 'not tier|group price website value'; + $colTierPriceWebsite = false; + $colGroupPriceWebsite = 'value'; + $expectedMessages = [AdvancedPricing\Validator\Website::ERROR_INVALID_WEBSITE]; + + $this->website->expects($this->once())->method('_clearMessages'); + $this->website->expects($this->atLeastOnce())->method('getAllWebsitesValue')->willReturn($allWebsitesValue); + $this->storeResolver->method('getWebsiteCodeToId')->willReturnMap([ + [$value[AdvancedPricing::COL_TIER_PRICE_WEBSITE], $colTierPriceWebsite], + [$value[AdvancedPricing::COL_GROUP_PRICE_WEBSITE], $colGroupPriceWebsite], + ]); + + $this->website->expects($this->once())->method('_addMessages')->with($expectedMessages); + $this->website->isValid($value); + } + + public function testGetAllWebsitesValue() + { + $currencyCode = 'currencyCodeValue'; + $currency = $this->getMock('\Magento\Directory\Model\Currency', ['getCurrencyCode'], [], '', false); + $currency->expects($this->once())->method('getCurrencyCode')->willReturn($currencyCode); + + $this->webSiteModel->expects($this->once())->method('getBaseCurrency')->willReturn($currency); + + $expectedResult = AdvancedPricing::VALUE_ALL_WEBSITES . ' [' . $currencyCode . ']'; + + $website = $this->getMock( + '\Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\Website', + null, + [ + $this->storeResolver, + $this->webSiteModel, + ], + '' + ); + + $result = $website->getAllWebsitesValue(); + $this->assertEquals($expectedResult, $result); + } + + public function isValidReturnDataProvider() + { + return [ + // False cases. + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'tier value', + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'group value', + ], + '$allWebsites' => 'not tier|group price website value', + '$colTierPriceWebsite' => false, + '$colGroupPriceWebsite' => 'value', + '$expectedResult' => false, + ], + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'tier value', + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'group value', + ], + '$allWebsites' => 'not tier|group price website value', + '$colTierPriceWebsite' => 'value', + '$colGroupPriceWebsite' => false, + '$expectedResult' => false, + ], + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'tier value', + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'group value', + ], + '$allWebsites' => 'not tier|group price website value', + '$colTierPriceWebsite' => 'value', + '$colGroupPriceWebsite' => false, + '$expectedResult' => false, + ], + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => false, + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'group value', + ], + '$allWebsites' => 'not tier|group price website value', + '$colTierPriceWebsite' => 'value', + '$colGroupPriceWebsite' => false, + '$expectedResult' => false, + ], + // True cases. + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'tier value', + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'group value', + ], + '$allWebsites' => 'tier value', + '$colTierPriceWebsite' => 'value', + '$colGroupPriceWebsite' => 'value', + '$expectedResult' => true, + ], + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'tier value', + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'group value', + ], + '$allWebsites' => 'group value', + '$colTierPriceWebsite' => 'value', + '$colGroupPriceWebsite' => 'value', + '$expectedResult' => true, + ], + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => false, + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'group value', + ], + '$allWebsites' => 'not tier|group price website value', + '$colTierPriceWebsite' => 'value', + '$colGroupPriceWebsite' => 'value', + '$expectedResult' => true, + ], + [ + '$value' => [ + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'tier value', + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => false, + ], + '$allWebsites' => 'not tier|group price website value', + '$colTierPriceWebsite' => 'value', + '$colGroupPriceWebsite' => 'value', + '$expectedResult' => true, + ], + ]; + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/ValidatorTest.php b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/ValidatorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..07033bb67525f5c750c39639a8d253afc7df7f81 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/ValidatorTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AdvancedPricingImportExport\Test\Unit\Model\Import\AdvancedPricing; + +use \Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator as Validator; +use \Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as RowValidatorInterface; + +class ValidatorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var Validator |\PHPUnit_Framework_MockObject_MockObject + */ + protected $validator; + + /** + * @var Validator |\PHPUnit_Framework_MockObject_MockObject + */ + protected $validators; + + /** + * @var RowValidatorInterface |\PHPUnit_Framework_MockObject_MockObject + */ + protected $validatorTest; + + public function setUp() + { + $this->validatorTest = $this->getMockForAbstractClass( + 'Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface', + [], + '', + false + ); + $messages = ['messages']; + $this->validatorTest->expects($this->any())->method('getMessages')->willReturn($messages); + $this->validators = [$this->validatorTest]; + + $this->validator = $this->getMock( + 'Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator', + ['_clearMessages', '_addMessages'], + [$this->validators] + ); + } + + /** + * @dataProvider isValidDataProvider + * + * @param array $validatorResult + * @param bool $expectedResult + */ + public function testIsValid($validatorResult, $expectedResult) + { + $this->validator->expects($this->once())->method('_clearMessages'); + $value = 'value'; + $this->validatorTest->expects($this->once())->method('isValid')->with($value)->willReturn($validatorResult); + + $result = $this->validator->isValid($value); + $this->assertEquals($expectedResult, $result); + } + + public function testIsValidAddMessagesCall() + { + $value = 'value'; + $this->validatorTest->expects($this->once())->method('isValid')->willReturn(false); + $this->validator->expects($this->once())->method('_addMessages'); + + $this->validator->isValid($value); + } + + public function testInit() + { + $this->validatorTest->expects($this->once())->method('init'); + + $this->validator->init(); + } + + public function isValidDataProvider() + { + return [ + [ + '$validatorResult' => true, + '$expectedResult' => true, + ], + [ + '$validatorResult' => false, + '$expectedResult' => false, + ] + ]; + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricingTest.php b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricingTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f26b5560ca977bb9c3b69080ab05745a17923a6a --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricingTest.php @@ -0,0 +1,676 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdvancedPricingImportExport\Test\Unit\Model\Import; + +use \Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing as AdvancedPricing; +use \Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceFactory as ResourceFactory; +use \Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as RowValidatorInterface; + +/** + * @SuppressWarnings(PHPMD) + */ +class AdvancedPricingTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @var ResourceFactory |\PHPUnit_Framework_MockObject_MockObject + */ + protected $resourceFactory; + + /** + * @var \Magento\Catalog\Helper\Data |\PHPUnit_Framework_MockObject_MockObject + */ + protected $catalogData; + + /** + * @var \Magento\Catalog\Model\Product |\PHPUnit_Framework_MockObject_MockObject + */ + protected $productModel; + + /** + * @var \Magento\CatalogImportExport\Model\Import\Product\StoreResolver |\PHPUnit_Framework_MockObject_MockObject + */ + protected $storeResolver; + + /** + * @var \Magento\CatalogImportExport\Model\Import\Product|PHPUnit_Framework_MockObject_MockObject + */ + protected $importProduct; + + /** + * @var AdvancedPricing\Validator |\PHPUnit_Framework_MockObject_MockObject + */ + protected $validator; + + /** + * @var AdvancedPricing\Validator\Website |\PHPUnit_Framework_MockObject_MockObject + */ + protected $websiteValidator; + + /** + * @var AdvancedPricing\Validator\GroupPrice |\PHPUnit_Framework_MockObject_MockObject + */ + protected $groupPriceValidator; + + /** + * @var \Magento\ImportExport\Model\Resource\Helper |\PHPUnit_Framework_MockObject_MockObject + */ + protected $resourceHelper; + + /** + * @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $connection; + + /** + * @var \Magento\ImportExport\Model\Resource\Import\Data|\PHPUnit_Framework_MockObject_MockObject + */ + protected $dataSourceModel; + + /** + * @var array + */ + protected $cachedSkuToDelete; + + /** + * @var array + */ + protected $oldSkus; + + /** + * @var AdvancedPricing |\PHPUnit_Framework_MockObject_MockObject + */ + protected $advancedPricing; + + public function setUp() + { + $this->jsonHelper = $this->getMock( + '\Magento\Framework\Json\Helper\Data', + [], + [], + '', + false + ); + $this->_importExportData = $this->getMock( + '\Magento\ImportExport\Helper\Data', + [], + [], + '', + false + ); + $this->resourceHelper = $this->getMock( + '\Magento\ImportExport\Model\Resource\Helper', + [], + [], + '', + false + ); + $this->_resource = $this->getMock( + '\Magento\Framework\App\Resource', + ['getConnection'], + [], + '', + false + ); + $this->connection = $this->getMockForAbstractClass( + '\Magento\Framework\DB\Adapter\AdapterInterface', + [], + '', + false + ); + $this->_resource->expects($this->any())->method('getConnection')->willReturn($this->connection); + $this->dataSourceModel = $this->getMock( + '\Magento\ImportExport\Model\Resource\Import\Data', + [], + [], + '', + false + ); + $this->resourceFactory = $this->getMock( + '\Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceFactory', + [], + [], + '', + false + ); + $this->productModel = $this->getMock( + '\Magento\Catalog\Model\Product', + [], + [], + '', + false + ); + $this->catalogData = $this->getMock( + '\Magento\Catalog\Helper\Data', + [], + [], + '', + false + ); + $this->storeResolver = $this->getMock( + '\Magento\CatalogImportExport\Model\Import\Product\StoreResolver', + [], + [], + '', + false + ); + $this->importProduct = $this->getMock( + '\Magento\CatalogImportExport\Model\Import\Product', + [], + [], + '', + false + ); + $this->validator = $this->getMock( + '\Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator', + ['isValid', 'getMessages'], + [], + '', + false + ); + $this->websiteValidator = $this->getMock( + '\Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\Website', + [], + [], + '', + false + ); + $this->groupPriceValidator = $this->getMock( + '\Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\GroupPrice', + [], + [], + '', + false + ); + + $this->advancedPricing = $this->getAdvancedPricingMock([ + 'retrieveOldSkus', + 'validateRow', + 'addRowError', + 'saveProductPrices', + 'getCustomerGroupId', + 'getWebSiteId', + 'deleteProductTierAndGroupPrices', + ]); + + $this->advancedPricing->expects($this->any())->method('retrieveOldSkus')->willReturn([]); + } + + public function testGetEntityTypeCode() + { + $result = $this->advancedPricing->getEntityTypeCode(); + $expectedResult = 'advanced_pricing'; + + $this->assertEquals($expectedResult, $result); + } + + /** + * @dataProvider validateRowResultDataProvider + */ + public function testValidateRowResult($rowData, $validatedRows, $invalidRows, $behavior, $expectedResult) + { + $rowNum = 0; + $advancedPricingMock = $this->getAdvancedPricingMock([ + 'retrieveOldSkus', + 'addRowError', + 'saveProductPrices', + 'getCustomerGroupId', + 'getWebSiteId', + 'getBehavior', + ]); + $this->setPropertyValue($advancedPricingMock, '_validatedRows', $validatedRows); + $this->setPropertyValue($advancedPricingMock, '_invalidRows', $invalidRows); + $this->validator->expects($this->any())->method('isValid')->willReturn(true); + $advancedPricingMock->expects($this->any())->method('getBehavior')->willReturn($behavior); + + $result = $advancedPricingMock->validateRow($rowData, $rowNum); + $this->assertEquals($expectedResult, $result); + } + + /** + * @dataProvider validateRowAddRowErrorCallDataProvider + */ + public function testValidateRowAddRowErrorCall($rowData, $validatedRows, $invalidRows, $behavior, $error) + { + $rowNum = 0; + $advancedPricingMock = $this->getAdvancedPricingMock([ + 'retrieveOldSkus', + 'addRowError', + 'saveProductPrices', + 'getCustomerGroupId', + 'getWebSiteId', + 'getBehavior', + ]); + $this->setPropertyValue($advancedPricingMock, '_validatedRows', $validatedRows); + $this->setPropertyValue($advancedPricingMock, '_invalidRows', $invalidRows); + $this->validator->expects($this->any())->method('isValid')->willReturn(true); + $advancedPricingMock->expects($this->any())->method('getBehavior')->willReturn($behavior); + $advancedPricingMock->expects($this->once())->method('addRowError')->with($error, $rowNum); + + $advancedPricingMock->validateRow($rowData, $rowNum); + } + + public function testValidateRowValidatorCall() + { + $rowNum = 0; + $rowData = [ + \Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing::COL_SKU => 'sku value', + ]; + $advancedPricingMock = $this->getAdvancedPricingMock([ + 'retrieveOldSkus', + 'addRowError', + 'saveProductPrices', + 'getCustomerGroupId', + 'getWebSiteId', + ]); + $this->setPropertyValue($advancedPricingMock, '_validatedRows', []); + $this->validator->expects($this->once())->method('isValid')->willReturn(false); + $messages = ['value']; + $this->validator->expects($this->once())->method('getMessages')->willReturn($messages); + $advancedPricingMock->expects($this->once())->method('addRowError')->with('value', $rowNum); + + $advancedPricingMock->validateRow($rowData, $rowNum); + } + + public function testSaveAdvancedPricingAddRowErrorCall() + { + $rowNum = 0; + $testBunch = [ + $rowNum => [ + 'bunch', + ] + ]; + $this->dataSourceModel->expects($this->at(0))->method('getNextBunch')->willReturn($testBunch); + $this->advancedPricing->expects($this->once())->method('validateRow')->willReturn(false); + $this->advancedPricing->expects($this->any())->method('saveProductPrices')->will($this->returnSelf()); + + $this->advancedPricing + ->expects($this->once()) + ->method('addRowError') + ->with(RowValidatorInterface::ERROR_SKU_IS_EMPTY, $rowNum); + + $this->advancedPricing->saveAdvancedPricing(); + } + + /** + * @dataProvider saveAdvancedPricingDataProvider + */ + public function testSaveAdvancedPricing( + $data, + $tierCustomerGroupId, + $groupCustomerGroupId, + $tierWebsiteId, + $groupWebsiteId, + $expectedTierPrices, + $expectedGroupPrices + ) { + $this->dataSourceModel->expects($this->at(0))->method('getNextBunch')->willReturn($data); + $this->advancedPricing->expects($this->once())->method('validateRow')->willReturn(true); + + $this->advancedPricing->expects($this->atLeastOnce())->method('getCustomerGroupId')->willReturnMap([ + [$data[0][AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP], $tierCustomerGroupId], + [$data[0][AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP], $groupCustomerGroupId] + ]); + + $this->advancedPricing->expects($this->atLeastOnce())->method('getWebSiteId')->willReturnMap([ + [$data[0][AdvancedPricing::COL_TIER_PRICE_WEBSITE], $tierWebsiteId], + [$data[0][AdvancedPricing::COL_GROUP_PRICE_WEBSITE], $groupWebsiteId] + ]); + + $this->advancedPricing->expects($this->exactly(2))->method('saveProductPrices')->withConsecutive( + [$expectedTierPrices, AdvancedPricing::TABLE_TIER_PRICE], + [$expectedGroupPrices, AdvancedPricing::TABLE_GROUPED_PRICE] + )->will($this->returnSelf()); + + $this->advancedPricing->saveAdvancedPricing(); + } + + public function testDeleteAdvancedPricingFormListSkuToDelete() + { + $skuOne = 'sku value'; + $skuTwo = 'sku value'; + $data = [ + 0 => [ + AdvancedPricing::COL_SKU => $skuOne + ], + 1 => [ + AdvancedPricing::COL_SKU => $skuTwo + ], + ]; + + $this->dataSourceModel->expects($this->at(0))->method('getNextBunch')->willReturn($data); + $this->advancedPricing->expects($this->any())->method('validateRow')->willReturn(true); + $expectedSkuList = ['sku value']; + $this->advancedPricing->expects($this->exactly(2))->method('deleteProductTierAndGroupPrices')->withConsecutive( + [$expectedSkuList, AdvancedPricing::TABLE_GROUPED_PRICE], + [$expectedSkuList, AdvancedPricing::TABLE_TIER_PRICE] + )->will($this->returnSelf()); + + $this->advancedPricing->deleteAdvancedPricing(); + } + + public function testDeleteAdvancedPricingResetCachedSkuToDelete() + { + $this->setPropertyValue($this->advancedPricing, '_cachedSkuToDelete', 'some value'); + $this->dataSourceModel->expects($this->at(0))->method('getNextBunch')->willReturn([]); + + $this->advancedPricing->deleteAdvancedPricing(); + + $cachedSkuToDelete = $this->getPropertyValue($this->advancedPricing, '_cachedSkuToDelete'); + $this->assertNull($cachedSkuToDelete); + } + + public function testReplaceAdvancedPricing() + { + $this->markTestSkipped('The method replaceAdvancedPricing is empty'); + } + + public function saveAdvancedPricingDataProvider() + { + // @codingStandardsIgnoreStart + return [ + [ + '$data' => [ + 0 => [ + AdvancedPricing::COL_SKU => 'sku value', + //tier + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'tier price website value', + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'tier price customer group value - not all groups ', + AdvancedPricing::COL_TIER_PRICE_QTY => 'tier price qty value', + AdvancedPricing::COL_TIER_PRICE => 'tier price value', + //group + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'group price website value', + AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP => 'group price customer group value - not all groups ', + AdvancedPricing::COL_GROUP_PRICE => 'group price value', + ], + ], + '$tierCustomerGroupId' => 'tier customer group id value', + '$groupCustomerGroupId' => 'group customer group id value', + '$tierWebsiteId' => 'tier website id value', + '$groupWebsiteId' => 'group website id value', + '$expectedTierPrices' => [ + 'sku value' => [ + [ + 'all_groups' => false,//$rowData[self::COL_TIER_PRICE_CUSTOMER_GROUP] == self::VALUE_ALL_GROUPS + 'customer_group_id' => 'tier customer group id value',//$tierCustomerGroupId + 'qty' => 'tier price qty value', + 'value' => 'tier price value', + 'website_id' => 'tier website id value', + ], + ], + ], + '$expectedGroupPrices' => [ + 'sku value' => [ + [ + 'all_groups' => AdvancedPricing::DEFAULT_ALL_GROUPS_GROUPED_PRICE_VALUE, + 'customer_group_id' => 'group customer group id value',//$groupCustomerGroupId + 'value' => 'group price value', + 'website_id' => 'group website id value', + ], + ], + ], + ], + [// tier customer group is equal to all group + '$data' => [ + 0 => [ + AdvancedPricing::COL_SKU => 'sku value', + //tier + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'tier price website value', + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => AdvancedPricing::VALUE_ALL_GROUPS, + AdvancedPricing::COL_TIER_PRICE_QTY => 'tier price qty value', + AdvancedPricing::COL_TIER_PRICE => 'tier price value', + //group + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'group price website value', + AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP => 'group price customer group value', + AdvancedPricing::COL_GROUP_PRICE => 'group price value', + ], + ], + '$tierCustomerGroupId' => 'tier customer group id value', + '$groupCustomerGroupId' => 'group customer group id value', + '$tierWebsiteId' => 'tier website id value', + '$groupWebsiteId' => 'group website id value', + '$expectedTierPrices' => [ + 'sku value' => [ + [ + 'all_groups' => true,//$rowData[self::COL_TIER_PRICE_CUSTOMER_GROUP] == self::VALUE_ALL_GROUPS + 'customer_group_id' => 'tier customer group id value',//$tierCustomerGroupId + 'qty' => 'tier price qty value', + 'value' => 'tier price value', + 'website_id' => 'tier website id value', + ], + ], + ], + '$expectedGroupPrices' => [ + 'sku value' => [ + [ + 'all_groups' => AdvancedPricing::DEFAULT_ALL_GROUPS_GROUPED_PRICE_VALUE, + 'customer_group_id' => 'group customer group id value',//$groupCustomerGroupId + 'value' => 'group price value', + 'website_id' => 'group website id value', + ], + ], + ], + ], + [ + '$data' => [ + 0 => [ + AdvancedPricing::COL_SKU => 'sku value', + //tier + AdvancedPricing::COL_TIER_PRICE_WEBSITE => null, + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'tier price customer group value - not all groups', + AdvancedPricing::COL_TIER_PRICE_QTY => 'tier price qty value', + AdvancedPricing::COL_TIER_PRICE => 'tier price value', + //group + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => 'group price website value', + AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP => 'group price customer group value', + AdvancedPricing::COL_GROUP_PRICE => 'group price value', + ], + ], + '$tierCustomerGroupId' => 'tier customer group id value', + '$groupCustomerGroupId' => 'group customer group id value', + '$tierWebsiteId' => 'tier website id value', + '$groupWebsiteId' => 'group website id value', + '$expectedTierPrices' => [], + '$expectedGroupPrices' => [ + 'sku value' => [ + [ + 'all_groups' => AdvancedPricing::DEFAULT_ALL_GROUPS_GROUPED_PRICE_VALUE, + 'customer_group_id' => 'group customer group id value',//$groupCustomerGroupId + 'value' => 'group price value', + 'website_id' => 'group website id value', + ], + ], + ], + ], + [ + '$data' => [ + 0 => [ + AdvancedPricing::COL_SKU => 'sku value', + //tier + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'tier price website value', + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'tier price customer group value - not all groups', + AdvancedPricing::COL_TIER_PRICE_QTY => 'tier price qty value', + AdvancedPricing::COL_TIER_PRICE => 'tier price value', + //group + AdvancedPricing::COL_GROUP_PRICE_WEBSITE => null, + AdvancedPricing::COL_GROUP_PRICE_CUSTOMER_GROUP => 'group price customer group value', + AdvancedPricing::COL_GROUP_PRICE => 'group price value', + ], + ], + '$tierCustomerGroupId' => 'tier customer group id value', + '$groupCustomerGroupId' => 'group customer group id value', + '$tierWebsiteId' => 'tier website id value', + '$groupWebsiteId' => 'group website id value', + '$expectedTierPrices' => [ + 'sku value' => [ + [ + 'all_groups' => false,//$rowData[self::COL_TIER_PRICE_CUSTOMER_GROUP] == self::VALUE_ALL_GROUPS + 'customer_group_id' => 'tier customer group id value',//$tierCustomerGroupId + 'qty' => 'tier price qty value', + 'value' => 'tier price value', + 'website_id' => 'tier website id value', + ], + ] + ], + '$expectedGroupPrices' => [], + ], + ]; + // @codingStandardsIgnoreEnd + } + + public function validateRowResultDataProvider() + { + return [ + [ + '$rowData' => [ + AdvancedPricing::COL_SKU => 'sku value', + ], + '$validatedRows' => [ + 0 => ['value'] + ], + '$invalidRows' => [ + 0 => ['value'] + ], + '$behavior' => null, + '$expectedResult' => false, + ], + [ + '$rowData' => [ + AdvancedPricing::COL_SKU => null, + ], + '$validatedRows' => [], + '$invalidRows' => [ + 0 => ['value'] + ], + '$behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_DELETE, + '$expectedResult' => false, + ], + [ + '$rowData' => [ + AdvancedPricing::COL_SKU => 'sku value', + ], + '$validatedRows' => [], + '$invalidRows' => [ + 0 => ['value'] + ], + '$behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_DELETE, + '$expectedResult' => true, + ], + [ + '$rowData' => [ + AdvancedPricing::COL_SKU => 'sku value', + ], + '$validatedRows' => [], + '$invalidRows' => [ + 0 => ['value'] + ], + '$behavior' => null, + '$expectedResult' => false, + ], + [ + '$rowData' => [ + AdvancedPricing::COL_SKU => 'sku value', + ], + '$validatedRows' => [], + '$invalidRows' => [], + '$behavior' => null, + '$expectedResult' => true, + ], + ]; + } + + public function validateRowAddRowErrorCallDataProvider() + { + return [ + [ + '$rowData' => [ + AdvancedPricing::COL_SKU => null, + ], + '$validatedRows' => [], + '$invalidRows' => [ + 0 => ['value'] + ], + '$behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_DELETE, + '$error' => RowValidatorInterface::ERROR_SKU_IS_EMPTY, + ], + [ + '$rowData' => [ + AdvancedPricing::COL_SKU => false, + ], + '$validatedRows' => [], + '$invalidRows' => [ + 0 => ['value'] + ], + '$behavior' => null, + '$error' => RowValidatorInterface::ERROR_ROW_IS_ORPHAN, + ], + ]; + } + + /** + * Get any object property value. + * + * @param $object + * @param $property + */ + protected function getPropertyValue($object, $property) + { + $reflection = new \ReflectionClass(get_class($object)); + $reflectionProperty = $reflection->getProperty($property); + $reflectionProperty->setAccessible(true); + + return $reflectionProperty->getValue($object); + } + + /** + * Set object property value. + * + * @param $object + * @param $property + * @param $value + */ + protected function setPropertyValue(&$object, $property, $value) + { + $reflection = new \ReflectionClass(get_class($object)); + $reflectionProperty = $reflection->getProperty($property); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($object, $value); + + return $object; + } + + /** + * Get AdvancedPricing Mock object with predefined methods. + * + * @param array $methods + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getAdvancedPricingMock($methods = []) + { + return $this->getMock( + '\Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing', + $methods, + [ + $this->jsonHelper, + $this->_importExportData, + $this->resourceHelper, + $this->dataSourceModel, + $this->_resource, + $this->resourceFactory, + $this->productModel, + $this->catalogData, + $this->storeResolver, + $this->importProduct, + $this->validator, + $this->websiteValidator, + $this->groupPriceValidator, + ], + '' + ); + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Indexer/Product/Price/Plugin/ImportTest.php b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Indexer/Product/Price/Plugin/ImportTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f12d4026ef829992e130ef794ecb43f34a1f99f9 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Indexer/Product/Price/Plugin/ImportTest.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdvancedPricingImportExport\Test\Unit\Model\Indexer\Product\Price\Plugin; + +use \Magento\AdvancedPricingImportExport\Model\Indexer\Product\Price\Plugin\Import as Import; + +class ImportTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Indexer\Model\IndexerInterface |\PHPUnit_Framework_MockObject_MockObject + */ + protected $indexer; + + /** + * @var Import |\PHPUnit_Framework_MockObject_MockObject + */ + protected $import; + + /** + * @var \Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing|\PHPUnit_Framework_MockObject_MockObject + */ + protected $advancedPricing; + + public function setUp() + { + $this->indexer = $this->getMockForAbstractClass('\Magento\Indexer\Model\IndexerInterface', [], '', false); + $this->import = $this->getMock( + '\Magento\AdvancedPricingImportExport\Model\Indexer\Product\Price\Plugin\Import', + ['getPriceIndexer', 'invalidateIndexer'], + [], + '', + false + ); + $this->advancedPricing = $this->getMock( + '\Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing', + [], + [], + '', + false + ); + $this->import->expects($this->any())->method('getPriceIndexer')->willReturn($this->indexer); + } + + public function testAfterSaveAdvancedPricing() + { + $this->indexer->expects($this->once())->method('isScheduled')->willReturn(false); + $this->import->expects($this->once())->method('invalidateIndexer'); + + $this->import->afterSaveAdvancedPricing($this->advancedPricing); + } + + public function testAfterDeleteAdvancedPricing() + { + $this->indexer->expects($this->once())->method('isScheduled')->willReturn(false); + $this->import->expects($this->once())->method('invalidateIndexer'); + + $this->import->afterSaveAdvancedPricing($this->advancedPricing); + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/composer.json b/app/code/Magento/AdvancedPricingImportExport/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..c8b52c1509eb996357137da8f799be5d85c864d7 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/composer.json @@ -0,0 +1,28 @@ +{ + "name": "magento/module-advanced-pricing-import-export", + "description": "N/A", + "require": { + "php": "~5.5.0|~5.6.0", + "magento/module-catalog": "0.74.0-beta13", + "magento/module-import-export": "0.74.0-beta13", + "magento/module-catalog-import-export": "0.74.0-beta13", + "magento/module-customer": "0.74.0-beta13", + "magento/module-store": "0.74.0-beta13", + "magento/framework": "0.74.0-beta13", + "magento/magento-composer-installer": "*" + }, + "type": "magento2-module", + "version": "0.74.0-beta13", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "extra": { + "map": [ + [ + "*", + "Magento/AdvancedPricingImportExport" + ] + ] + } +} \ No newline at end of file diff --git a/app/code/Magento/AdvancedPricingImportExport/etc/di.xml b/app/code/Magento/AdvancedPricingImportExport/etc/di.xml new file mode 100644 index 0000000000000000000000000000000000000000..eb738446923909369a712aa14e5f6b4379b1d5e5 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/etc/di.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd"> + <type name="Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing"> + <plugin name="invalidateAdvancedPriceIndexerOnImport" type="Magento\AdvancedPricingImportExport\Model\Indexer\Product\Price\Plugin\Import" /> + </type> + <type name="Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator"> + <arguments> + <argument name="validators" xsi:type="array"> + <item name="groupPrice" xsi:type="object">Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\GroupPrice</item> + <item name="tierPrice" xsi:type="object">Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\TierPrice</item> + <item name="website" xsi:type="object">Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\Website</item> + </argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/AdvancedPricingImportExport/etc/import.xml b/app/code/Magento/AdvancedPricingImportExport/etc/import.xml new file mode 100644 index 0000000000000000000000000000000000000000..0d113f745d6f8608e5ebbfcbff755ee8aa684aaf --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/etc/import.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../ImportExport/etc/import.xsd"> + <entity name="advanced_pricing" label="Advanced Pricing" model="Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing" behaviorModel="Magento\ImportExport\Model\Source\Import\Behavior\Basic" /> +</config> \ No newline at end of file diff --git a/app/code/Magento/AdvancedPricingImportExport/etc/module.xml b/app/code/Magento/AdvancedPricingImportExport/etc/module.xml new file mode 100644 index 0000000000000000000000000000000000000000..ef30304b507d2d1448bfc3b406ab6ae98d23b526 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/etc/module.xml @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd"> + <module name="Magento_AdvancedPricingImportExport" setup_version="2.0.0"> + </module> +</config> diff --git a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php index 9f30386f84f1d857e61ee21e8572af5e5a47ad42..e573c60346bb905ed6f6799f0d84321535068489 100755 --- a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php +++ b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php @@ -88,6 +88,13 @@ class Bundle extends \Magento\CatalogImportExport\Model\Import\Product\Type\Abst */ protected $_cachedSkuToProducts = []; + /** + * Array of queries selecting cached options. + * + * @var array + */ + protected $_cachedOptionSelectQuery = []; + /** * Column names that holds values with particular meaning. * diff --git a/app/code/Magento/Catalog/Block/Product/AbstractProduct.php b/app/code/Magento/Catalog/Block/Product/AbstractProduct.php index 0b62afa09de3400b5e24dff8f464d3a7dd5242d8..08617d3ebd8d49aeca3d59db2f58838cd16c03c7 100644 --- a/app/code/Magento/Catalog/Block/Product/AbstractProduct.php +++ b/app/code/Magento/Catalog/Block/Product/AbstractProduct.php @@ -598,4 +598,48 @@ class AbstractProduct extends \Magento\Framework\View\Element\Template \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); } + + /** + * Retrieve product details html + * + * @param \Magento\Catalog\Model\Product $product + * @return mixed + */ + public function getProductDetailsHtml(\Magento\Catalog\Model\Product $product) + { + $renderer = $this->getDetailsRenderer($product->getTypeId()); + if ($renderer) { + $renderer->setProduct($product); + return $renderer->toHtml(); + } + return ''; + } + + /** + * @param null $type + * @return bool|\Magento\Framework\View\Element\AbstractBlock + */ + public function getDetailsRenderer($type = null) + { + if ($type === null) { + $type = 'default'; + } + $rendererList = $this->getDetailsRendererList(); + if ($rendererList) { + return $rendererList->getRenderer($type, 'default'); + } + return null; + } + + /** + * @return \Magento\Framework\View\Element\RendererList + */ + protected function getDetailsRendererList() + { + return $this->getDetailsRendererListName() ? $this->getLayout()->getBlock( + $this->getDetailsRendererListName() + ) : $this->getChildBlock( + 'details.renderers' + ); + } } diff --git a/app/code/Magento/Catalog/view/frontend/layout/catalog_category_view.xml b/app/code/Magento/Catalog/view/frontend/layout/catalog_category_view.xml index f13c637c087fa6fb212bfd6ffec39d644a672213..dbc1ff5933f3b96bc560a9415461395cf9596319 100644 --- a/app/code/Magento/Catalog/view/frontend/layout/catalog_category_view.xml +++ b/app/code/Magento/Catalog/view/frontend/layout/catalog_category_view.xml @@ -18,6 +18,9 @@ <block class="Magento\Catalog\Block\Category\View" name="category.products" template="Magento_Catalog::category/products.phtml"> <block class="Magento\Catalog\Block\Product\ListProduct" name="category.products.list" as="product_list" template="Magento_Catalog::product/list.phtml"> <container name="category.product.list.additional" as="additional" /> + <block class="Magento\Framework\View\Element\RendererList" name="category.product.type.details.renderers" as="details.renderers"> + <block class="Magento\Framework\View\Element\Template" as="default"/> + </block> <block class="Magento\Catalog\Block\Product\ProductList\Toolbar" name="product_list_toolbar" template="product/list/toolbar.phtml"> <block class="Magento\Theme\Block\Html\Pager" name="product_list_toolbar_pager"/> <!-- The following code shows how to set your own pager increments --> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml index 154454f16dbfe579d73ac63eab37ae723710f7d5..c376d81d7fae39cbd40de974793de4129085bac2 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml @@ -69,6 +69,7 @@ $imageBlock = $block->getLayout()->createBlock('Magento\Catalog\Block\Product\I </strong> <?php echo $block->getReviewsSummaryHtml($_product, $templateType); ?> <?php echo $block->getProductPrice($_product) ?> + <?php echo $block->getProductDetailsHtml($_product); ?> <div class="product-item-inner"> <div class="product actions product-item-actions"<?php echo strpos($pos, $viewMode . '-actions') ? $position : ''; ?>> @@ -94,7 +95,7 @@ $imageBlock = $block->getLayout()->createBlock('Magento\Catalog\Block\Product\I </div> <div data-role="add-to-links" class="actions-secondary"<?php echo strpos($pos, $viewMode . '-secondary') ? $position : ''; ?>> <?php if ($this->helper('Magento\Wishlist\Helper\Data')->isAllow()): ?> - <a href="#" + <a href="#" class="action towishlist" title="<?php echo $block->escapeHtml(__('Add to Wish List')); ?>" aria-label="<?php echo $block->escapeHtml(__('Add to Wish List')); ?>" @@ -107,7 +108,7 @@ $imageBlock = $block->getLayout()->createBlock('Magento\Catalog\Block\Product\I <?php $compareHelper = $this->helper('Magento\Catalog\Helper\Product\Compare'); ?> - <a href="#" + <a href="#" class="action tocompare" title="<?php echo $block->escapeHtml(__('Add to Compare')); ?>" aria-label="<?php echo $block->escapeHtml(__('Add to Compare')); ?>" diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index d009107df13e59af9c7d1e0361a5259f95a50bd3..b7573b98eeade5a61dab274c12fb0e152704f217 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -6,6 +6,7 @@ namespace Magento\CatalogImportExport\Model\Export; use \Magento\Store\Model\Store; +use \Magento\CatalogImportExport\Model\Import\Product as ImportProduct; /** * Export entity product model @@ -41,6 +42,8 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity const COL_TYPE = '_type'; + const COL_PRODUCT_WEBSITES = '_product_websites'; + const COL_CATEGORY = '_category'; const COL_ROOT_CATEGORY = '_root_category'; @@ -49,6 +52,10 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity const COL_VISIBILITY = 'visibility'; + const COL_MEDIA_IMAGE = '_media_image'; + + const COL_ADDITIONAL_ATTRIBUTES = 'additional_attributes'; + /** * Pairs of attribute set ID-to-name. * @@ -77,10 +84,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity */ protected $_indexValueAttributes = [ 'status', - 'tax_class_id', - 'visibility', 'gift_message_available', - 'custom_design', ]; /** @@ -213,6 +217,96 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity */ protected $rowCustomizer; + /** + * Map between import file fields and system fields/attributes + * + * @var array + */ + 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', + Product::COL_STORE => 'store_view_code', + Product::COL_ATTR_SET => 'attribute_set_code', + Product::COL_TYPE => 'product_type', + Product::COL_CATEGORY => 'categories', + Product::COL_PRODUCT_WEBSITES => 'product_websites', + 'status' => 'product_online', + 'news_from_date' => 'new_from_date', + 'news_to_date' => 'new_to_date', + 'options_container' => 'display_product_options_in', + 'minimal_price' => 'map_price', + 'msrp' => 'msrp_price', + 'msrp_enabled' => 'map_enabled', + 'special_from_date' => 'special_price_from_date', + 'special_to_date' => 'special_price_to_date', + 'min_qty' => 'out_of_stock_qty', + 'backorders' => 'allow_backorders', + 'min_sale_qty' => 'min_cart_qty', + 'max_sale_qty' => 'max_cart_qty', + 'notify_stock_qty' => 'notify_on_stock_below', + 'meta_keyword' => 'meta_keywords', + 'tax_class_id' => 'tax_class_name', + ]; + + /** + * Attributes codes which are appropriate for export and not the part of additional_attributes. + * + * @var array + */ + protected $_exportMainAttrCodes = [ + self::COL_SKU, + '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', + 'special_price_from_date', + 'special_price_to_date', + 'gift_message_available', + 'custom_design', + 'custom_design_from', + 'custom_design_to', + 'custom_layout_update', + 'page_layout', + 'product_options_container', + 'msrp_price', + 'msrp_display_actual_price_type', + 'map_enabled', + 'country_of_manufacture', + 'map_price', + 'display_product_options_in', + ]; + /** * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate * @param \Magento\Eav\Model\Config $config @@ -370,75 +464,6 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity return $this; } - /** - * Prepare products tier prices - * - * @param int[] $productIds - * @return array - */ - protected function prepareTierPrices(array $productIds) - { - if (empty($productIds)) { - return []; - } - $select = $this->_connection->select()->from( - $this->_resourceModel->getTableName('catalog_product_entity_tier_price') - )->where( - 'entity_id IN(?)', - $productIds - ); - - $rowTierPrices = []; - $stmt = $this->_connection->query($select); - while ($tierRow = $stmt->fetch()) { - $rowTierPrices[$tierRow['entity_id']][] = [ - '_tier_price_customer_group' => $tierRow['all_groups'] - ? self::VALUE_ALL - : $tierRow['customer_group_id'], - '_tier_price_website' => 0 == - $tierRow['website_id'] ? self::VALUE_ALL : $this->_websiteIdToCode[$tierRow['website_id']], - '_tier_price_qty' => $tierRow['qty'], - '_tier_price_price' => $tierRow['value'], - ]; - } - - return $rowTierPrices; - } - - /** - * Prepare products group prices - * - * @param int[] $productIds - * @return array - */ - protected function prepareGroupPrices(array $productIds) - { - if (empty($productIds)) { - return []; - } - $select = $this->_connection->select()->from( - $this->_resourceModel->getTableName('catalog_product_entity_group_price') - )->where( - 'entity_id IN(?)', - $productIds - ); - - $rowGroupPrices = []; - $statement = $this->_connection->query($select); - while ($groupRow = $statement->fetch()) { - $rowGroupPrices[$groupRow['entity_id']][] = [ - '_group_price_customer_group' => $groupRow['all_groups'] - ? self::VALUE_ALL - : $groupRow['customer_group_id'], - '_group_price_website' => 0 == - $groupRow['website_id'] ? self::VALUE_ALL : $this->_websiteIdToCode[$groupRow['website_id']], - '_group_price_price' => $groupRow['value'], - ]; - } - - return $rowGroupPrices; - } - /** * Prepare products media gallery * @@ -599,14 +624,16 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity if (!isset($rowCategories[$productId])) { return false; } - - $categoryId = array_shift($rowCategories[$productId]); - if ($categoryId) { - $dataRow[self::COL_ROOT_CATEGORY] = $this->_rootCategories[$categoryId]; + $categories = []; + foreach ($rowCategories[$productId] as $categoryId) { + $categoryPath = $this->_rootCategories[$categoryId]; if (isset($this->_categories[$categoryId])) { - $dataRow[self::COL_CATEGORY] = $this->_categories[$categoryId]; + $categoryPath .= '/' . $this->_categories[$categoryId]; } + $categories[] = $categoryPath; } + $dataRow[self::COL_CATEGORY] = implode(ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR, $categories); + unset($rowCategories[$productId]); return true; } @@ -616,7 +643,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity */ public function _getHeaderColumns() { - return $this->_headerColumns; + return $this->_customHeadersMapping($this->_headerColumns); } /** @@ -630,18 +657,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity { if (!$this->_headerColumns) { $customOptCols = [ - '_custom_option_store', - '_custom_option_type', - '_custom_option_title', - '_custom_option_is_required', - '_custom_option_price', - '_custom_option_sku', - '_custom_option_max_characters', - '_custom_option_sort_order', - '_custom_option_row_title', - '_custom_option_row_price', - '_custom_option_row_sku', - '_custom_option_row_sort', + 'custom_options', ]; $this->_headerColumns = array_merge( [ @@ -650,23 +666,18 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity self::COL_ATTR_SET, self::COL_TYPE, self::COL_CATEGORY, - self::COL_ROOT_CATEGORY, '_product_websites', ], - $this->_getExportAttrCodes(), + $this->_getExportMainAttrCodes(), + [self::COL_ADDITIONAL_ATTRIBUTES], reset($stockItemRows) ? array_keys(end($stockItemRows)) : [], [], [ - '_related_sku', - '_related_position', - '_crosssell_sku', - '_crosssell_position', - '_upsell_sku', - '_upsell_position' + 'related_skus', + 'crosssell_skus', + 'upsell_skus', ], - ['_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'] + ['additional_images', 'additional_image_labels'] ); // have we merge custom options columns if ($customOptionsData) { @@ -675,6 +686,16 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity } } + /** + * Get attributes codes which are appropriate for export and not the part of additional_attributes. + * + * @return array + */ + protected function _getExportMainAttrCodes() + { + return $this->_exportMainAttrCodes; + } + /** * {@inheritdoc} */ @@ -767,7 +788,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity $writer->setHeaderCols($this->_getHeaderColumns()); } foreach ($exportData as $dataRow) { - $writer->writeRow($dataRow); + $writer->writeRow($this->_customFieldsMapping($dataRow)); } if ($entityCollection->getCurPage() >= $entityCollection->getLastPageNumber()) { break; @@ -833,6 +854,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity * @var \Magento\Catalog\Model\Product $item */ foreach ($collection as $itemId => $item) { + $additionalAttributes = []; foreach ($this->_getExportAttrCodes() as $code) { $attrValue = $item->getData($code); if (!$this->isValidAttributeValue($code, $attrValue)) { @@ -842,29 +864,42 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity if (isset($this->_attributeValues[$code][$attrValue]) && !empty($this->_attributeValues[$code])) { $attrValue = $this->_attributeValues[$code][$attrValue]; } + $fieldName = isset($this->_fieldsMap[$code]) ? $this->_fieldsMap[$code] : $code; if ($storeId != Store::DEFAULT_STORE_ID - && isset($data[$itemId][Store::DEFAULT_STORE_ID][$code]) - && $data[$itemId][Store::DEFAULT_STORE_ID][$code] == $attrValue + && isset($data[$itemId][Store::DEFAULT_STORE_ID][$fieldName]) + && $data[$itemId][Store::DEFAULT_STORE_ID][$fieldName] == $attrValue ) { continue; } if ($this->_attributeTypes[$code] !== 'multiselect') { if (is_scalar($attrValue)) { - $data[$itemId][$storeId][$code] = $attrValue; + if (!in_array($fieldName, $this->_getExportMainAttrCodes())) { + $additionalAttributes[$fieldName] = $fieldName . + ImportProduct::PAIR_NAME_VALUE_SEPARATOR . $attrValue; + } + $data[$itemId][$storeId][$fieldName] = $attrValue; + } else { + $this->collectMultiselectValues($item, $code, $storeId); } - } else { - $this->collectMultiselectValues($item, $code, $storeId); } } + if (!empty($additionalAttributes)) { + $data[$itemId][$storeId][self::COL_ADDITIONAL_ATTRIBUTES] = + implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $additionalAttributes); + } else { + unset($data[$itemId][$storeId][self::COL_ADDITIONAL_ATTRIBUTES]); + } + if (!empty($data[$itemId][$storeId]) || $this->hasMultiselectData($item, $storeId)) { $attrSetId = $item->getAttributeSetId(); $data[$itemId][$storeId][self::COL_STORE] = $storeCode; $data[$itemId][$storeId][self::COL_ATTR_SET] = $this->_attrSetIdToName[$attrSetId]; $data[$itemId][$storeId][self::COL_TYPE] = $item->getTypeId(); } + $data[$itemId][$storeId][self::COL_SKU] = $item->getSku(); $data[$itemId][$storeId]['store_id'] = $storeId; $data[$itemId][$storeId]['product_id'] = $itemId; } @@ -906,8 +941,6 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity $data['rowWebsites'] = $rowWebsites; $data['rowCategories'] = $rowCategories; $data['mediaGalery'] = $this->getMediaGallery($productIds); - $data['rowTierPrices'] = $this->prepareTierPrices($productIds); - $data['rowGroupPrices'] = $this->prepareGroupPrices($productIds); $data['linksRows'] = $this->prepareLinks($productIds); $data['customOptionsData'] = $this->getCustomOptionsData($productIds); @@ -934,7 +967,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity protected function collectMultiselectValues($item, $attrCode, $storeId) { $attrValue = $item->getData($attrCode); - $optionIds = explode(',', $attrValue); + $optionIds = explode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $attrValue); $options = array_intersect_key( $this->_attributeValues[$attrCode], array_flip($optionIds) @@ -969,85 +1002,152 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity /** * @param array $dataRow - * @param array $multirawData + * @param array $multiRawData * @return array * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ - protected function addMultirowData($dataRow, $multirawData) + protected function addMultirowData($dataRow, $multiRawData) { $result = []; $productId = $dataRow['product_id']; $storeId = $dataRow['store_id']; + $sku = $dataRow[self::COL_SKU]; unset($dataRow['product_id']); unset($dataRow['store_id']); - - while (true) { - if (Store::DEFAULT_STORE_ID == $storeId) { - unset($dataRow[self::COL_STORE]); - $this->updateDataWithCategoryColumns($dataRow, $multirawData['rowCategories'], $productId); - if (!empty($multirawData['rowWebsites'][$productId])) { - $dataRow['_product_websites'] = $this->_websiteIdToCode[ - array_shift($multirawData['rowWebsites'][$productId]) - ]; - } - if (!empty($multirawData['rowTierPrices'][$productId])) { - $dataRow = array_merge($dataRow, array_shift($multirawData['rowTierPrices'][$productId])); + unset($dataRow[self::COL_SKU]); + + if (Store::DEFAULT_STORE_ID == $storeId) { + unset($dataRow[self::COL_STORE]); + $this->updateDataWithCategoryColumns($dataRow, $multiRawData['rowCategories'], $productId); + if (!empty($multiRawData['rowWebsites'][$productId])) { + $websiteCodes = []; + foreach ($multiRawData['rowWebsites'][$productId] as $productWebsite) { + $websiteCodes[] = $this->_websiteIdToCode[$productWebsite]; } - if (!empty($multirawData['rowGroupPrices'][$productId])) { - $dataRow = array_merge($dataRow, array_shift($multirawData['rowGroupPrices'][$productId])); - } - if (!empty($multirawData['mediaGalery'][$productId])) { - $dataRow = array_merge($dataRow, array_shift($multirawData['mediaGalery'][$productId])); + $dataRow['_product_websites'] = + implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $websiteCodes); + $multiRawData['rowWebsites'][$productId] = []; + } + if (!empty($multiRawData['mediaGalery'][$productId])) { + $additionalImages = []; + $additionalImageLabels = []; + foreach ($multiRawData['mediaGalery'][$productId] as $mediaItem) { + $additionalImages[] = $mediaItem['_media_image']; + $additionalImageLabels[] = $mediaItem['_media_label']; } - foreach ($this->_linkTypeProvider->getLinkTypes() as $linkTypeName => $linkId) { - if (!empty($multirawData['linksRows'][$productId][$linkId])) { - $colPrefix = '_' . $linkTypeName . '_'; - - $linkData = array_shift($multirawData['linksRows'][$productId][$linkId]); - $dataRow[$colPrefix . 'position'] = $linkData['position']; - $dataRow[$colPrefix . 'sku'] = $linkData['sku']; + $dataRow['additional_images'] = + implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $additionalImages); + $dataRow['additional_image_labels'] = + implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $additionalImageLabels); + $multiRawData['mediaGalery'][$productId] = []; + } + foreach ($this->_linkTypeProvider->getLinkTypes() as $linkTypeName => $linkId) { + if (!empty($multiRawData['linksRows'][$productId][$linkId])) { + $colPrefix = $linkTypeName . '_'; + $associations = []; + foreach ($multiRawData['linksRows'][$productId][$linkId] as $linkData) { if ($linkData['default_qty'] !== null) { - $dataRow[$colPrefix . 'default_qty'] = $linkData['default_qty']; + $skuItem = $linkData['sku'] . ImportProduct::PAIR_NAME_VALUE_SEPARATOR . + $linkData['default_qty']; + } else { + $skuItem = $linkData['sku']; } + $associations[$skuItem] = $linkData['position']; } + $multiRawData['linksRows'][$productId][$linkId] = []; + asort($associations); + $dataRow[$colPrefix . 'skus'] = + implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, array_keys($associations)); } - $dataRow = $this->rowCustomizer->addData($dataRow, $productId); + } + $dataRow = $this->rowCustomizer->addData($dataRow, $productId); - if (!empty($multirawData['customOptionsData'][$productId])) { - $dataRow = array_merge($dataRow, array_shift($multirawData['customOptionsData'][$productId])); + } + + if (!empty($this->collectedMultiselectsData[$storeId][$productId])) { + 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, + $this->collectedMultiselectsData[$storeId][$productId][$attrKey] + ); } } + } - if (!empty($this->collectedMultiselectsData[$storeId][$productId])) { - foreach (array_keys($this->collectedMultiselectsData[$storeId][$productId]) as $attrKey) { - if (!empty($this->collectedMultiselectsData[$storeId][$productId][$attrKey])) { - $dataRow[$attrKey] = array_shift( - $this->collectedMultiselectsData[$storeId][$productId][$attrKey] - ); - } - } + if (!empty($multiRawData['customOptionsData'][$productId][$storeId])) { + $customOptionsRows = $multiRawData['customOptionsData'][$productId][$storeId]; + $multiRawData['customOptionsData'][$productId][$storeId] = []; + $customOptions = implode(ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR, $customOptionsRows); + + $dataRow = array_merge($dataRow, ['custom_options' => $customOptions]); + } + + if (empty($dataRow)) { + return $result; + } elseif ($storeId != Store::DEFAULT_STORE_ID) { + $dataRow[self::COL_STORE] = $this->_storeIdToCode[$storeId]; + if (isset($productData[Store::DEFAULT_STORE_ID][self::COL_VISIBILITY])) { + $dataRow[self::COL_VISIBILITY] = $productData[Store::DEFAULT_STORE_ID][self::COL_VISIBILITY]; } + } + $dataRow[self::COL_SKU] = $sku; + $result[] = $dataRow; - if (empty($dataRow)) { - break; - } elseif ($storeId != Store::DEFAULT_STORE_ID) { - $dataRow[self::COL_STORE] = $this->_storeIdToCode[$storeId]; - $dataRow[self::COL_SKU] = null; - $dataRow[self::COL_ATTR_SET] = null; - $dataRow[self::COL_TYPE] = null; - if (isset($productData[Store::DEFAULT_STORE_ID][self::COL_VISIBILITY])) { - $dataRow[self::COL_VISIBILITY] = $productData[Store::DEFAULT_STORE_ID][self::COL_VISIBILITY]; - } + return $result; + } + + /** + * Custom fields mapping for changed purposes of fields and field names + * + * @param array $rowData + * + * @return array + */ + protected function _customFieldsMapping($rowData) + { + foreach ($this->_fieldsMap as $systemFieldName => $fileFieldName) { + if (isset($rowData[$systemFieldName])) { + $rowData[$fileFieldName] = $rowData[$systemFieldName]; + unset($rowData[$systemFieldName]); + } + } + return $rowData; + } + + /** + * Custom headers mapping for changed field names + * + * @param array $rowData + * + * @return array + */ + protected function _customHeadersMapping($rowData) + { + foreach ($rowData as $key => $fieldName) { + if (isset($this->_fieldsMap[$fieldName])) { + $rowData[$key] = $this->_fieldsMap[$fieldName]; } + } + return $rowData; + } + + /** + * @param array $option + * @return string + */ + protected function optionRowToCellString($option) + { + $result = []; - $result[] = $dataRow; - $dataRow = []; + foreach ($option as $key => $value) { + $result[] = $key . ImportProduct::PAIR_NAME_VALUE_SEPARATOR . $value; } - return $result; + return implode(ImportProduct::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $result); } /** @@ -1059,10 +1159,15 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity protected function getCustomOptionsData($productIds) { $customOptionsData = []; - $customOptionsDataPre = []; foreach (array_keys($this->_storeIdToCode) as $storeId) { - $options = $this->_optionColFactory->create()->reset()->addTitleToResult( + if (Store::DEFAULT_STORE_ID != $storeId) { + continue; + } + $options = $this->_optionColFactory->create(); + /* @var \Magento\Catalog\Model\Resource\Product\Option\Collection $options*/ + $options->addOrder('sort_order'); + $options->reset()->addOrder('sort_order')->addTitleToResult( $storeId )->addPriceToResult( $storeId @@ -1075,75 +1180,33 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity foreach ($options as $option) { $row = []; $productId = $option['product_id']; - $optionId = $option['option_id']; - - if (Store::DEFAULT_STORE_ID == $storeId) { - $row['_custom_option_type'] = $option['type']; - $row['_custom_option_title'] = $option['title']; - $row['_custom_option_is_required'] = $option['is_require']; - $row['_custom_option_price'] = $option['price'] . ($option['price_type'] == 'percent' ? '%' : ''); - $row['_custom_option_sku'] = $option['sku']; - $row['_custom_option_max_characters'] = $option['max_characters']; - $row['_custom_option_sort_order'] = $option['sort_order']; - } else { - $row['_custom_option_title'] = $option['title']; - } - $values = $option->getValues(); - if ($values) { - $firstValue = array_shift($values); - $priceType = $firstValue['price_type'] == 'percent' ? '%' : ''; - - if (Store::DEFAULT_STORE_ID == $storeId) { - $row['_custom_option_row_title'] = $firstValue['title']; - $row['_custom_option_row_price'] = $firstValue['price'] . $priceType; - $row['_custom_option_row_sku'] = $firstValue['sku']; - $row['_custom_option_row_sort'] = $firstValue['sort_order']; - } else { - $row['_custom_option_row_title'] = $firstValue['title']; - } - } - if (Store::DEFAULT_STORE_ID != $storeId) { - $row['_custom_option_store'] = $this->_storeIdToCode[$storeId]; - } - $customOptionsDataPre[$productId][$optionId][] = $row; + $row['name'] = $option['title']; + $row['type'] = $option['type']; + $row['required'] = $option['is_require']; + $row['price'] = $option['price']; + $row['price_type'] = ($option['price_type'] == 'percent') ? $option['price_type'] : 'fixed'; + $row['sku'] = $option['sku']; + + $values = $option->getValues(); if ($values) { foreach ($values as $value) { - $row = []; - $valuePriceType = $value['price_type'] == 'percent' ? '%' : ''; - - if (Store::DEFAULT_STORE_ID == $storeId) { - $row['_custom_option_row_title'] = $value['title']; - $row['_custom_option_row_price'] = $value['price'] . $valuePriceType; - $row['_custom_option_row_sku'] = $value['sku']; - $row['_custom_option_row_sort'] = $value['sort_order']; - } else { - $row['_custom_option_row_title'] = $value['title']; - } - if ($row) { - if (Store::DEFAULT_STORE_ID != $storeId) { - $row['_custom_option_store'] = $this->_storeIdToCode[$storeId]; - } - $customOptionsDataPre[$option['product_id']][$optionId][] = $row; - } + $valuePriceType = ($value['price_type'] == 'percent') ? $value['price_type'] : 'fixed'; + $row['option_title'] = $value['title']; + $row['price'] = $value['price']; + $row['price_type'] = $valuePriceType; + $row['sku'] = $value['sku']; + $customOptionsData[$productId][$storeId][] = $this->optionRowToCellString($row); } + } else { + $customOptionsData[$productId][$storeId][] = $this->optionRowToCellString($row); } $option = null; } $options = null; } - foreach ($customOptionsDataPre as $productId => $optionsData) { - $customOptionsData[$productId] = []; - foreach ($optionsData as $optionId => $optionRows) { - $customOptionsData[$productId] = array_merge( - $customOptionsData[$productId], - $optionRows - ); - } - } - return $customOptionsData; } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 82e2153c4a5295829fdf4e879739d101dfa6b9b7..ca248b50e394bb2d74e84a81d33d76fd1621963d 100755 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -541,6 +541,13 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity */ protected $_replaceFlag = null; + /** + * Flag for replace operation. + * + * @var null + */ + protected $cachedImages = null; + /** * @param \Magento\Framework\Json\Helper\Data $jsonHelper * @param \Magento\ImportExport\Helper\Data $importExportData @@ -1209,6 +1216,41 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity return $this; } + /** + * Prepare all media files + * + * @return $this + */ + protected function _prepareAllMediaFiles() + { + static $productEntityTableName = null; + static $productMediaGalleryTableName = null; + static $resource = null; + if (!$resource) { + $resource = $this->_resourceFactory->create(); + } + if (!$productEntityTableName) { + $productEntityTableName = $resource->getTable('catalog_product_entity'); + } + if (!$productMediaGalleryTableName) { + $productMediaGalleryTableName = $resource->getTable('catalog_product_entity_media_gallery'); + } + if(empty($this->cachedImages)) { + $allMedia = $this->_connection->fetchAll($this->_connection->select() + ->from( + ["entity" => $productEntityTableName], + ['sku'] + )->joinLeft( + ["media_gallery" => $productMediaGalleryTableName], + "entity.entity_id = media_gallery.entity_id", + ['value'] + ) + ); + $this->cachedImages = $allMedia; + } + return $this; + } + /** * Gather and save information about product entities. * @@ -1222,6 +1264,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity /** @var $resource \Magento\CatalogImportExport\Model\Import\Proxy\Product\Resource */ $resource = $this->_resourceFactory->create(); $priceIsGlobal = $this->_catalogData->isPriceGlobal(); + $this->_prepareAllMediaFiles(); $productLimit = null; $productsQty = null; @@ -1285,7 +1328,8 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity // 2. Product-to-Website phase if (!empty($rowData[self::COL_PRODUCT_WEBSITES])) { - $websites[$rowSku][$this->storeResolver->getWebsiteCodeToId($rowData[self::COL_PRODUCT_WEBSITES])] = true; + $websiteId = $this->storeResolver->getWebsiteCodeToId($rowData[self::COL_PRODUCT_WEBSITES]); + $websites[$rowSku][$websiteId] = true; } // 3. Categories phase @@ -1322,54 +1366,136 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity } // 5. Media gallery phase - $mediaGalleryImages = array(); - $mediaGalleryLabels = array(); + + $fullDispersionPath = ''; + $imageIsSet = null; + $imageFromProduct = null; + $imageInProductIsSet = null; if (!empty($rowData[self::COL_MEDIA_IMAGE])) { - $mediaGalleryImages = explode($this->getMultipleValueSeparator(), $rowData[self::COL_MEDIA_IMAGE]); - $mediaGalleryLabels = isset($rowData['_media_image_label']) ? explode($this->getMultipleValueSeparator(), $rowData['_media_image_label']) : array(); - if (count($mediaGalleryLabels) > count($mediaGalleryImages)) { - $mediaGalleryLabels = array_slice($mediaGalleryLabels, 0, count($mediaGalleryImages)); - } elseif (count($mediaGalleryLabels) < count($mediaGalleryImages)) { - $mediaGalleryLabels = array_pad($mediaGalleryLabels, count($mediaGalleryImages), ''); + $dispersionPath = + \Magento\Framework\File\Uploader::getDispretionPath($rowData[self::COL_MEDIA_IMAGE]); + $imageName = preg_replace('/[^a-z0-9\._-]+/i', '', $rowData[self::COL_MEDIA_IMAGE]); + $fullDispersionPath = $dispersionPath . '/' . $imageName; + foreach ($this->cachedImages as $image) { + if (($image['sku'] == $rowData[self::COL_SKU]) + && (preg_replace('/_[0-9]+/', '', $image['value']) == $fullDispersionPath) + ) { + $imageInProductIsSet = true; + $imageFromProduct = preg_replace('/_[0-9]+/', '', $image['value']); + break; + } elseif (in_array($fullDispersionPath, $image)) { + $imageIsSet = true; + break; + } } } + if (($imageInProductIsSet && ($imageFromProduct != $fullDispersionPath)) + || (!isset($imageIsSet) && !isset($imageInProductIsSet)) + ) { + $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]; - $mediaGalleryLabels[] = isset($rowData[$imageCol . '_label']) ? $rowData[$imageCol . '_label'] : ''; + 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']); + } else { + $mediaGalleryLabels[] = ''; + } + } } - } - $rowData[self::COL_MEDIA_IMAGE] = array(); + $rowData[self::COL_MEDIA_IMAGE] = array(); + foreach ($mediaGalleryImages as $mediaImage) { + if (!array_key_exists($mediaImage, $uploadedGalleryFiles)) { + $uploadedGalleryFiles[$mediaImage] = $this->_uploadMediaFiles( + trim($mediaImage) + ); + } + $rowData[self::COL_MEDIA_IMAGE][] = $uploadedGalleryFiles[$mediaImage]; + } - foreach ($mediaGalleryImages as $mediaImage) { - if (!array_key_exists($mediaImage, $uploadedGalleryFiles)) { - $uploadedGalleryFiles[$mediaImage] = $this->_uploadMediaFiles( - trim($mediaImage) - ); + foreach ($this->_imagesArrayKeys as $imageCol) { + if (!empty($rowData[$imageCol]) && ($imageCol != self::COL_MEDIA_IMAGE)) { + $rowData[$imageCol] = $uploadedGalleryFiles[$rowData[$imageCol]]; + } } - $rowData[self::COL_MEDIA_IMAGE][] = $uploadedGalleryFiles[$mediaImage]; - } - foreach ($this->_imagesArrayKeys as $imageCol) { - if (!empty($rowData[$imageCol]) && ($imageCol != self::COL_MEDIA_IMAGE)) { - $rowData[$imageCol] = $uploadedGalleryFiles[$rowData[$imageCol]]; + if (!empty($rowData[self::COL_MEDIA_IMAGE]) && is_array($rowData[self::COL_MEDIA_IMAGE])) { + $position = 0; + + foreach ($rowData[self::COL_MEDIA_IMAGE] as $media_image) { + $mediaGallery[$rowSku][] = [ + 'attribute_id' => $this->getMediaGalleryAttributeId(), + 'label' => isset($mediaGalleryLabels[$position]) ? $mediaGalleryLabels[$position] : '', + 'position' => $position++, + 'disabled' => '', + 'value' => $media_image, + ]; + } + } + } elseif ($imageInProductIsSet && $imageFromProduct == $fullDispersionPath) { + $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 = array(); + } + if (count($mediaGalleryLabels) > count($mediaGalleryImages)) { + $mediaGalleryLabels = array_slice($mediaGalleryLabels, 0, count($mediaGalleryImages)); + } elseif (count($mediaGalleryLabels) < count($mediaGalleryImages)) { + $mediaGalleryLabels = array_pad($mediaGalleryLabels, count($mediaGalleryImages), ''); + } } - } - if (!empty($rowData[self::COL_MEDIA_IMAGE]) && is_array($rowData[self::COL_MEDIA_IMAGE])) { - $position = 0; + foreach ($this->_imagesArrayKeys as $imageCol) { + if (!empty($rowData[$imageCol]) + && ($imageCol != self::COL_MEDIA_IMAGE) + && !in_array($rowData[$imageCol], $mediaGalleryImages)) { + $mediaGalleryImages[] = $rowData[$imageCol]; + if (isset($rowData[$imageCol . '_label'])) { + $mediaGalleryLabels[] = $rowData[$imageCol . '_label']; + } else { + $mediaGalleryLabels[] = ''; + } + } + } - foreach($rowData[self::COL_MEDIA_IMAGE] as $media_image) { - $mediaGallery[$rowSku][] = [ - 'attribute_id' => $this->getMediaGalleryAttributeId(), - 'label' => isset($mediaGalleryLabels[$position]) ? $mediaGalleryLabels[$position] : '', - 'position' => $position++, - 'disabled' => '', - 'value' => $media_image, - ]; + $rowData[self::COL_MEDIA_IMAGE] = array(); + foreach ($mediaGalleryImages as $mediaImage) { + if (!array_key_exists($mediaImage, $uploadedGalleryFiles)) { + $uploadedGalleryFiles[$mediaImage] = $this->_uploadMediaFiles( + trim($mediaImage), + true + ); + } + $rowData[self::COL_MEDIA_IMAGE][] = $uploadedGalleryFiles[$mediaImage]; } + } else { + $this->addRowError(__("Image already exists for '%s'"), $rowNum, self::COL_MEDIA_IMAGE); } // 6. Attributes phase @@ -1398,7 +1524,8 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $productTypeModel = $this->_productTypeModels[$productType]; if (!empty($rowData['tax_class_name'])) { - $rowData['tax_class_id'] = $this->taxClassProcessor->upsertTaxClass($rowData['tax_class_name'], $productTypeModel); + $rowData['tax_class_id'] = + $this->taxClassProcessor->upsertTaxClass($rowData['tax_class_name'], $productTypeModel); } if ($this->getBehavior() == \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND || @@ -1482,7 +1609,10 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $attributes ); - $this->_eventManager->dispatch('catalog_product_import_bunch_save_after', ['adapter' => $this, 'bunch' => $bunch]); + $this->_eventManager->dispatch( + 'catalog_product_import_bunch_save_after', + ['adapter' => $this, 'bunch' => $bunch] + ); } return $this; } @@ -1614,10 +1744,10 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity * @param string $fileName * @return string */ - protected function _uploadMediaFiles($fileName) + protected function _uploadMediaFiles($fileName, $renameFileOff=false) { try { - $res = $this->_getUploader()->move($fileName); + $res = $this->_getUploader()->move($fileName, $renameFileOff); return $res['file']; } catch (\Exception $e) { return ''; diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php index d694b7720237f590ec31b5bd06dbbb7c09852966..0de5545a49c108f501d406d0a2546ba84ef5a8db 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php @@ -135,10 +135,14 @@ class Uploader extends \Magento\MediaStorage\Model\File\Uploader * Proceed moving a file from TMP to destination folder * * @param string $fileName + * @param bool $renameFileOff * @return array */ - public function move($fileName) + public function move($fileName, $renameFileOff = false) { + if ($renameFileOff) { + $this->setAllowRenameFiles(false); + } if (preg_match('/\bhttps?:\/\//i', $fileName, $matches)) { $url = str_replace($matches[0], '', $fileName); $read = $this->_readFactory->create($url, DriverPool::HTTP); diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Export/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Export/ProductTest.php index 3379c3fc25a544de356e5f4998c586e2846307aa..b6333d9bcb301ebf537b96dbc445af9ed869b690 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Export/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Export/ProductTest.php @@ -5,21 +5,345 @@ */ namespace Magento\CatalogImportExport\Test\Unit\Model\Export; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use \Magento\Store\Model\Store; + +/** + * @SuppressWarnings(PHPMD) + */ class ProductTest extends \PHPUnit_Framework_TestCase { + /** + * @var \Magento\Framework\Stdlib\DateTime\Timezone|\PHPUnit_Framework_MockObject_MockObject + */ + protected $localeDate; + + /** + * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject + */ + protected $config; + + /** + * @var \Magento\Framework\App\Resource|\PHPUnit_Framework_MockObject_MockObject + */ + protected $resource; + + /** + * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $storeManager; + + /** + * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $logger; + + /** + * @var \Magento\Catalog\Model\Resource\Product\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $collection; + + /** + * @var \Magento\Eav\Model\Entity\Collection\AbstractCollection|\PHPUnit_Framework_MockObject_MockObject + */ + protected $abstractCollection; + + /** + * @var \Magento\ImportExport\Model\Export\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $exportConfig; + + /** + * @var \Magento\Catalog\Model\Resource\ProductFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productFactory; + + /** + * @var \Magento\Eav\Model\Resource\Entity\Attribute\Set\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $attrSetColFactory; + + /** + * @var \Magento\Catalog\Model\Resource\Category\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $categoryColFactory; + + /** + * @var \Magento\CatalogInventory\Model\Resource\Stock\ItemFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $itemFactory; + + /** + * @var \Magento\Catalog\Model\Resource\Product\Option\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $optionColFactory; + + /** + * @var \Magento\Catalog\Model\Resource\Product\Attribute\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $attributeColFactory; + + /** + * @var \Magento\CatalogImportExport\Model\Export\Product\Type\Factory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $typeFactory; + + /** + * @var \Magento\Catalog\Model\Product\LinkTypeProvider|\PHPUnit_Framework_MockObject_MockObject + */ + protected $linkTypeProvider; + + /** + * @var \Magento\CatalogImportExport\Model\Export\RowCustomizer\Composite|\PHPUnit_Framework_MockObject_MockObject + */ + protected $rowCustomizer; + + /** + * @var \Magento\ImportExport\Model\Export\Adapter\AbstractAdapter| \PHPUnit_Framework_MockObject_MockObject + */ + protected $writer; + + /** + * @var \Magento\CatalogImportExport\Model\Export\Product|\PHPUnit_Framework_MockObject_MockObject + */ + protected $product; + + /** * @var StubProduct|\Magento\CatalogImportExport\Model\Export\Product */ - protected $_object; + protected $object; protected function setUp() { - $this->_object = new StubProduct(); + $this->localeDate = $this->getMock( + 'Magento\Framework\Stdlib\DateTime\Timezone', + [], + [], + '', + false + ); + + $this->config = $this->getMock( + 'Magento\Eav\Model\Config', + ['getEntityType'], + [], + '', + false + ); + $type = $this->getMock( + '\Magento\Eav\Model\Entity\Type', + [], + [], + '', + false + ); + $this->config->expects($this->once())->method('getEntityType')->willReturn($type); + + $this->resource = $this->getMock( + 'Magento\Framework\App\Resource', + [], + [], + '', + false + ); + + $this->storeManager = $this->getMock( + 'Magento\Store\Model\StoreManager', + [], + [], + '', + false + ); + $this->logger = $this->getMock( + 'Magento\Framework\Logger\Monolog', + [], + [], + '', + false + ); + + $this->collection = $this->getMock( + '\Magento\Catalog\Model\Resource\Product\CollectionFactory', + [], + [], + '', + false + ); + $this->abstractCollection = $this->getMockForAbstractClass( + '\Magento\Eav\Model\Entity\Collection\AbstractCollection', + [], + '', + false, + true, + true, + [ + 'count', + 'setOrder', + 'setStoreId', + 'getCurPage', + 'getLastPageNumber', + ] + ); + $this->exportConfig = $this->getMock( + 'Magento\ImportExport\Model\Export\Config', + [], + [], + '', + false + ); + + $this->productFactory = $this->getMock( + 'Magento\Catalog\Model\Resource\ProductFactory', + [ + 'create', + 'getTypeId', + ], + [], + '', + false + ); + + $this->attrSetColFactory = $this->getMock( + 'Magento\Eav\Model\Resource\Entity\Attribute\Set\CollectionFactory', + [ + 'create', + 'setEntityTypeFilter', + ], + [], + '', + false + ); + + $this->categoryColFactory = $this->getMock( + 'Magento\Catalog\Model\Resource\Category\CollectionFactory', + [ + 'create', + 'addNameToResult', + ], + [], + '', + false + ); + + $this->itemFactory = $this->getMock( + 'Magento\CatalogInventory\Model\Resource\Stock\ItemFactory', + [], + [], + '', + false + ); + $this->optionColFactory = $this->getMock( + 'Magento\Catalog\Model\Resource\Product\Option\CollectionFactory', + [], + [], + '', + false + ); + + $this->attributeColFactory = $this->getMock( + 'Magento\Catalog\Model\Resource\Product\Attribute\CollectionFactory', + [], + [], + '', + false + ); + $this->typeFactory = $this->getMock( + 'Magento\CatalogImportExport\Model\Export\Product\Type\Factory', + [], + [], + '', + false + ); + + $this->linkTypeProvider = $this->getMock( + 'Magento\Catalog\Model\Product\LinkTypeProvider', + [], + [], + '', + false + ); + $this->rowCustomizer = $this->getMock( + 'Magento\CatalogImportExport\Model\Export\RowCustomizer\Composite', + [], + [], + '', + false + ); + + $this->writer = $this->getMock( + 'Magento\ImportExport\Model\Export\Adapter\AbstractAdapter', + [ + 'setHeaderCols', + 'writeRow', + 'getContents', + ], + [], + '', + false + ); + + $constructorMethods = [ + 'initTypeModels', + 'initAttributes', + '_initStores', + 'initAttributeSets', + 'initWebsites', + 'initCategories' + ]; + + $mockMethods = array_merge($constructorMethods, [ + '_customHeadersMapping', + '_prepareEntityCollection', + '_getEntityCollection', + 'getWriter', + 'getExportData', + '_headerColumns', + '_customFieldsMapping', + 'getItemsPerPage', + 'paginateCollection', + '_getHeaderColumns', + ]); + $this->product = $this->getMock( + 'Magento\CatalogImportExport\Model\Export\Product', + $mockMethods, + [], + '', + false + ); + + foreach ($constructorMethods as $method) { + $this->product->expects($this->once())->method($method)->will($this->returnSelf()); + } + + $this->product->__construct( + $this->localeDate, + $this->config, + $this->resource, + $this->storeManager, + $this->logger, + $this->collection, + $this->exportConfig, + $this->productFactory, + $this->attrSetColFactory, + $this->categoryColFactory, + $this->itemFactory, + $this->optionColFactory, + $this->attributeColFactory, + $this->typeFactory, + $this->linkTypeProvider, + $this->rowCustomizer + ); + + $this->object = new StubProduct(); } - protected function tearDown() + /** + * Test getEntityTypeCode() + */ + public function testGetEntityTypeCode() { - unset($this->_object); + $this->assertEquals($this->product->getEntityTypeCode(), 'catalog_product'); } public function testUpdateDataWithCategoryColumnsNoCategoriesAssigned() @@ -28,6 +352,137 @@ class ProductTest extends \PHPUnit_Framework_TestCase $productId = 1; $rowCategories = [$productId => []]; - $this->assertTrue($this->_object->updateDataWithCategoryColumns($dataRow, $rowCategories, $productId)); + $this->assertTrue($this->object->updateDataWithCategoryColumns($dataRow, $rowCategories, $productId)); + } + + public function testGetHeaderColumns() + { + $product = $this->getMock( + 'Magento\CatalogImportExport\Model\Export\Product', + ['_customHeadersMapping'], + [], + '', + false + ); + $headerColumnsValue = ['headerColumns value']; + $expectedResult = 'result'; + $this->setPropertyValue($product, '_headerColumns', $headerColumnsValue); + $product + ->expects($this->once()) + ->method('_customHeadersMapping') + ->with($headerColumnsValue) + ->willReturn($expectedResult); + + $result = $product->_getHeaderColumns(); + + $this->assertEquals($expectedResult, $result); + } + + public function testExportCountZeroBreakInternalCalls() + { + $page = 1; + $itemsPerPage = 10; + + $this->product->expects($this->once())->method('getWriter')->willReturn($this->writer); + $this->product + ->expects($this->exactly(1)) + ->method('_getEntityCollection') + ->willReturn($this->abstractCollection); + $this->product->expects($this->once())->method('_prepareEntityCollection')->with($this->abstractCollection); + $this->product->expects($this->once())->method('getItemsPerPage')->willReturn($itemsPerPage); + $this->product->expects($this->once())->method('paginateCollection')->with($page, $itemsPerPage); + $this->abstractCollection->expects($this->once())->method('setOrder')->with('has_options', 'asc'); + $this->abstractCollection->expects($this->once())->method('setStoreId')->with(Store::DEFAULT_STORE_ID); + + $this->abstractCollection->expects($this->once())->method('count')->willReturn(0); + + $this->abstractCollection->expects($this->never())->method('getCurPage'); + $this->abstractCollection->expects($this->never())->method('getLastPageNumber'); + $this->product->expects($this->never())->method('_getHeaderColumns'); + $this->writer->expects($this->never())->method('setHeaderCols'); + $this->writer->expects($this->never())->method('writeRow'); + $this->product->expects($this->never())->method('getExportData'); + $this->product->expects($this->never())->method('_customFieldsMapping'); + + $this->writer->expects($this->once())->method('getContents'); + + $this->product->export(); + } + + public function testExportCurPageEqualToLastBreakInternalCalls() + { + $curPage = $lastPage = $page = 1; + $itemsPerPage = 10; + + $this->product->expects($this->once())->method('getWriter')->willReturn($this->writer); + $this->product + ->expects($this->exactly(1)) + ->method('_getEntityCollection') + ->willReturn($this->abstractCollection); + $this->product->expects($this->once())->method('_prepareEntityCollection')->with($this->abstractCollection); + $this->product->expects($this->once())->method('getItemsPerPage')->willReturn($itemsPerPage); + $this->product->expects($this->once())->method('paginateCollection')->with($page, $itemsPerPage); + $this->abstractCollection->expects($this->once())->method('setOrder')->with('has_options', 'asc'); + $this->abstractCollection->expects($this->once())->method('setStoreId')->with(Store::DEFAULT_STORE_ID); + + $this->abstractCollection->expects($this->once())->method('count')->willReturn(1); + + $this->abstractCollection->expects($this->once())->method('getCurPage')->willReturn($curPage); + $this->abstractCollection->expects($this->once())->method('getLastPageNumber')->willReturn($lastPage); + $headers = ['headers']; + $this->product->expects($this->once())->method('_getHeaderColumns')->willReturn($headers); + $this->writer->expects($this->once())->method('setHeaderCols')->with($headers); + $row = 'value'; + $data = [$row]; + $this->product->expects($this->once())->method('getExportData')->willReturn($data); + $customFieldsMappingResult = ['result']; + $this->product + ->expects($this->once()) + ->method('_customFieldsMapping') + ->with($row) + ->willReturn($customFieldsMappingResult); + $this->writer->expects($this->once())->method('writeRow')->with($customFieldsMappingResult); + + $this->writer->expects($this->once())->method('getContents'); + + $this->product->export(); + } + + protected function tearDown() + { + unset($this->object); + } + + /** + * Get any object property value. + * + * @param $object + * @param $property + * @return mixed + */ + protected function getPropertyValue($object, $property) + { + $reflection = new \ReflectionClass(get_class($object)); + $reflectionProperty = $reflection->getProperty($property); + $reflectionProperty->setAccessible(true); + + return $reflectionProperty->getValue($object); + } + + /** + * Set object property value. + * + * @param $object + * @param $property + * @param $value + */ + protected function setPropertyValue(&$object, $property, $value) + { + $reflection = new \ReflectionClass(get_class($object)); + $reflectionProperty = $reflection->getProperty($property); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($object, $value); + + return $object; } } 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 758e4e0eef502ba678d58127d705293617e2843c..95e9af858941dab9a501a539aa3634a1cd28aa48 100755 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php @@ -89,8 +89,10 @@ class ProductTest extends \PHPUnit_Framework_TestCase // @codingStandardsIgnoreStart /** @var \Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceFactory|\PHPUnit_Framework_MockObject_MockObject */ + // @codingStandardsIgnoreEnd protected $_resourceFactory; + // @codingStandardsIgnoreStart /** @var \Magento\Eav\Model\Resource\Entity\Attribute\Set\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject */ protected $_setColFactory; diff --git a/app/code/Magento/CatalogSearch/view/frontend/layout/catalogsearch_advanced_result.xml b/app/code/Magento/CatalogSearch/view/frontend/layout/catalogsearch_advanced_result.xml index 4fe368190cb1e08847a32dc916048ad659e3bc68..5dc1857a43c04b5f4638453f213a574bbb3b382c 100644 --- a/app/code/Magento/CatalogSearch/view/frontend/layout/catalogsearch_advanced_result.xml +++ b/app/code/Magento/CatalogSearch/view/frontend/layout/catalogsearch_advanced_result.xml @@ -22,6 +22,9 @@ <action method="setToolbarBlockName"> <argument name="name" xsi:type="string">product_list_toolbar</argument> </action> + <block class="Magento\Framework\View\Element\RendererList" name="category.product.type.details.renderers" as="details.renderers"> + <block class="Magento\Framework\View\Element\Template" as="default"/> + </block> </block> <action method="setListOrders"/> <action method="setListModes"/> diff --git a/app/code/Magento/CatalogSearch/view/frontend/layout/catalogsearch_result_index.xml b/app/code/Magento/CatalogSearch/view/frontend/layout/catalogsearch_result_index.xml index b3c7f66aef61059bbe1e053d6d560e7e282453f0..34a0e54cfc942d52f591c471d13f06a5a835c7ce 100644 --- a/app/code/Magento/CatalogSearch/view/frontend/layout/catalogsearch_result_index.xml +++ b/app/code/Magento/CatalogSearch/view/frontend/layout/catalogsearch_result_index.xml @@ -23,6 +23,9 @@ <action method="setToolbarBlockName"> <argument name="name" xsi:type="string">product_list_toolbar</argument> </action> + <block class="Magento\Framework\View\Element\RendererList" name="category.product.type.details.renderers" as="details.renderers"> + <block class="Magento\Framework\View\Element\Template" as="default"/> + </block> </block> <action method="setListOrders"/> <action method="setListModes"/> diff --git a/app/code/Magento/ConfigurableProduct/Model/SuggestedAttributeList.php b/app/code/Magento/ConfigurableProduct/Model/SuggestedAttributeList.php index 5a47edceda3f8b03677e55a2d7816351654acdfb..610d597a08ad7896500ba50d4e2d7995b184b067 100644 --- a/app/code/Magento/ConfigurableProduct/Model/SuggestedAttributeList.php +++ b/app/code/Magento/ConfigurableProduct/Model/SuggestedAttributeList.php @@ -14,25 +14,45 @@ class SuggestedAttributeList * * @var \Magento\Catalog\Model\Resource\Product\Attribute\CollectionFactory */ - protected $_attributeColFactory; + protected $attributeCollectionFactory; /** * Catalog resource helper * * @var \Magento\Catalog\Model\Resource\Helper */ - protected $_resourceHelper; + protected $resourceHelper; /** - * @param \Magento\Catalog\Model\Resource\Product\Attribute\CollectionFactory $attributeColFactory + * Application Event Dispatcher + * + * @var \Magento\Framework\Event\ManagerInterface + */ + protected $eventManager; + + /** + * Object Factory + * + * @var \Magento\Framework\ObjectFactory + */ + protected $objectFactory; + + /** + * @param \Magento\Catalog\Model\Resource\Product\Attribute\CollectionFactory $attributeCollectionFactory * @param \Magento\Catalog\Model\Resource\Helper $resourceHelper + * @param \Magento\Framework\Event\ManagerInterface $eventManager + * @param \Magento\Framework\ObjectFactory $objectFactory */ public function __construct( - \Magento\Catalog\Model\Resource\Product\Attribute\CollectionFactory $attributeColFactory, - \Magento\Catalog\Model\Resource\Helper $resourceHelper + \Magento\Catalog\Model\Resource\Product\Attribute\CollectionFactory $attributeCollectionFactory, + \Magento\Catalog\Model\Resource\Helper $resourceHelper, + \Magento\Framework\Event\ManagerInterface $eventManager, + \Magento\Framework\ObjectFactory $objectFactory ) { - $this->_attributeColFactory = $attributeColFactory; - $this->_resourceHelper = $resourceHelper; + $this->attributeCollectionFactory = $attributeCollectionFactory; + $this->resourceHelper = $resourceHelper; + $this->objectFactory = $objectFactory; + $this->eventManager = $eventManager; } /** @@ -43,12 +63,14 @@ class SuggestedAttributeList */ public function getSuggestedAttributes($labelPart) { - $escapedLabelPart = $this->_resourceHelper->addLikeEscape($labelPart, ['position' => 'any']); + $escapedLabelPart = $this->resourceHelper->addLikeEscape($labelPart, ['position' => 'any']); + $availableFrontendTypes = $this->getAvailableFrontendTypes(); + /** @var $collection \Magento\Catalog\Model\Resource\Product\Attribute\Collection */ - $collection = $this->_attributeColFactory->create(); + $collection = $this->attributeCollectionFactory->create(); $collection->addFieldToFilter( - 'frontend_input', - 'select' + 'main_table.frontend_input', + ['in' => $availableFrontendTypes->getData('values')] )->addFieldToFilter( 'frontend_label', ['like' => $escapedLabelPart] @@ -79,4 +101,22 @@ class SuggestedAttributeList } return $result; } + + /** + * @return \Magento\Framework\Object + */ + private function getAvailableFrontendTypes() + { + $availableFrontendTypes = $this->objectFactory->create(); + $availableFrontendTypes->setData( + [ + 'values' => ['select'] + ] + ); + $this->eventManager->dispatch( + 'product_suggested_attribute_frontend_type_init_after', + ['types_dto' => $availableFrontendTypes] + ); + return $availableFrontendTypes; + } } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/SuggestedAttributeListTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/SuggestedAttributeListTest.php index cf9344a03bb5c84e66d0ff3795dac67f5ade26c2..72ee34783301ec735a5114c27b845e6e58c82ada 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/SuggestedAttributeListTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/SuggestedAttributeListTest.php @@ -20,6 +20,16 @@ class SuggestedAttributeListTest extends \PHPUnit_Framework_TestCase */ protected $attributeFactoryMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $eventManagerMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $objectFactoryMock; + /** * @var \PHPUnit_Framework_MockObject_MockObject */ @@ -53,6 +63,20 @@ class SuggestedAttributeListTest extends \PHPUnit_Framework_TestCase '', false ); + $this->eventManagerMock = $this->getMock( + '\Magento\Framework\Event\ManagerInterface', + [], + [], + '', + false + ); + $this->objectFactoryMock = $this->getMock( + '\Magento\Framework\ObjectFactory', + ['create'], + [], + '', + false + ); $this->collectionMock = $this->getMock( 'Magento\Catalog\Model\Resource\Product\Attribute\Collection', [], @@ -78,7 +102,7 @@ class SuggestedAttributeListTest extends \PHPUnit_Framework_TestCase $this->returnValue($this->collectionMock) ); $valueMap = [ - ['frontend_input', 'select', $this->collectionMock], + ['main_table.frontend_input', ['in' => 123 ], $this->collectionMock], ['frontend_label', ['like' => $this->labelPart], $this->collectionMock], ['is_user_defined', 1, $this->collectionMock], ['is_global', \Magento\Catalog\Model\Resource\Eav\Attribute::SCOPE_GLOBAL, $this->collectionMock], @@ -107,12 +131,19 @@ class SuggestedAttributeListTest extends \PHPUnit_Framework_TestCase ); $this->suggestedListModel = new \Magento\ConfigurableProduct\Model\SuggestedAttributeList( $this->attributeFactoryMock, - $this->resourceHelperMock + $this->resourceHelperMock, + $this->eventManagerMock, + $this->objectFactoryMock ); } public function testGetSuggestedAttributesIfTheyApplicable() { + $object = $this->getMock('\Magento\Framework\Object', [], [], '', false); + $object->expects($this->once())->method('setData'); + $object->expects($this->once())->method('getData')->willReturn(123); + $this->objectFactoryMock->expects($this->once())->method('create')->willReturn($object); + $source = $this->getMock( 'Magento\Eav\Model\Entity\Attribute\Source\AbstractSource', [], @@ -132,6 +163,10 @@ class SuggestedAttributeListTest extends \PHPUnit_Framework_TestCase public function testGetSuggestedAttributesIfTheyNotApplicable() { + $object = $this->getMock('\Magento\Framework\Object', [], [], '', false); + $object->expects($this->once())->method('setData'); + $object->expects($this->once())->method('getData')->willReturn(123); + $this->objectFactoryMock->expects($this->once())->method('create')->willReturn($object); $this->attributeMock->expects($this->any())->method('getApplyTo')->will($this->returnValue(['simple'])); $this->attributeMock->expects($this->never())->method('getId'); $this->attributeMock->expects($this->never())->method('getFrontendLabel'); diff --git a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Options/Options.php b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Options/Options.php index 13a9b831f2b43a9e5aec96db58821ec003f4607e..d25fdcc5d8bb80241adee1b2ef26843fd5e1ab35 100644 --- a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Options/Options.php +++ b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Options/Options.php @@ -268,7 +268,7 @@ class Options extends \Magento\Backend\Block\Template * * @return \Magento\Eav\Model\Entity\Attribute\AbstractAttribute */ - private function getAttributeObject() + protected function getAttributeObject() { return $this->_registry->registry('entity_attribute'); } diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php b/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php index c7d648b4d54e6121c87ef6fcec65ec59401667d2..57845f2a27ec12a5c029fb5ce22bf044dba38ff1 100644 --- a/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php +++ b/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php @@ -28,6 +28,15 @@ class Filter extends \Magento\Backend\Block\Widget\Grid\Extended */ protected $_importExportData = null; + /** + * Local filters types base on attribute code + * + * @var \Magento\ImportExport\Helper\Data + */ + protected $_filterTypeByAttrCode = [ + 'updated_at' => \Magento\ImportExport\Model\Export::FILTER_TYPE_DATE, + ]; + /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Backend\Helper\Data $backendHelper @@ -325,7 +334,15 @@ class Filter extends \Magento\Backend\Block\Widget\Grid\Extended if (is_array($values) && isset($values[$row->getAttributeCode()])) { $value = $values[$row->getAttributeCode()]; } - switch (\Magento\ImportExport\Model\Export::getAttributeFilterType($row)) { + + $code = $row->getAttributeCode(); + if (isset($this->_filterTypeByAttrCode[$code])) { + $filterType =$this->_filterTypeByAttrCode[$code]; + } else { + $filterType = \Magento\ImportExport\Model\Export::getAttributeFilterType($row); + } + + switch ($filterType) { case \Magento\ImportExport\Model\Export::FILTER_TYPE_SELECT: $cell = $this->_getSelectHtmlWithValue($row, $value); break; 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 fd6f58306f1f4fecb8ef37e950fcfc17262fc8c8..7ce277171063b3a4fd37e0780659614672b526e5 100644 --- a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php +++ b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php @@ -84,7 +84,8 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic 'label' => __('Entity Type'), 'required' => true, 'onchange' => 'varienImport.handleEntityTypeSelector();', - 'values' => $this->_entityFactory->create()->toOptionArray() + 'values' => $this->_entityFactory->create()->toOptionArray(), + 'after_element_html' => $this->getDownloadSampleFileHtml(), ] ); @@ -174,4 +175,17 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic return parent::_prepareForm(); } + + /** + * Get download sample file html + * + * @return string + */ + protected function getDownloadSampleFileHtml() + { + $html = '<span id="sample-file-span" class="no-display"><a id="sample-file-link" href="#">' + . __('Download Sample File') + . '</a></span>'; + return $html; + } } diff --git a/app/code/Magento/ImportExport/Test/Unit/Block/Adminhtml/Export/FilterTest.php b/app/code/Magento/ImportExport/Test/Unit/Block/Adminhtml/Export/FilterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..2fbda38c94cfce8233952c14e00da233b4e46b8d --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Unit/Block/Adminhtml/Export/FilterTest.php @@ -0,0 +1,323 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ImportExport\Test\Unit\Block\Adminhtml\Export; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * @SuppressWarnings(PHPMD) + */ +class FilterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Framework\Model\Context|\PHPUnit_Framework_MockObject_MockObject + */ + protected $modelContext; + + /** + * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject + */ + protected $registry; + + /** + * @var \Magento\Framework\Api\ExtensionAttributesFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $extensionFactory; + + /** + * @var \Magento\Framework\Api\AttributeValueFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $customAttributeFactory; + + /** + * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject + */ + protected $eavConfig; + + /** + * @var \Magento\Eav\Model\Entity\TypeFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $eavTypeFactory; + + /** + * @var \Magento\Store\Model\StoreManager|\PHPUnit_Framework_MockObject_MockObject + */ + protected $storeManager; + + /** + * @var \Magento\Eav\Model\Resource\Helper|\PHPUnit_Framework_MockObject_MockObject + */ + protected $resourceHelper; + + /** + * @var \Magento\Framework\Validator\UniversalFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $universalFactory; + + /** + * @var \Magento\Eav\Api\Data\AttributeOptionInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $optionDataFactory; + + /** + * @var \Magento\Framework\Reflection\DataObjectProcessor|\PHPUnit_Framework_MockObject_MockObject + */ + protected $dataObjectProcessor; + + /** + * @var \Magento\Framework\Api\DataObjectHelper|\PHPUnit_Framework_MockObject_MockObject + */ + protected $dataObjectHelper; + + /** + * @var \Magento\Framework\Stdlib\DateTime\Timezone|\PHPUnit_Framework_MockObject_MockObject + */ + protected $localeDate; + + /** + * @var \Magento\Catalog\Model\Product\ReservedAttributeList|\PHPUnit_Framework_MockObject_MockObject + */ + protected $reservedAttributeList; + + /** + * @var \Magento\Framework\Locale\Resolver|\PHPUnit_Framework_MockObject_MockObject + */ + protected $localeResolver; + + /** + * @var \Magento\Catalog\Model\Resource\Product|\PHPUnit_Framework_MockObject_MockObject + */ + protected $resource; + + /** + * @var \Magento\Framework\Data\Collection\Db|\PHPUnit_Framework_MockObject_MockObject + */ + protected $resourceCollection; + + /** + * @var \Magento\Backend\Block\Template\Context|\PHPUnit_Framework_MockObject_MockObject + */ + protected $context; + + /** + * @var \Magento\Backend\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + */ + protected $backendHelper; + + /** + * @var \Magento\ImportExport\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + */ + protected $importExportData; + + /** + * @var ObjectManagerHelper + */ + protected $objectManagerHelper; + + /** + * @var \Magento\ImportExport\Block\Adminhtml\Export\Filter|\PHPUnit_Framework_MockObject_MockObject + */ + protected $filter; + + public function setUp() + { + $this->modelContext = $this->getMock('Magento\Framework\Model\Context', [], [], '', false); + $this->registry = $this->getMock('Magento\Framework\Registry', [], [], '', false); + $this->extensionFactory = $this->getMock( + 'Magento\Framework\Api\ExtensionAttributesFactory', + [], + [], + '', + false + ); + $this->customAttributeFactory = $this->getMock( + 'Magento\Framework\Api\AttributeValueFactory', + [], + [], + '', + false + ); + $this->eavConfig = $this->getMock('Magento\Eav\Model\Config', [], [], '', false); + $this->eavTypeFactory = $this->getMock('Magento\Eav\Model\Entity\TypeFactory', [], [], '', false); + $this->storeManager = $this->getMock('Magento\Store\Model\StoreManager', [], [], '', false); + $this->resourceHelper = $this->getMock('Magento\Eav\Model\Resource\Helper', [], [], '', false); + $this->universalFactory = $this->getMock('Magento\Framework\Validator\UniversalFactory', [], [], '', false); + $this->optionDataFactory = $this->getMock( + 'Magento\Eav\Api\Data\AttributeOptionInterfaceFactory', + [], + [], + '', + false + ); + $this->dataObjectProcessor = $this->getMock( + 'Magento\Framework\Reflection\DataObjectProcessor', + [], + [], + '', + false + ); + $this->dataObjectHelper = $this->getMock('Magento\Framework\Api\DataObjectHelper', [], [], '', false); + $this->localeDate = $this->getMock('Magento\Framework\Stdlib\DateTime\Timezone', [], [], '', false); + $this->localeDate->expects($this->any())->method('getDateFormat')->will($this->returnValue('12-12-2012')); + $this->reservedAttributeList = $this->getMock( + 'Magento\Catalog\Model\Product\ReservedAttributeList', + [], + [], + '', + false + ); + $this->localeResolver = $this->getMock('Magento\Framework\Locale\Resolver', [], [], '', false); + $this->resource = $this->getMock('Magento\Catalog\Model\Resource\Product', [], [], '', false); + $this->resourceCollection = $this->getMockForAbstractClass( + 'Magento\Framework\Data\Collection\AbstractDb', + [], + '', + false + ); + $this->context = $this->getMock( + 'Magento\Backend\Block\Template\Context', + ['getFileSystem', 'getEscaper', 'getLocaleDate', 'getLayout'], + [], + '', + false + ); + $filesystem = $this->getMock('Magento\Framework\Filesystem', [], [], '', false); + $this->context->expects($this->any())->method('getFileSystem')->will($this->returnValue($filesystem)); + $escaper = $this->getMock('Magento\Framework\Escaper', ['escapeHtml'], [], '', false); + $escaper->expects($this->any())->method('escapeHtml')->will($this->returnValue('')); + $this->context->expects($this->any())->method('getEscaper')->will($this->returnValue($escaper)); + $timeZone = $this->getMock('Magento\Framework\Stdlib\DateTime\TimeZone', [], [], '', false); + $timeZone->expects($this->any())->method('getDateFormat')->will($this->returnValue('M/d/yy')); + $this->context->expects($this->any())->method('getLocaleDate')->will($this->returnValue($timeZone)); + $dateBlock = $this->getMock( + 'Magento\Framework\View\Element\Html\Date', + ['setValue', 'getHtml', 'setId', 'getId'], + [], + '', + false + ); + $dateBlock->expects($this->any())->method('setValue')->will($this->returnSelf()); + $dateBlock->expects($this->any())->method('getHtml')->will($this->returnValue('')); + $dateBlock->expects($this->any())->method('setId')->will($this->returnSelf()); + $dateBlock->expects($this->any())->method('getId')->will($this->returnValue(1)); + $layout = $this->getMock('Magento\Framework\View\Layout', [], [], '', false); + $layout->expects($this->any())->method('createBlock')->will($this->returnValue($dateBlock)); + $this->context->expects($this->any())->method('getLayout')->will($this->returnValue($layout)); + $this->backendHelper = $this->getMock('Magento\Backend\Helper\Data', [], [], '', false); + $this->importExportData = $this->getMock('Magento\ImportExport\Helper\Data', [], [], '', false); + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->filter = $this->objectManagerHelper->getObject( + 'Magento\ImportExport\Block\Adminhtml\Export\Filter', + [ + 'context' => $this->context, + 'backendHelper' => $this->backendHelper, + 'importExportData' => $this->importExportData + ] + ); + } + + /** + * Test decorateFilter() + * + * @param array $attributeData + * @param string $backendType + * @param array $columnValue + * @dataProvider decorateFilterDataProvider + */ + public function testDecorateFilter($attributeData, $backendType, $columnValue) + { + $value = ''; + $attribute = new \Magento\Eav\Model\Entity\Attribute( + $this->modelContext, + $this->registry, + $this->extensionFactory, + $this->customAttributeFactory, + $this->eavConfig, + $this->eavTypeFactory, + $this->storeManager, + $this->resourceHelper, + $this->universalFactory, + $this->optionDataFactory, + $this->dataObjectProcessor, + $this->dataObjectHelper, + $this->localeDate, + $this->reservedAttributeList, + $this->localeResolver, + $this->resource, + $this->resourceCollection + ); + $attribute->setAttributeCode($attributeData['code']); + $attribute->setFrontendInput($attributeData['input']); + $attribute->setOptions($attributeData['options']); + $attribute->setFilterOptions($attributeData['filter_options']); + $attribute->setBackendType($backendType); + $column = new \Magento\Framework\Object(); + $column->setData($columnValue, 'value'); + $isExport = true; + $this->filter->decorateFilter($value, $attribute, $column, $isExport); + } + + /** + * Dataprovider for testDecorateFilter() + * + * @return array + */ + public function decorateFilterDataProvider() + { + return [ + [ + 'attributeCode' => [ + 'code' =>'updated_at', + 'input' => '', + 'options' => [], + 'filter_options' => [] + ], + 'backendType' => 'datetime', + 'columnValue' => ['values' => ['updated_at' => '12/12/12']] + ], + [ + 'attributeCode' => [ + 'code' => 'category_ids', + 'input' => '', + 'options' => [], + 'filter_options' => [] + ], + 'backendType' => 'varchar', + 'columnValue' => ['values' => ['category_ids' => '1']] + ], + [ + 'attributeCode' => [ + 'code' => 'cost', + 'input' => '', + 'options' => [], + 'filter_options' => [] + ], + 'backendType' => 'decimal', + 'columnValue' => ['values' => ['cost' => 'cost']] + ], + [ + 'attributeCode' => [ + 'code' => 'color', + 'input' => 'select', + 'options' => ['red' => 'red'], + 'filter_options' => ['opt' => 'val'] + ], + 'backendType' => 'select', + 'columnValue' => ['values' => ['color' => 'red']] + ] + ]; + } + + /** + * Test for protected method prepareForm() + * + * @todo to implement it. + */ + public function testPrepareForm() + { + $this->markTestIncomplete('This test has not been implemented yet.'); + } +} diff --git a/app/code/Magento/ImportExport/Test/Unit/Block/Adminhtml/Import/Edit/FormTest.php b/app/code/Magento/ImportExport/Test/Unit/Block/Adminhtml/Import/Edit/FormTest.php index cdafbd029a2fdf11b3599c63ba712586d5c63090..142a6c2b5d47b7c7227111f0c7ac77d2594fcc62 100644 --- a/app/code/Magento/ImportExport/Test/Unit/Block/Adminhtml/Import/Edit/FormTest.php +++ b/app/code/Magento/ImportExport/Test/Unit/Block/Adminhtml/Import/Edit/FormTest.php @@ -1,4 +1,5 @@ <?php + /** * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. diff --git a/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml b/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml index 661218ae1e3fcda2041e094df42564425e48a4b8..e0eb45426019d2edb777771e49956713f46c0329 100644 --- a/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml +++ b/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml @@ -31,6 +31,12 @@ require(['jquery', 'prototype'], function(jQuery){ */ entityBehaviors: <?php echo $block->getEntityBehaviors() ?>, + /** + * Base url + * @type {string} + */ + sampleFilesBaseUrl: '<?php echo $block->getBaseUrl() ?>' + 'pub/', + /** * Reset selected index * @param {string} elementId @@ -92,6 +98,23 @@ require(['jquery', 'prototype'], function(jQuery){ } }, + /** + * Show/hide sample file link + * @param {string} entityId + */ + showSampleFile: function(entityId) { + var sampleFileSpan = jQuery('#sample-file-span'); + if (entityId) { + var sampleFileLink = this.sampleFilesBaseUrl + entityId + '.csv'; + jQuery('#sample-file-link').attr('href', sampleFileLink); + if (sampleFileSpan.is(':hidden')) { + sampleFileSpan.show(); + } + } else { + sampleFileSpan.hide(); + } + }, + /** * Handle value change in entity type selector */ @@ -100,9 +123,11 @@ require(['jquery', 'prototype'], function(jQuery){ if (entity && entity.val()) { this.showBehavior(entity.val()); this.showUploadFile(true); + this.showSampleFile(entity.val()); } else { this.showBehavior(false); this.showUploadFile(false); + this.showSampleFile(false); } }, diff --git a/app/code/Magento/Quote/Model/BillingAddressManagement.php b/app/code/Magento/Quote/Model/BillingAddressManagement.php index cad5a3b9c42d1ff5755bb34795be8c8fc1b176c9..193232fdb2571c685440e5a6e18542ea37a17c92 100644 --- a/app/code/Magento/Quote/Model/BillingAddressManagement.php +++ b/app/code/Magento/Quote/Model/BillingAddressManagement.php @@ -87,6 +87,8 @@ class BillingAddressManagement implements BillingAddressManagementInterface $shippingAddress = $quote->getShippingAddress()->importCustomerAddressData($addressData); $shippingAddress->setSaveInAddressBook($saveInAddressBook); } + } elseif ($quote->getCustomerId()) { + $address->setEmail($quote->getCustomerEmail()); } $address->setSaveInAddressBook($saveInAddressBook); $quote->setBillingAddress($address); diff --git a/app/code/Magento/Quote/Model/ShippingAddressManagement.php b/app/code/Magento/Quote/Model/ShippingAddressManagement.php index d6ed68479310ca4dd21af16ca2d43da91b66adb6..07f4f61eb198bc59cf00df9c77b34a404ffc35c2 100644 --- a/app/code/Magento/Quote/Model/ShippingAddressManagement.php +++ b/app/code/Magento/Quote/Model/ShippingAddressManagement.php @@ -89,6 +89,8 @@ class ShippingAddressManagement implements ShippingAddressManagementInterface if ($customerAddressId) { $addressData = $this->addressRepository->getById($customerAddressId); $address = $quote->getShippingAddress()->importCustomerAddressData($addressData); + } elseif ($quote->getCustomerId()) { + $address->setEmail($quote->getCustomerEmail()); } $address->setSameAsBilling($sameAsBilling); $address->setSaveInAddressBook($saveInAddressBook); diff --git a/app/design/frontend/Magento/blank/etc/view.xml b/app/design/frontend/Magento/blank/etc/view.xml index 84c78279b3c69c5cc89b48d2502827025907ffb4..b9864e298b7df1afb47615252bd2c93f392ea34b 100644 --- a/app/design/frontend/Magento/blank/etc/view.xml +++ b/app/design/frontend/Magento/blank/etc/view.xml @@ -185,6 +185,14 @@ <var name="product_stock_alert_email_product_image:width">76</var> <var name="product_stock_alert_email_product_image:ratio">1</var> <var name="product_stock_alert_email_product_image:height">76</var> + + <var name="swatch_image:width">30</var> + <var name="swatch_image:ratio">0.8</var> + <var name="swatch_image:height">20</var> + + <var name="swatch_thumb:width">110</var> + <var name="swatch_thumb:ratio">0.8</var> + <var name="swatch_thumb:height">90</var> </vars> <vars module="Magento_Bundle"> <var name="product_summary_image_size">58</var> <!-- New Product image size used for summary block--> diff --git a/app/design/frontend/Magento/luma/etc/view.xml b/app/design/frontend/Magento/luma/etc/view.xml index a72c461ffa06f0e1a028a7c0f8e0394855755904..1d3fb18268ee0270dd432198b7940546d3987516 100644 --- a/app/design/frontend/Magento/luma/etc/view.xml +++ b/app/design/frontend/Magento/luma/etc/view.xml @@ -190,6 +190,14 @@ <var name="product_stock_alert_email_product_image:width">76</var> <var name="product_stock_alert_email_product_image:ratio">1</var> <var name="product_stock_alert_email_product_image:height">76</var> + + <var name="swatch_image:width">30</var> + <var name="swatch_image:ratio">0.8</var> + <var name="swatch_image:height">20</var> + + <var name="swatch_thumb:width">110</var> + <var name="swatch_thumb:ratio">0.8</var> + <var name="swatch_thumb:height">90</var> </vars> <vars module="Magento_Bundle"> <var name="product_summary_image_size">58</var> <!-- New Product image size used for summary block--> diff --git a/composer.json b/composer.json index a62e8ac4890d7f482ea330efd803bc451ea1ed8f..94549cfd2878cbc97c0476f54c7a7f2d4f1b093f 100644 --- a/composer.json +++ b/composer.json @@ -61,6 +61,7 @@ }, "replace": { "magento/module-admin-notification": "self.version", + "magento/module-advanced-pricing-import-export": "self.version", "magento/module-authorization": "self.version", "magento/module-backend": "self.version", "magento/module-backup": "self.version", diff --git a/composer.lock b/composer.lock index beefd078b4aef16f33d39fdb819c7bb8aa06aef3..77a18945169153ccdfd51d970fdd2255c499d42b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "a62e70bed8efa22bebb0fedf63b51925", + "hash": "daa84b5d3ea8eabcd1bb1f33c07e08dd", "packages": [ { "name": "composer/composer", @@ -684,12 +684,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-code.git", - "reference": "cfd5951ff4348e4430850560416c7ddb755f95d3" + "reference": "0ed94f842ba60cdc900c46a61bdbd7ac95a3e140" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-code/zipball/0ed94f842ba60cdc900c46a61bdbd7ac95a3e140", - "reference": "cfd5951ff4348e4430850560416c7ddb755f95d3", + "reference": "0ed94f842ba60cdc900c46a61bdbd7ac95a3e140", "shasum": "" }, "require": { @@ -698,6 +698,9 @@ }, "require-dev": { "doctrine/common": ">=2.1", + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-stdlib": "self.version" }, "suggest": { @@ -713,7 +716,7 @@ }, "autoload": { "psr-4": { - "Zend\\Code\\": "" + "Zend\\Code\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -721,12 +724,12 @@ "BSD-3-Clause" ], "description": "provides facilities to generate arbitrary code using an object oriented interface", - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-code", "keywords": [ "code", "zf2" ], - "time": "2015-04-01 17:59:08" + "time": "2015-03-31 15:39:14" }, { "name": "zendframework/zend-config", @@ -734,12 +737,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-config.git", - "reference": "8682fe4e2923b383bb6472fc84b5796a07589163" + "reference": "95f3a4b3fa85d49e6f060183122de4596fa6d29d" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-config/zipball/95f3a4b3fa85d49e6f060183122de4596fa6d29d", - "reference": "8682fe4e2923b383bb6472fc84b5796a07589163", + "reference": "95f3a4b3fa85d49e6f060183122de4596fa6d29d", "shasum": "" }, "require": { @@ -747,6 +750,9 @@ "zendframework/zend-stdlib": "self.version" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-filter": "self.version", "zendframework/zend-i18n": "self.version", "zendframework/zend-json": "self.version", @@ -767,7 +773,7 @@ }, "autoload": { "psr-4": { - "Zend\\Config\\": "" + "Zend\\Config\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -775,12 +781,12 @@ "BSD-3-Clause" ], "description": "provides a nested object property based user interface for accessing this configuration data within application code", - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-config", "keywords": [ "config", "zf2" ], - "time": "2015-04-01 17:59:31" + "time": "2015-03-25 20:55:48" }, { "name": "zendframework/zend-console", @@ -788,18 +794,23 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-console.git", - "reference": "94ab6663b07e19f20b3319ecf317bd72b6a72dca" + "reference": "54823d9ba6f8ce39046384ee5a043b5b3d5f56d7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-console/zipball/54823d9ba6f8ce39046384ee5a043b5b3d5f56d7", - "reference": "94ab6663b07e19f20b3319ecf317bd72b6a72dca", + "reference": "54823d9ba6f8ce39046384ee5a043b5b3d5f56d7", "shasum": "" }, "require": { "php": ">=5.3.23", "zendframework/zend-stdlib": "self.version" }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, "suggest": { "zendframework/zend-filter": "To support DefaultRouteMatcher usage", "zendframework/zend-validator": "To support DefaultRouteMatcher usage" @@ -813,19 +824,19 @@ }, "autoload": { "psr-4": { - "Zend\\Console\\": "" + "Zend\\Console\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-console", "keywords": [ "console", "zf2" ], - "time": "2015-04-01 17:59:48" + "time": "2015-03-25 20:55:48" }, { "name": "zendframework/zend-di", @@ -833,12 +844,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-di.git", - "reference": "0811f2a67ad0b50dfb8d602ed67cde0b82249190" + "reference": "b9f8de081adecf71a003a569e9ba76c0a4c00bf2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-di/zipball/b9f8de081adecf71a003a569e9ba76c0a4c00bf2", - "reference": "0811f2a67ad0b50dfb8d602ed67cde0b82249190", + "reference": "b9f8de081adecf71a003a569e9ba76c0a4c00bf2", "shasum": "" }, "require": { @@ -847,6 +858,9 @@ "zendframework/zend-stdlib": "self.version" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-servicemanager": "self.version" }, "suggest": { @@ -861,19 +875,19 @@ }, "autoload": { "psr-4": { - "Zend\\Di\\": "" + "Zend\\Di\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-di", "keywords": [ "di", "zf2" ], - "time": "2015-04-01 18:01:30" + "time": "2015-03-25 20:55:48" }, { "name": "zendframework/zend-escaper", @@ -881,17 +895,22 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-escaper.git", - "reference": "65b3328627362b0be1d5e9067bc846511d1fbc96" + "reference": "15e5769e4fcdb4bf07ebd76500810e7070e23a97" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/15e5769e4fcdb4bf07ebd76500810e7070e23a97", - "reference": "65b3328627362b0be1d5e9067bc846511d1fbc96", + "reference": "15e5769e4fcdb4bf07ebd76500810e7070e23a97", "shasum": "" }, "require": { "php": ">=5.3.23" }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, "type": "library", "extra": { "branch-alias": { @@ -901,19 +920,19 @@ }, "autoload": { "psr-4": { - "Zend\\Escaper\\": "" + "Zend\\Escaper\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-escaper", "keywords": [ "escaper", "zf2" ], - "time": "2015-04-01 18:02:07" + "time": "2015-03-23 18:29:14" }, { "name": "zendframework/zend-eventmanager", @@ -921,18 +940,23 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-eventmanager.git", - "reference": "38df5b567d4ff4d22144745c503ba0502d0d5695" + "reference": "58d21c95c7005a527262fd536499195f104e83f9" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/58d21c95c7005a527262fd536499195f104e83f9", - "reference": "38df5b567d4ff4d22144745c503ba0502d0d5695", + "reference": "58d21c95c7005a527262fd536499195f104e83f9", "shasum": "" }, "require": { "php": ">=5.3.23", "zendframework/zend-stdlib": "self.version" }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, "type": "library", "extra": { "branch-alias": { @@ -942,19 +966,19 @@ }, "autoload": { "psr-4": { - "Zend\\EventManager\\": "" + "Zend\\EventManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-event-manager", "keywords": [ "eventmanager", "zf2" ], - "time": "2015-04-01 18:05:26" + "time": "2015-03-23 18:29:14" }, { "name": "zendframework/zend-filter", @@ -962,12 +986,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-filter.git", - "reference": "b13741a88553351fc52472de529b57b580b8f6f1" + "reference": "6d8aed2da81b62a04747346c4370562cdbe34595" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/6d8aed2da81b62a04747346c4370562cdbe34595", - "reference": "b13741a88553351fc52472de529b57b580b8f6f1", + "reference": "6d8aed2da81b62a04747346c4370562cdbe34595", "shasum": "" }, "require": { @@ -975,6 +999,9 @@ "zendframework/zend-stdlib": "self.version" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-crypt": "self.version", "zendframework/zend-servicemanager": "self.version", "zendframework/zend-uri": "self.version" @@ -994,7 +1021,7 @@ }, "autoload": { "psr-4": { - "Zend\\Filter\\": "" + "Zend\\Filter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1002,12 +1029,12 @@ "BSD-3-Clause" ], "description": "provides a set of commonly needed data filters", - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-filter", "keywords": [ "filter", "zf2" ], - "time": "2015-04-01 18:09:25" + "time": "2015-03-25 20:55:48" }, { "name": "zendframework/zend-form", @@ -1015,12 +1042,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-form.git", - "reference": "09f5bd46ffbf783df22281898e2175b291bd43a3" + "reference": "bca0db55718355d25c2c10fdd41a83561f1c94b3" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-form/zipball/bca0db55718355d25c2c10fdd41a83561f1c94b3", - "reference": "09f5bd46ffbf783df22281898e2175b291bd43a3", + "reference": "bca0db55718355d25c2c10fdd41a83561f1c94b3", "shasum": "" }, "require": { @@ -1029,6 +1056,9 @@ "zendframework/zend-stdlib": "self.version" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-captcha": "self.version", "zendframework/zend-code": "self.version", "zendframework/zend-eventmanager": "self.version", @@ -1059,19 +1089,19 @@ }, "autoload": { "psr-4": { - "Zend\\Form\\": "" + "Zend\\Form\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-form", "keywords": [ "form", "zf2" ], - "time": "2015-04-01 18:09:25" + "time": "2015-03-28 20:29:18" }, { "name": "zendframework/zend-http", @@ -1079,12 +1109,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-http.git", - "reference": "ee6220609845b32d1b2873c9ac694aef56d508f5" + "reference": "9c6047a0bdb3094d3ea07a215ff929cc47de4deb" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-http/zipball/9c6047a0bdb3094d3ea07a215ff929cc47de4deb", - "reference": "ee6220609845b32d1b2873c9ac694aef56d508f5", + "reference": "9c6047a0bdb3094d3ea07a215ff929cc47de4deb", "shasum": "" }, "require": { @@ -1094,6 +1124,11 @@ "zendframework/zend-uri": "self.version", "zendframework/zend-validator": "self.version" }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, "type": "library", "extra": { "branch-alias": { @@ -1103,7 +1138,7 @@ }, "autoload": { "psr-4": { - "Zend\\Http\\": "" + "Zend\\Http\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1111,12 +1146,12 @@ "BSD-3-Clause" ], "description": "provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests", - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-http", "keywords": [ "http", "zf2" ], - "time": "2015-04-01 18:09:25" + "time": "2015-03-27 15:46:30" }, { "name": "zendframework/zend-i18n", @@ -1124,12 +1159,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-i18n.git", - "reference": "33051775d9a8c341fe3b77d1f3daa0e921e2f4bd" + "reference": "9aebc5287373a802540d75fe5508417f866c2e52" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/9aebc5287373a802540d75fe5508417f866c2e52", - "reference": "33051775d9a8c341fe3b77d1f3daa0e921e2f4bd", + "reference": "9aebc5287373a802540d75fe5508417f866c2e52", "shasum": "" }, "require": { @@ -1137,6 +1172,9 @@ "zendframework/zend-stdlib": "self.version" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-cache": "self.version", "zendframework/zend-config": "self.version", "zendframework/zend-eventmanager": "self.version", @@ -1165,19 +1203,19 @@ }, "autoload": { "psr-4": { - "Zend\\I18n\\": "" + "Zend\\I18n\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-i18n", "keywords": [ "i18n", "zf2" ], - "time": "2015-04-01 18:09:26" + "time": "2015-03-25 20:55:48" }, { "name": "zendframework/zend-inputfilter", @@ -1185,12 +1223,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-inputfilter.git", - "reference": "16856fec61f285e41e5492235220a4dec06ab90f" + "reference": "4b1398f3635fae3cc5e873c5bb067274f3d10a93" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/4b1398f3635fae3cc5e873c5bb067274f3d10a93", - "reference": "16856fec61f285e41e5492235220a4dec06ab90f", + "reference": "4b1398f3635fae3cc5e873c5bb067274f3d10a93", "shasum": "" }, "require": { @@ -1200,6 +1238,9 @@ "zendframework/zend-validator": "self.version" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-servicemanager": "self.version" }, "suggest": { @@ -1214,19 +1255,19 @@ }, "autoload": { "psr-4": { - "Zend\\InputFilter\\": "" + "Zend\\InputFilter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-input-filter", "keywords": [ "inputfilter", "zf2" ], - "time": "2015-04-01 18:09:26" + "time": "2015-03-23 18:29:14" }, { "name": "zendframework/zend-json", @@ -1234,12 +1275,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-json.git", - "reference": "76aeb27e4baf39799e5ca3cf6f2fdd6748ee930c" + "reference": "2d845e151c1b9a237cf1899ac31e17fb10bd1e3f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-json/zipball/2d845e151c1b9a237cf1899ac31e17fb10bd1e3f", - "reference": "76aeb27e4baf39799e5ca3cf6f2fdd6748ee930c", + "reference": "2d845e151c1b9a237cf1899ac31e17fb10bd1e3f", "shasum": "" }, "require": { @@ -1247,6 +1288,9 @@ "zendframework/zend-stdlib": "self.version" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-http": "self.version", "zendframework/zend-server": "self.version" }, @@ -1264,7 +1308,7 @@ }, "autoload": { "psr-4": { - "Zend\\Json\\": "" + "Zend\\Json\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1272,12 +1316,12 @@ "BSD-3-Clause" ], "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-json", "keywords": [ "json", "zf2" ], - "time": "2015-04-01 18:09:26" + "time": "2015-03-25 20:55:48" }, { "name": "zendframework/zend-loader", @@ -1285,17 +1329,22 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-loader.git", - "reference": "6868b8a0c346f17fb97724c3a63aa2cbf6b94865" + "reference": "65de2c7a56f8eee633c6bf1cfab73e45648880d4" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/65de2c7a56f8eee633c6bf1cfab73e45648880d4", - "reference": "6868b8a0c346f17fb97724c3a63aa2cbf6b94865", + "reference": "65de2c7a56f8eee633c6bf1cfab73e45648880d4", "shasum": "" }, "require": { "php": ">=5.3.23" }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, "type": "library", "extra": { "branch-alias": { @@ -1305,19 +1354,19 @@ }, "autoload": { "psr-4": { - "Zend\\Loader\\": "" + "Zend\\Loader\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-loader", "keywords": [ "loader", "zf2" ], - "time": "2015-04-01 18:09:26" + "time": "2015-03-23 18:29:14" }, { "name": "zendframework/zend-log", @@ -1325,12 +1374,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-log.git", - "reference": "2d5d20fd45470506bdaff727c46dc25fe953146e" + "reference": "002e3c810cad7e31e51c9895e9e3cb6fbd312cdd" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-log/zipball/002e3c810cad7e31e51c9895e9e3cb6fbd312cdd", - "reference": "2d5d20fd45470506bdaff727c46dc25fe953146e", + "reference": "002e3c810cad7e31e51c9895e9e3cb6fbd312cdd", "shasum": "" }, "require": { @@ -1339,6 +1388,9 @@ "zendframework/zend-stdlib": "self.version" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-console": "self.version", "zendframework/zend-db": "self.version", "zendframework/zend-escaper": "self.version", @@ -1362,7 +1414,7 @@ }, "autoload": { "psr-4": { - "Zend\\Log\\": "" + "Zend\\Log\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1370,13 +1422,13 @@ "BSD-3-Clause" ], "description": "component for general purpose logging", - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-log", "keywords": [ "log", "logging", "zf2" ], - "time": "2015-04-01 18:09:26" + "time": "2015-03-25 20:55:48" }, { "name": "zendframework/zend-math", @@ -1384,17 +1436,22 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-math.git", - "reference": "634123f83ca90b6613f132d0d100e6b5e9890a29" + "reference": "f41fe4acfd809c14f2a802d1aa45dec8fcd2cc73" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-math/zipball/f41fe4acfd809c14f2a802d1aa45dec8fcd2cc73", - "reference": "634123f83ca90b6613f132d0d100e6b5e9890a29", + "reference": "f41fe4acfd809c14f2a802d1aa45dec8fcd2cc73", "shasum": "" }, "require": { "php": ">=5.3.23" }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, "suggest": { "ext-bcmath": "If using the bcmath functionality", "ext-gmp": "If using the gmp functionality", @@ -1410,19 +1467,19 @@ }, "autoload": { "psr-4": { - "Zend\\Math\\": "" + "Zend\\Math\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-math", "keywords": [ "math", "zf2" ], - "time": "2015-04-01 18:09:27" + "time": "2015-03-23 18:29:14" }, { "name": "zendframework/zend-modulemanager", @@ -1430,12 +1487,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-modulemanager.git", - "reference": "cbe16b0eafe734a062ed0182381e64b9c953dccf" + "reference": "af7ae3cd29a1efb73cc66ae1081e606039d5c20f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/af7ae3cd29a1efb73cc66ae1081e606039d5c20f", - "reference": "cbe16b0eafe734a062ed0182381e64b9c953dccf", + "reference": "af7ae3cd29a1efb73cc66ae1081e606039d5c20f", "shasum": "" }, "require": { @@ -1444,6 +1501,9 @@ "zendframework/zend-stdlib": "self.version" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-config": "self.version", "zendframework/zend-console": "self.version", "zendframework/zend-loader": "self.version", @@ -1465,19 +1525,19 @@ }, "autoload": { "psr-4": { - "Zend\\ModuleManager\\": "" + "Zend\\ModuleManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-module-manager", "keywords": [ "modulemanager", "zf2" ], - "time": "2015-04-01 18:09:27" + "time": "2015-03-23 18:29:14" }, { "name": "zendframework/zend-mvc", @@ -1485,12 +1545,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-mvc.git", - "reference": "bfff0f5f9e4d925ee13b8c159c9d6ae7e0db5412" + "reference": "0b4a4a829b30be510a3f215c4ff00c703ee8b431" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/0b4a4a829b30be510a3f215c4ff00c703ee8b431", - "reference": "bfff0f5f9e4d925ee13b8c159c9d6ae7e0db5412", + "reference": "0b4a4a829b30be510a3f215c4ff00c703ee8b431", "shasum": "" }, "require": { @@ -1501,6 +1561,9 @@ "zendframework/zend-stdlib": "self.version" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-authentication": "self.version", "zendframework/zend-console": "self.version", "zendframework/zend-di": "self.version", @@ -1549,19 +1612,19 @@ }, "autoload": { "psr-4": { - "Zend\\Mvc\\": "" + "Zend\\Mvc\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-mvc", "keywords": [ "mvc", "zf2" ], - "time": "2015-04-01 18:09:27" + "time": "2015-03-26 18:55:14" }, { "name": "zendframework/zend-serializer", @@ -1569,12 +1632,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-serializer.git", - "reference": "a46960854d6326f0036d98c9abc7a79e36e25928" + "reference": "3c531789a9882a5deb721356a7bd2642b65d4b09" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/3c531789a9882a5deb721356a7bd2642b65d4b09", - "reference": "a46960854d6326f0036d98c9abc7a79e36e25928", + "reference": "3c531789a9882a5deb721356a7bd2642b65d4b09", "shasum": "" }, "require": { @@ -1584,6 +1647,9 @@ "zendframework/zend-stdlib": "self.version" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-servicemanager": "self.version" }, "suggest": { @@ -1598,7 +1664,7 @@ }, "autoload": { "psr-4": { - "Zend\\Serializer\\": "" + "Zend\\Serializer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1606,12 +1672,12 @@ "BSD-3-Clause" ], "description": "provides an adapter based interface to simply generate storable representation of PHP types by different facilities, and recover", - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-serializer", "keywords": [ "serializer", "zf2" ], - "time": "2015-04-01 18:09:28" + "time": "2015-03-25 20:55:48" }, { "name": "zendframework/zend-server", @@ -1619,12 +1685,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-server.git", - "reference": "fc73c34490908ba143af3c57c7e166b40c4b9f8e" + "reference": "d11ff0bd529d202022823d4accf5983cbd50fc49" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-server/zipball/d11ff0bd529d202022823d4accf5983cbd50fc49", - "reference": "fc73c34490908ba143af3c57c7e166b40c4b9f8e", + "reference": "d11ff0bd529d202022823d4accf5983cbd50fc49", "shasum": "" }, "require": { @@ -1632,6 +1698,11 @@ "zendframework/zend-code": "self.version", "zendframework/zend-stdlib": "self.version" }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, "type": "library", "extra": { "branch-alias": { @@ -1641,19 +1712,19 @@ }, "autoload": { "psr-4": { - "Zend\\Server\\": "" + "Zend\\Server\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-server", "keywords": [ "server", "zf2" ], - "time": "2015-04-01 18:09:28" + "time": "2015-03-25 20:55:48" }, { "name": "zendframework/zend-servicemanager", @@ -1661,18 +1732,21 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-servicemanager.git", - "reference": "d3c27c708a148a30608f313a5b7a61a531bd9cb9" + "reference": "57cf99fa5ac08c05a135a8d0d676c52a5e450083" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/57cf99fa5ac08c05a135a8d0d676c52a5e450083", - "reference": "d3c27c708a148a30608f313a5b7a61a531bd9cb9", + "reference": "57cf99fa5ac08c05a135a8d0d676c52a5e450083", "shasum": "" }, "require": { "php": ">=5.3.23" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-di": "self.version" }, "suggest": { @@ -1688,19 +1762,19 @@ }, "autoload": { "psr-4": { - "Zend\\ServiceManager\\": "" + "Zend\\ServiceManager\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-service-manager", "keywords": [ "servicemanager", "zf2" ], - "time": "2015-04-01 18:09:28" + "time": "2015-03-23 18:29:14" }, { "name": "zendframework/zend-soap", @@ -1708,12 +1782,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-soap.git", - "reference": "e42b900798ea95a9063fa4922da976d8b3a8ab6f" + "reference": "a599463aba97ce247faf3fb443e3c7858b46449b" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/a599463aba97ce247faf3fb443e3c7858b46449b", - "reference": "e42b900798ea95a9063fa4922da976d8b3a8ab6f", + "reference": "a599463aba97ce247faf3fb443e3c7858b46449b", "shasum": "" }, "require": { @@ -1723,6 +1797,9 @@ "zendframework/zend-uri": "self.version" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-http": "self.version" }, "suggest": { @@ -1737,19 +1814,19 @@ }, "autoload": { "psr-4": { - "Zend\\Soap\\": "" + "Zend\\Soap\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-soap", "keywords": [ "soap", "zf2" ], - "time": "2015-04-01 18:09:29" + "time": "2015-03-25 20:55:48" }, { "name": "zendframework/zend-stdlib", @@ -1757,18 +1834,21 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-stdlib.git", - "reference": "eab586f4c18af3fa63c977611939f1f4a3cf1030" + "reference": "cf05c5ba75606e47ffee91cedc72778da46f74c3" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/cf05c5ba75606e47ffee91cedc72778da46f74c3", - "reference": "eab586f4c18af3fa63c977611939f1f4a3cf1030", + "reference": "cf05c5ba75606e47ffee91cedc72778da46f74c3", "shasum": "" }, "require": { "php": ">=5.3.23" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-eventmanager": "self.version", "zendframework/zend-filter": "self.version", "zendframework/zend-serializer": "self.version", @@ -1789,19 +1869,19 @@ }, "autoload": { "psr-4": { - "Zend\\Stdlib\\": "" + "Zend\\Stdlib\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-stdlib", "keywords": [ "stdlib", "zf2" ], - "time": "2015-04-01 18:09:29" + "time": "2015-03-25 20:55:48" }, { "name": "zendframework/zend-text", @@ -1809,12 +1889,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-text.git", - "reference": "35f519e20e575a331c2ee554e5a555a59ce4b9e2" + "reference": "d962ea25647b20527f3ca34ae225bbc885dabfc7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-text/zipball/d962ea25647b20527f3ca34ae225bbc885dabfc7", - "reference": "35f519e20e575a331c2ee554e5a555a59ce4b9e2", + "reference": "d962ea25647b20527f3ca34ae225bbc885dabfc7", "shasum": "" }, "require": { @@ -1822,6 +1902,11 @@ "zendframework/zend-servicemanager": "self.version", "zendframework/zend-stdlib": "self.version" }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, "type": "library", "extra": { "branch-alias": { @@ -1831,19 +1916,19 @@ }, "autoload": { "psr-4": { - "Zend\\Text\\": "" + "Zend\\Text\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-text", "keywords": [ "text", "zf2" ], - "time": "2015-04-01 18:09:29" + "time": "2015-03-25 20:55:48" }, { "name": "zendframework/zend-uri", @@ -1851,12 +1936,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-uri.git", - "reference": "53f5b162b293f80de8b951eece8e08be83c4fe16" + "reference": "bd9e625639415376f6a82551c73328448d7bc7d1" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/bd9e625639415376f6a82551c73328448d7bc7d1", - "reference": "53f5b162b293f80de8b951eece8e08be83c4fe16", + "reference": "bd9e625639415376f6a82551c73328448d7bc7d1", "shasum": "" }, "require": { @@ -1864,6 +1949,11 @@ "zendframework/zend-escaper": "self.version", "zendframework/zend-validator": "self.version" }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, "type": "library", "extra": { "branch-alias": { @@ -1873,7 +1963,7 @@ }, "autoload": { "psr-4": { - "Zend\\Uri\\": "" + "Zend\\Uri\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1881,12 +1971,12 @@ "BSD-3-Clause" ], "description": "a component that aids in manipulating and validating » Uniform Resource Identifiers (URIs)", - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-uri", "keywords": [ "uri", "zf2" ], - "time": "2015-04-01 18:09:29" + "time": "2015-03-25 20:55:48" }, { "name": "zendframework/zend-validator", @@ -1894,12 +1984,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-validator.git", - "reference": "eb678d20256f120a72ca27276bbb2875841701ab" + "reference": "45fac2545a0f2eb66d71cb7966feee481e7c475f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/45fac2545a0f2eb66d71cb7966feee481e7c475f", - "reference": "eb678d20256f120a72ca27276bbb2875841701ab", + "reference": "45fac2545a0f2eb66d71cb7966feee481e7c475f", "shasum": "" }, "require": { @@ -1907,6 +1997,9 @@ "zendframework/zend-stdlib": "self.version" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-db": "self.version", "zendframework/zend-filter": "self.version", "zendframework/zend-i18n": "self.version", @@ -1934,7 +2027,7 @@ }, "autoload": { "psr-4": { - "Zend\\Validator\\": "" + "Zend\\Validator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1942,12 +2035,12 @@ "BSD-3-Clause" ], "description": "provides a set of commonly needed validators", - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-validator", "keywords": [ "validator", "zf2" ], - "time": "2015-04-01 18:09:30" + "time": "2015-03-25 20:55:48" }, { "name": "zendframework/zend-view", @@ -1955,12 +2048,12 @@ "source": { "type": "git", "url": "https://github.com/zendframework/zend-view.git", - "reference": "e119b4b5f082af58a96eb206e782b62c193227bf" + "reference": "37beb1ad46e530f627b4b6c3716efd728e976ba9" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/zendframework/zend-view/zipball/37beb1ad46e530f627b4b6c3716efd728e976ba9", - "reference": "e119b4b5f082af58a96eb206e782b62c193227bf", + "reference": "37beb1ad46e530f627b4b6c3716efd728e976ba9", "shasum": "" }, "require": { @@ -1970,6 +2063,9 @@ "zendframework/zend-stdlib": "self.version" }, "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", "zendframework/zend-authentication": "self.version", "zendframework/zend-escaper": "self.version", "zendframework/zend-feed": "self.version", @@ -2008,7 +2104,7 @@ }, "autoload": { "psr-4": { - "Zend\\View\\": "" + "Zend\\View\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2016,12 +2112,12 @@ "BSD-3-Clause" ], "description": "provides a system of helpers, output filters, and variable escaping", - "homepage": "https://github.com/zendframework/zf2", + "homepage": "https://github.com/zendframework/zend-view", "keywords": [ "view", "zf2" ], - "time": "2015-04-01 18:09:30" + "time": "2015-03-25 20:55:48" } ], "packages-dev": [ @@ -2347,16 +2443,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "2.1.5", + "version": "2.1.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "be2286cb8c7e1773eded49d9719219e6f74f9e3e" + "reference": "631e365cf26bb2c078683e8d9bcf8bc631ac4d44" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/be2286cb8c7e1773eded49d9719219e6f74f9e3e", - "reference": "be2286cb8c7e1773eded49d9719219e6f74f9e3e", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/631e365cf26bb2c078683e8d9bcf8bc631ac4d44", + "reference": "631e365cf26bb2c078683e8d9bcf8bc631ac4d44", "shasum": "" }, "require": { @@ -2405,7 +2501,7 @@ "testing", "xunit" ], - "time": "2015-06-09 13:05:42" + "time": "2015-06-19 07:11:55" }, { "name": "phpunit/php-file-iterator", @@ -2454,16 +2550,16 @@ }, { "name": "phpunit/php-text-template", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", - "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", "shasum": "" }, "require": { @@ -2472,20 +2568,17 @@ "type": "library", "autoload": { "classmap": [ - "Text/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -2494,7 +2587,7 @@ "keywords": [ "template" ], - "time": "2014-01-30 17:20:04" + "time": "2015-06-21 13:50:34" }, { "name": "phpunit/php-timer", @@ -2539,16 +2632,16 @@ }, { "name": "phpunit/php-token-stream", - "version": "1.4.2", + "version": "1.4.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "db63be1159c81df649cd0260e30249a586d4129e" + "reference": "7a9b0969488c3c54fd62b4d504b3ec758fd005d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/db63be1159c81df649cd0260e30249a586d4129e", - "reference": "db63be1159c81df649cd0260e30249a586d4129e", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/7a9b0969488c3c54fd62b4d504b3ec758fd005d9", + "reference": "7a9b0969488c3c54fd62b4d504b3ec758fd005d9", "shasum": "" }, "require": { @@ -2584,7 +2677,7 @@ "keywords": [ "tokenizer" ], - "time": "2015-06-12 07:34:24" + "time": "2015-06-19 03:43:16" }, { "name": "phpunit/phpunit", @@ -3002,16 +3095,16 @@ }, { "name": "sebastian/version", - "version": "1.0.5", + "version": "1.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "ab931d46cd0d3204a91e1b9a40c4bc13032b58e4" + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/ab931d46cd0d3204a91e1b9a40c4bc13032b58e4", - "reference": "ab931d46cd0d3204a91e1b9a40c4bc13032b58e4", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", "shasum": "" }, "type": "library", @@ -3033,7 +3126,7 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2015-02-24 06:35:25" + "time": "2015-06-21 13:59:46" }, { "name": "sjparkinson/static-review", diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/BillingAddressManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/BillingAddressManagementTest.php index 4cc58bed391e49689102645eebd393e1288265a6..bf42f3fba147321bb4b4cc34be562638f04c8d79 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/BillingAddressManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/BillingAddressManagementTest.php @@ -103,7 +103,7 @@ class BillingAddressManagementTest extends WebapiAbstract $addressData = [ 'firstname' => 'John', 'lastname' => 'Smith', - 'email' => 'cat@dog.com', + 'email' => '', 'company' => 'eBay Inc', 'street' => ['Typical Street', 'Tiny House 18'], 'city' => 'Big City', @@ -134,6 +134,7 @@ class BillingAddressManagementTest extends WebapiAbstract $this->assertContains($streetLine, $quote->getBillingAddress()->getStreet()); } unset($addressData['street']); + unset($addressData['email']); $this->assertEquals('billing', $savedData['address_type']); //check the rest of fields foreach ($addressData as $key => $value) { @@ -226,7 +227,6 @@ class BillingAddressManagementTest extends WebapiAbstract $addressData = [ 'firstname' => 'John', 'lastname' => 'Smith', - 'email' => 'cat@dog.com', 'company' => 'eBay Inc', 'street' => ['Typical Street', 'Tiny House 18'], 'city' => 'Big City', diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestBillingAddressManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestBillingAddressManagementTest.php index cbf99b7f9922c70d8da68086d89c5dec51e8f017..554b6728a02a6885a1cef6359a266dc3a3b1e5de 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestBillingAddressManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestBillingAddressManagementTest.php @@ -112,7 +112,7 @@ class GuestBillingAddressManagementTest extends WebapiAbstract $addressData = [ 'firstname' => 'John', 'lastname' => 'Smith', - 'email' => 'cat@dog.com', + 'email' => '', 'company' => 'eBay Inc', 'street' => ['Typical Street', 'Tiny House 18'], 'city' => 'Big City', @@ -143,6 +143,7 @@ class GuestBillingAddressManagementTest extends WebapiAbstract $this->assertContains($streetLine, $quote->getBillingAddress()->getStreet()); } unset($addressData['street']); + unset($addressData['email']); $this->assertEquals('billing', $savedData['address_type']); //check the rest of fields foreach ($addressData as $key => $value) { diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestShippingAddressManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestShippingAddressManagementTest.php index 2e01c93f13a3f3acc9225ea2ff1a7b3d238ec2cb..6ac15b22292da1603e0c56f1f49f99bbab2c5509 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestShippingAddressManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestShippingAddressManagementTest.php @@ -153,7 +153,7 @@ class GuestShippingAddressManagementTest extends WebapiAbstract $addressData = [ 'firstname' => 'John', 'lastname' => 'Smith', - 'email' => 'cat@dog.com', + 'email' => '', 'company' => 'eBay Inc', 'street' => ['Typical Street', 'Tiny House 18'], 'city' => 'Big City', @@ -183,6 +183,7 @@ class GuestShippingAddressManagementTest extends WebapiAbstract //custom checks for street, region and address_type $this->assertEquals($addressData['street'], $quote->getShippingAddress()->getStreet()); unset($addressData['street']); + unset($addressData['email']); $this->assertEquals('shipping', $savedData['address_type']); //check the rest of fields diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/ShippingAddressManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/ShippingAddressManagementTest.php index b789a1d4031906e8982c85f4700df15c65236b45..a15ea0d24fbc85d7e741e329dc266fe4cc5a8b18 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/ShippingAddressManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/ShippingAddressManagementTest.php @@ -128,7 +128,7 @@ class ShippingAddressManagementTest extends WebapiAbstract $addressData = [ 'firstname' => 'John', 'lastname' => 'Smith', - 'email' => 'cat@dog.com', + 'email' => '', 'company' => 'eBay Inc', 'street' => ['Typical Street', 'Tiny House 18'], 'city' => 'Big City', @@ -158,6 +158,7 @@ class ShippingAddressManagementTest extends WebapiAbstract //custom checks for street, region and address_type $this->assertEquals($addressData['street'], $quote->getShippingAddress()->getStreet()); unset($addressData['street']); + unset($addressData['email']); $this->assertEquals('shipping', $savedData['address_type']); //check the rest of fields @@ -306,7 +307,6 @@ class ShippingAddressManagementTest extends WebapiAbstract $addressData = [ 'firstname' => 'John', 'lastname' => 'Smith', - 'email' => 'cat@dog.com', 'company' => 'eBay Inc', 'street' => ['Typical Street', 'Tiny House 18'], 'city' => 'Big City', diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductAttribute/Curl.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductAttribute/Curl.php index 692126a34d1d3f0f8fd0599d228924a9d48b5807..531996bb5b186be6347180c6b1a70ffef8809370 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductAttribute/Curl.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductAttribute/Curl.php @@ -90,7 +90,7 @@ class Curl extends AbstractCurl implements CatalogProductAttributeInterface if ($fixture->hasData('options')) { $optionsData = $fixture->getData()['options']; - foreach ($matches[1] as $key => $optionId) { + foreach (array_unique($matches[1]) as $key => $optionId) { $optionsData[$key]['id'] = $optionId; } $resultData['options'] = $optionsData; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/LayoutTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/LayoutTest.php index 28353e7f953138cfa0dd3d91b04c17f70062e07a..751ffc6cb8c6f0c2ce01289c788d22b66a110588 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/LayoutTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/LayoutTest.php @@ -349,7 +349,8 @@ class LayoutTest extends \PHPUnit_Framework_TestCase 'setWrapperClass', 'unsetChild', 'unsetChildren', - 'updateButton' + 'updateButton', + 'setIsProductListingContext' ]; } } diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php index ac58d760d24f1522b6844e96ab94b8f68fd935b4..6a29c4ccecc5c13c26750ec49375acfe81db6f82 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php @@ -771,6 +771,8 @@ return [ ['getAffectedEntityIds', 'Magento\CatalogImportExport\Model\Import\Product'], ['getCategoryWithRoot', 'Magento\CatalogImportExport\Model\Import\Product\CategoryProcessor'], ['getCategory', 'Magento\CatalogImportExport\Model\Import\Product\CategoryProcessor'], + ['prepareGroupPrices', 'Magento\CatalogImportExport\Model\Export\Product'], + ['prepareTierPrices', 'Magento\CatalogImportExport\Model\Export\Product'], ['prepareGoogleOptimizerScripts'], ['prepareRedirect', 'Magento\Core\Controller\Varien\Exception'], ['preparePriceAlertData', 'Magento\ProductAlert\Block\Product\View'], diff --git a/pub/advanced_pricing.csv b/pub/advanced_pricing.csv new file mode 100644 index 0000000000000000000000000000000000000000..bfd24ea9bcc7eadce4507e02f2953a5351a83ca1 --- /dev/null +++ b/pub/advanced_pricing.csv @@ -0,0 +1,5 @@ +sku,tier_price_website,tier_price_customer_group,tier_price_qty,tier_price,group_price_website,group_price_customer_group,group_price_price +sku123,website1,General,2,10,,, +sku124,All Websites [USD],ALL GROUPS,3,15,,, +sku123,,,,,website1,General,11 +sku124,,,,,All Websites [USD],General,12 diff --git a/pub/catalog_product.csv b/pub/catalog_product.csv new file mode 100644 index 0000000000000000000000000000000000000000..4b334bff3ee0f83e797ea89cf1f526ae0c6f3ca1 --- /dev/null +++ b/pub/catalog_product.csv @@ -0,0 +1,47 @@ +sku,website_code,store_view_code,attribute_set_code,product_type,name,description,short_description,weight,product_online,visibility,product_websites,categories,price,special_price,special_price_from_date,special_price_to_date,tax_class_name,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,additional_images,additional_image_labels,configurable_variation_labels,configurable_variations,bundle_price_type,bundle_sku_type,bundle_weight_type,bundle_values,downloadble_samples,downloadble_links,associated_skus,related_skus,crosssell_skus,upsell_skus,custom_options,additional_attributes,manage_stock,is_in_stock,qty,out_of_stock_qty,is_qty_decimal,allow_backorders,min_cart_qty,max_cart_qty,notify_on_stock_below,qty_increments,enable_qty_increments,is_decimal_divided,new_from_date,new_to_date,created_at,updated_at,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_price,msrp_display_actual_price_type,map_enabled +simplesku1,base,default,default,simple,Simple Product 1,Simple Product 1 Description,Simple Product 1 Short Description,1.1,1,"catalog, search",base,Category1/SubCategory1|Category2/SubCategory2,99.99,89.99,3/2/2015,3/2/2015,Taxable Goods,simple1urlkey,Simple Product 1 Meta Title,"simple, product",Simple Product 1 Meta Description,/mediaimport/simple1.png,Base Image Label,/mediaimport/simple1.png,Small Image Label,/mediaimport/simple1.png,Thumbnail Image Label,"/mediaimport/simple1.png, /mediaimport/simple1a.png","Label Simple 1, Label Simple 1a",,,,,,,,,,"sku1, sku2, sku5","sku1, sku2, sku6","sku1, sku2, sku7",,"attribute1_code=attribute1_value,attribute2_code=attribute2_value",1,1,1000,2,0,1,1,1000,1,0,0,0,3/2/2015,3/2/2015,3/2/2015,3/2/2015,Magento Blank,3/2/2015,4/2/2015,,1 columnt,Product Info Column,9,On Gesture,1 +simplesku2withcustomoptions,base,default,default,simple,Simple Product 2 With Options,"<ul> +<li>Line 1</li> +<li>Line 2</li> +<li>Line 3</li> +<li>Simple Product 2 With Options Description</li> +</ul>",Simple Product 2 With Options Short Description,1.1,1,"catalog, search",base,Category1/SubCategory1|Category2/SubCategory2,99.99,89.99,3/2/2015,3/2/2015,Taxable Goods,simple2urlkey,Simple Product 2 Meta Title,"simple, product",Simple Product 2 Meta Description,/mediaimport/simple2.png,Base Image Label,/mediaimport/simple2.png,Small Image Label,/mediaimport/simple2.png,Thumbnail Image Label,"/mediaimport/simple2.png, /mediaimport/simple2a.png","Label Simple 2, Label Simple 2a",,,,,,,,,,"sku1, sku2, sku5","sku1, sku2, sku6","sku1, sku2, sku7","name=Custom Option One,type=dropdown,required=1,option_title=custom option 1; sku=sku-option1,price=10.00, price_type=fixed|name=Custom Option One,type=dropdown,required=1,option_title=custom option 2,sku=sku-option3,price=11.00,price_type=fixed","attribute1_code=attribute1_value,attribute2_code=attribute2_value",1,1,1000,2,0,1,1,1000,1,0,0,0,3/2/2015,3/2/2015,3/2/2015,3/2/2015,Magento Blank,3/2/2015,4/2/2015,,1 columnt,Product Info Column,9,On Gesture,1 +configurablesku1,base,default,default,configurable,Configurable Product 1,"<ul> +<li>Line 1</li> +<li>Line 2</li> +<li>Line 3</li> +<li>Configurable Product 1 Description</li> +</ul>",Configurable Product 1 Short Description,1.1,1,"catalog, search",base,Category1/SubCategory1|Category2/SubCategory2,99.99,89.99,3/2/2015,3/2/2015,Taxable Goods,configurable1urlkey,Configurable Product 1 Meta Title,"configurable, product",Configurable Product 1 Meta Description,/mediaimport/configurable1.png,Base Image Label,/mediaimport/configurable1.png,Small Image Label,/mediaimport/configurable1.png,Thumbnail Image Label,"/mediaimport/configurable1.png, /mediaimport/configurable1a.png","Label Configurable 1, Label Configurable 1a","color=Select Color, size=Select Size","sku=sku-red-xs,color=red,size=xs,price=10.99,display=1,image=http: +example.com/1.png|sku=sku-red-m,color=red,size=m,price=20.88,display=1,image=/example.com/2.png",,,,,,,,"sku1, sku2, sku5","sku1, sku2, sku6","sku1, sku2, sku7",,"attribute1_code=attribute1_value,attribute2_code=attribute2_value",1,1,1000,2,0,1,1,1000,1,0,0,0,3/2/2015,3/2/2015,3/2/2015,3/2/2015,Magento Blank,3/2/2015,4/2/2015,,1 columnt,Product Info Column,9,On Gesture,1 +bundlesku1,base,default,default,bundle,Bundle Product 1,"<ul> +<li>Line 1</li> +<li>Line 2</li> +<li>Line 3</li> +<li>Bundle Product 1 Description</li> +</ul>",Bundle Product 1 Short Description,1.1,1,"catalog, search",base,Category1/SubCategory1|Category2/SubCategory2,99.99,89.99,3/2/2015,3/2/2015,Taxable Goods,bundle1urlkey,Bundle Product 1 Meta Title,bundle product,Bundle Product 1 Meta Description,/mediaimport/bundle1.png,Base Image Label,/mediaimport/bundle1.png,Small Image Label,/mediaimport/bundle1.png,Thumbnail Image Label,"/mediaimport/bundle1.png, /mediaimport/bundle1a.png","Label Bundle 1, Label Bundle 1a",,,fixed,fixed,fixed,"name=Bundle Option One,type=dropdown,required=1,sku=sku-option1,price=10; price_type=fixed, default_qty=1, is_defaul=1 | +name=Bundle Option One,type=dropdown; required=1, sku=sku-option1,price=10, price_type=fixed, default_qty=1, is_defaul=1",,,,"sku1, sku2, sku5","sku1, sku2, sku6","sku1, sku2, sku7",,"attribute1_code=attribute1_value,attribute2_code=attribute2_value",1,1,1000,2,0,1,1,1000,1,0,0,0,3/2/2015,3/2/2015,3/2/2015,3/2/2015,Magento Blank,3/2/2015,4/2/2015,,1 columnt,Product Info Column,9,On Gesture,1 +bundlesku2,base,default,default,bundle,Bundle Product 2,"<ul> +<li>Line 1</li> +<li>Line 2</li> +<li>Line 3</li> +<li>Bundle Product 2 Description</li> +</ul>",Bundle Product 2 Short Description,1.1,1,"catalog, search",base,Category1/SubCategory1|Category2/SubCategory2,99.99,89.99,3/2/2015,3/2/2015,Taxable Goods,bundle1urlkey,Bundle Product 2 Meta Title,bundle product,Bundle Product 2 Meta Description,/mediaimport/bundle1.png,Base Image Label,/mediaimport/bundle1.png,Small Image Label,/mediaimport/bundle2.png,Thumbnail Image Label,"/mediaimport/bundle2.png, /mediaimport/bundl2a.png","Label Bundle 2, Label Bundle 2a",,,dynamic,fixed,fixed,"name=Bundle Option One,type=dropdown,required=1,sku=sku-option1, default_qty=1, is_defaul=1| +name=Bundle Option One,type=dropdown; required=1, sku=sku-option1, default_qty=1",,,,"sku1, sku2, sku5","sku1, sku2, sku6","sku1, sku2, sku7",,"attribute1_code=attribute1_value,attribute2_code=attribute2_value",1,1,1000,2,0,1,1,1000,1,0,0,0,3/2/2015,3/2/2015,3/2/2015,3/2/2015,Magento Blank,3/2/2015,4/2/2015,,1 columnt,Product Info Column,9,On Gesture,1 +groupedsku1,base,default,default,grouped,Grouped Product 1,"<ul> +<li>Line 1</li> +<li>Line 2</li> +<li>Line 3</li> +<li>Grouped Product 1 Description</li> +</ul>",Grouped Product 1 Short Description,1.1,1,"catalog, search",base,Category1/SubCategory1|Category2/SubCategory2,99.99,89.99,3/2/2015,3/2/2015,Taxable Goods,grouped1urlkey,Grouped Product 1 Meta Title,grouped product,Grouped Product 1 Meta Description,/mediaimport/grouped1.png,Base Image Label,/mediaimport/grouped1.png,Small Image Label,/mediaimport/grouped1.png,Thumbnail Image Label,"/mediaimport/grouped1.png, /mediaimport/grouped1a.png","Label grouped 1, Label grouped 1a",,,,,,,,,"sku-product1=5,sku-product2=1,sku-product3=3","sku1, sku2, sku5","sku1, sku2, sku6","sku1, sku2, sku7",,"attribute1_code=attribute1_value,attribute2_code=attribute2_value",1,1,1000,2,0,1,1,1000,1,0,0,0,3/2/2015,3/2/2015,3/2/2015,3/2/2015,Magento Blank,3/2/2015,4/2/2015,,1 columnt,Product Info Column,9,On Gesture,1 +giftsku1,base,default,default,gift,Gift Product 1,"<ul> +<li>Line 1</li> +<li>Line 2</li> +<li>Line 3</li> +<li>Gift Product 1 Description</li> +</ul>",Gift Product 1 Short Description,1.1,1,"catalog, search",base,Category1/SubCategory1|Category2/SubCategory2,99.99,89.99,3/2/2015,3/2/2015,Taxable Goods,Gift1urlkey,Gift Product 1 Meta Title,"gift, product",Gift Product 1 Meta Description,/mediaimport/gift1.png,Base Image Label,/mediaimport/gift1.png,Small Image Label,/mediaimport/gift1.png,Thumbnail Image Label,"/mediaimport/gift1.png, /mediaimport/gift1a.png","Label Gift 1, Label Gift 1a",,,,,,,,,,"sku1, sku2, sku5","sku1, sku2, sku6","sku1, sku2, sku7",,"attribute1_code=attribute1_value,attribute2_code=attribute2_value",1,1,1000,2,0,1,1,1000,1,0,0,0,3/2/2015,3/2/2015,3/2/2015,3/2/2015,Magento Blank,3/2/2015,4/2/2015,,1 columnt,Product Info Column,9,On Gesture,1 +downloadblesku1,base,default,default,downloadble,Downloadble Product 1,"<ul> +<li>Line 1</li> +<li>Line 2</li> +<li>Line 3</li> +<li>Downloadble Product 1 Description</li> +</ul>",Downloadble Product 1 Short Description,1.1,1,"catalog, search",base,Category1/SubCategory1|Category2/SubCategory2,99.99,89.99,3/2/2015,3/2/2015,Taxable Goods,Downloadble1urlkey,Downloadble Product 1 Meta Title,"Downloadble, product",Downloadble Product 1 Meta Description,/mediaimport/Downloadble1.png,Base Image Label,/mediaimport/Downloadble1.png,Small Image Label,/mediaimport/Downloadble1.png,Thumbnail Image Label,"/mediaimport/Downloadble1.png, /mediaimport/Downloadble1a.png","Label Downloadble 1, Label Downloadble 1a",,,,,,,"group_title=Group Title Samples, title=Title 1, file=media/file.mp4,sortorder=1|group_title=Group Title, title=Title 2, url=media/file2.mp4,sortorder=0","group_title=Group Title Links, title=Title 1, price=10, downloads=unlimited, file=media/file.mp4,sortorder=1|group_title=Group Title, title=Title 2, price=10, downloads=unlimited, url=media/file2.mp4,sortorder=0",,"sku1, sku2, sku5","sku1, sku2, sku6","sku1, sku2, sku7",,"attribute1_code=attribute1_value,attribute2_code=attribute2_value",1,1,1000,2,0,1,1,1000,1,0,0,0,3/2/2015,3/2/2015,3/2/2015,3/2/2015,Magento Blank,3/2/2015,4/2/2015,,1 columnt,Product Info Column,9,On Gesture,1 diff --git a/pub/media/import/.htaccess b/pub/media/import/.htaccess new file mode 100644 index 0000000000000000000000000000000000000000..93169e4eb44ffa5e6251f3532bb1902854703f07 --- /dev/null +++ b/pub/media/import/.htaccess @@ -0,0 +1,2 @@ +Order deny,allow +Deny from all