diff --git a/app/code/Magento/Braintree/Model/AvsEmsCodeMapper.php b/app/code/Magento/Braintree/Model/AvsEmsCodeMapper.php new file mode 100644 index 0000000000000..5832e5bf0a35b --- /dev/null +++ b/app/code/Magento/Braintree/Model/AvsEmsCodeMapper.php @@ -0,0 +1,71 @@ + 'Y', + 'NM' => 'A', + 'MN' => 'Z', + 'NN' => 'N', + 'UU' => 'U', + 'II' => 'U', + 'AA' => 'E' + ]; + + /** + * Gets payment AVS verification code. + * + * @param OrderPaymentInterface $orderPayment + * @return string + * @throws \InvalidArgumentException If specified order payment has different payment method code. + */ + public function getCode(OrderPaymentInterface $orderPayment) + { + if ($orderPayment->getMethod() !== ConfigProvider::CODE) { + throw new \InvalidArgumentException( + 'The "' . $orderPayment->getMethod() . '" does not supported by Braintree AVS mapper.' + ); + } + + $additionalInfo = $orderPayment->getAdditionalInformation(); + if (empty($additionalInfo[PaymentDetailsHandler::AVS_POSTAL_RESPONSE_CODE]) || + empty($additionalInfo[PaymentDetailsHandler::AVS_STREET_ADDRESS_RESPONSE_CODE]) + ) { + return self::$unavailableCode; + } + + $streetCode = $additionalInfo[PaymentDetailsHandler::AVS_STREET_ADDRESS_RESPONSE_CODE]; + $zipCode = $additionalInfo[PaymentDetailsHandler::AVS_POSTAL_RESPONSE_CODE]; + $key = $zipCode . $streetCode; + return isset(self::$avsMap[$key]) ? self::$avsMap[$key] : self::$unavailableCode; + } +} diff --git a/app/code/Magento/Braintree/Model/CvvEmsCodeMapper.php b/app/code/Magento/Braintree/Model/CvvEmsCodeMapper.php new file mode 100644 index 0000000000000..5accc222c1fe5 --- /dev/null +++ b/app/code/Magento/Braintree/Model/CvvEmsCodeMapper.php @@ -0,0 +1,66 @@ + 'M', + 'N' => 'N', + 'U' => 'P', + 'I' => 'P', + 'S' => 'S', + 'A' => '' + ]; + + /** + * Gets payment CVV verification code. + * + * @param OrderPaymentInterface $orderPayment + * @return string + * @throws \InvalidArgumentException If specified order payment has different payment method code. + */ + public function getCode(OrderPaymentInterface $orderPayment) + { + if ($orderPayment->getMethod() !== ConfigProvider::CODE) { + throw new \InvalidArgumentException( + 'The "' . $orderPayment->getMethod() . '" does not supported by Braintree CVV mapper.' + ); + } + + $additionalInfo = $orderPayment->getAdditionalInformation(); + if (empty($additionalInfo[PaymentDetailsHandler::CVV_RESPONSE_CODE])) { + return self::$notProvidedCode; + } + + $cvv = $additionalInfo[PaymentDetailsHandler::CVV_RESPONSE_CODE]; + return isset(self::$cvvMap[$cvv]) ? self::$cvvMap[$cvv] : self::$notProvidedCode; + } +} diff --git a/app/code/Magento/Braintree/Test/Unit/Model/AvsEmsCodeMapperTest.php b/app/code/Magento/Braintree/Test/Unit/Model/AvsEmsCodeMapperTest.php new file mode 100644 index 0000000000000..586a60fe91f3b --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Model/AvsEmsCodeMapperTest.php @@ -0,0 +1,101 @@ +mapper = new AvsEmsCodeMapper(); + } + + /** + * Checks different variations for AVS codes mapping. + * + * @covers \Magento\Braintree\Model\AvsEmsCodeMapper::getCode + * @param string $avsZip + * @param string $avsStreet + * @param string $expected + * @dataProvider getCodeDataProvider + */ + public function testGetCode($avsZip, $avsStreet, $expected) + { + /** @var OrderPaymentInterface|MockObject $orderPayment */ + $orderPayment = $this->getMockBuilder(OrderPaymentInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $orderPayment->expects(self::once()) + ->method('getMethod') + ->willReturn(ConfigProvider::CODE); + + $orderPayment->expects(self::once()) + ->method('getAdditionalInformation') + ->willReturn([ + 'avsPostalCodeResponseCode' => $avsZip, + 'avsStreetAddressResponseCode' => $avsStreet + ]); + + self::assertEquals($expected, $this->mapper->getCode($orderPayment)); + } + + /** + * Checks a test case, when payment order is not Braintree payment method. + * + * @covers \Magento\Braintree\Model\AvsEmsCodeMapper::getCode + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "some_payment" does not supported by Braintree AVS mapper. + */ + public function testGetCodeWithException() + { + /** @var OrderPaymentInterface|MockObject $orderPayment */ + $orderPayment = $this->getMockBuilder(OrderPaymentInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $orderPayment->expects(self::exactly(2)) + ->method('getMethod') + ->willReturn('some_payment'); + + $this->mapper->getCode($orderPayment); + } + + /** + * Gets list of AVS codes. + * + * @return array + */ + public function getCodeDataProvider() + { + return [ + ['avsZip' => null, 'avsStreet' => null, 'expected' => 'U'], + ['avsZip' => null, 'avsStreet' => 'M', 'expected' => 'U'], + ['avsZip' => 'M', 'avsStreet' => null, 'expected' => 'U'], + ['avsZip' => 'M', 'avsStreet' => 'Unknown', 'expected' => 'U'], + ['avsZip' => 'I', 'avsStreet' => 'A', 'expected' => 'U'], + ['avsZip' => 'M', 'avsStreet' => 'M', 'expected' => 'Y'], + ['avsZip' => 'N', 'avsStreet' => 'M', 'expected' => 'A'], + ['avsZip' => 'M', 'avsStreet' => 'N', 'expected' => 'Z'], + ['avsZip' => 'N', 'avsStreet' => 'N', 'expected' => 'N'], + ['avsZip' => 'U', 'avsStreet' => 'U', 'expected' => 'U'], + ['avsZip' => 'I', 'avsStreet' => 'I', 'expected' => 'U'], + ['avsZip' => 'A', 'avsStreet' => 'A', 'expected' => 'E'], + ]; + } +} diff --git a/app/code/Magento/Braintree/Test/Unit/Model/CvvEmsCodeMapperTest.php b/app/code/Magento/Braintree/Test/Unit/Model/CvvEmsCodeMapperTest.php new file mode 100644 index 0000000000000..fd54f9e751012 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Model/CvvEmsCodeMapperTest.php @@ -0,0 +1,94 @@ +mapper = new CvvEmsCodeMapper(); + } + + /** + * Checks different variations for cvv codes mapping. + * + * @covers \Magento\Braintree\Model\CvvEmsCodeMapper::getCode + * @param string $cvvCode + * @param string $expected + * @dataProvider getCodeDataProvider + */ + public function testGetCode($cvvCode, $expected) + { + /** @var OrderPaymentInterface|MockObject $orderPayment */ + $orderPayment = $this->getMockBuilder(OrderPaymentInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $orderPayment->expects(self::once()) + ->method('getMethod') + ->willReturn(ConfigProvider::CODE); + + $orderPayment->expects(self::once()) + ->method('getAdditionalInformation') + ->willReturn(['cvvResponseCode' => $cvvCode]); + + self::assertEquals($expected, $this->mapper->getCode($orderPayment)); + } + + /** + * Checks a test case, when payment order is not Braintree payment method. + * + * @covers \Magento\Braintree\Model\CvvEmsCodeMapper::getCode + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "some_payment" does not supported by Braintree CVV mapper. + */ + public function testGetCodeWithException() + { + /** @var OrderPaymentInterface|MockObject $orderPayment */ + $orderPayment = $this->getMockBuilder(OrderPaymentInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $orderPayment->expects(self::exactly(2)) + ->method('getMethod') + ->willReturn('some_payment'); + + $this->mapper->getCode($orderPayment); + } + + /** + * Gets variations of cvv codes and expected mapping result. + * + * @return array + */ + public function getCodeDataProvider() + { + return [ + ['cvvCode' => '', 'expected' => 'P'], + ['cvvCode' => null, 'expected' => 'P'], + ['cvvCode' => 'Unknown', 'expected' => 'P'], + ['cvvCode' => 'M', 'expected' => 'M'], + ['cvvCode' => 'N', 'expected' => 'N'], + ['cvvCode' => 'U', 'expected' => 'P'], + ['cvvCode' => 'I', 'expected' => 'P'], + ['cvvCode' => 'S', 'expected' => 'S'], + ['cvvCode' => 'A', 'expected' => ''], + ]; + } +} diff --git a/app/code/Magento/Braintree/etc/config.xml b/app/code/Magento/Braintree/etc/config.xml index eaa233da109ce..43328406e2012 100644 --- a/app/code/Magento/Braintree/etc/config.xml +++ b/app/code/Magento/Braintree/etc/config.xml @@ -40,6 +40,8 @@ cvv,number avsPostalCodeResponseCode,avsStreetAddressResponseCode,cvvResponseCode,processorAuthorizationCode,processorResponseCode,processorResponseText,liabilityShifted,liabilityShiftPossible,riskDataId,riskDataDecision cc_type,cc_number,avsPostalCodeResponseCode,avsStreetAddressResponseCode,cvvResponseCode,processorAuthorizationCode,processorResponseCode,processorResponseText,liabilityShifted,liabilityShiftPossible,riskDataId,riskDataDecision + Magento\Braintree\Model\AvsEmsCodeMapper + Magento\Braintree\Model\CvvEmsCodeMapper BraintreePayPalFacade diff --git a/app/code/Magento/Payment/Api/PaymentVerificationInterface.php b/app/code/Magento/Payment/Api/PaymentVerificationInterface.php new file mode 100644 index 0000000000000..f5c05080cfe9c --- /dev/null +++ b/app/code/Magento/Payment/Api/PaymentVerificationInterface.php @@ -0,0 +1,34 @@ + 'Y', + 'NY' => 'A', + 'YN' => 'Z', + 'NN' => 'N' + ]; + + /** + * Gets payment AVS verification code. + * + * @param OrderPaymentInterface $orderPayment + * @return string + * @throws \InvalidArgumentException If specified order payment has different payment method code. + */ + public function getCode(OrderPaymentInterface $orderPayment) + { + if ($orderPayment->getMethod() !== Config::METHOD_PAYFLOWPRO) { + throw new \InvalidArgumentException( + 'The "' . $orderPayment->getMethod() . '" does not supported by Payflow AVS mapper.' + ); + } + + $additionalInfo = $orderPayment->getAdditionalInformation(); + if (empty($additionalInfo[Info::PAYPAL_AVSADDR]) || + empty($additionalInfo[Info::PAYPAL_AVSZIP]) + ) { + return self::$unavailableCode; + } + + $streetCode = $additionalInfo[Info::PAYPAL_AVSADDR]; + $zipCode = $additionalInfo[Info::PAYPAL_AVSZIP]; + $key = $zipCode . $streetCode; + + return isset(self::$avsMap[$key]) ? self::$avsMap[$key] : self::$unavailableCode; + } +} diff --git a/app/code/Magento/Paypal/Model/Payflow/CvvEmsCodeMapper.php b/app/code/Magento/Paypal/Model/Payflow/CvvEmsCodeMapper.php new file mode 100644 index 0000000000000..214881504b4c3 --- /dev/null +++ b/app/code/Magento/Paypal/Model/Payflow/CvvEmsCodeMapper.php @@ -0,0 +1,63 @@ + 'M', + 'N' => 'N' + ]; + + /** + * Gets payment CVV verification code. + * + * @param OrderPaymentInterface $orderPayment + * @return string + * @throws \InvalidArgumentException If specified order payment has different payment method code. + */ + public function getCode(OrderPaymentInterface $orderPayment) + { + if ($orderPayment->getMethod() !== Config::METHOD_PAYFLOWPRO) { + throw new \InvalidArgumentException( + 'The "' . $orderPayment->getMethod() . '" does not supported by Payflow CVV mapper.' + ); + } + + $additionalInfo = $orderPayment->getAdditionalInformation(); + if (empty($additionalInfo[Info::PAYPAL_CVV2MATCH])) { + return self::$notProvidedCode; + } + + $cvv = $additionalInfo[Info::PAYPAL_CVV2MATCH]; + + return isset(self::$cvvMap[$cvv]) ? self::$cvvMap[$cvv] : self::$notProvidedCode; + } +} diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/AvsEmsCodeMapperTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/AvsEmsCodeMapperTest.php new file mode 100644 index 0000000000000..7051903150ba7 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/AvsEmsCodeMapperTest.php @@ -0,0 +1,101 @@ +mapper = new AvsEmsCodeMapper(); + } + + /** + * Checks different variations for AVS codes mapping. + * + * @covers \Magento\Paypal\Model\Payflow\AvsEmsCodeMapper::getCode + * @param string $avsZip + * @param string $avsStreet + * @param string $expected + * @dataProvider getCodeDataProvider + */ + public function testGetCode($avsZip, $avsStreet, $expected) + { + /** @var OrderPaymentInterface|MockObject $orderPayment */ + $orderPayment = $this->getMockBuilder(OrderPaymentInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $orderPayment->expects(self::once()) + ->method('getMethod') + ->willReturn(Config::METHOD_PAYFLOWPRO); + + $orderPayment->expects(self::once()) + ->method('getAdditionalInformation') + ->willReturn([ + Info::PAYPAL_AVSZIP => $avsZip, + Info::PAYPAL_AVSADDR => $avsStreet + ]); + + self::assertEquals($expected, $this->mapper->getCode($orderPayment)); + } + + /** + * Checks a test case, when payment order is not Payflow payment method. + * + * @covers \Magento\Paypal\Model\Payflow\AvsEmsCodeMapper::getCode + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "some_payment" does not supported by Payflow AVS mapper. + */ + public function testGetCodeWithException() + { + /** @var OrderPaymentInterface|MockObject $orderPayment */ + $orderPayment = $this->getMockBuilder(OrderPaymentInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $orderPayment->expects(self::exactly(2)) + ->method('getMethod') + ->willReturn('some_payment'); + + $this->mapper->getCode($orderPayment); + } + + /** + * Gets list of AVS codes. + * + * @return array + */ + public function getCodeDataProvider() + { + return [ + ['avsZip' => null, 'avsStreet' => null, 'expected' => 'U'], + ['avsZip' => null, 'avsStreet' => 'Y', 'expected' => 'U'], + ['avsZip' => 'Y', 'avsStreet' => null, 'expected' => 'U'], + ['avsZip' => 'Y', 'avsStreet' => 'Y', 'expected' => 'Y'], + ['avsZip' => 'N', 'avsStreet' => 'Y', 'expected' => 'A'], + ['avsZip' => 'Y', 'avsStreet' => 'N', 'expected' => 'Z'], + ['avsZip' => 'N', 'avsStreet' => 'N', 'expected' => 'N'], + ['avsZip' => 'X', 'avsStreet' => 'Y', 'expected' => 'U'], + ['avsZip' => 'N', 'avsStreet' => 'X', 'expected' => 'U'], + ['avsZip' => '', 'avsStreet' => 'Y', 'expected' => 'U'], + ['avsZip' => 'N', 'avsStreet' => '', 'expected' => 'U'] + ]; + } +} diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/CvvEmsCodeMapperTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/CvvEmsCodeMapperTest.php new file mode 100644 index 0000000000000..b07c6b4e3f936 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/CvvEmsCodeMapperTest.php @@ -0,0 +1,91 @@ +mapper = new CvvEmsCodeMapper(); + } + + /** + * Checks different variations for cvv codes mapping. + * + * @covers \Magento\Paypal\Model\Payflow\CvvEmsCodeMapper::getCode + * @param string $cvvCode + * @param string $expected + * @dataProvider getCodeDataProvider + */ + public function testGetCode($cvvCode, $expected) + { + /** @var OrderPaymentInterface|MockObject $orderPayment */ + $orderPayment = $this->getMockBuilder(OrderPaymentInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $orderPayment->expects(self::once()) + ->method('getMethod') + ->willReturn(Config::METHOD_PAYFLOWPRO); + + $orderPayment->expects(self::once()) + ->method('getAdditionalInformation') + ->willReturn([Info::PAYPAL_CVV2MATCH => $cvvCode]); + + self::assertEquals($expected, $this->mapper->getCode($orderPayment)); + } + + /** + * Checks a test case, when payment order is not Payflow payment method. + * + * @covers \Magento\Paypal\Model\Payflow\CvvEmsCodeMapper::getCode + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "some_payment" does not supported by Payflow CVV mapper. + */ + public function testGetCodeWithException() + { + /** @var OrderPaymentInterface|MockObject $orderPayment */ + $orderPayment = $this->getMockBuilder(OrderPaymentInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $orderPayment->expects(self::exactly(2)) + ->method('getMethod') + ->willReturn('some_payment'); + + $this->mapper->getCode($orderPayment); + } + + /** + * Gets variations of cvv codes and expected mapping result. + * + * @return array + */ + public function getCodeDataProvider() + { + return [ + ['cvvCode' => '', 'expected' => 'P'], + ['cvvCode' => null, 'expected' => 'P'], + ['cvvCode' => 'Y', 'expected' => 'M'], + ['cvvCode' => 'N', 'expected' => 'N'], + ['cvvCode' => 'X', 'expected' => 'P'] + ]; + } +} diff --git a/app/code/Magento/Paypal/etc/config.xml b/app/code/Magento/Paypal/etc/config.xml index f72b9324beeb5..442c87ad840ca 100644 --- a/app/code/Magento/Paypal/etc/config.xml +++ b/app/code/Magento/Paypal/etc/config.xml @@ -91,6 +91,8 @@ 2 1 1 + Magento\Paypal\Model\Payflow\AvsEmsCodeMapper + Magento\Paypal\Model\Payflow\CvvEmsCodeMapper PayflowProCreditCardVaultFacade diff --git a/app/code/Magento/Sales/Model/ResourceModel/AbstractGrid.php b/app/code/Magento/Sales/Model/ResourceModel/AbstractGrid.php index 10cbb175c70bc..9da95269ca418 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/AbstractGrid.php +++ b/app/code/Magento/Sales/Model/ResourceModel/AbstractGrid.php @@ -67,6 +67,7 @@ public function getGridTable() { return $this->getTable($this->gridTableName); } + /** * Purge grid row * @@ -89,6 +90,9 @@ public function purge($value, $field = null) * * @param string $default * @return string + * @deprecated this method is not used in abstract model but only in single child so + * this deprecation is a part of cleaning abstract classes. + * @see \Magento\Sales\Model\ResourceModel\Provider\UpdatedIdListProvider */ protected function getLastUpdatedAtValue($default = '0000-00-00 00:00:00') { diff --git a/app/code/Magento/Sales/Model/ResourceModel/Grid.php b/app/code/Magento/Sales/Model/ResourceModel/Grid.php index ba3419d0cd96e..f537074ad66f1 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/Grid.php +++ b/app/code/Magento/Sales/Model/ResourceModel/Grid.php @@ -5,9 +5,10 @@ */ namespace Magento\Sales\Model\ResourceModel; +use Magento\Framework\App\ObjectManager; use Magento\Framework\DB\Adapter\AdapterInterface; -use Magento\Sales\Model\ResourceModel\AbstractGrid; use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProviderInterface; /** * Class Grid @@ -39,6 +40,11 @@ class Grid extends AbstractGrid */ protected $columns; + /** + * @var NotSyncedDataProviderInterface + */ + private $notSyncedDataProvider; + /** * @param Context $context * @param string $mainTableName @@ -47,6 +53,7 @@ class Grid extends AbstractGrid * @param array $joins * @param array $columns * @param string $connectionName + * @param NotSyncedDataProviderInterface $notSyncedDataProvider */ public function __construct( Context $context, @@ -55,13 +62,16 @@ public function __construct( $orderIdField, array $joins = [], array $columns = [], - $connectionName = null + $connectionName = null, + NotSyncedDataProviderInterface $notSyncedDataProvider = null ) { $this->mainTableName = $mainTableName; $this->gridTableName = $gridTableName; $this->orderIdField = $orderIdField; $this->joins = $joins; $this->columns = $columns; + $this->notSyncedDataProvider = + $notSyncedDataProvider ?: ObjectManager::getInstance()->get(NotSyncedDataProviderInterface::class); parent::__construct($context, $connectionName); } @@ -99,7 +109,10 @@ public function refresh($value, $field = null) public function refreshBySchedule() { $select = $this->getGridOriginSelect() - ->where($this->mainTableName . '.updated_at >= ?', $this->getLastUpdatedAtValue()); + ->where( + $this->mainTableName . '.entity_id IN (?)', + $this->notSyncedDataProvider->getIds($this->mainTableName, $this->gridTableName) + ); return $this->getConnection()->query( $this->getConnection() diff --git a/app/code/Magento/Sales/Model/ResourceModel/Provider/NotSyncedDataProvider.php b/app/code/Magento/Sales/Model/ResourceModel/Provider/NotSyncedDataProvider.php new file mode 100644 index 0000000000000..8215eaaf0ad2e --- /dev/null +++ b/app/code/Magento/Sales/Model/ResourceModel/Provider/NotSyncedDataProvider.php @@ -0,0 +1,48 @@ +providers = $tmapFactory->create( + [ + 'array' => $providers, + 'type' => NotSyncedDataProviderInterface::class + ] + ); + } + + /** + * @inheritDoc + */ + public function getIds($mainTableName, $gridTableName) + { + $result = []; + foreach ($this->providers as $provider) { + $result = array_merge($result, $provider->getIds($mainTableName, $gridTableName)); + } + + return array_unique($result); + } +} diff --git a/app/code/Magento/Sales/Model/ResourceModel/Provider/NotSyncedDataProviderInterface.php b/app/code/Magento/Sales/Model/ResourceModel/Provider/NotSyncedDataProviderInterface.php new file mode 100644 index 0000000000000..489e771d968b5 --- /dev/null +++ b/app/code/Magento/Sales/Model/ResourceModel/Provider/NotSyncedDataProviderInterface.php @@ -0,0 +1,21 @@ +resourceConnection = $resourceConnection; + } + + /** + * @inheritdoc + */ + public function getIds($mainTableName, $gridTableName) + { + $select = $this->getConnection()->select() + ->from($this->getConnection()->getTableName($mainTableName), [$mainTableName.'.entity_id']) + ->joinLeft( + [$gridTableName => $this->getConnection()->getTableName($gridTableName)], + sprintf( + '%s.%s = %s.%s', + $mainTableName, + 'entity_id', + $gridTableName, + 'entity_id' + ), + [] + ) + ->where($gridTableName.'.entity_id IS NULL'); + + return $this->getConnection()->fetchAll($select, [], \Zend_Db::FETCH_COLUMN); + } + + /** + * Returns connection. + * + * @return AdapterInterface + */ + private function getConnection() + { + if (!$this->connection) { + $this->connection = $this->resourceConnection->getConnection('sales'); + } + + return $this->connection; + } +} diff --git a/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Provider/NotSyncedDataProviderTest.php b/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Provider/NotSyncedDataProviderTest.php new file mode 100644 index 0000000000000..92a40e2638fe8 --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Provider/NotSyncedDataProviderTest.php @@ -0,0 +1,99 @@ +getMockBuilder(TMapFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $tMap = $this->getMockBuilder(TMap::class) + ->disableOriginalConstructor() + ->getMock(); + + $tMapFactory->expects(static::once()) + ->method('create') + ->with( + [ + 'array' => [], + 'type' => NotSyncedDataProviderInterface::class + ] + ) + ->willReturn($tMap); + $tMap->expects(static::once()) + ->method('getIterator') + ->willReturn(new \ArrayIterator([])); + + $provider = new NotSyncedDataProvider($tMapFactory, []); + static::assertEquals([], $provider->getIds('main_table', 'grid_table')); + } + + /** + * @covers \Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProvider::getIds + */ + public function testGetIds() + { + /** @var TMapFactory|MockObject $tMapFactory */ + $tMapFactory = $this->getMockBuilder(TMapFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $tMap = $this->getMockBuilder(TMap::class) + ->disableOriginalConstructor() + ->getMock(); + + $provider1 = $this->getMockBuilder(NotSyncedDataProviderInterface::class) + ->getMockForAbstractClass(); + $provider1->expects(static::once()) + ->method('getIds') + ->willReturn([1, 2]); + + $provider2 = $this->getMockBuilder(NotSyncedDataProviderInterface::class) + ->getMockForAbstractClass(); + $provider2->expects(static::once()) + ->method('getIds') + ->willReturn([2, 3, 4]); + + $tMapFactory->expects(static::once()) + ->method('create') + ->with( + [ + 'array' => [ + 'provider1' => NotSyncedDataProviderInterface::class, + 'provider2' => NotSyncedDataProviderInterface::class + ], + 'type' => NotSyncedDataProviderInterface::class + ] + ) + ->willReturn($tMap); + $tMap->expects(static::once()) + ->method('getIterator') + ->willReturn(new \ArrayIterator([$provider1, $provider2])); + + $provider = new NotSyncedDataProvider( + $tMapFactory, + [ + 'provider1' => NotSyncedDataProviderInterface::class, + 'provider2' => NotSyncedDataProviderInterface::class, + ] + ); + + static::assertEquals([1, 2, 3, 4], array_values($provider->getIds('main_table', 'grid_table'))); + } +} diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index 91e5f07e7f61a..0f14946c89a42 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -114,6 +114,14 @@ + + + + + Magento\Sales\Model\ResourceModel\Provider\UpdatedIdListProvider + + + diff --git a/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/_module.less index 7449dce5e2ebb..994a26c12095c 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Signifyd/web/css/source/_module.less @@ -11,3 +11,13 @@ &:extend(.abs-order-tables all); &:extend(.abs-order-tbody-border all); } + +// +// Layout +// --------------------------------------------- +.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { + .case-information { + float: left; + #mix-grid .width(6,12); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertAuthorizationInCommentsHistory.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertAuthorizationInCommentsHistory.php index 78e5a3b31c4cd..898fba0c32f6a 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertAuthorizationInCommentsHistory.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertAuthorizationInCommentsHistory.php @@ -40,11 +40,12 @@ public function processAssert( /** @var \Magento\Sales\Test\Block\Adminhtml\Order\View\Tab\Info $infoTab */ $infoTab = $salesOrderView->getOrderForm()->openTab('info')->getTab('info'); - $latestComment = $infoTab->getCommentsHistoryBlock()->getLatestComment(); + $orderComments = $infoTab->getCommentsHistoryBlock()->getComments(); + $commentsMessages = array_column($orderComments, 'comment'); \PHPUnit_Framework_Assert::assertRegExp( sprintf(self::AUTHORIZED_AMOUNT_PATTERN, $prices['grandTotal']), - $latestComment['comment'], + implode('. ', $commentsMessages), 'Incorrect authorized amount value for the order #' . $orderId ); } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertCancelInCommentsHistory.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertCancelInCommentsHistory.php new file mode 100644 index 0000000000000..56a9b02265da4 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertCancelInCommentsHistory.php @@ -0,0 +1,60 @@ +open(); + $salesOrder->getSalesOrderGrid()->searchAndOpen(['id' => $orderId]); + + /** @var \Magento\Sales\Test\Block\Adminhtml\Order\View\Tab\Info $infoTab */ + $infoTab = $salesOrderView->getOrderForm()->openTab('info')->getTab('info'); + $comments = $infoTab->getCommentsHistoryBlock()->getComments(); + $commentsMessages = array_column($comments, 'comment'); + + \PHPUnit_Framework_Assert::assertRegExp( + sprintf($this->canceledAmountPattern, $prices['grandTotal']), + implode('. ', $commentsMessages), + 'Incorrect canceled amount value for the order #' . $orderId + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return "Message about canceled amount is available in Comments History section."; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CancelOrderStep.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CancelOrderStep.php new file mode 100644 index 0000000000000..deee46aa79a3e --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CancelOrderStep.php @@ -0,0 +1,65 @@ +orderIndex = $orderIndex; + $this->order = $order; + $this->salesOrderView = $salesOrderView; + } + + /** + * Run step flow + * + * @return void + */ + public function run() + { + $this->orderIndex->open(); + $this->orderIndex->getSalesOrderGrid()->searchAndOpen(['id' => $this->order->getId()]); + $this->salesOrderView->getPageActions()->cancel(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/etc/di.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/etc/di.xml index b6c087e8edba9..3b497fa10d654 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/etc/di.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/etc/di.xml @@ -116,4 +116,9 @@ S1 + + + S1 + +