attributeList;
}
+
+ /**
+ * Retrieve attribute set id for customer address.
+ *
+ * @return int
+ */
+ public function getAttributeSetId()
+ {
+ return parent::getAttributeSetId() ?: AddressMetadataInterface::ATTRIBUTE_SET_ID_ADDRESS;
+ }
}
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/AbstractFrontend.php b/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/AbstractFrontend.php
index ab0760e780a47..32b72023340b5 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/AbstractFrontend.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/AbstractFrontend.php
@@ -11,11 +11,46 @@
*/
namespace Magento\Eav\Model\Entity\Attribute\Frontend;
+use Magento\Framework\App\CacheInterface;
+use Magento\Framework\Serialize\Serializer\Json as Serializer;
+use Magento\Store\Api\StoreResolverInterface;
+use Magento\Framework\App\ObjectManager;
+use Magento\Eav\Model\Cache\Type as CacheType;
+use Magento\Eav\Model\Entity\Attribute;
+use Magento\Eav\Model\Entity\Attribute\Source\BooleanFactory;
+
/**
* @api
*/
abstract class AbstractFrontend implements \Magento\Eav\Model\Entity\Attribute\Frontend\FrontendInterface
{
+ /**
+ * Default cache tags values
+ * will be used if no values in the constructor provided
+ * @var array
+ */
+ private static $defaultCacheTags = [CacheType::CACHE_TAG, Attribute::CACHE_TAG];
+
+ /**
+ * @var CacheInterface
+ */
+ private $cache;
+
+ /**
+ * @var StoreResolverInterface
+ */
+ private $storeResolver;
+
+ /**
+ * @var Serializer
+ */
+ private $serializer;
+
+ /**
+ * @var array
+ */
+ private $cacheTags;
+
/**
* Reference to the attribute instance
*
@@ -24,17 +59,30 @@ abstract class AbstractFrontend implements \Magento\Eav\Model\Entity\Attribute\F
protected $_attribute;
/**
- * @var \Magento\Eav\Model\Entity\Attribute\Source\BooleanFactory
+ * @var BooleanFactory
*/
protected $_attrBooleanFactory;
/**
- * @param \Magento\Eav\Model\Entity\Attribute\Source\BooleanFactory $attrBooleanFactory
+ * @param BooleanFactory $attrBooleanFactory
+ * @param CacheInterface $cache
+ * @param StoreResolverInterface $storeResolver
+ * @param array $cacheTags
+ * @param Serializer $serializer
* @codeCoverageIgnore
*/
- public function __construct(\Magento\Eav\Model\Entity\Attribute\Source\BooleanFactory $attrBooleanFactory)
- {
+ public function __construct(
+ BooleanFactory $attrBooleanFactory,
+ CacheInterface $cache = null,
+ StoreResolverInterface $storeResolver = null,
+ array $cacheTags = null,
+ Serializer $serializer = null
+ ) {
$this->_attrBooleanFactory = $attrBooleanFactory;
+ $this->cache = $cache ?: ObjectManager::getInstance()->get(CacheInterface::class);
+ $this->storeResolver = $storeResolver ?: ObjectManager::getInstance()->get(StoreResolverInterface::class);
+ $this->cacheTags = $cacheTags ?: self::$defaultCacheTags;
+ $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Serializer::class);
}
/**
@@ -249,7 +297,21 @@ public function getConfigField($fieldName)
*/
public function getSelectOptions()
{
- return $this->getAttribute()->getSource()->getAllOptions();
+ $cacheKey = 'attribute-navigation-option-' .
+ $this->getAttribute()->getAttributeCode() . '-' .
+ $this->storeResolver->getCurrentStoreId();
+ $optionString = $this->cache->load($cacheKey);
+ if (false === $optionString) {
+ $options = $this->getAttribute()->getSource()->getAllOptions();
+ $this->cache->save(
+ $this->serializer->serialize($options),
+ $cacheKey,
+ $this->cacheTags
+ );
+ } else {
+ $options = $this->serializer->unserialize($optionString);
+ }
+ return $options;
}
/**
diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php
index e8f67879eba83..5dd00fb3bc4fb 100644
--- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php
+++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php
@@ -6,6 +6,12 @@
namespace Magento\Eav\Test\Unit\Model\Entity\Attribute\Frontend;
use Magento\Eav\Model\Entity\Attribute\Frontend\DefaultFrontend;
+use Magento\Eav\Model\Entity\Attribute\Source\BooleanFactory;
+use Magento\Framework\Serialize\Serializer\Json as Serializer;
+use Magento\Store\Api\StoreResolverInterface;
+use Magento\Framework\App\CacheInterface;
+use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
+use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource;
class DefaultFrontendTest extends \PHPUnit_Framework_TestCase
{
@@ -15,18 +21,73 @@ class DefaultFrontendTest extends \PHPUnit_Framework_TestCase
protected $model;
/**
- * @var \Magento\Eav\Model\Entity\Attribute\Source\BooleanFactory|\PHPUnit_Framework_MockObject_MockObject
+ * @var BooleanFactory|\PHPUnit_Framework_MockObject_MockObject
*/
protected $booleanFactory;
+ /**
+ * @var Serializer|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $serializerMock;
+
+ /**
+ * @var StoreResolverInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $storeResolverMock;
+
+ /**
+ * @var CacheInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $cacheMock;
+
+ /**
+ * @var AbstractAttribute|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $attributeMock;
+
+ /**
+ * @var array
+ */
+ private $cacheTags;
+
+ /**
+ * @var AbstractSource|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $sourceMock;
+
protected function setUp()
{
- $this->booleanFactory = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Source\BooleanFactory::class)
+ $this->cacheTags = ['tag1', 'tag2'];
+
+ $this->booleanFactory = $this->getMockBuilder(BooleanFactory::class)
->disableOriginalConstructor()
->getMock();
+ $this->serializerMock = $this->getMockBuilder(Serializer::class)
+ ->getMock();
+ $this->storeResolverMock = $this->getMockBuilder(StoreResolverInterface::class)
+ ->getMockForAbstractClass();
+ $this->cacheMock = $this->getMockBuilder(CacheInterface::class)
+ ->getMockForAbstractClass();
+ $this->attributeMock = $this->getMockBuilder(AbstractAttribute::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getAttributeCode', 'getSource'])
+ ->getMockForAbstractClass();
+ $this->sourceMock = $this->getMockBuilder(AbstractSource::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getAllOptions'])
+ ->getMockForAbstractClass();
- $this->model = new DefaultFrontend(
- $this->booleanFactory
+ $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+ $this->model = $objectManager->getObject(
+ DefaultFrontend::class,
+ [
+ '_attrBooleanFactory' => $this->booleanFactory,
+ 'cache' => $this->cacheMock,
+ 'storeResolver' => $this->storeResolverMock,
+ 'serializer' => $this->serializerMock,
+ '_attribute' => $this->attributeMock,
+ 'cacheTags' => $this->cacheTags
+ ]
);
}
@@ -118,4 +179,39 @@ public function testGetClassLength()
$this->assertContains('maximum-length-2', $result);
$this->assertContains('validate-length', $result);
}
+
+ public function testGetSelectOptions()
+ {
+ $storeId = 1;
+ $attributeCode = 'attr1';
+ $cacheKey = 'attribute-navigation-option-' . $attributeCode . '-' . $storeId;
+ $options = ['option1', 'option2'];
+ $serializedOptions = "{['option1', 'option2']}";
+
+ $this->storeResolverMock->expects($this->once())
+ ->method('getCurrentStoreId')
+ ->willReturn($storeId);
+ $this->attributeMock->expects($this->once())
+ ->method('getAttributeCode')
+ ->willReturn($attributeCode);
+ $this->cacheMock->expects($this->once())
+ ->method('load')
+ ->with($cacheKey)
+ ->willReturn(false);
+ $this->attributeMock->expects($this->once())
+ ->method('getSource')
+ ->willReturn($this->sourceMock);
+ $this->sourceMock->expects($this->once())
+ ->method('getAllOptions')
+ ->willReturn($options);
+ $this->serializerMock->expects($this->once())
+ ->method('serialize')
+ ->with($options)
+ ->willReturn($serializedOptions);
+ $this->cacheMock->expects($this->once())
+ ->method('save')
+ ->with($serializedOptions, $cacheKey, $this->cacheTags);
+
+ $this->assertSame($options, $this->model->getSelectOptions());
+ }
}
diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Export/Export.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Export/Export.php
index 5d3b80d68a447..06d8610a247cc 100644
--- a/app/code/Magento/ImportExport/Controller/Adminhtml/Export/Export.php
+++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Export/Export.php
@@ -20,15 +20,24 @@ class Export extends ExportController
*/
protected $fileFactory;
+ /**
+ * @var \Magento\Framework\Session\SessionManagerInterface
+ */
+ private $sessionManager;
+
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\App\Response\Http\FileFactory $fileFactory
+ * @param \Magento\Framework\Session\SessionManagerInterface $sessionManager [optional]
*/
public function __construct(
Context $context,
- FileFactory $fileFactory
+ FileFactory $fileFactory,
+ \Magento\Framework\Session\SessionManagerInterface $sessionManager = null
) {
$this->fileFactory = $fileFactory;
+ $this->sessionManager = $sessionManager ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(\Magento\Framework\Session\SessionManagerInterface::class);
parent::__construct($context);
}
@@ -45,6 +54,7 @@ public function execute()
$model = $this->_objectManager->create(\Magento\ImportExport\Model\Export::class);
$model->setData($this->getRequest()->getParams());
+ $this->sessionManager->writeClose();
return $this->fileFactory->create(
$model->getFileName(),
$model->export(),
diff --git a/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml b/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml
index 691677d6bcafb..9755772fbdeb5 100644
--- a/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml
+++ b/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml
@@ -56,7 +56,7 @@ if ($product->isSaleable()) {
$data = ['addToCart' => [
'origin'=> 'msrp',
'popupId' => '#' . $popupId,
- 'productName' => $product->getName(),
+ 'productName' => $block->escapeJs($block->escapeHtml($product->getName())),
'productId' => $productId,
'productIdInput' => 'input[type="hidden"][name="product"]',
'realPrice' => $block->getRealPriceHtml(),
@@ -80,7 +80,7 @@ if ($product->isSaleable()) {
diff --git a/app/code/Magento/Msrp/view/frontend/templates/render/item/price_msrp_item.phtml b/app/code/Magento/Msrp/view/frontend/templates/render/item/price_msrp_item.phtml
index e61adcad58bdc..f440651af64d3 100644
--- a/app/code/Magento/Msrp/view/frontend/templates/render/item/price_msrp_item.phtml
+++ b/app/code/Magento/Msrp/view/frontend/templates/render/item/price_msrp_item.phtml
@@ -38,7 +38,7 @@
+
diff --git a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php
index dabfdd71b625b..fb02725c38392 100644
--- a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php
+++ b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php
@@ -13,6 +13,7 @@
use Magento\Sales\Model\Order\Email\Sender\OrderSender;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\App\ObjectManager;
+use Magento\Directory\Model\AllowedCountries;
/**
* Multishipping checkout model
@@ -146,12 +147,19 @@ class Multishipping extends \Magento\Framework\DataObject
*/
private $cartExtensionFactory;
+ /**
+ * @var AllowedCountries
+ */
+ private $allowedCountryReader;
+
/**
* @var \Magento\Quote\Model\Quote\ShippingAssignment\ShippingAssignmentProcessor
*/
private $shippingAssignmentProcessor;
/**
+ * Multishipping constructor.
+ *
* @param \Magento\Checkout\Model\Session $checkoutSession
* @param \Magento\Customer\Model\Session $customerSession
* @param \Magento\Sales\Model\OrderFactory $orderFactory
@@ -174,6 +182,7 @@ class Multishipping extends \Magento\Framework\DataObject
* @param \Magento\Framework\Api\FilterBuilder $filterBuilder
* @param \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector
* @param array $data
+ * @param AllowedCountries|null $allowedCountryReader
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
@@ -198,7 +207,8 @@ public function __construct(
\Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder,
\Magento\Framework\Api\FilterBuilder $filterBuilder,
\Magento\Quote\Model\Quote\TotalsCollector $totalsCollector,
- array $data = []
+ array $data = [],
+ AllowedCountries $allowedCountryReader = null
) {
$this->_eventManager = $eventManager;
$this->_scopeConfig = $scopeConfig;
@@ -221,6 +231,8 @@ public function __construct(
$this->quotePaymentToOrderPayment = $quotePaymentToOrderPayment;
$this->quoteAddressToOrderAddress = $quoteAddressToOrderAddress;
$this->totalsCollector = $totalsCollector;
+ $this->allowedCountryReader = $allowedCountryReader ?: ObjectManager::getInstance()
+ ->get(AllowedCountries::class);
parent::__construct($data);
$this->_init();
}
@@ -696,6 +708,18 @@ protected function _validate()
__('Please specify shipping methods for all addresses.')
);
}
+
+ // Checks if a country id present in the allowed countries list.
+ if (
+ !in_array(
+ $address->getCountryId(),
+ $this->allowedCountryReader->getAllowedCountries()
+ )
+ ) {
+ throw new \Magento\Framework\Exception\LocalizedException(
+ __('Some addresses cannot be used due to country-specific configurations.')
+ );
+ }
}
$addressValidation = $quote->getBillingAddress()->validate();
if ($addressValidation !== true) {
diff --git a/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php b/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php
index 4de048baf2033..81f8f78de9c0a 100644
--- a/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php
+++ b/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php
@@ -44,6 +44,9 @@
use Magento\Store\Model\StoreManagerInterface;
use PHPUnit_Framework_MockObject_MockObject;
use PHPUnit_Framework_TestCase;
+use Magento\Quote\Model\Quote\Payment;
+use Magento\Payment\Model\Method\AbstractMethod;
+use Magento\Directory\Model\AllowedCountries;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -146,6 +149,13 @@ protected function setUp()
$this->customerSessionMock->expects($this->atLeastOnce())->method('getCustomerDataObject')
->willReturn($this->customerMock);
$this->totalsCollectorMock = $this->createSimpleMock(TotalsCollector::class);
+ $allowedCountryReaderMock = $this->getMockBuilder(AllowedCountries::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getAllowedCountries'])
+ ->getMock();
+ $allowedCountryReaderMock->method('getAllowedCountries')
+ ->willReturn(['EN'=>'EN']);
+
$this->model = new Multishipping(
$this->checkoutSessionMock,
$this->customerSessionMock,
@@ -168,7 +178,8 @@ protected function setUp()
$this->searchCriteriaBuilderMock,
$this->filterBuilderMock,
$this->totalsCollectorMock,
- $data
+ $data,
+ $allowedCountryReaderMock
);
$this->cartExtensionFactoryMock = $this->getMockBuilder(CartExtensionFactory::class)
@@ -357,6 +368,49 @@ public function testSetShippingMethods()
$this->model->setShippingMethods($methodsArray);
}
+ /**
+ * Tests exception for addresses with country id not in the allowed countries list.
+ *
+ * @expectedException \Magento\Framework\Exception\LocalizedException
+ * @expectedExceptionMessage Some addresses cannot be used due to country-specific configurations.
+ */
+ public function testCreateOrdersCountryNotPresentInAllowedListException()
+ {
+ $abstractMethod = $this->getMockBuilder(AbstractMethod::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['isAvailable'])
+ ->getMockForAbstractClass();
+ $abstractMethod->method('isAvailable')
+ ->willReturn(true);
+
+ $paymentMock = $this->getMockBuilder(Payment::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getMethodInstance'])
+ ->getMock();
+ $paymentMock->method('getMethodInstance')
+ ->willReturn($abstractMethod);
+
+ $shippingAddressMock = $this->getMockBuilder(Address::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['validate', 'getShippingMethod', 'getShippingRateByCode', 'getCountryId'])
+ ->getMock();
+ $shippingAddressMock->method('validate')
+ ->willReturn(true);
+ $shippingAddressMock->method('getShippingMethod')
+ ->willReturn('carrier');
+ $shippingAddressMock->method('getShippingRateByCode')
+ ->willReturn('code');
+ $shippingAddressMock->method('getCountryId')
+ ->willReturn('EU');
+
+ $this->quoteMock->method('getPayment')
+ ->willReturn($paymentMock);
+ $this->quoteMock->method('getAllShippingAddresses')
+ ->willReturn([$shippingAddressMock]);
+
+ $this->model->createOrders();
+ }
+
/**
* @param ShippingAssignment $shippingAssignmentMock
* @return CartExtension|PHPUnit_Framework_MockObject_MockObject
diff --git a/app/code/Magento/Multishipping/composer.json b/app/code/Magento/Multishipping/composer.json
index eb277c3a0c4f3..165102bb7fccc 100644
--- a/app/code/Magento/Multishipping/composer.json
+++ b/app/code/Magento/Multishipping/composer.json
@@ -11,7 +11,8 @@
"magento/module-customer": "100.2.*",
"magento/module-theme": "100.2.*",
"magento/module-quote": "100.2.*",
- "magento/framework": "100.2.*"
+ "magento/framework": "100.2.*",
+ "magento/module-directory": "100.2.*"
},
"type": "magento2-module",
"version": "100.2.0-dev",
diff --git a/app/code/Magento/Multishipping/i18n/en_US.csv b/app/code/Magento/Multishipping/i18n/en_US.csv
index 7a6798accd33a..7f76402e1f640 100644
--- a/app/code/Magento/Multishipping/i18n/en_US.csv
+++ b/app/code/Magento/Multishipping/i18n/en_US.csv
@@ -87,3 +87,4 @@ Options,Options
"Review Order","Review Order"
"Select Shipping Method","Select Shipping Method"
"We received your order!","We received your order!"
+"Some addresses cannot be used due to country-specific configurations.","Some addresses cannot be used due to country-specific configurations."
diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Flatrate.php b/app/code/Magento/OfflineShipping/Model/Carrier/Flatrate.php
index 3fb5147fafd13..6e048adb16892 100644
--- a/app/code/Magento/OfflineShipping/Model/Carrier/Flatrate.php
+++ b/app/code/Magento/OfflineShipping/Model/Carrier/Flatrate.php
@@ -146,10 +146,7 @@ private function getShippingPrice(RateRequest $request, $freeBoxes)
$shippingPrice = $this->getFinalPriceWithHandlingFee($shippingPrice);
- if ($shippingPrice !== false && (
- $request->getFreeShipping() === true || $request->getPackageQty() == $freeBoxes
- )
- ) {
+ if ($shippingPrice !== false && $request->getPackageQty() == $freeBoxes) {
$shippingPrice = '0.00';
}
return $shippingPrice;
diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php
index bdbdf8dc5ff6b..c19ea44294015 100644
--- a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php
+++ b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php
@@ -156,14 +156,14 @@ public function collectRates(RateRequest $request)
$request->setPackageQty($oldQty);
if (!empty($rate) && $rate['price'] >= 0) {
- if ($request->getFreeShipping() === true || $request->getPackageQty() == $freeQty) {
+ if ($request->getPackageQty() == $freeQty) {
$shippingPrice = 0;
} else {
$shippingPrice = $this->getFinalPriceWithHandlingFee($rate['price']);
}
$method = $this->createShippingMethod($shippingPrice, $rate['cost']);
$result->append($method);
- } elseif (empty($rate) && $request->getFreeShipping() === true || $request->getPackageQty() == $freeQty) {
+ } elseif ($request->getPackageQty() == $freeQty) {
/**
* Promotion rule was applied for the whole cart.
diff --git a/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/FlatrateTest.php b/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/FlatrateTest.php
new file mode 100644
index 0000000000000..23ea03de69375
--- /dev/null
+++ b/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/FlatrateTest.php
@@ -0,0 +1,229 @@
+scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create', 'isSetFlag', 'getValue'])
+ ->getMock();
+
+ $this->errorFactoryMock = $this
+ ->getMockBuilder(\Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->loggerMock = $this->getMockBuilder(\Psr\Log\LoggerInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->resultFactoryMock = $this->getMockBuilder(\Magento\Shipping\Model\Rate\ResultFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+
+ $this->methodFactoryMock = $this
+ ->getMockBuilder(\Magento\Quote\Model\Quote\Address\RateResult\MethodFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+
+ $this->priceCalculatorMock = $this
+ ->getMockBuilder(\Magento\OfflineShipping\Model\Carrier\Flatrate\ItemPriceCalculator::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getShippingPricePerOrder'])
+ ->getMock();
+
+ $this->helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+ $this->model = $this->helper->getObject(
+ \Magento\OfflineShipping\Model\Carrier\Flatrate::class,
+ [
+ 'scopeConfig' => $this->scopeConfigMock,
+ 'rateErrorFactory' => $this->errorFactoryMock,
+ 'logger' => $this->loggerMock,
+ 'rateResultFactory' => $this->resultFactoryMock,
+ 'rateMethodFactory' => $this->methodFactoryMock,
+ 'itemPriceCalculator' => $this->priceCalculatorMock
+ ]
+ );
+ }
+
+ /**
+ * @param bool $freeshipping
+ * @dataProvider collectRatesWithGlobalFreeShippingDataProvider
+ * @return void
+ */
+ public function testCollectRatesWithGlobalFreeShipping($freeshipping)
+ {
+ $expectedPrice = 5;
+
+ $request = $this->getMockBuilder(\Magento\Quote\Model\Quote\Address\RateRequest::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getAllItems'])
+ ->getMock();
+
+ $item = $this->getMockBuilder(\Magento\Sales\Model\Order\Item::class)
+ ->disableOriginalConstructor()
+ ->setMethods(
+ [
+ 'getProduct',
+ 'getParentItem',
+ 'getHasChildren',
+ 'isShipSeparately',
+ 'getChildren',
+ 'getQty',
+ 'getFreeShipping'
+ ]
+ )
+ ->getMock();
+
+ $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['isVirtual'])
+ ->getMock();
+
+ $this->scopeConfigMock->expects($this->any())->method('isSetFlag')->willReturn(true);
+ $this->scopeConfigMock->expects($this->any())->method('getValue')->willReturnMap([
+ ['carriers/flatrate/active', \Magento\Store\Model\ScopeInterface::SCOPE_STORE, null, true],
+ ['carriers/flatrate/price', \Magento\Store\Model\ScopeInterface::SCOPE_STORE, null, 5],
+ ['carriers/flatrate/type', \Magento\Store\Model\ScopeInterface::SCOPE_STORE, null, 'O'],
+ ['carriers/flatrate/handling_fee', \Magento\Store\Model\ScopeInterface::SCOPE_STORE, null, 0],
+ [
+ 'carriers/flatrate/handling_type',
+ \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+ null,
+ AbstractCarrier::HANDLING_TYPE_FIXED
+ ],
+ [
+ 'carriers/flatrate/handling_action',
+ \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+ null,
+ AbstractCarrier::HANDLING_ACTION_PERORDER
+ ],
+ ]);
+
+ $this->priceCalculatorMock
+ ->expects($this->once())
+ ->method('getShippingPricePerOrder')
+ ->willReturn($expectedPrice);
+
+ $method = $this->getMockBuilder(Method::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['setCarrier', 'setCarrierTitle', 'setMethod', 'setMethodTitle', 'setPrice', 'setCost'])
+ ->getMock();
+ $this->methodFactoryMock->expects($this->once())->method('create')->willReturn($method);
+
+ $result = $this->getMockBuilder(Result::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['append'])
+ ->getMock();
+ $this->resultFactoryMock->expects($this->once())->method('create')->willReturn($result);
+
+ $product->expects($this->any())->method('isVirtual')->willReturn(false);
+
+ $item->expects($this->any())->method('getProduct')->willReturn($product);
+ $item->expects($this->any())->method('getFreeShipping')->willReturn(1);
+ $item->expects($this->any())->method('getQty')->willReturn(1);
+
+ $request->expects($this->any())->method('getAllItems')->willReturn([$item]);
+ $request->expects($this->any())->method('getPackageQty')->willReturn(1);
+
+ $request->expects($this->never())->method('getFreeShipping')->willReturn($freeshipping);
+
+ $returnPrice = null;
+ $method->expects($this->once())->method('setPrice')->with($this->captureArg($returnPrice));
+
+ $returnCost = null;
+ $method->expects($this->once())->method('setCost')->with($this->captureArg($returnCost));
+
+ $returnMethod = null;
+ $result->expects($this->once())->method('append')->with($this->captureArg($returnMethod));
+
+ $returnResult = $this->model->collectRates($request);
+
+ $this->assertEquals($expectedPrice, $returnPrice);
+ $this->assertEquals($expectedPrice, $returnCost);
+ $this->assertEquals($method, $returnMethod);
+ $this->assertEquals($result, $returnResult);
+ }
+
+ /**
+ * Captures the argument and saves it in the given variable
+ *
+ * @param $captureVar
+ * @return \PHPUnit_Framework_Constraint_Callback
+ */
+ private function captureArg(&$captureVar)
+ {
+ return $this->callback(function ($argToMock) use (&$captureVar) {
+ $captureVar = $argToMock;
+
+ return true;
+ });
+ }
+
+ /**
+ * @return array
+ */
+ public function collectRatesWithGlobalFreeShippingDataProvider()
+ {
+ return [
+ ['freeshipping' => true],
+ ['freeshipping' => false]
+ ];
+ }
+}
diff --git a/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/TablerateTest.php b/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/TablerateTest.php
new file mode 100644
index 0000000000000..08f3ec97cceaa
--- /dev/null
+++ b/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/TablerateTest.php
@@ -0,0 +1,218 @@
+scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create', 'isSetFlag', 'getValue'])
+ ->getMock();
+
+ $this->errorFactoryMock = $this
+ ->getMockBuilder(\Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->loggerMock = $this->getMockBuilder(\Psr\Log\LoggerInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->resultFactoryMock = $this->getMockBuilder(\Magento\Shipping\Model\Rate\ResultFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+
+ $this->methodFactoryMock = $this
+ ->getMockBuilder(\Magento\Quote\Model\Quote\Address\RateResult\MethodFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+
+ $this->tablerateFactoryMock = $this
+ ->getMockBuilder(\Magento\OfflineShipping\Model\ResourceModel\Carrier\TablerateFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create', 'getRate'])
+ ->getMock();
+
+ $this->helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+ $this->model = $this->helper->getObject(
+ \Magento\OfflineShipping\Model\Carrier\Tablerate::class,
+ [
+ 'scopeConfig' => $this->scopeConfigMock,
+ 'rateErrorFactory' => $this->errorFactoryMock,
+ 'logger' => $this->loggerMock,
+ 'rateResultFactory' => $this->resultFactoryMock,
+ 'resultMethodFactory' => $this->methodFactoryMock,
+ 'tablerateFactory' => $this->tablerateFactoryMock
+ ]
+ );
+ }
+
+ /**
+ * @param bool $freeshipping
+ * @dataProvider collectRatesWithGlobalFreeShippingDataProvider
+ * @return void
+ */
+ public function testCollectRatesWithGlobalFreeShipping($freeshipping)
+ {
+ $rate = [
+ 'price' => 15,
+ 'cost' => 2
+ ];
+
+ $request = $this->getMockBuilder(\Magento\Quote\Model\Quote\Address\RateRequest::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getAllItems', 'getPackageQty', 'getFreeShipping'])
+ ->getMock();
+
+ $item = $this->getMockBuilder(\Magento\Sales\Model\Order\Item::class)
+ ->disableOriginalConstructor()
+ ->setMethods(
+ [
+ 'getProduct',
+ 'getParentItem',
+ 'getHasChildren',
+ 'isShipSeparately',
+ 'getChildren',
+ 'getQty',
+ 'getFreeShipping',
+ 'getBaseRowTotal'
+ ]
+ )
+ ->getMock();
+
+ $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['isVirtual'])
+ ->getMock();
+
+ $tablerate = $this->getMockBuilder(\Magento\OfflineShipping\Model\Carrier\Tablerate::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getRate'])
+ ->getMock();
+
+ $this->scopeConfigMock->expects($this->any())->method('isSetFlag')->willReturn(true);
+
+ $tablerate->expects($this->any())->method('getRate')->willReturn($rate);
+ $this->tablerateFactoryMock->expects($this->once())->method('create')->willReturn($tablerate);
+
+ $method = $this->getMockBuilder(Method::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['setCarrier', 'setCarrierTitle', 'setMethod', 'setMethodTitle', 'setPrice', 'setCost'])
+ ->getMock();
+ $this->methodFactoryMock->expects($this->once())->method('create')->willReturn($method);
+
+ $result = $this->getMockBuilder(Result::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['append'])
+ ->getMock();
+ $this->resultFactoryMock->expects($this->once())->method('create')->willReturn($result);
+
+ $product->expects($this->any())->method('isVirtual')->willReturn(false);
+
+ $item->expects($this->any())->method('getProduct')->willReturn($product);
+ $item->expects($this->any())->method('getFreeShipping')->willReturn(1);
+ $item->expects($this->any())->method('getQty')->willReturn(1);
+
+ $request->expects($this->any())->method('getAllItems')->willReturn([$item]);
+ $request->expects($this->any())->method('getPackageQty')->willReturn(1);
+
+ $returnPrice = null;
+ $method->expects($this->once())->method('setPrice')->with($this->captureArg($returnPrice));
+
+ $returnCost = null;
+ $method->expects($this->once())->method('setCost')->with($this->captureArg($returnCost));
+
+ $returnMethod = null;
+ $result->expects($this->once())->method('append')->with($this->captureArg($returnMethod));
+
+ $request->expects($this->never())->method('getFreeShipping')->willReturn($freeshipping);
+
+ $returnResult = $this->model->collectRates($request);
+
+ $this->assertEquals($rate['price'], $returnPrice);
+ $this->assertEquals($rate['cost'], $returnCost);
+ $this->assertEquals($method, $returnMethod);
+ $this->assertEquals($result, $returnResult);
+ }
+
+ /**
+ * Captures the argument and saves it in the given variable
+ *
+ * @param $captureVar
+ * @return \PHPUnit_Framework_Constraint_Callback
+ */
+ private function captureArg(&$captureVar)
+ {
+ return $this->callback(function ($argToMock) use (&$captureVar) {
+ $captureVar = $argToMock;
+
+ return true;
+ });
+ }
+
+ /**
+ * @return array
+ */
+ public function collectRatesWithGlobalFreeShippingDataProvider()
+ {
+ return [
+ ['freeshipping' => true],
+ ['freeshipping' => false]
+ ];
+ }
+}
diff --git a/app/code/Magento/PageCache/etc/varnish4.vcl b/app/code/Magento/PageCache/etc/varnish4.vcl
index 936e7ef908477..e366e080a0211 100644
--- a/app/code/Magento/PageCache/etc/varnish4.vcl
+++ b/app/code/Magento/PageCache/etc/varnish4.vcl
@@ -96,11 +96,15 @@ sub vcl_recv {
set req.url = regsuball(req.url,"\?gclid=[^&]+&","?"); # strips when QS = "?gclid=AAA&foo=bar"
set req.url = regsuball(req.url,"&gclid=[^&]+",""); # strips when QS = "?foo=bar&gclid=AAA" or QS = "?foo=bar&gclid=AAA&bar=baz"
- # static files are always cacheable. remove SSL flag and cookie
- if (req.url ~ "^/(pub/)?(media|static)/.*\.(ico|html|css|js|jpg|jpeg|png|gif|tiff|bmp|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)$") {
- unset req.http.Https;
- unset req.http./* {{ ssl_offloaded_header }} */;
- unset req.http.Cookie;
+ # Static files caching
+ if (req.url ~ "^/(pub/)?(media|static)/.*\.(7z|avi|bmp|bz2|css|csv|doc|docx|eot|flac|flv|gif|gz|html|ico|jpeg|jpg|js|less|mka|mkv|mov|mp3|mp4|mpeg|mpg|odt|otf|ogg|ogm|opus|pdf|png|ppt|pptx|rar|rtf|svg|svgz|swf|tar|tbz|tgz|tiff|ttf|txt|txz|wav|webm|webp|woff|woff2|xls|xlsx|xml|xz|zip)$") {
+ # Static files should not be cached by default
+ return (pass);
+
+ # But if you use a few locales and don't use CDN you can enable caching static files by commenting previous line (#return (pass);) and uncommenting next 3 lines
+ #unset req.http.Https;
+ #unset req.http./* {{ ssl_offloaded_header }} */;
+ #unset req.http.Cookie;
}
return (hash);
@@ -156,7 +160,7 @@ sub vcl_backend_response {
# images, css and js are cacheable by default so we have to remove cookie also
if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) {
unset beresp.http.set-cookie;
- if (bereq.url !~ "\.(ico|css|js|jpg|jpeg|png|gif|tiff|bmp|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)(\?|$)") {
+ if (bereq.url !~ "\.(7z|avi|bmp|bz2|css|csv|doc|docx|eot|flac|flv|gif|gz|html|ico|jpeg|jpg|js|less|mka|mkv|mov|mp3|mp4|mpeg|mpg|odt|otf|ogg|ogm|opus|pdf|png|ppt|pptx|rar|rtf|svg|svgz|swf|tar|tbz|tgz|tiff|ttf|txt|txz|wav|webm|webp|woff|woff2|xls|xlsx|xml|xz|zip)(\?|$)") {
set beresp.http.Pragma = "no-cache";
set beresp.http.Expires = "-1";
set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
diff --git a/app/code/Magento/PageCache/etc/varnish5.vcl b/app/code/Magento/PageCache/etc/varnish5.vcl
index 2264176e4bcf5..937d87b113019 100644
--- a/app/code/Magento/PageCache/etc/varnish5.vcl
+++ b/app/code/Magento/PageCache/etc/varnish5.vcl
@@ -97,11 +97,15 @@ sub vcl_recv {
set req.url = regsuball(req.url,"\?gclid=[^&]+&","?"); # strips when QS = "?gclid=AAA&foo=bar"
set req.url = regsuball(req.url,"&gclid=[^&]+",""); # strips when QS = "?foo=bar&gclid=AAA" or QS = "?foo=bar&gclid=AAA&bar=baz"
- # static files are always cacheable. remove SSL flag and cookie
- if (req.url ~ "^/(pub/)?(media|static)/.*\.(ico|html|css|js|jpg|jpeg|png|gif|tiff|bmp|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)$") {
- unset req.http.Https;
- unset req.http./* {{ ssl_offloaded_header }} */;
- unset req.http.Cookie;
+ # Static files caching
+ if (req.url ~ "^/(pub/)?(media|static)/.*\.(7z|avi|bmp|bz2|css|csv|doc|docx|eot|flac|flv|gif|gz|html|ico|jpeg|jpg|js|less|mka|mkv|mov|mp3|mp4|mpeg|mpg|odt|otf|ogg|ogm|opus|pdf|png|ppt|pptx|rar|rtf|svg|svgz|swf|tar|tbz|tgz|tiff|ttf|txt|txz|wav|webm|webp|woff|woff2|xls|xlsx|xml|xz|zip)$") {
+ # Static files should not be cached by default
+ return (pass);
+
+ # But if you use a few locales and don't use CDN you can enable caching static files by commenting previous line (#return (pass);) and uncommenting next 3 lines
+ #unset req.http.Https;
+ #unset req.http./* {{ ssl_offloaded_header }} */;
+ #unset req.http.Cookie;
}
return (hash);
@@ -157,7 +161,7 @@ sub vcl_backend_response {
# images, css and js are cacheable by default so we have to remove cookie also
if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) {
unset beresp.http.set-cookie;
- if (bereq.url !~ "\.(ico|css|js|jpg|jpeg|png|gif|tiff|bmp|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)(\?|$)") {
+ if (bereq.url !~ "\.(7z|avi|bmp|bz2|css|csv|doc|docx|eot|flac|flv|gif|gz|html|ico|jpeg|jpg|js|less|mka|mkv|mov|mp3|mp4|mpeg|mpg|odt|otf|ogg|ogm|opus|pdf|png|ppt|pptx|rar|rtf|svg|svgz|swf|tar|tbz|tgz|tiff|ttf|txt|txz|wav|webm|webp|woff|woff2|xls|xlsx|xml|xz|zip)(\?|$)") {
set beresp.http.Pragma = "no-cache";
set beresp.http.Expires = "-1";
set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
diff --git a/app/code/Magento/Payment/view/frontend/templates/info/pdf/default.phtml b/app/code/Magento/Payment/view/frontend/templates/info/pdf/default.phtml
new file mode 100644
index 0000000000000..28f922aba2305
--- /dev/null
+++ b/app/code/Magento/Payment/view/frontend/templates/info/pdf/default.phtml
@@ -0,0 +1,23 @@
+
+escapeHtml($block->getMethod()->getTitle()); ?>{{pdf_row_separator}}
+
+getSpecificInformation()):?>
+ $value):?>
+ escapeHtml($label) ?>:
+ escapeHtml(implode(' ', $block->getValueAsArray($value)));?>
+ {{pdf_row_separator}}
+
+
+
+escapeHtml(implode('{{pdf_row_separator}}', $block->getChildPdfAsArray())); ?>
diff --git a/app/code/Magento/Paypal/Block/Express/InContext/Minicart/Button.php b/app/code/Magento/Paypal/Block/Express/InContext/Minicart/Button.php
index e5919548c5089..79142ecb1bfad 100644
--- a/app/code/Magento/Paypal/Block/Express/InContext/Minicart/Button.php
+++ b/app/code/Magento/Paypal/Block/Express/InContext/Minicart/Button.php
@@ -26,6 +26,8 @@ class Button extends Template implements ShortcutInterface
const BUTTON_ELEMENT_INDEX = 'button_id';
+ const LINK_DATA_ACTION = 'link_data_action';
+
const CART_BUTTON_ELEMENT_INDEX = 'add_to_cart_selector';
/**
@@ -132,6 +134,14 @@ public function getContainerId()
return $this->getData(self::BUTTON_ELEMENT_INDEX);
}
+ /**
+ * @return string
+ */
+ public function getLinkAction()
+ {
+ return $this->getData(self::LINK_DATA_ACTION);
+ }
+
/**
* @return string
*/
diff --git a/app/code/Magento/Paypal/Model/Api/AbstractApi.php b/app/code/Magento/Paypal/Model/Api/AbstractApi.php
index 94a5b50311bdf..0d1cd44639e93 100644
--- a/app/code/Magento/Paypal/Model/Api/AbstractApi.php
+++ b/app/code/Magento/Paypal/Model/Api/AbstractApi.php
@@ -52,7 +52,9 @@ abstract class AbstractApi extends \Magento\Framework\DataObject
/**
* @var array
*/
- protected $_lineItemExportItemsFilters = [];
+ protected $_lineItemExportItemsFilters = [
+ 'name' => 'strval'
+ ];
/**
* @var array
@@ -440,14 +442,7 @@ protected function _exportLineItems(array &$request, $i = 0)
foreach ($this->_lineItemExportItemsFormat as $publicKey => $privateFormat) {
$result = true;
$value = $item->getDataUsingMethod($publicKey);
- if (isset($this->_lineItemExportItemsFilters[$publicKey])) {
- $callback = $this->_lineItemExportItemsFilters[$publicKey];
- $value = call_user_func([$this, $callback], $value);
- }
- if (is_float($value)) {
- $value = $this->formatPrice($value);
- }
- $request[sprintf($privateFormat, $i)] = $value;
+ $request[sprintf($privateFormat, $i)] = $this->formatValue($value, $publicKey);
}
$i++;
}
@@ -635,4 +630,25 @@ public function getDebugReplacePrivateDataKeys()
{
return $this->_debugReplacePrivateDataKeys;
}
+
+ /**
+ * Formats value according to configured filters or converts to 0.00 format if value is float.
+ *
+ * @param string|int|float|\Magento\Framework\Phrase $value
+ * @param string $publicKey
+ * @return string
+ */
+ private function formatValue($value, $publicKey)
+ {
+ if (!empty($this->_lineItemExportItemsFilters[$publicKey])) {
+ $callback = $this->_lineItemExportItemsFilters[$publicKey];
+ $value = method_exists($this, $callback) ? $this->{$callback}($value) : $callback($value);
+ }
+
+ if (is_float($value)) {
+ $value = $this->formatPrice($value);
+ }
+
+ return $value;
+ }
}
diff --git a/app/code/Magento/Paypal/Model/Api/PayflowNvp.php b/app/code/Magento/Paypal/Model/Api/PayflowNvp.php
index 50c6ae9b1a2a7..eb1eb62f84160 100644
--- a/app/code/Magento/Paypal/Model/Api/PayflowNvp.php
+++ b/app/code/Magento/Paypal/Model/Api/PayflowNvp.php
@@ -773,11 +773,6 @@ protected function _exportLineItems(array &$request, $i = 0)
Cart::AMOUNT_SHIPPING => 'FREIGHTAMT',
'amount' => 'PAYMENTREQUEST_0_ITEMAMT',
];
- $this->_lineItemExportItemsFormat = [
- 'name' => 'L_PAYMENTREQUEST_0_NAME%d',
- 'qty' => 'L_PAYMENTREQUEST_0_QTY%d',
- 'amount' => 'L_PAYMENTREQUEST_0_AMT%d',
- ];
$request = $requestBefore;
$result = parent::_exportLineItems($request, $i);
/** @var Nvp $paypalNvp */
diff --git a/app/code/Magento/Paypal/Model/Info.php b/app/code/Magento/Paypal/Model/Info.php
index f0cafe05d842b..10a80bc01b07e 100644
--- a/app/code/Magento/Paypal/Model/Info.php
+++ b/app/code/Magento/Paypal/Model/Info.php
@@ -677,31 +677,24 @@ protected function _getAvsLabel($value)
{
if (!isset($this->_labelCodesCache[self::PAYPAL_AVS_CODE])) {
$this->_labelCodesCache[self::PAYPAL_AVS_CODE] = [
- // Visa, MasterCard, Discover and American Express
- 'A' => __('Matched Address only (no ZIP)'),
- // international "A"
- 'B' => __('Matched Address only (no ZIP) International'),
+ 'A' => __('Matched Address only (no ZIP)'), // Visa, MasterCard, Discover and American Express
+ 'B' => __('Matched Address only (no ZIP) International'), // international "A"
'N' => __('No Details matched'),
- // international "N"
- 'C' => __('No Details matched. International'),
+ 'C' => __('No Details matched. International'), // international "N"
'X' => __('Exact Match.'),
- // international "X"
- 'D' => __('Exact Match. Address and Postal Code. International'),
- // UK-specific "X"
- 'F' => __('Exact Match. Address and Postal Code. UK-specific'),
+ 'D' => __('Exact Match. Address and Postal Code. International'), // international "X"
+ 'F' => __('Exact Match. Address and Postal Code. UK-specific'), // UK-specific "X"
'E' => __('N/A. Not allowed for MOTO (Internet/Phone) transactions'),
'G' => __('N/A. Global Unavailable'),
'I' => __('N/A. International Unavailable'),
'Z' => __('Matched five-digit ZIP only (no Address)'),
- // international "Z"
- 'P' => __('Matched Postal Code only (no Address)'),
+ 'P' => __('Matched Postal Code only (no Address)'), // international "Z"
'R' => __('N/A. Retry'),
'S' => __('N/A. Service not Supported'),
'U' => __('N/A. Unavailable'),
'W' => __('Matched whole nine-digit ZIP (no Address)'),
'Y' => __('Yes. Matched Address and five-digit ZIP'),
- // Maestro and Solo
- '0' => __('All the address information matched'),
+ '0' => __('All the address information matched'), // Maestro and Solo
'1' => __('None of the address information matched'),
'2' => __('Part of the address information matched'),
'3' => __('N/A. The merchant did not provide AVS information'),
diff --git a/app/code/Magento/Paypal/Model/Payflow/Service/Response/Validator/AVSResponse.php b/app/code/Magento/Paypal/Model/Payflow/Service/Response/Validator/AVSResponse.php
index dc4a3e8af20fc..a128820c41dc4 100644
--- a/app/code/Magento/Paypal/Model/Payflow/Service/Response/Validator/AVSResponse.php
+++ b/app/code/Magento/Paypal/Model/Payflow/Service/Response/Validator/AVSResponse.php
@@ -35,6 +35,8 @@ class AVSResponse implements ValidatorInterface
* Indicates whether AVS response is international (Y),
* US (N), or cannot be determined (X). Client version
* 3.06 or later is required.
+ * @deprecated
+ * @see \Magento\Paypal\Model\Payflow\Service\Response\Validator\IAVSResponse
*/
const IAVS = 'iavs';
@@ -58,7 +60,6 @@ class AVSResponse implements ValidatorInterface
protected $avsCheck = [
'avsaddr' => 'avs_street',
'avszip' => 'avs_zip',
- 'iavs' => 'avs_international',
];
/**
@@ -67,7 +68,6 @@ class AVSResponse implements ValidatorInterface
protected $errorsMessages = [
'avs_street' => 'AVS address does not match.',
'avs_zip' => 'AVS zip does not match.',
- 'avs_international' => 'International AVS indicator does not match.',
];
/**
diff --git a/app/code/Magento/Paypal/Model/Payflow/Service/Response/Validator/IAVSResponse.php b/app/code/Magento/Paypal/Model/Payflow/Service/Response/Validator/IAVSResponse.php
new file mode 100644
index 0000000000000..0433c68227d37
--- /dev/null
+++ b/app/code/Magento/Paypal/Model/Payflow/Service/Response/Validator/IAVSResponse.php
@@ -0,0 +1,48 @@
+getConfig();
+ // the IAVS configuration setting is not enabled
+ if (!$config->getValue('avs_international')) {
+ return true;
+ }
+
+ if (strtolower((string) $response->getData(self::$iavs)) === self::$negativeResponseCode) {
+ $response->setRespmsg('International AVS indicator does not match.');
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/Service/Response/Validator/AVSResponseTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/Service/Response/Validator/AVSResponseTest.php
index 8402ed517950c..e0e27639b6008 100644
--- a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/Service/Response/Validator/AVSResponseTest.php
+++ b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/Service/Response/Validator/AVSResponseTest.php
@@ -8,42 +8,35 @@
use Magento\Payment\Model\Method\ConfigInterface;
use Magento\Paypal\Model\Payflow\Service\Response\Validator\AVSResponse;
use Magento\Paypal\Model\Payflow\Transparent;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
-/**
- * Class AVSResponseTest
- *
- * Test class for \Magento\Paypal\Model\Payflow\Service\Response\Validator\AVSResponse
- */
class AVSResponseTest extends \PHPUnit_Framework_TestCase
{
/**
- * @var \Magento\Paypal\Model\Payflow\Service\Response\Validator\AVSResponse
+ * @var AVSResponse
*/
- protected $validator;
+ private $validator;
/**
- * @var ConfigInterface|\PHPUnit_Framework_MockObject_MockObject
+ * @var ConfigInterface|MockObject
*/
- protected $configMock;
+ private $config;
/**
- * @var Transparent|\PHPUnit_Framework_MockObject_MockObject
+ * @var Transparent|MockObject
*/
- protected $payflowproFacade;
+ private $payflowproFacade;
/**
- * Set up
- *
- * @return void
+ * @inheritdoc
*/
protected function setUp()
{
- $this->configMock = $this->getMockBuilder(ConfigInterface::class)
+ $this->config = $this->getMockBuilder(ConfigInterface::class)
->getMockForAbstractClass();
$this->payflowproFacade = $this->getMockBuilder(Transparent::class)
->disableOriginalConstructor()
- ->setMethods([])
->getMock();
$this->validator = new AVSResponse();
@@ -53,22 +46,18 @@ protected function setUp()
* @param bool $expectedResult
* @param \Magento\Framework\DataObject $response
* @param array $configMap
- * @param int $exactlyCount
*
* @dataProvider validationDataProvider
*/
public function testValidation(
$expectedResult,
\Magento\Framework\DataObject $response,
- array $configMap,
- $exactlyCount
+ array $configMap
) {
- $this->payflowproFacade->expects(static::once())
- ->method('getConfig')
- ->willReturn($this->configMock);
+ $this->payflowproFacade->method('getConfig')
+ ->willReturn($this->config);
- $this->configMock->expects(static::exactly($exactlyCount))
- ->method('getValue')
+ $this->config->method('getValue')
->willReturnMap($configMap);
static::assertEquals($expectedResult, $this->validator->validate($response, $this->payflowproFacade));
@@ -92,15 +81,12 @@ public function validationDataProvider()
[
'avsaddr' => 'Y',
'avszip' => 'Y',
- 'iavs' => 'Y',
]
),
'configMap' => [
['avs_street', null, '0'],
['avs_zip', null, '0'],
- ['avs_international', null, '0'],
],
- 'exactlyCount' => 3,
],
[
'expectedResult' => true,
@@ -108,15 +94,12 @@ public function validationDataProvider()
[
'avsaddr' => 'Y',
'avszip' => 'Y',
- 'iavs' => 'Y',
]
),
'configMap' => [
['avs_street', null, '1'],
['avs_zip', null, '1'],
- ['avs_international', null, '1'],
],
- 'exactlyCount' => 3,
],
[
'expectedResult' => false,
@@ -124,15 +107,12 @@ public function validationDataProvider()
[
'avsaddr' => 'Y',
'avszip' => 'N',
- 'iavs' => 'Y',
]
),
'configMap' => [
['avs_street', null, '1'],
['avs_zip', null, '1'],
- ['avs_international', null, '1'],
],
- 'exactlyCount' => 2,
],
[
'expectedResult' => true,
@@ -140,15 +120,12 @@ public function validationDataProvider()
[
'avsaddr' => 'Y',
'avszip' => 'N',
- 'iavs' => 'N',
]
),
'configMap' => [
['avs_street', null, '1'],
['avs_zip', null, '0'],
- ['avs_international', null, '0'],
],
- 'exactlyCount' => 3,
],
[
'expectedResult' => true,
@@ -156,15 +133,12 @@ public function validationDataProvider()
[
'avsaddr' => 'Y',
'avszip' => 'N',
- 'iavs' => 'N',
]
),
'configMap' => [
['avs_street', null, '0'],
['avs_zip', null, '0'],
- ['avs_international', null, '0'],
],
- 'exactlyCount' => 3,
],
[
'expectedResult' => true,
@@ -172,15 +146,12 @@ public function validationDataProvider()
[
'avsaddr' => 'X',
'avszip' => 'Y',
- 'iavs' => 'X',
]
),
'configMap' => [
['avs_street', null, '1'],
['avs_zip', null, '1'],
- ['avs_international', null, '1'],
],
- 'exactlyCount' => 3,
],
[
'expectedResult' => true,
@@ -188,15 +159,12 @@ public function validationDataProvider()
[
'avsaddr' => 'X',
'avszip' => 'Y',
- 'iavs' => 'X',
]
),
'configMap' => [
['avs_street', null, '1'],
['avs_zip', null, '0'],
- ['avs_international', null, '1'],
],
- 'exactlyCount' => 3,
],
];
}
diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/Service/Response/Validator/IAVSResponseTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/Service/Response/Validator/IAVSResponseTest.php
new file mode 100644
index 0000000000000..dcd5708efeffd
--- /dev/null
+++ b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/Service/Response/Validator/IAVSResponseTest.php
@@ -0,0 +1,65 @@
+ $iavs
+ ]);
+
+ /** @var PayflowConfig|MockObject $config */
+ $config = $this->getMockBuilder(PayflowConfig::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ /** @var Transparent|MockObject $model */
+ $model = $this->getMockBuilder(Transparent::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $model->method('getConfig')
+ ->willReturn($config);
+
+ $config->method('getValue')
+ ->willReturn($configValue);
+
+ $validator = new IAVSResponse();
+ self::assertEquals($expected, $validator->validate($response, $model));
+ }
+
+ /**
+ * Gets list of different variations like configuration, IAVS value.
+ *
+ * @return array
+ */
+ public function variationsDataProvider()
+ {
+ return [
+ ['configValue' => 1, 'iavs' => 'Y', 'expected' => false],
+ ['configValue' => 0, 'iavs' => 'Y', 'expected' => true],
+ ['configValue' => 1, 'iavs' => 'N', 'expected' => true],
+ ['configValue' => 1, 'iavs' => 'X', 'expected' => true],
+ ['configValue' => 0, 'iavs' => 'X', 'expected' => true],
+ ];
+ }
+}
diff --git a/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro.xml b/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro.xml
index 2476923b792c8..fcf3fb39d1b7d 100644
--- a/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro.xml
+++ b/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro.xml
@@ -188,7 +188,7 @@