123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631 |
- <?php
- /**
- * Copyright © Magento, Inc. 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;
- use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
- /**
- * Class AdvancedPricing
- *
- * @SuppressWarnings(PHPMD.ExcessiveParameterList)
- * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
- * @SuppressWarnings(PHPMD.TooManyFields)
- */
- 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_TIER_PRICE_PERCENTAGE_VALUE = 'percentage_value';
- const COL_TIER_PRICE_TYPE = 'tier_price_value_type';
- const TIER_PRICE_TYPE_FIXED = 'Fixed';
- const TIER_PRICE_TYPE_PERCENT = 'Discount';
- const TABLE_TIER_PRICE = 'catalog_product_entity_tier_price';
- const DEFAULT_ALL_GROUPS_GROUPED_PRICE_VALUE = '0';
- const ENTITY_TYPE_CODE = 'advanced_pricing';
- const VALIDATOR_MAIN = 'validator';
- const VALIDATOR_WEBSITE = 'validator_website';
- const VALIDATOR_TEAR_PRICE = 'validator_tear_price';
- /**
- * 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_INVALID_TIER_PRICE_TYPE => 'Value for \'tier_price_value_type\' ' .
- 'attribute contains incorrect value, acceptable values are Fixed, Discount',
- ValidatorInterface::ERROR_TIER_DATA_INCOMPLETE => 'Tier Price data is incomplete',
- ValidatorInterface::ERROR_INVALID_ATTRIBUTE_DECIMAL =>
- 'Value for \'%s\' attribute contains incorrect value, acceptable values are in decimal format',
- ];
- /**
- * If we should check column names
- *
- * @var bool
- */
- protected $needColumnCheck = true;
- /**
- * Valid column names.
- *
- * @array
- */
- protected $validColumnNames = [
- self::COL_SKU,
- self::COL_TIER_PRICE_WEBSITE,
- self::COL_TIER_PRICE_CUSTOMER_GROUP,
- self::COL_TIER_PRICE_QTY,
- self::COL_TIER_PRICE,
- self::COL_TIER_PRICE_TYPE
- ];
- /**
- * Need to log in import history
- *
- * @var bool
- */
- protected $logInHistory = true;
- /**
- * @var \Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceModelFactory
- */
- 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 array
- */
- protected $_validators = [];
- /**
- * @var array
- */
- protected $_cachedSkuToDelete;
- /**
- * @var array
- */
- protected $_oldSkus = null;
- /**
- * Permanent entity columns.
- *
- * @var string[]
- */
- protected $_permanentAttributes = [self::COL_SKU];
- /**
- * Catalog product entity
- *
- * @var string
- */
- protected $_catalogProductEntity;
- /**
- * @var \Magento\Framework\Stdlib\DateTime\DateTime
- */
- protected $dateTime;
- /**
- * Product entity link field
- *
- * @var string
- */
- private $productEntityLinkField;
- /**
- * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
- * @param \Magento\Framework\Json\Helper\Data $jsonHelper
- * @param \Magento\ImportExport\Helper\Data $importExportData
- * @param \Magento\ImportExport\Model\ResourceModel\Import\Data $importData
- * @param \Magento\Eav\Model\Config $config
- * @param \Magento\Framework\App\ResourceConnection $resource
- * @param \Magento\ImportExport\Model\ResourceModel\Helper $resourceHelper
- * @param \Magento\Framework\Stdlib\StringUtils $string
- * @param ProcessingErrorAggregatorInterface $errorAggregator
- * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime
- * @param \Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceModelFactory $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\TierPrice $tierPriceValidator
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
- */
- public function __construct(
- \Magento\Framework\Json\Helper\Data $jsonHelper,
- \Magento\ImportExport\Helper\Data $importExportData,
- \Magento\ImportExport\Model\ResourceModel\Import\Data $importData,
- \Magento\Eav\Model\Config $config,
- \Magento\Framework\App\ResourceConnection $resource,
- \Magento\ImportExport\Model\ResourceModel\Helper $resourceHelper,
- \Magento\Framework\Stdlib\StringUtils $string,
- ProcessingErrorAggregatorInterface $errorAggregator,
- \Magento\Framework\Stdlib\DateTime\DateTime $dateTime,
- \Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceModelFactory $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\TierPrice $tierPriceValidator
- ) {
- $this->dateTime = $dateTime;
- $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->_validators[self::VALIDATOR_MAIN] = $validator->init($this);
- $this->_catalogProductEntity = $this->_resourceFactory->create()->getTable('catalog_product_entity');
- $this->_oldSkus = $this->retrieveOldSkus();
- $this->_validators[self::VALIDATOR_WEBSITE] = $websiteValidator;
- $this->_validators[self::VALIDATOR_TEAR_PRICE] = $tierPriceValidator;
- $this->errorAggregator = $errorAggregator;
- foreach (array_merge($this->errorMessageTemplates, $this->_messageTemplates) as $errorCode => $message) {
- $this->getErrorAggregator()->addErrorMessageTemplate($errorCode, $message);
- }
- }
- /**
- * Validator object getter.
- *
- * @param string $type
- * @return AdvancedPricing\Validator|AdvancedPricing\Validator\Website
- */
- protected function _getValidator($type)
- {
- return $this->_validators[$type];
- }
- /**
- * 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 !$this->getErrorAggregator()->isRowInvalid($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->_getValidator(self::VALIDATOR_MAIN)->isValid($rowData)) {
- foreach ($this->_getValidator(self::VALIDATOR_MAIN)->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 !$this->getErrorAggregator()->isRowInvalid($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 $this
- */
- public function saveAdvancedPricing()
- {
- $this->saveAndReplaceAdvancedPrices();
- return $this;
- }
- /**
- * Deletes Advanced price data from raw data.
- *
- * @return $this
- */
- public function deleteAdvancedPricing()
- {
- $this->_cachedSkuToDelete = null;
- $listSku = [];
- while ($bunch = $this->_dataSourceModel->getNextBunch()) {
- foreach ($bunch as $rowNum => $rowData) {
- $this->validateRow($rowData, $rowNum);
- if (!$this->getErrorAggregator()->isRowInvalid($rowNum)) {
- $rowSku = $rowData[self::COL_SKU];
- $listSku[] = $rowSku;
- }
- if ($this->getErrorAggregator()->hasToBeTerminated()) {
- $this->getErrorAggregator()->addRowToSkip($rowNum);
- }
- }
- }
- if ($listSku) {
- $this->deleteProductTierPrices(array_unique($listSku), self::TABLE_TIER_PRICE);
- $this->setUpdatedAt($listSku);
- }
- return $this;
- }
- /**
- * Replace advanced pricing
- *
- * @return $this
- */
- public function replaceAdvancedPricing()
- {
- $this->saveAndReplaceAdvancedPrices();
- return $this;
- }
- /**
- * Save and replace advanced prices
- *
- * @return $this
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- * @SuppressWarnings(PHPMD.NPathComplexity)
- */
- protected function saveAndReplaceAdvancedPrices()
- {
- $behavior = $this->getBehavior();
- if (\Magento\ImportExport\Model\Import::BEHAVIOR_REPLACE == $behavior) {
- $this->_cachedSkuToDelete = null;
- }
- $listSku = [];
- while ($bunch = $this->_dataSourceModel->getNextBunch()) {
- $tierPrices = [];
- foreach ($bunch as $rowNum => $rowData) {
- if (!$this->validateRow($rowData, $rowNum)) {
- $this->addRowError(ValidatorInterface::ERROR_SKU_IS_EMPTY, $rowNum);
- continue;
- }
- if ($this->getErrorAggregator()->hasToBeTerminated()) {
- $this->getErrorAggregator()->addRowToSkip($rowNum);
- continue;
- }
- $rowSku = $rowData[self::COL_SKU];
- $listSku[] = $rowSku;
- 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_TYPE] === self::TIER_PRICE_TYPE_FIXED
- ? $rowData[self::COL_TIER_PRICE] : 0,
- 'percentage_value' => $rowData[self::COL_TIER_PRICE_TYPE] === self::TIER_PRICE_TYPE_PERCENT
- ? $rowData[self::COL_TIER_PRICE] : null,
- 'website_id' => $this->getWebSiteId($rowData[self::COL_TIER_PRICE_WEBSITE])
- ];
- }
- }
- if (\Magento\ImportExport\Model\Import::BEHAVIOR_REPLACE == $behavior) {
- if ($listSku) {
- $this->processCountNewPrices($tierPrices);
- if ($this->deleteProductTierPrices(array_unique($listSku), self::TABLE_TIER_PRICE)) {
- $this->saveProductPrices($tierPrices, self::TABLE_TIER_PRICE);
- $this->setUpdatedAt($listSku);
- }
- }
- } elseif (\Magento\ImportExport\Model\Import::BEHAVIOR_APPEND == $behavior) {
- $this->processCountExistingPrices($tierPrices, self::TABLE_TIER_PRICE)
- ->processCountNewPrices($tierPrices);
- $this->saveProductPrices($tierPrices, self::TABLE_TIER_PRICE);
- if ($listSku) {
- $this->setUpdatedAt($listSku);
- }
- }
- }
- return $this;
- }
- /**
- * 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 = [];
- $entityIds = [];
- $oldSkus = $this->retrieveOldSkus();
- foreach ($priceData as $sku => $priceRows) {
- if (isset($oldSkus[$sku])) {
- $productId = $oldSkus[$sku];
- foreach ($priceRows as $row) {
- $row[$this->getProductEntityLinkField()] = $productId;
- $priceIn[] = $row;
- $entityIds[] = $productId;
- }
- }
- }
- if ($priceIn) {
- $this->_connection->insertOnDuplicate($tableName, $priceIn, ['value', 'percentage_value']);
- }
- }
- return $this;
- }
- /**
- * Deletes tier prices prices.
- *
- * @param array $listSku
- * @param string $table
- * @return boolean
- */
- protected function deleteProductTierPrices(array $listSku, $table)
- {
- $tableName = $this->_resourceFactory->create()->getTable($table);
- $productEntityLinkField = $this->getProductEntityLinkField();
- if ($tableName && $listSku) {
- if (!$this->_cachedSkuToDelete) {
- $this->_cachedSkuToDelete = $this->_connection->fetchCol(
- $this->_connection->select()
- ->from($this->_catalogProductEntity, $productEntityLinkField)
- ->where('sku IN (?)', $listSku)
- );
- }
- if ($this->_cachedSkuToDelete) {
- try {
- $this->countItemsDeleted += $this->_connection->delete(
- $tableName,
- $this->_connection->quoteInto($productEntityLinkField . ' IN (?)', $this->_cachedSkuToDelete)
- );
- return true;
- } catch (\Exception $e) {
- return false;
- }
- } else {
- $this->addRowError(ValidatorInterface::ERROR_SKU_IS_EMPTY, 0);
- return false;
- }
- }
- return false;
- }
- /**
- * Set updated_at for product
- *
- * @param array $listSku
- * @return $this
- */
- protected function setUpdatedAt(array $listSku)
- {
- $updatedAt = $this->dateTime->gmtDate('Y-m-d H:i:s');
- $this->_connection->update(
- $this->_catalogProductEntity,
- [\Magento\Catalog\Model\Category::KEY_UPDATED_AT => $updatedAt],
- $this->_connection->quoteInto('sku IN (?)', array_unique($listSku))
- );
- return $this;
- }
- /**
- * Get website id by code
- *
- * @param string $websiteCode
- * @return array|int|string
- */
- protected function getWebSiteId($websiteCode)
- {
- $result = $websiteCode == $this->_getValidator(self::VALIDATOR_WEBSITE)->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->_getValidator(self::VALIDATOR_TEAR_PRICE)->getCustomerGroups();
- return $customerGroup == self::VALUE_ALL_GROUPS ? 0 : $customerGroups[$customerGroup];
- }
- /**
- * Retrieve product skus
- *
- * @return array
- */
- protected function retrieveOldSkus()
- {
- if ($this->_oldSkus === null) {
- $this->_oldSkus = $this->_connection->fetchPairs(
- $this->_connection->select()->from(
- $this->_catalogProductEntity,
- ['sku', $this->getProductEntityLinkField()]
- )
- );
- }
- return $this->_oldSkus;
- }
- /**
- * Count existing prices
- *
- * @param array $prices
- * @param string $table
- * @return $this
- */
- protected function processCountExistingPrices($prices, $table)
- {
- $oldSkus = $this->retrieveOldSkus();
- $existProductIds = array_intersect_key($oldSkus, $prices);
- if (!count($existProductIds)) {
- return $this;
- }
- $tableName = $this->_resourceFactory->create()->getTable($table);
- $productEntityLinkField = $this->getProductEntityLinkField();
- $existingPrices = $this->_connection->fetchAssoc(
- $this->_connection->select()->from(
- $tableName,
- ['value_id', $productEntityLinkField, 'all_groups', 'customer_group_id']
- )->where($productEntityLinkField . ' IN (?)', $existProductIds)
- );
- foreach ($existingPrices as $existingPrice) {
- foreach ($prices as $sku => $skuPrices) {
- if (isset($oldSkus[$sku]) && $existingPrice[$productEntityLinkField] == $oldSkus[$sku]) {
- $this->incrementCounterUpdated($skuPrices, $existingPrice);
- }
- }
- }
- return $this;
- }
- /**
- * Increment counter of updated items
- *
- * @param array $prices
- * @param array $existingPrice
- * @return void
- */
- protected function incrementCounterUpdated($prices, $existingPrice)
- {
- foreach ($prices as $price) {
- if ($existingPrice['all_groups'] == $price['all_groups']
- && $existingPrice['customer_group_id'] == $price['customer_group_id']
- ) {
- $this->countItemsUpdated++;
- }
- }
- }
- /**
- * Count new prices
- *
- * @param array $tierPrices
- * @return $this
- */
- protected function processCountNewPrices(array $tierPrices)
- {
- foreach ($tierPrices as $productPrices) {
- $this->countItemsCreated += count($productPrices);
- }
- $this->countItemsCreated -= $this->countItemsUpdated;
- return $this;
- }
- /**
- * Get product entity link field
- *
- * @return string
- * @throws \Exception
- */
- private function getProductEntityLinkField()
- {
- if (!$this->productEntityLinkField) {
- $this->productEntityLinkField = $this->getMetadataPool()
- ->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class)
- ->getLinkField();
- }
- return $this->productEntityLinkField;
- }
- }
|