Skip to content

Commit

Permalink
Merge branch '2.4-develop' into refactor/mftf-customer
Browse files Browse the repository at this point in the history
  • Loading branch information
lbajsarowicz committed Jan 15, 2020
2 parents b5500f7 + 199d7c2 commit 632b3e5
Show file tree
Hide file tree
Showing 141 changed files with 6,211 additions and 742 deletions.
Expand Up @@ -30,6 +30,7 @@
<argument name="submenuUiId" value="{{AdminMenuReportsBusinessIntelligenceAdvancedReporting.dataUiId}}"/>
</actionGroup>
<switchToNextTab stepKey="switchToNewTab"/>
<waitForPageLoad stepKey="waitForAdvancedReportingPageLoad"/>
<seeInCurrentUrl url="advancedreporting.rjmetrics.com/report" stepKey="seeAssertAdvancedReportingPageUrl"/>
</test>
</tests>
42 changes: 30 additions & 12 deletions app/code/Magento/Bundle/view/base/web/js/price-bundle.js
Expand Up @@ -28,7 +28,8 @@ define([
controlContainer: 'dd', // should be eliminated
priceFormat: {},
isFixedPrice: false,
optionTierPricesBlocksSelector: '#option-tier-prices-{1} [data-role="selection-tier-prices"]'
optionTierPricesBlocksSelector: '#option-tier-prices-{1} [data-role="selection-tier-prices"]',
isOptionsInitialized: false
};

$.widget('mage.priceBundle', {
Expand All @@ -53,20 +54,37 @@ define([
priceBox = $(this.options.priceBoxSelector, form),
qty = $(this.options.qtyFieldSelector, form);

if (priceBox.data('magePriceBox') &&
priceBox.priceBox('option') &&
priceBox.priceBox('option').priceConfig
) {
if (priceBox.priceBox('option').priceConfig.optionTemplate) {
this._setOption('optionTemplate', priceBox.priceBox('option').priceConfig.optionTemplate);
this._updatePriceBox();
priceBox.on('price-box-initialized', this._updatePriceBox.bind(this));
options.on('change', this._onBundleOptionChanged.bind(this));
qty.on('change', this._onQtyFieldChanged.bind(this));
},

/**
* Update price box config with bundle option prices
* @private
*/
_updatePriceBox: function () {
var form = this.element,
options = $(this.options.productBundleSelector, form),
priceBox = $(this.options.priceBoxSelector, form);

if (!this.options.isOptionsInitialized) {
if (priceBox.data('magePriceBox') &&
priceBox.priceBox('option') &&
priceBox.priceBox('option').priceConfig
) {
if (priceBox.priceBox('option').priceConfig.optionTemplate) { //eslint-disable-line max-depth
this._setOption('optionTemplate', priceBox.priceBox('option').priceConfig.optionTemplate);
}
this._setOption('priceFormat', priceBox.priceBox('option').priceConfig.priceFormat);
priceBox.priceBox('setDefault', this.options.optionConfig.prices);
this.options.isOptionsInitialized = true;
}
this._setOption('priceFormat', priceBox.priceBox('option').priceConfig.priceFormat);
priceBox.priceBox('setDefault', this.options.optionConfig.prices);
this._applyOptionNodeFix(options);
}
this._applyOptionNodeFix(options);

options.on('change', this._onBundleOptionChanged.bind(this));
qty.on('change', this._onQtyFieldChanged.bind(this));
return this;
},

/**
Expand Down
Expand Up @@ -29,6 +29,7 @@ public function getFinalPrice();

/**
* Set the final price: usually it calculated as minimal price of the product
*
* Can be different depends on type of product
*
* @param string $finalPrice
Expand All @@ -39,6 +40,7 @@ public function setFinalPrice($finalPrice);

/**
* Retrieve max price of a product
*
* E.g. for product with custom options is price with the most expensive custom option
*
* @return string
Expand All @@ -57,6 +59,7 @@ public function setMaxPrice($maxPrice);

/**
* Retrieve the minimal price of the product or variation
*
* The minimal price is for example, the lowest price of all variations for complex product
*
* @return string
Expand All @@ -66,7 +69,7 @@ public function getMinimalPrice();

/**
* Set max regular price
* Max regular price is the same, as maximum price, except of excluding calculating special price and catalogules
* Max regular price is the same, as maximum price, except of excluding calculating special price and catalog rules
* in it
*
* @param string $maxRegularPrice
Expand Down Expand Up @@ -130,6 +133,7 @@ public function setMinimalPrice($minimalPrice);

/**
* Regular price - is price of product without discounts and special price with taxes and fixed product tax
*
* Usually this price is corresponding to price in admin panel of product
*
* @return string
Expand Down
Expand Up @@ -39,6 +39,7 @@
<click selector="{{AdminCategoryBasicFieldSection.includeInMenuLabel}}" stepKey="disableIncludeInMenu"/>
<scrollTo selector="{{AdminCategoryContentSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToContent"/>
<click selector="{{AdminCategoryContentSection.sectionHeader}}" stepKey="selectContent"/>
<scrollTo selector="{{AdminCategoryContentSection.description}}" x="0" y="-80" stepKey="scrollToDescription"/>
<fillField selector="{{AdminCategoryContentSection.description}}" userInput="Updated category Description Fields" stepKey="fillUpdatedDescription"/>
<scrollTo selector="{{AdminCategorySEOSection.SectionHeader}}" x="0" y="-80" stepKey="scrollToSearchEngineOptimization"/>
<click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="selectSearchEngineOptimization"/>
Expand Down
Expand Up @@ -51,15 +51,29 @@ define([
*/
function categoryProductRowClick(grid, event) {
var trElement = Event.findElement(event, 'tr'),
isInput = Event.element(event).tagName === 'INPUT',
eventElement = Event.element(event),
isInputCheckbox = eventElement.tagName === 'INPUT' && eventElement.type === 'checkbox',
isInputPosition = grid.targetElement &&
grid.targetElement.tagName === 'INPUT' &&
grid.targetElement.name === 'position',
checked = false,
checkbox = null;

if (trElement) {
if (eventElement.tagName === 'LABEL' &&
trElement.querySelector('#' + eventElement.htmlFor) &&
trElement.querySelector('#' + eventElement.htmlFor).type === 'checkbox'
) {
event.stopPropagation();
trElement.querySelector('#' + eventElement.htmlFor).trigger('click');

return;
}

if (trElement && !isInputPosition) {
checkbox = Element.getElementsBySelector(trElement, 'input');

if (checkbox[0]) {
checked = isInput ? checkbox[0].checked : !checkbox[0].checked;
checked = isInputCheckbox ? checkbox[0].checked : !checkbox[0].checked;
gridJsObject.setCheckboxChecked(checkbox[0], checked);
}
}
Expand Down
1 change: 1 addition & 0 deletions app/code/Magento/Catalog/view/base/web/js/price-box.js
Expand Up @@ -49,6 +49,7 @@ define([

box.on('reloadPrice', this.reloadPrice.bind(this));
box.on('updatePrice', this.onUpdatePrice.bind(this));
box.trigger('price-box-initialized');
},

/**
Expand Down
Expand Up @@ -6,6 +6,6 @@
-->
<span class="product-image-container" data-bind="style: {width: width + 'px'}">
<span class="product-image-wrapper" data-bind="style: {'padding-bottom': height/width*100 + '%'}">
<img class="product-image-photo" data-bind="attr: {src: src, alt: alt}, style: {width: width + 'px', height: height + 'px'}" />
<img class="product-image-photo" data-bind="attr: {src: src, alt: alt}, style: {width: 'auto', height: 'auto'}" />
</span>
</span>
Expand Up @@ -53,7 +53,7 @@ public function resolve(
$product = $value['model'];

$productId = $product->getData(
$this->metadataPool->getMetadata(ProductInterface::class)->getLinkField()
$this->metadataPool->getMetadata(ProductInterface::class)->getIdentifierField()
);

return $productId;
Expand Down
Expand Up @@ -7,7 +7,6 @@

namespace Magento\CatalogGraphQl\Model\Resolver\Products\Query;

use GraphQL\Language\AST\SelectionNode;
use Magento\Framework\GraphQl\Query\FieldTranslator;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;

Expand Down Expand Up @@ -37,57 +36,18 @@ public function __construct(FieldTranslator $fieldTranslator)
*/
public function getProductsFieldSelection(ResolveInfo $resolveInfo): array
{
return $this->getProductFields($resolveInfo);
}
$productFields = $resolveInfo->getFieldSelection(1);
$sectionNames = ['items', 'product'];

/**
* Return field names for all requested product fields.
*
* @param ResolveInfo $info
* @return string[]
*/
private function getProductFields(ResolveInfo $info): array
{
$fieldNames = [];
foreach ($info->fieldNodes as $node) {
if ($node->name->value !== 'products' && $node->name->value !== 'variants') {
continue;
}
foreach ($node->selectionSet->selections as $selection) {
if ($selection->name->value !== 'items' && $selection->name->value !== 'product') {
continue;
}
$fieldNames[] = $this->collectProductFieldNames($selection, $fieldNames);
}
}
if (!empty($fieldNames)) {
$fieldNames = array_merge(...$fieldNames);
}
return $fieldNames;
}

/**
* Collect field names for each node in selection
*
* @param SelectionNode $selection
* @param array $fieldNames
* @return array
*/
private function collectProductFieldNames(SelectionNode $selection, array $fieldNames = []): array
{
foreach ($selection->selectionSet->selections as $itemSelection) {
if ($itemSelection->kind === 'InlineFragment') {
foreach ($itemSelection->selectionSet->selections as $inlineSelection) {
if ($inlineSelection->kind === 'InlineFragment') {
continue;
}
$fieldNames[] = $this->fieldTranslator->translate($inlineSelection->name->value);
foreach ($sectionNames as $sectionName) {
if (isset($productFields[$sectionName])) {
foreach (array_keys($productFields[$sectionName]) as $fieldName) {
$fieldNames[] = $this->fieldTranslator->translate($fieldName);
}
continue;
}
$fieldNames[] = $this->fieldTranslator->translate($itemSelection->name->value);
}

return $fieldNames;
return array_unique($fieldNames);
}
}
4 changes: 2 additions & 2 deletions app/code/Magento/CatalogGraphQl/etc/schema.graphqls
Expand Up @@ -100,8 +100,8 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\
created_at: String @doc(description: "Timestamp indicating when the product was created.")
updated_at: String @doc(description: "Timestamp indicating when the product was updated.")
country_of_manufacture: String @doc(description: "The product's country of origin.")
type_id: String @doc(description: "One of simple, virtual, bundle, downloadable, grouped, or configurable.")
websites: [Website] @doc(description: "An array of websites in which the product is available.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Websites")
type_id: String @doc(description: "One of simple, virtual, bundle, downloadable, grouped, or configurable.") @deprecated(reason: "Use __typename instead.")
websites: [Website] @doc(description: "An array of websites in which the product is available.") @deprecated(reason: "The field should not be used on the storefront.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Websites")
product_links: [ProductLinksInterface] @doc(description: "An array of ProductLinks objects.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\BatchProductLinks")
media_gallery_entries: [MediaGalleryEntry] @deprecated(reason: "Use product's `media_gallery` instead") @doc(description: "An array of MediaGalleryEntry objects.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\MediaGalleryEntries")
price: ProductPrices @deprecated(reason: "Use price_range for product price information.") @doc(description: "A ProductPrices object, indicating the price of an item.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Price")
Expand Down
@@ -0,0 +1,100 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CatalogSearch\Model\Search\FilterMapper;

use Magento\Catalog\Model\Product;
use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver;
use Magento\Eav\Model\Config as EavConfig;
use Magento\Framework\DB\Select;
use Magento\Framework\Search\Request\FilterInterface;

/**
* Add stock status filter for each requested filter
*/
class CustomAttributeStockStatusFilter
{
/**
* Suffix to append to filter name in order to generate stock status table alias for JOIN clause
*/
private const STOCK_STATUS_TABLE_ALIAS_SUFFIX = '_stock_index';
/**
* Attribute types to apply
*/
private const TARGET_ATTRIBUTE_TYPES = [
'select',
'multiselect'
];
/**
* @var EavConfig
*/
private $eavConfig;
/**
* @var AliasResolver
*/
private $aliasResolver;
/**
* @var StockStatusQueryBuilder|null
*/
private $stockStatusQueryBuilder;

/**
* @param EavConfig $eavConfig
* @param AliasResolver $aliasResolver
* @param StockStatusQueryBuilder $stockStatusQueryBuilder
*/
public function __construct(
EavConfig $eavConfig,
AliasResolver $aliasResolver,
StockStatusQueryBuilder $stockStatusQueryBuilder
) {
$this->eavConfig = $eavConfig;
$this->aliasResolver = $aliasResolver;
$this->stockStatusQueryBuilder = $stockStatusQueryBuilder;
}

/**
* Apply stock status filter to provided filter
*
* @param Select $select
* @param mixed $values
* @param FilterInterface[] $filters
* @return Select
*/
public function apply(Select $select, $values = null, FilterInterface ...$filters): Select
{
$select = clone $select;
foreach ($filters as $filter) {
if ($this->isApplicable($filter)) {
$mainTableAlias = $this->aliasResolver->getAlias($filter);
$stockTableAlias = $mainTableAlias . self::STOCK_STATUS_TABLE_ALIAS_SUFFIX;
$select = $this->stockStatusQueryBuilder->apply(
$select,
$mainTableAlias,
$stockTableAlias,
'source_id',
$values
);
}
}
return $select;
}

/**
* Check if stock status filter is applicable to provided filter
*
* @param FilterInterface $filter
* @return bool
*/
private function isApplicable(FilterInterface $filter): bool
{
$attribute = $this->eavConfig->getAttribute(Product::ENTITY, $filter->getField());
return $attribute
&& $filter->getType() === FilterInterface::TYPE_TERM
&& in_array($attribute->getFrontendInput(), self::TARGET_ATTRIBUTE_TYPES, true);
}
}

0 comments on commit 632b3e5

Please sign in to comment.