From 30fbdcba0d6b9756402bb13bce303f001b3fc17b Mon Sep 17 00:00:00 2001 From: aplapana Date: Wed, 7 Feb 2024 15:25:50 +0200 Subject: [PATCH 01/27] ACP2E-2770: Double-byte characters (special characters) in Product Name field blocks product creation in backend --- app/code/Magento/Catalog/Helper/Product.php | 8 +++++--- app/code/Magento/Catalog/Model/Product/Url.php | 9 ++++++++- app/code/Magento/Catalog/etc/adminhtml/system.xml | 4 ++++ app/code/Magento/Catalog/etc/config.xml | 1 + app/code/Magento/Catalog/i18n/en_US.csv | 1 + 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Helper/Product.php b/app/code/Magento/Catalog/Helper/Product.php index 73b5e4af78d4..2adaee10fe47 100644 --- a/app/code/Magento/Catalog/Helper/Product.php +++ b/app/code/Magento/Catalog/Helper/Product.php @@ -18,11 +18,13 @@ */ class Product extends \Magento\Framework\Url\Helper\Data { - const XML_PATH_PRODUCT_URL_USE_CATEGORY = 'catalog/seo/product_use_categories'; + public const XML_PATH_PRODUCT_URL_USE_CATEGORY = 'catalog/seo/product_use_categories'; - const XML_PATH_USE_PRODUCT_CANONICAL_TAG = 'catalog/seo/product_canonical_tag'; + public const XML_PATH_USE_PRODUCT_CANONICAL_TAG = 'catalog/seo/product_canonical_tag'; - const XML_PATH_AUTO_GENERATE_MASK = 'catalog/fields_masks'; + public const XML_PATH_AUTO_GENERATE_MASK = 'catalog/fields_masks'; + + public const XML_PATH_APPLY_TRANSLITERATION_TO_URL = 'catalog/seo/product_url_transliteration'; /** * Flag that shows if Magento has to check product to be saleable (enabled and/or inStock) diff --git a/app/code/Magento/Catalog/Model/Product/Url.php b/app/code/Magento/Catalog/Model/Product/Url.php index 1ca946c2c47a..1a9f4ea8a750 100644 --- a/app/code/Magento/Catalog/Model/Product/Url.php +++ b/app/code/Magento/Catalog/Model/Product/Url.php @@ -114,7 +114,14 @@ public function getProductUrl($product, $useSid = null) */ public function formatUrlKey($str) { - return $this->filter->translitUrl($str); + if ($this->scopeConfig->getValue( + \Magento\Catalog\Helper\Product::XML_PATH_APPLY_TRANSLITERATION_TO_URL, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + )) { + return $this->filter->translitUrl($str); + } + + return $str; } /** diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index 5df7412ccdf6..18e0e256ae81 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -127,6 +127,10 @@ Magento\Config\Model\Config\Source\Yesno + + + Magento\Config\Model\Config\Source\Yesno + diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml index f8a1b600bb0b..04f5e8fb3e88 100644 --- a/app/code/Magento/Catalog/etc/config.xml +++ b/app/code/Magento/Catalog/etc/config.xml @@ -48,6 +48,7 @@ - 0 0 + 1 m,d,y diff --git a/app/code/Magento/Catalog/i18n/en_US.csv b/app/code/Magento/Catalog/i18n/en_US.csv index b9fdf6b4324c..914c250dd770 100644 --- a/app/code/Magento/Catalog/i18n/en_US.csv +++ b/app/code/Magento/Catalog/i18n/en_US.csv @@ -665,6 +665,7 @@ Comma-separated.,Comma-separated. "Page Title Separator","Page Title Separator" "Use Canonical Link Meta Tag For Categories","Use Canonical Link Meta Tag For Categories" "Use Canonical Link Meta Tag For Products","Use Canonical Link Meta Tag For Products" +"Apply transliteration for product URL","Apply transliteration for product URL" "Catalog Price Scope","Catalog Price Scope" "This defines the base currency scope (""Currency Setup"" > ""Currency Options"" > ""Base Currency"").","This defines the base currency scope (""Currency Setup"" > ""Currency Options"" > ""Base Currency"")." "Category Top Navigation","Category Top Navigation" From ed998c3ec083415d3813b36e88c98c2172444f44 Mon Sep 17 00:00:00 2001 From: aplapana Date: Wed, 7 Feb 2024 18:21:54 +0200 Subject: [PATCH 02/27] ACP2E-2770: Double-byte characters (special characters) in Product Name field blocks product creation in backend --- app/code/Magento/Catalog/Helper/Product.php | 4 ---- .../Test/Unit/Model/Product/UrlTest.php | 20 ++++++++++++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Helper/Product.php b/app/code/Magento/Catalog/Helper/Product.php index 2adaee10fe47..11806685b664 100644 --- a/app/code/Magento/Catalog/Helper/Product.php +++ b/app/code/Magento/Catalog/Helper/Product.php @@ -49,8 +49,6 @@ class Product extends \Magento\Framework\Url\Helper\Data protected $_assetRepo; /** - * Core registry - * * @var \Magento\Framework\Registry */ protected $_coreRegistry; @@ -61,8 +59,6 @@ class Product extends \Magento\Framework\Url\Helper\Data protected $_attributeConfig; /** - * Catalog session - * * @var \Magento\Catalog\Model\Session */ protected $_catalogSession; diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php index 227ee5739ba6..b576c3089422 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php @@ -11,6 +11,7 @@ use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Url; use Magento\Catalog\Model\Product\Url as ProductUrl; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Filter\FilterManager; use Magento\Framework\Session\SidResolverInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; @@ -53,6 +54,11 @@ class UrlTest extends TestCase */ protected $sidResolver; + /** + * @var ScopeConfigInterface + */ + private ScopeConfigInterface $scopeConfig; + protected function setUp(): void { $this->filter = $this->getMockBuilder( @@ -87,6 +93,8 @@ protected function setUp(): void $urlFactory->method('create') ->willReturn($this->url); + $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); + $objectManager = new ObjectManager($this); $this->model = $objectManager->getObject( ProductUrl::class, @@ -96,11 +104,15 @@ protected function setUp(): void 'storeManager' => $storeManager, 'urlFactory' => $urlFactory, 'sidResolver' => $this->sidResolver, + 'scopeConfig' => $this->scopeConfig ] ); } - public function testFormatUrlKey() + /** + * @return void + */ + public function testFormatUrlKey(): void { $strIn = 'Some string'; $resultString = 'some'; @@ -115,6 +127,12 @@ public function testFormatUrlKey() $resultString ); + $this->scopeConfig->expects($this->once()) + ->method('getValue') + ->with( + \Magento\Catalog\Helper\Product::XML_PATH_APPLY_TRANSLITERATION_TO_URL, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + )->willReturn(true); $this->assertEquals($resultString, $this->model->formatUrlKey($strIn)); } From 2cc8a847962123e0f43b0347a2dbc9f8e4ac959a Mon Sep 17 00:00:00 2001 From: aplapana Date: Wed, 7 Feb 2024 19:43:17 +0200 Subject: [PATCH 03/27] ACP2E-2770: Double-byte characters (special characters) in Product Name field blocks product creation in backend --- app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php index b576c3089422..3a3138fe502f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php @@ -22,6 +22,9 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class UrlTest extends TestCase { /** From 9d73f27a30138040889d93d1d0da0858a06aae5c Mon Sep 17 00:00:00 2001 From: aplapana Date: Fri, 9 Feb 2024 12:17:53 +0200 Subject: [PATCH 04/27] ACP2E-2770: Double-byte characters (special characters) in Product Name field blocks product creation in backend --- app/code/Magento/Catalog/Model/Product/Url.php | 4 ++++ .../Catalog/Test/Unit/Model/Product/UrlTest.php | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/app/code/Magento/Catalog/Model/Product/Url.php b/app/code/Magento/Catalog/Model/Product/Url.php index 1a9f4ea8a750..2f10ba1a0cfd 100644 --- a/app/code/Magento/Catalog/Model/Product/Url.php +++ b/app/code/Magento/Catalog/Model/Product/Url.php @@ -119,6 +119,10 @@ public function formatUrlKey($str) \Magento\Store\Model\ScopeInterface::SCOPE_STORE )) { return $this->filter->translitUrl($str); + } else { + $str = preg_replace('/\s+/', '-', $str); + $str = mb_strtolower($str); + $str = trim($str, '-'); } return $str; diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php index 3a3138fe502f..14b613606329 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/UrlTest.php @@ -139,6 +139,23 @@ public function testFormatUrlKey(): void $this->assertEquals($resultString, $this->model->formatUrlKey($strIn)); } + /** + * @return void + */ + public function testFormatUrlKeyWithoutTransliteration(): void + { + $strIn = 'Some string '; + $resultString = 'some-string'; + + $this->scopeConfig->expects($this->once()) + ->method('getValue') + ->with( + \Magento\Catalog\Helper\Product::XML_PATH_APPLY_TRANSLITERATION_TO_URL, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + )->willReturn(false); + $this->assertEquals($resultString, $this->model->formatUrlKey($strIn)); + } + /** * @dataProvider getUrlDataProvider * @covers \Magento\Catalog\Model\Product\Url::getUrl From f23f4a8e5de9ee72856d74192e1141ebdc97037f Mon Sep 17 00:00:00 2001 From: aplapana Date: Fri, 9 Feb 2024 15:25:19 +0200 Subject: [PATCH 05/27] ACP2E-2770: Double-byte characters (special characters) in Product Name field blocks product creation in backend --- app/code/Magento/Catalog/etc/adminhtml/system.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index 18e0e256ae81..e82b08b5229b 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -116,21 +116,21 @@ - + + + Magento\Config\Model\Config\Source\Yesno + + - + Magento\Config\Model\Config\Source\Yesno - + Magento\Config\Model\Config\Source\Yesno - - - Magento\Config\Model\Config\Source\Yesno - From 2505e1c77668c2783e5821e558726cb0410c7282 Mon Sep 17 00:00:00 2001 From: flowers Date: Mon, 12 Feb 2024 12:24:47 -0600 Subject: [PATCH 06/27] ACP2E-2734: Emails are failing to send --- app/code/Magento/Sales/Model/EmailSenderHandler.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Sales/Model/EmailSenderHandler.php b/app/code/Magento/Sales/Model/EmailSenderHandler.php index 3a7a5727d834..68eae2052c40 100644 --- a/app/code/Magento/Sales/Model/EmailSenderHandler.php +++ b/app/code/Magento/Sales/Model/EmailSenderHandler.php @@ -136,6 +136,11 @@ public function sendEmails() $item->setEmailSent(true), 'email_sent' ); + } else { + $this->entityResource->saveAttribute( + $item->setEmailSent(false), + 'email_sent' + ); } } } From b8429cb2e2e1e087129ea0c1671594f0625eec4c Mon Sep 17 00:00:00 2001 From: flowers Date: Mon, 12 Feb 2024 14:26:41 -0600 Subject: [PATCH 07/27] ACP2E-2734: Emails are failing to send --- app/code/Magento/Sales/Model/EmailSenderHandler.php | 4 ++++ .../Sales/Test/Unit/Model/EmailSenderHandlerTest.php | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/app/code/Magento/Sales/Model/EmailSenderHandler.php b/app/code/Magento/Sales/Model/EmailSenderHandler.php index 68eae2052c40..ddcaa989a0aa 100644 --- a/app/code/Magento/Sales/Model/EmailSenderHandler.php +++ b/app/code/Magento/Sales/Model/EmailSenderHandler.php @@ -105,6 +105,7 @@ public function __construct( /** * Handles asynchronous email sending + * * @return void */ public function sendEmails() @@ -137,6 +138,9 @@ public function sendEmails() 'email_sent' ); } else { + // When the email is failed to send, the email_sent attribute + // should be set to false, preventing this email from being + // sent over and over again in the next cron runs. $this->entityResource->saveAttribute( $item->setEmailSent(false), 'email_sent' diff --git a/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php b/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php index af044c866978..9dbc8002e5da 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php @@ -256,6 +256,12 @@ public function testExecute( ->expects($this->once()) ->method('saveAttribute') ->with($collectionItem); + } else { + $collectionItem + ->expects($this->once()) + ->method('setEmailSent') + ->with(false) + ->willReturn($collectionItem); } } } From f005d0e7bd69c43c34276c23295994b20506e55a Mon Sep 17 00:00:00 2001 From: flowers Date: Tue, 13 Feb 2024 10:20:42 -0600 Subject: [PATCH 08/27] ACP2E-2734: Emails are failing to send --- .../Sales/Model/EmailSenderHandler.php | 19 ++++--------- .../Unit/Model/EmailSenderHandlerTest.php | 28 +++++++------------ 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/app/code/Magento/Sales/Model/EmailSenderHandler.php b/app/code/Magento/Sales/Model/EmailSenderHandler.php index ddcaa989a0aa..82d3e6482404 100644 --- a/app/code/Magento/Sales/Model/EmailSenderHandler.php +++ b/app/code/Magento/Sales/Model/EmailSenderHandler.php @@ -132,20 +132,11 @@ public function sendEmails() /** @var \Magento\Sales\Model\AbstractModel $item */ foreach ($entityCollection->getItems() as $item) { - if ($this->emailSender->send($item, true)) { - $this->entityResource->saveAttribute( - $item->setEmailSent(true), - 'email_sent' - ); - } else { - // When the email is failed to send, the email_sent attribute - // should be set to false, preventing this email from being - // sent over and over again in the next cron runs. - $this->entityResource->saveAttribute( - $item->setEmailSent(false), - 'email_sent' - ); - } + $isEmailSent = $this->emailSender->send($item, true); + $this->entityResource->saveAttribute( + $item->setEmailSent($isEmailSent), + 'email_sent' + ); } } } diff --git a/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php b/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php index 9dbc8002e5da..4cbdf8ebfc35 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php @@ -245,24 +245,16 @@ public function testExecute( ->method('isEnabled') ->willReturn(true); - if ($emailSendingResult) { - $collectionItem - ->expects($this->once()) - ->method('setEmailSent') - ->with(true) - ->willReturn($collectionItem); - - $this->entityResource - ->expects($this->once()) - ->method('saveAttribute') - ->with($collectionItem); - } else { - $collectionItem - ->expects($this->once()) - ->method('setEmailSent') - ->with(false) - ->willReturn($collectionItem); - } + $collectionItem + ->expects($this->once()) + ->method('setEmailSent') + ->with($emailSendingResult) + ->willReturn($collectionItem); + + $this->entityResource + ->expects($this->once()) + ->method('saveAttribute') + ->with($collectionItem); } } From b171dc29fe363b8f1cc7433cb44764e6f878273e Mon Sep 17 00:00:00 2001 From: Olga Moyseyenko Date: Tue, 13 Feb 2024 21:15:31 -0600 Subject: [PATCH 09/27] ACP2E-2763: Table Rates Still Showing Even Though Free Shipping Is Applied --- app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php index 01328e0df63a..6e78d9d03209 100644 --- a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php +++ b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php @@ -151,9 +151,6 @@ public function collectRates(RateRequest $request) $freeWeight += (int) $item->getWeight(); } } - - $request->setPackageValue($request->getPackageValue() - $freePackageValue); - $request->setPackageValueWithDiscount($request->getPackageValueWithDiscount() - $freePackageValue); } if ($freeWeight > 0) { From 8a8b44b032abf5d690459b806197ac81d3bf4709 Mon Sep 17 00:00:00 2001 From: flowers Date: Thu, 15 Feb 2024 10:51:12 -0600 Subject: [PATCH 10/27] ACP2E-2734: Emails are failing to send --- .../Sales/Model/EmailSenderHandler.php | 36 +++++++++++++++---- app/code/Magento/Sales/etc/db_schema.xml | 8 ++--- app/code/Magento/Sales/etc/di.xml | 1 + 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Sales/Model/EmailSenderHandler.php b/app/code/Magento/Sales/Model/EmailSenderHandler.php index 82d3e6482404..33d7a024a143 100644 --- a/app/code/Magento/Sales/Model/EmailSenderHandler.php +++ b/app/code/Magento/Sales/Model/EmailSenderHandler.php @@ -5,11 +5,15 @@ */ namespace Magento\Sales\Model; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Config\ValueFactory; use Magento\Framework\App\Config\ValueInterface; use Magento\Framework\App\ObjectManager; use Magento\Sales\Model\Order\Email\Container\IdentityInterface; +use Magento\Sales\Model\Order\Email\Sender; use Magento\Sales\Model\ResourceModel\Collection\AbstractCollection; +use Magento\Sales\Model\ResourceModel\EntityAbstract; +use Magento\Store\Model\StoreManagerInterface; /** * Sales emails sending @@ -68,16 +72,18 @@ class EmailSenderHandler * @var string */ private $modifyStartFromDate; + private int $maxSendAttempts; /** - * @param \Magento\Sales\Model\Order\Email\Sender $emailSender - * @param \Magento\Sales\Model\ResourceModel\EntityAbstract $entityResource + * @param Sender $emailSender + * @param EntityAbstract $entityResource * @param AbstractCollection $entityCollection - * @param \Magento\Framework\App\Config\ScopeConfigInterface $globalConfig + * @param ScopeConfigInterface $globalConfig * @param IdentityInterface|null $identityContainer - * @param \Magento\Store\Model\StoreManagerInterface|null $storeManager + * @param StoreManagerInterface|null $storeManager * @param ValueFactory|null $configValueFactory * @param string|null $modifyStartFromDate + * @param int $maxSendAttempts */ public function __construct( \Magento\Sales\Model\Order\Email\Sender $emailSender, @@ -87,7 +93,8 @@ public function __construct( IdentityInterface $identityContainer = null, \Magento\Store\Model\StoreManagerInterface $storeManager = null, ?ValueFactory $configValueFactory = null, - ?string $modifyStartFromDate = null + ?string $modifyStartFromDate = null, + int $maxSendAttempts = null ) { $this->emailSender = $emailSender; $this->entityResource = $entityResource; @@ -101,6 +108,7 @@ public function __construct( $this->configValueFactory = $configValueFactory ?: ObjectManager::getInstance()->get(ValueFactory::class); $this->modifyStartFromDate = $modifyStartFromDate ?: $this->modifyStartFromDate; + $this->maxSendAttempts = $maxSendAttempts ?? 3; } /** @@ -112,7 +120,13 @@ public function sendEmails() { if ($this->globalConfig->getValue('sales_email/general/async_sending')) { $this->entityCollection->addFieldToFilter('send_email', ['eq' => 1]); - $this->entityCollection->addFieldToFilter('email_sent', ['null' => true]); + $this->entityCollection->addFieldToFilter( + 'email_sent', + [ + ['null' => true], + ['lteq' => -1] + ] + ); $this->filterCollectionByStartFromDate($this->entityCollection); $this->entityCollection->setPageSize( $this->globalConfig->getValue('sales_email/general/sending_limit') @@ -132,9 +146,17 @@ public function sendEmails() /** @var \Magento\Sales\Model\AbstractModel $item */ foreach ($entityCollection->getItems() as $item) { + $sendAttempts = $item->getEmailSent() ?? -$this->maxSendAttempts; $isEmailSent = $this->emailSender->send($item, true); + + if ($isEmailSent) { + $sendAttempts = 1; + } else { + $sendAttempts++; + } + $this->entityResource->saveAttribute( - $item->setEmailSent($isEmailSent), + $item->setEmailSent($sendAttempts), 'email_sent' ); } diff --git a/app/code/Magento/Sales/etc/db_schema.xml b/app/code/Magento/Sales/etc/db_schema.xml index bdadd8df0b3c..c01354500eb7 100644 --- a/app/code/Magento/Sales/etc/db_schema.xml +++ b/app/code/Magento/Sales/etc/db_schema.xml @@ -149,7 +149,7 @@ - @@ -708,7 +708,7 @@ comment="Total Weight"/> - @@ -981,7 +981,7 @@ identity="false" comment="Is Used For Refund"/> - @@ -1278,7 +1278,7 @@ comment="Tax Amount"/> - diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index d5dc1938bdab..1289cf4da01f 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -220,6 +220,7 @@ -1 day + 3 From 3b30eba011668a3b16059473754ce25cf5ffe5c8 Mon Sep 17 00:00:00 2001 From: flowers Date: Thu, 15 Feb 2024 12:55:11 -0600 Subject: [PATCH 11/27] ACP2E-2734: Emails are failing to send --- .../Sales/Model/EmailSenderHandler.php | 4 +++ .../Unit/Model/EmailSenderHandlerTest.php | 25 +++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Sales/Model/EmailSenderHandler.php b/app/code/Magento/Sales/Model/EmailSenderHandler.php index 33d7a024a143..2a4a3ab17b2b 100644 --- a/app/code/Magento/Sales/Model/EmailSenderHandler.php +++ b/app/code/Magento/Sales/Model/EmailSenderHandler.php @@ -72,6 +72,10 @@ class EmailSenderHandler * @var string */ private $modifyStartFromDate; + + /** + * @var int + */ private int $maxSendAttempts; /** diff --git a/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php b/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php index 4cbdf8ebfc35..b9cf2560633f 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php @@ -151,6 +151,7 @@ protected function setUp(): void * @param int $configValue * @param array|null $collectionItems * @param bool|null $emailSendingResult + * @param int|null $expectedIsEmailSent * * @return void * @dataProvider executeDataProvider @@ -159,7 +160,8 @@ protected function setUp(): void public function testExecute( int $configValue, ?array $collectionItems, - ?bool $emailSendingResult + ?bool $emailSendingResult, + ?int $expectedIsEmailSent ): void { $path = 'sales_email/general/async_sending'; @@ -175,7 +177,12 @@ public function testExecute( ->method('addFieldToFilter') ->withConsecutive( ['send_email', ['eq' => 1]], - ['email_sent', ['null' => true]], + ['email_sent', + [ + ['null' => true], + ['lteq' => -1] + ] + ], ['created_at', ['from' => $fromDate]] ); @@ -248,7 +255,7 @@ public function testExecute( $collectionItem ->expects($this->once()) ->method('setEmailSent') - ->with($emailSendingResult) + ->with($expectedIsEmailSent) ->willReturn($collectionItem); $this->entityResource @@ -280,22 +287,26 @@ public function executeDataProvider(): array [ 'configValue' => 1, 'collectionItems' => [clone $entityModel], - 'emailSendingResult' => true + 'emailSendingResult' => true, + 'expectedIsEmailSent' => 1 ], [ 'configValue' => 1, 'collectionItems' => [clone $entityModel], - 'emailSendingResult' => false + 'emailSendingResult' => false, + 'expectedIsEmailSent' => -2 ], [ 'configValue' => 1, 'collectionItems' => [], - 'emailSendingResult' => null + 'emailSendingResult' => null, + 'expectedIsEmailSent' => 1 ], [ 'configValue' => 0, 'collectionItems' => null, - 'emailSendingResult' => null + 'emailSendingResult' => null, + 'expectedIsEmailSent' => 1 ] ]; } From 9668008d9956199e220df4c27e348f7c87f619b4 Mon Sep 17 00:00:00 2001 From: flowers Date: Fri, 16 Feb 2024 13:22:25 -0600 Subject: [PATCH 12/27] ACP2E-2734: Emails are failing to send --- .../Sales/Model/EmailSenderHandler.php | 40 ++++++++----------- .../Unit/Model/EmailSenderHandlerTest.php | 13 ++++-- app/code/Magento/Sales/etc/config.xml | 1 + app/code/Magento/Sales/etc/di.xml | 1 - 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/app/code/Magento/Sales/Model/EmailSenderHandler.php b/app/code/Magento/Sales/Model/EmailSenderHandler.php index 2a4a3ab17b2b..7d59ea4566b9 100644 --- a/app/code/Magento/Sales/Model/EmailSenderHandler.php +++ b/app/code/Magento/Sales/Model/EmailSenderHandler.php @@ -26,14 +26,14 @@ class EmailSenderHandler /** * Email sender model. * - * @var \Magento\Sales\Model\Order\Email\Sender + * @var Sender */ protected $emailSender; /** * Entity resource model. * - * @var \Magento\Sales\Model\ResourceModel\EntityAbstract + * @var EntityAbstract */ protected $entityResource; @@ -47,7 +47,7 @@ class EmailSenderHandler /** * Global configuration storage. * - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ protected $globalConfig; @@ -57,7 +57,7 @@ class EmailSenderHandler private $identityContainer; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ private $storeManager; @@ -73,11 +73,6 @@ class EmailSenderHandler */ private $modifyStartFromDate; - /** - * @var int - */ - private int $maxSendAttempts; - /** * @param Sender $emailSender * @param EntityAbstract $entityResource @@ -87,18 +82,16 @@ class EmailSenderHandler * @param StoreManagerInterface|null $storeManager * @param ValueFactory|null $configValueFactory * @param string|null $modifyStartFromDate - * @param int $maxSendAttempts */ public function __construct( - \Magento\Sales\Model\Order\Email\Sender $emailSender, - \Magento\Sales\Model\ResourceModel\EntityAbstract $entityResource, - AbstractCollection $entityCollection, - \Magento\Framework\App\Config\ScopeConfigInterface $globalConfig, - IdentityInterface $identityContainer = null, - \Magento\Store\Model\StoreManagerInterface $storeManager = null, - ?ValueFactory $configValueFactory = null, - ?string $modifyStartFromDate = null, - int $maxSendAttempts = null + Sender $emailSender, + EntityAbstract $entityResource, + AbstractCollection $entityCollection, + ScopeConfigInterface $globalConfig, + IdentityInterface $identityContainer = null, + StoreManagerInterface $storeManager = null, + ?ValueFactory $configValueFactory = null, + ?string $modifyStartFromDate = null, ) { $this->emailSender = $emailSender; $this->entityResource = $entityResource; @@ -108,11 +101,10 @@ public function __construct( $this->identityContainer = $identityContainer ?: ObjectManager::getInstance() ->get(\Magento\Sales\Model\Order\Email\Container\NullIdentity::class); $this->storeManager = $storeManager ?: ObjectManager::getInstance() - ->get(\Magento\Store\Model\StoreManagerInterface::class); + ->get(StoreManagerInterface::class); $this->configValueFactory = $configValueFactory ?: ObjectManager::getInstance()->get(ValueFactory::class); $this->modifyStartFromDate = $modifyStartFromDate ?: $this->modifyStartFromDate; - $this->maxSendAttempts = $maxSendAttempts ?? 3; } /** @@ -139,6 +131,8 @@ public function sendEmails() /** @var \Magento\Store\Api\Data\StoreInterface[] $stores */ $stores = $this->getStores(clone $this->entityCollection); + $maxSendAttempts = $this->globalConfig->getValue('sales_email/general/async_sending_attempts'); + /** @var \Magento\Store\Model\Store $store */ foreach ($stores as $store) { $this->identityContainer->setStore($store); @@ -150,7 +144,7 @@ public function sendEmails() /** @var \Magento\Sales\Model\AbstractModel $item */ foreach ($entityCollection->getItems() as $item) { - $sendAttempts = $item->getEmailSent() ?? -$this->maxSendAttempts; + $sendAttempts = $item->getEmailSent() ?? -$maxSendAttempts; $isEmailSent = $this->emailSender->send($item, true); if ($isEmailSent) { @@ -183,7 +177,7 @@ private function getStores( $entityCollection->addAttributeToSelect('store_id')->getSelect()->group('store_id'); /** @var \Magento\Sales\Model\EntityInterface $item */ foreach ($entityCollection->getItems() as $item) { - /** @var \Magento\Store\Model\StoreManagerInterface $store */ + /** @var StoreManagerInterface $store */ $store = $this->storeManager->getStore($item->getStoreId()); $stores[$item->getStoreId()] = $store; } diff --git a/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php b/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php index b9cf2560633f..599888a0d9a8 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/EmailSenderHandlerTest.php @@ -163,12 +163,17 @@ public function testExecute( ?bool $emailSendingResult, ?int $expectedIsEmailSent ): void { - $path = 'sales_email/general/async_sending'; - $this->globalConfig ->method('getValue') - ->withConsecutive([$path]) - ->willReturnOnConsecutiveCalls($configValue); + ->willReturnCallback(function ($path) use ($configValue) { + if ($path === 'sales_email/general/async_sending') { + return $configValue; + } + if ($path === 'sales_email/general/async_sending_attempts') { + return 3; + } + return null; + }); if ($configValue) { $nowDate = date('Y-m-d H:i:s'); diff --git a/app/code/Magento/Sales/etc/config.xml b/app/code/Magento/Sales/etc/config.xml index 92e1a46bb2d5..16543fd9f7e4 100644 --- a/app/code/Magento/Sales/etc/config.xml +++ b/app/code/Magento/Sales/etc/config.xml @@ -36,6 +36,7 @@ 0 + 3 50 diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index 1289cf4da01f..d5dc1938bdab 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -220,7 +220,6 @@ -1 day - 3 From 49c9cce0f68679b55a6a928721f96ff7892ff3e4 Mon Sep 17 00:00:00 2001 From: flowers Date: Fri, 16 Feb 2024 13:24:44 -0600 Subject: [PATCH 13/27] ACP2E-2734: Emails are failing to send --- .../Magento/Sales/Model/EmailSenderHandler.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Sales/Model/EmailSenderHandler.php b/app/code/Magento/Sales/Model/EmailSenderHandler.php index 7d59ea4566b9..748a12dff340 100644 --- a/app/code/Magento/Sales/Model/EmailSenderHandler.php +++ b/app/code/Magento/Sales/Model/EmailSenderHandler.php @@ -84,14 +84,14 @@ class EmailSenderHandler * @param string|null $modifyStartFromDate */ public function __construct( - Sender $emailSender, - EntityAbstract $entityResource, - AbstractCollection $entityCollection, - ScopeConfigInterface $globalConfig, - IdentityInterface $identityContainer = null, + Sender $emailSender, + EntityAbstract $entityResource, + AbstractCollection $entityCollection, + ScopeConfigInterface $globalConfig, + IdentityInterface $identityContainer = null, StoreManagerInterface $storeManager = null, - ?ValueFactory $configValueFactory = null, - ?string $modifyStartFromDate = null, + ?ValueFactory $configValueFactory = null, + ?string $modifyStartFromDate = null, ) { $this->emailSender = $emailSender; $this->entityResource = $entityResource; From a787a48cbc550d00b3afc385e5ff453739e45bab Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin Date: Mon, 19 Feb 2024 14:06:07 -0600 Subject: [PATCH 14/27] ACP2E-2630: Issues saving advanced pricing on bundle products --- .../Bundle/Model/Option/SaveAction.php | 95 +++++++++++++++---- .../Bundle/Model/Product/SaveHandler.php | 26 ++--- .../Test/Unit/Model/Option/SaveActionTest.php | 3 - .../Magento/Framework/Data/Collection.php | 2 +- 4 files changed, 90 insertions(+), 36 deletions(-) diff --git a/app/code/Magento/Bundle/Model/Option/SaveAction.php b/app/code/Magento/Bundle/Model/Option/SaveAction.php index 2776f11db33f..7904a00f244d 100644 --- a/app/code/Magento/Bundle/Model/Option/SaveAction.php +++ b/app/code/Magento/Bundle/Model/Option/SaveAction.php @@ -84,19 +84,29 @@ public function __construct( * Bulk options save * * @param ProductInterface $bundleProduct - * @param OptionInterface[] $options + * @param array $options + * @param array $existingBundleProductOptions * @return void * @throws CouldNotSaveException - * @throws NoSuchEntityException * @throws InputException + * @throws NoSuchEntityException */ - public function saveBulk(ProductInterface $bundleProduct, array $options): void - { + public function saveBulk( + ProductInterface $bundleProduct, + array $options, + array $existingBundleProductOptions = [] + ): void { $metadata = $this->metadataPool->getMetadata(ProductInterface::class); $optionCollection = $this->type->getOptionsCollection($bundleProduct); foreach ($options as $option) { - $this->saveOptionItem($bundleProduct, $option, $optionCollection, $metadata); + $this->saveOptionItem( + $bundleProduct, + $option, + $optionCollection, + $metadata, + $existingBundleProductOptions + ); } $bundleProduct->setIsRelationsChanged(true); @@ -109,16 +119,18 @@ public function saveBulk(ProductInterface $bundleProduct, array $options): void * @param OptionInterface $option * @param Collection $optionCollection * @param EntityMetadataInterface $metadata + * @param array $existingBundleProductOptions * @return void * @throws CouldNotSaveException - * @throws NoSuchEntityException * @throws InputException + * @throws NoSuchEntityException */ private function saveOptionItem( ProductInterface $bundleProduct, OptionInterface $option, Collection $optionCollection, - EntityMetadataInterface $metadata + EntityMetadataInterface $metadata, + array $existingBundleProductOptions = [] ) : void { $linksToAdd = []; @@ -126,10 +138,8 @@ private function saveOptionItem( $parentId = $bundleProduct->getData($metadata->getLinkField()); $option->setParentId($parentId); $optionId = $option->getOptionId(); + $existingOption = $this->retrieveExistingOption($optionCollection, $option, $existingBundleProductOptions); - /** @var \Magento\Bundle\Model\Option $existingOption */ - $existingOption = $optionCollection->getItemById($option->getOptionId()) - ?? $optionCollection->getNewEmptyItem(); if (!$optionId || $existingOption->getParentId() != $parentId) { $option->setOptionId(null); $option->setDefaultTitle($option->getTitle()); @@ -137,14 +147,14 @@ private function saveOptionItem( $linksToAdd = $option->getProductLinks(); } } else { - if (!$existingOption->getOptionId()) { + if (!$existingOption || !$existingOption->getOptionId()) { throw new NoSuchEntityException( __("The option that was requested doesn't exist. Verify the entity and try again.") ); } $option->setData(array_merge($existingOption->getData(), $option->getData())); - $this->updateOptionSelection($bundleProduct, $option); + $this->updateOptionSelection($bundleProduct, $option, $existingOption); } try { @@ -183,15 +193,21 @@ public function save(ProductInterface $bundleProduct, OptionInterface $option) * * @param ProductInterface $product * @param OptionInterface $option + * @param OptionInterface|null $existingOption * @return void + * @throws CouldNotSaveException + * @throws InputException + * @throws NoSuchEntityException */ - private function updateOptionSelection(ProductInterface $product, OptionInterface $option) - { - $optionId = $option->getOptionId(); - $existingLinks = $this->linkManagement->getChildren($product->getSku(), $optionId); + private function updateOptionSelection( + ProductInterface $product, + OptionInterface $option, + ?OptionInterface $existingOption = null + ):void { $linksToAdd = []; $linksToUpdate = []; $linksToDelete = []; + if (is_array($option->getProductLinks())) { $productLinks = $option->getProductLinks(); foreach ($productLinks as $productLink) { @@ -201,13 +217,16 @@ private function updateOptionSelection(ProductInterface $product, OptionInterfac $linksToUpdate[] = $productLink; } } - /** @var LinkInterface[] $linksToDelete */ - $linksToDelete = $this->compareLinks($existingLinks, $linksToUpdate); - $linksToUpdate = $this->verifyLinksToUpdate($existingLinks, $linksToUpdate); + if (!empty($existingOption) && !empty($existingOption->getProductLinks())) { + $linksToDelete = $this->compareLinks($existingOption->getProductLinks(), $linksToUpdate); + $linksToUpdate = $this->verifyLinksToUpdate($existingOption->getProductLinks(), $linksToUpdate); + } } + foreach ($linksToUpdate as $linkedProduct) { $this->linkManagement->saveChild($product->getSku(), $linkedProduct); } + foreach ($linksToDelete as $linkedProduct) { $this->linkManagement->removeChild( $product->getSku(), @@ -215,6 +234,7 @@ private function updateOptionSelection(ProductInterface $product, OptionInterfac $linkedProduct->getSku() ); } + $this->addChildren->addChildren($product, (int)$option->getOptionId(), $linksToAdd); } @@ -300,4 +320,41 @@ private function compareLinks(array $firstArray, array $secondArray) return $result; } + + /** + * Retrieve option from list. + * + * @param Collection $optionCollection + * @param OptionInterface $option + * @param array $existingBundleProductOptions + * @return OptionInterface + */ + private function retrieveExistingOption( + Collection $optionCollection, + OptionInterface $option, + array $existingBundleProductOptions + ): OptionInterface { + $existingOption = $optionCollection->getItemById($option->getOptionId()); + + $incomingOption = current( + array_filter($existingBundleProductOptions, function ($obj) use ($option) { + return $obj->getData()['option_id'] == $option->getId(); + }) + ); + + if (!empty($incomingOption)) { + $existingOption->setData( + array_merge( + $existingOption->getData(), + $incomingOption->getData() + ) + ); + } + + if (empty($existingOption)) { + $existingOption = $optionCollection->getNewEmptyItem(); + } + + return $existingOption; + } } diff --git a/app/code/Magento/Bundle/Model/Product/SaveHandler.php b/app/code/Magento/Bundle/Model/Product/SaveHandler.php index 412783565486..62359a104ab7 100644 --- a/app/code/Magento/Bundle/Model/Product/SaveHandler.php +++ b/app/code/Magento/Bundle/Model/Product/SaveHandler.php @@ -21,6 +21,7 @@ /** * Bundle product save handler + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SaveHandler implements ExtensionInterface { @@ -107,8 +108,7 @@ public function execute($entity, $arguments = []) if (!$entity->getCopyFromView()) { $this->processRemovedOptions($entity, $existingOptionsIds, $optionIds); - $newOptionsIds = array_diff($optionIds, $existingOptionsIds); - $this->saveOptions($entity, $bundleProductOptions, $newOptionsIds); + $this->saveOptions($entity, $bundleProductOptions, $existingBundleProductOptions); } else { //save only labels and not selections + product links $this->saveOptions($entity, $bundleProductOptions); @@ -150,20 +150,20 @@ protected function removeOptionLinks($entitySku, $option) /** * Perform save for all options entities. * - * @param object $entity + * @param ProductInterface $entity * @param array $options - * @param array $newOptionsIds - * + * @param array $existingBundleProductOptions * @return void + * @throws InputException + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\CouldNotSaveException */ - private function saveOptions($entity, array $options, array $newOptionsIds = []): void - { - foreach ($options as $option) { - if (in_array($option->getOptionId(), $newOptionsIds)) { - $option->setOptionId(null); - } - } - $this->optionSave->saveBulk($entity, $options); + private function saveOptions( + ProductInterface $entity, + array $options, + array $existingBundleProductOptions = [] + ): void { + $this->optionSave->saveBulk($entity, $options, $existingBundleProductOptions); } /** diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Option/SaveActionTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Option/SaveActionTest.php index 1b8fd65455f3..932e70067827 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Option/SaveActionTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Option/SaveActionTest.php @@ -109,9 +109,6 @@ public function testSaveBulk() ->method('getMetadata') ->willReturn($metadata); - $this->linkManagement->expects($this->once()) - ->method('getChildren') - ->willReturn([]); $this->product->expects($this->once()) ->method('setIsRelationsChanged') ->with(true); diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php index cbaa573aba8b..31e4995e6743 100644 --- a/lib/internal/Magento/Framework/Data/Collection.php +++ b/lib/internal/Magento/Framework/Data/Collection.php @@ -825,7 +825,7 @@ protected function _toOptionHash($valueField = 'id', $labelField = 'name') * Retrieve item by id * * @param string|int $idValue - * @return DataObject + * @return DataObject|null */ public function getItemById($idValue) { From 1351e67bec1f63a5db7de6714bef0abdf0ddea08 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin Date: Mon, 19 Feb 2024 16:38:27 -0600 Subject: [PATCH 15/27] ACP2E-2630: Issues saving advanced pricing on bundle products --- lib/internal/Magento/Framework/Data/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php index 31e4995e6743..cbaa573aba8b 100644 --- a/lib/internal/Magento/Framework/Data/Collection.php +++ b/lib/internal/Magento/Framework/Data/Collection.php @@ -825,7 +825,7 @@ protected function _toOptionHash($valueField = 'id', $labelField = 'name') * Retrieve item by id * * @param string|int $idValue - * @return DataObject|null + * @return DataObject */ public function getItemById($idValue) { From 4c7a62b29b8a3269421625801debef4a2c75c4ca Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin Date: Mon, 19 Feb 2024 19:26:29 -0600 Subject: [PATCH 16/27] ACP2E-2630: Issues saving advanced pricing on bundle products --- app/code/Magento/Bundle/Model/Option/SaveAction.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Bundle/Model/Option/SaveAction.php b/app/code/Magento/Bundle/Model/Option/SaveAction.php index 7904a00f244d..2471f932a754 100644 --- a/app/code/Magento/Bundle/Model/Option/SaveAction.php +++ b/app/code/Magento/Bundle/Model/Option/SaveAction.php @@ -351,6 +351,7 @@ private function retrieveExistingOption( ); } + // @phpstan-ignore-next-line if (empty($existingOption)) { $existingOption = $optionCollection->getNewEmptyItem(); } From bc03a5dffb1e01ba21eca57e95efcb0f0534ac5e Mon Sep 17 00:00:00 2001 From: flowers Date: Tue, 20 Feb 2024 08:52:30 -0600 Subject: [PATCH 17/27] ACP2E-2734: Emails are failing to send --- app/code/Magento/Sales/Model/EmailSenderHandler.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/EmailSenderHandler.php b/app/code/Magento/Sales/Model/EmailSenderHandler.php index 748a12dff340..c59e3ddf0dc6 100644 --- a/app/code/Magento/Sales/Model/EmailSenderHandler.php +++ b/app/code/Magento/Sales/Model/EmailSenderHandler.php @@ -23,6 +23,11 @@ */ class EmailSenderHandler { + /** + * Configuration path for defining asynchronous email sending attempts + */ + public const XML_PATH_ASYNC_SENDING_ATTEMPTS = 'sales_email/general/async_sending_attempts'; + /** * Email sender model. * @@ -131,7 +136,7 @@ public function sendEmails() /** @var \Magento\Store\Api\Data\StoreInterface[] $stores */ $stores = $this->getStores(clone $this->entityCollection); - $maxSendAttempts = $this->globalConfig->getValue('sales_email/general/async_sending_attempts'); + $maxSendAttempts = $this->globalConfig->getValue(self::XML_PATH_ASYNC_SENDING_ATTEMPTS); /** @var \Magento\Store\Model\Store $store */ foreach ($stores as $store) { From 7855197f098d28bbb569decbc75802d9b4c644d5 Mon Sep 17 00:00:00 2001 From: Olga Moyseyenko Date: Wed, 21 Feb 2024 15:41:43 -0600 Subject: [PATCH 18/27] ACP2E-2763: Table Rates Still Showing Even Though Free Shipping Is Applied --- .../Test/Mftf/Data/SalesRuleData.xml | 19 +++ ...hFreeShippingAfterApplyingCartRuleTest.xml | 132 ++++++++++++++++++ .../tests/_data/price_tablerates.csv | 5 + 3 files changed, 156 insertions(+) create mode 100644 app/code/Magento/Shipping/Test/Mftf/Test/StorefrontAssertShippingPricesPresentWithFreeShippingAfterApplyingCartRuleTest.xml create mode 100644 dev/tests/acceptance/tests/_data/price_tablerates.csv diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml index c378c58008a2..a7f2da506e8b 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml @@ -379,6 +379,25 @@ Free Shipping in conditions Free Shipping in conditions + + Cart Price Rule For Tree Shipping With Coupon + Description for Cart Price Rule + Yes + Main Website + NOT LOGGED IN + Specific Coupon + FreeShipping + Fixed amount discount + 50 + 0 + 0 + 0 + For shipment with matching items + false + Percent of product price discount + Free Shipping Rule + Free Shipping Rule + Cart Price Rule For Rule Condition Description for Cart Price Rule diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontAssertShippingPricesPresentWithFreeShippingAfterApplyingCartRuleTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontAssertShippingPricesPresentWithFreeShippingAfterApplyingCartRuleTest.xml new file mode 100644 index 000000000000..d9443f79474a --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontAssertShippingPricesPresentWithFreeShippingAfterApplyingCartRuleTest.xml @@ -0,0 +1,132 @@ + + + + + + + + + + <description value="Table rate price should be displayed correctly on checkout after applied free shipment cart price rule"/> + <severity value="AVERAGE"/> + <testCaseId value="ACP2E-2763"/> + <useCaseId value="ACP2E-2763"/> + <group value="shipping"/> + <group value="SalesRule"/> + <group value="cloud"/> + </annotations> + <before> + <createData entity="ApiSimpleProductWithCustomPrice" stepKey="createProduct"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!-- Enable free shipping method --> + <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShippingMethod"/> + <createData entity="setFreeShippingSubtotal" stepKey="setFreeShippingSubtotal"/> + + <!-- Turn on and configure table rates shipping method --> + <actionGroup ref="AdminOpenShippingMethodsConfigPageActionGroup" stepKey="openShippingMethodConfigPage"/> + <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="switchDefaultWebsite"> + <argument name="website" value="_defaultWebsite"/> + </actionGroup> + <actionGroup ref="AdminChangeTableRatesShippingMethodStatusActionGroup" stepKey="enableTableRatesShippingMethodForDefaultWebsite"> + <argument name="status" value="1"/> + </actionGroup> + <actionGroup ref="AdminImportFileTableRatesShippingMethodActionGroup" stepKey="importCSVFile"> + <argument name="file" value="price_tablerates.csv"/> + </actionGroup> + <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveConfig"/> + + <!-- Create cart price rule for free shipping --> + <actionGroup ref="AdminCartPriceRuleDeleteAllActionGroup" stepKey="deleteAllExistingCartPriceRules"/> + + <actionGroup ref="AdminOpenNewCartPriceRuleFormPageActionGroup" stepKey="createCartPriceRule"/> + <actionGroup ref="AdminCartPriceRuleFillMainInfoActionGroup" stepKey="fillCartPriceRuleMainInfo"> + <argument name="name" value="{{CartPriceRuleFreeShippingWithCouponAppliedOnly.name}}"/> + <argument name="description" value="{{CartPriceRuleFreeShippingWithCouponAppliedOnly.description}}"/> + </actionGroup> + <actionGroup ref="AdminCartPriceRuleFillCouponInfoActionGroup" stepKey="fillCartPriceRuleCouponInfo"/> + <actionGroup ref="AdminCreateCartPriceRuleActionsSectionDiscountFieldsActionGroup" stepKey="fillCartPriceRuleActionsSection"> + <argument name="rule" value="CartPriceRuleFreeShippingWithCouponAppliedOnly"/> + </actionGroup> + <actionGroup ref="AdminCreateCartPriceRuleActionsSectionFreeShippingActionGroup" stepKey="fillCartPriceRuleFreeShippingActionsSection"> + <argument name="freeShippingOption" value="{{CartPriceRuleFreeShippingWithCouponAppliedOnly.simple_free_shipping}}"/> + </actionGroup> + <actionGroup ref="AdminCartPriceRuleSaveActionGroup" stepKey="saveCartPriceRule"/> + </before> + <after> + + <!-- Delete product --> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + + <!-- Delete cart price rule for free shipping --> + <actionGroup ref="AdminCartPriceRuleDeleteAllActionGroup" stepKey="deleteAllCartPriceRules"/> + <actionGroup ref="AdminOpenShippingMethodsConfigPageActionGroup" stepKey="openShippingMethodConfigPage2"/> + <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="switchDefaultWebsite2"> + <argument name="website" value="_defaultWebsite"/> + </actionGroup> + + <!-- Turn off table rates shipping method --> + <actionGroup ref="AdminChangeTableRatesShippingMethodStatusActionGroup" stepKey="disableTableRatesShippingMethodForDefaultWebsite"> + <argument name="status" value="0"/> + </actionGroup> + <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveConfig2"/> + + <!-- Disable free shipping method --> + <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShippingMethod"/> + <createData entity="setFreeShippingSubtotalToDefault" stepKey="setFreeShippingSubtotalToDefault"/> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!-- Add product to cart and check shipping prices --> + <actionGroup ref="OpenStoreFrontProductPageActionGroup" stepKey="navigateToProductPage"> + <argument name="productUrlKey" value="$createProduct.custom_attributes[url_key]$"/> + </actionGroup> + + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$createProduct$" /> + <argument name="productCount" value="1" /> + </actionGroup> + + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + + <actionGroup ref="GuestCheckoutFillNewShippingAddressActionGroup" stepKey="guestCheckoutFillingShippingSection"> + <argument name="customer" value="CustomerEntityOne" /> + <argument name="address" value="CustomerAddressSimple" /> + </actionGroup> + <see selector="{{CheckoutShippingMethodsSection.shippingRatePriceByName('Fixed')}}" userInput="$5.00" stepKey="assertFlatRatedMethodPrice"/> + <see selector="{{CheckoutShippingMethodsSection.shippingRatePriceByName('Table Rate')}}" userInput="$0.00" stepKey="assertTableRatedMethodPrice"/> + <dontSee selector="{{CheckoutShippingMethodsSection.shippingRatePriceByName('Free Shipping')}}" userInput="$0.00" stepKey="assertFreeShippingMethodNotAvailable"/> + <waitForElementClickable selector="{{CheckoutShippingMethodsSection.shippingMethodFlatRate}}" stepKey="waitForFlatRateShippingMethod"/> + + <!-- Apply cart price rule for free shipping --> + <click selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('Flat Rate')}}" stepKey="selectFlatRateShippingMethod"/> + <actionGroup ref="StorefrontCheckoutClickNextButtonActionGroup" stepKey="goToPaymentStep"/> + <actionGroup ref="StorefrontApplyDiscountCodeActionGroup" stepKey="applyCoupon"> + <argument name="discountCode" value="{{_defaultCoupon.code}}"/> + </actionGroup> + + <!-- Check shipping prices after applying cart price rule --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnHomePageAfterCartRuleApplied"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart2"/> + <see selector="{{CheckoutShippingMethodsSection.shippingRatePriceByName('Fixed')}}" userInput="$5.00" stepKey="assertFlatRatedMethodPriceAfterCartRule"/> + <see selector="{{CheckoutShippingMethodsSection.shippingRatePriceByName('Table Rate')}}" userInput="$15.95" stepKey="assertTableRatedMethodPriceAfterCartRule"/> + <see selector="{{CheckoutShippingMethodsSection.shippingRatePriceByName('Free Shipping')}}" userInput="$0.00" stepKey="assertFreeShippingMethodAvailable"/> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/_data/price_tablerates.csv b/dev/tests/acceptance/tests/_data/price_tablerates.csv new file mode 100644 index 000000000000..51977f498761 --- /dev/null +++ b/dev/tests/acceptance/tests/_data/price_tablerates.csv @@ -0,0 +1,5 @@ +Country,Region/State,Zip/Postal Code,Order Subtotal (and above),Shipping Price +USA,*,*,0,15.9500 +USA,*,*,100.0000,0.0000 +USA,AK,*,0.0000,0.0000 +USA,HI,*,0.0000,0.0000 \ No newline at end of file From eef550166260e957dcc95581e0a66023f0e0126a Mon Sep 17 00:00:00 2001 From: arnsaha <arnsaha@adobe.com> Date: Thu, 22 Feb 2024 12:31:35 -0600 Subject: [PATCH 19/27] ACP2E-2799: [Cloud] Wrong bundle product price when used with tier prices - With test --- .../Pricing/Price/DiscountCalculator.php | 16 ++++- .../Pricing/Price/DiscountCalculatorTest.php | 68 +++++++++++++++---- 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Bundle/Pricing/Price/DiscountCalculator.php b/app/code/Magento/Bundle/Pricing/Price/DiscountCalculator.php index 6990149efc37..253e7e06fec4 100644 --- a/app/code/Magento/Bundle/Pricing/Price/DiscountCalculator.php +++ b/app/code/Magento/Bundle/Pricing/Price/DiscountCalculator.php @@ -7,12 +7,21 @@ namespace Magento\Bundle\Pricing\Price; use Magento\Catalog\Model\Product; +use Magento\Framework\Pricing\PriceCurrencyInterface; /** - * Class DiscountCalculator + * Check the product available discount and apply the correct discount to the price */ class DiscountCalculator { + + /** + * @param PriceCurrencyInterface $priceCurrency + */ + public function __construct(private readonly PriceCurrencyInterface $priceCurrency) + { + } + /** * Apply percentage discount * @@ -20,7 +29,7 @@ class DiscountCalculator * @param float|null $value * @return float|null */ - public function calculateDiscount(Product $product, $value = null) + public function calculateDiscount(Product $product, float $value = null): ?float { if ($value === null) { $value = $product->getPriceInfo()->getPrice(FinalPrice::PRICE_CODE)->getValue(); @@ -32,6 +41,7 @@ public function calculateDiscount(Product $product, $value = null) $discount = min($price->getDiscountPercent(), $discount ?: $price->getDiscountPercent()); } } - return (null !== $discount) ? $discount/100 * $value : $value; + return (null !== $discount) ? + $this->priceCurrency->roundPrice($discount/100 * $value, 2) : $value; } } diff --git a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/DiscountCalculatorTest.php b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/DiscountCalculatorTest.php index 0962a931761e..010c6149b50f 100644 --- a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/DiscountCalculatorTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/DiscountCalculatorTest.php @@ -11,7 +11,9 @@ use Magento\Bundle\Pricing\Price\DiscountProviderInterface; use Magento\Catalog\Model\Product; use Magento\Catalog\Pricing\Price\FinalPrice; +use Magento\Framework\Pricing\PriceCurrencyInterface; use Magento\Framework\Pricing\PriceInfo\Base; +use Magento\Framework\Pricing\Price\PriceInterface; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -42,21 +44,31 @@ class DiscountCalculatorTest extends TestCase */ protected $priceMock; + /** + * @var PriceCurrencyInterface|MockObject + */ + private $priceCurrencyMock; + /** * Test setUp */ protected function setUp(): void { $this->productMock = $this->createMock(Product::class); - $this->priceInfoMock = $this->createPartialMock( - Base::class, - ['getPrice', 'getPrices'] - ); + $this->priceInfoMock = $this->getMockBuilder(Base::class) + ->disableOriginalConstructor() + ->onlyMethods(['getPrice', 'getPrices']) + ->addMethods(['getValue']) + ->getMock(); $this->finalPriceMock = $this->createMock(FinalPrice::class); $this->priceMock = $this->getMockForAbstractClass( DiscountProviderInterface::class ); - $this->calculator = new DiscountCalculator(); + $this->priceCurrencyMock = $this->getMockBuilder(PriceCurrencyInterface::class) + ->disableOriginalConstructor() + ->addMethods(['roundPrice']) + ->getMockForAbstractClass(); + $this->calculator = new DiscountCalculator($this->priceCurrencyMock); } /** @@ -98,26 +110,52 @@ public function testCalculateDiscountWithDefaultAmount() $this->getPriceMock(40), ] ); + $this->priceCurrencyMock->expects($this->once()) + ->method('roundPrice') + ->willReturn(20); $this->assertEquals(20, $this->calculator->calculateDiscount($this->productMock)); } /** * test method calculateDiscount with custom price amount + * + * @dataProvider providerForWithDifferentAmount */ - public function testCalculateDiscountWithCustomAmount() + public function testCalculateDiscountWithCustomAmount(mixed $discount, mixed $value, float $expectedResult) { - $this->productMock->expects($this->once()) + $this->productMock->expects($this->any()) ->method('getPriceInfo') ->willReturn($this->priceInfoMock); - $this->priceInfoMock->expects($this->once()) + $this->priceInfoMock->expects($this->any()) ->method('getPrices') - ->willReturn( - [ - $this->getPriceMock(30), - $this->getPriceMock(20), - $this->getPriceMock(40), - ] + ->willReturn([$this->getPriceMock($discount)]); + if ($value === null) { + $abstractPriceMock = $this->getMockForAbstractClass( + PriceInterface::class ); - $this->assertEquals(10, $this->calculator->calculateDiscount($this->productMock, 50)); + $this->priceInfoMock->expects($this->any()) + ->method('getPrice') + ->willReturn($abstractPriceMock); + $abstractPriceMock->expects($this->any()) + ->method('getValue') + ->willReturn($expectedResult); + } + $this->priceCurrencyMock->expects($this->any()) + ->method('roundPrice') + ->willReturn($expectedResult); + $this->assertEquals($expectedResult, $this->calculator->calculateDiscount($this->productMock, $value)); + } + + /** + * @return array + */ + public function providerForWithDifferentAmount() + { + return [ + 'test case 1 with discount amount' => [20, 50, 10], + 'test case 2 for null discount amount' => [null, 30, 30], + 'test case 3 with discount amount' => [99, 5.5, 5.45], + 'test case 4 with null value' => [50, null, 50] + ]; } } From 1a2560f6011ceec8b0e424a44cc9b82713e0e499 Mon Sep 17 00:00:00 2001 From: Olga Moyseyenko <moyseyen@adobe.com> Date: Thu, 22 Feb 2024 16:09:36 -0600 Subject: [PATCH 20/27] ACP2E-2763: Table Rates Still Showing Even Though Free Shipping Is Applied This value was updated as now the whole subtotal is taken into account for Table Rate tier, not only products excluded from the rule --- .../Magento/Quote/Model/ShippingMethodManagementTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php index 8c1904acc39e..988ca6b8a2ed 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php @@ -168,12 +168,12 @@ public function testTableRateWithCartRuleForFreeShipping() $rate = reset($result); $expectedResult = [ 'method_code' => 'bestway', - 'amount' => 10 + 'amount' => 5 ]; $this->assertEquals($expectedResult['method_code'], $rate->getMethodCode()); $this->assertEquals($expectedResult['amount'], $rate->getAmount()); } - + /** * @magentoDataFixture Magento/SalesRule/_files/cart_rule_100_percent_off.php * @magentoDataFixture Magento/Sales/_files/quote_with_customer.php From cec419fdaa3532f4852af518a564155f3beaac44 Mon Sep 17 00:00:00 2001 From: Olga Moyseyenko <moyseyen@adobe.com> Date: Thu, 22 Feb 2024 18:19:56 -0600 Subject: [PATCH 21/27] ACP2E-2763: Table Rates Still Showing Even Though Free Shipping Is Applied --- ...atesShippingMethodConditionActionGroup.xml | 31 +++++++++++++++++++ ...hFreeShippingAfterApplyingCartRuleTest.xml | 6 +++- .../tests/_data/price_tablerates.csv | 2 +- 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodConditionActionGroup.xml diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodConditionActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodConditionActionGroup.xml new file mode 100644 index 000000000000..95b8f75998c8 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodConditionActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /************************************************************************ + * + * Copyright 2024 Adobe + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Adobe and its suppliers, if any. The intellectual + * and technical concepts contained herein are proprietary to Adobe + * and its suppliers and are protected by all applicable intellectual + * property laws, including trade secret and copyright laws. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Adobe. + * ************************************************************************ + */ + --> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Enable/Disable Table Rates shipping method --> + <actionGroup name="AdminChangeTableRatesShippingMethodConditionActionGroup"> + <arguments> + <argument name="condition" type="string" defaultValue="{{TableRateShippingMethodConfig.package_value_with_discount}}"/> + </arguments> + <conditionalClick selector="{{AdminShippingMethodTableRatesSection.carriersTableRateTab}}" dependentSelector="{{AdminShippingMethodTableRatesSection.carriersTableRateActive}}" visible="false" stepKey="expandTab"/> + <uncheckOption selector="{{AdminShippingMethodTableRatesSection.carriersTableRateConditionName}}" stepKey="disableUseDefaultCondition"/> + <selectOption selector="{{AdminShippingMethodTableRatesSection.condition}}" userInput="{{condition}}" stepKey="setCondition"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontAssertShippingPricesPresentWithFreeShippingAfterApplyingCartRuleTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontAssertShippingPricesPresentWithFreeShippingAfterApplyingCartRuleTest.xml index d9443f79474a..da57675ed6a4 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontAssertShippingPricesPresentWithFreeShippingAfterApplyingCartRuleTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontAssertShippingPricesPresentWithFreeShippingAfterApplyingCartRuleTest.xml @@ -26,7 +26,7 @@ <title value="Assert that table rate shipping price will be correct after cart price rule for free shipping applied"/> <description value="Table rate price should be displayed correctly on checkout after applied free shipment cart price rule"/> <severity value="AVERAGE"/> - <testCaseId value="ACP2E-2763"/> + <testCaseId value="AC-11402"/> <useCaseId value="ACP2E-2763"/> <group value="shipping"/> <group value="SalesRule"/> @@ -47,6 +47,9 @@ <actionGroup ref="AdminChangeTableRatesShippingMethodStatusActionGroup" stepKey="enableTableRatesShippingMethodForDefaultWebsite"> <argument name="status" value="1"/> </actionGroup> + <actionGroup ref="AdminChangeTableRatesShippingMethodConditionActionGroup" stepKey="selectPriceVsDestinationCondition"> + <argument name="condition" value="{{TableRateShippingMethodConfig.package_value_with_discount}}"/> + </actionGroup> <actionGroup ref="AdminImportFileTableRatesShippingMethodActionGroup" stepKey="importCSVFile"> <argument name="file" value="price_tablerates.csv"/> </actionGroup> @@ -85,6 +88,7 @@ <actionGroup ref="AdminChangeTableRatesShippingMethodStatusActionGroup" stepKey="disableTableRatesShippingMethodForDefaultWebsite"> <argument name="status" value="0"/> </actionGroup> + <checkOption selector="{{AdminShippingMethodTableRatesSection.carriersTableRateConditionName}}" stepKey="disableUseDefaultCondition"/> <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveConfig2"/> <!-- Disable free shipping method --> diff --git a/dev/tests/acceptance/tests/_data/price_tablerates.csv b/dev/tests/acceptance/tests/_data/price_tablerates.csv index 51977f498761..509d544585c6 100644 --- a/dev/tests/acceptance/tests/_data/price_tablerates.csv +++ b/dev/tests/acceptance/tests/_data/price_tablerates.csv @@ -2,4 +2,4 @@ Country,Region/State,Zip/Postal Code,Order Subtotal (and above),Shipping Price USA,*,*,0,15.9500 USA,*,*,100.0000,0.0000 USA,AK,*,0.0000,0.0000 -USA,HI,*,0.0000,0.0000 \ No newline at end of file +USA,HI,*,0.0000,0.0000 From 0a772398ff12a7881a2645ea858fbaf708e6e0cd Mon Sep 17 00:00:00 2001 From: Olga Moyseyenko <moyseyen@adobe.com> Date: Thu, 22 Feb 2024 22:08:50 -0600 Subject: [PATCH 22/27] ACP2E-2763: Table Rates Still Showing Even Though Free Shipping Is Applied --- ...WithFreeShippingAfterApplyingCartRuleTest.xml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontAssertShippingPricesPresentWithFreeShippingAfterApplyingCartRuleTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontAssertShippingPricesPresentWithFreeShippingAfterApplyingCartRuleTest.xml index da57675ed6a4..39f0b0db669c 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontAssertShippingPricesPresentWithFreeShippingAfterApplyingCartRuleTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontAssertShippingPricesPresentWithFreeShippingAfterApplyingCartRuleTest.xml @@ -71,31 +71,33 @@ <argument name="freeShippingOption" value="{{CartPriceRuleFreeShippingWithCouponAppliedOnly.simple_free_shipping}}"/> </actionGroup> <actionGroup ref="AdminCartPriceRuleSaveActionGroup" stepKey="saveCartPriceRule"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsAdmin"/> </before> <after> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin2"/> <!-- Delete product --> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <!-- Delete cart price rule for free shipping --> <actionGroup ref="AdminCartPriceRuleDeleteAllActionGroup" stepKey="deleteAllCartPriceRules"/> + + <!-- Disable free shipping method --> <actionGroup ref="AdminOpenShippingMethodsConfigPageActionGroup" stepKey="openShippingMethodConfigPage2"/> + <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShippingMethod"/> + <createData entity="setFreeShippingSubtotalToDefault" stepKey="setFreeShippingSubtotalToDefault"/> + + <!-- Turn off table rates shipping method --> <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="switchDefaultWebsite2"> <argument name="website" value="_defaultWebsite"/> </actionGroup> - - <!-- Turn off table rates shipping method --> <actionGroup ref="AdminChangeTableRatesShippingMethodStatusActionGroup" stepKey="disableTableRatesShippingMethodForDefaultWebsite"> <argument name="status" value="0"/> </actionGroup> <checkOption selector="{{AdminShippingMethodTableRatesSection.carriersTableRateConditionName}}" stepKey="disableUseDefaultCondition"/> <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveConfig2"/> - <!-- Disable free shipping method --> - <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShippingMethod"/> - <createData entity="setFreeShippingSubtotalToDefault" stepKey="setFreeShippingSubtotalToDefault"/> - - <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout2"/> </after> <!-- Add product to cart and check shipping prices --> From 62512838806e427bc5514926ee8841b7b2690fe5 Mon Sep 17 00:00:00 2001 From: arnsaha <arnsaha@adobe.com> Date: Fri, 23 Feb 2024 08:33:07 -0600 Subject: [PATCH 23/27] ACP2E-2799: [Cloud] Wrong bundle product price when used with tier prices - With test --- app/code/Magento/Bundle/Pricing/Price/DiscountCalculator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Pricing/Price/DiscountCalculator.php b/app/code/Magento/Bundle/Pricing/Price/DiscountCalculator.php index 253e7e06fec4..1cef9f71eff0 100644 --- a/app/code/Magento/Bundle/Pricing/Price/DiscountCalculator.php +++ b/app/code/Magento/Bundle/Pricing/Price/DiscountCalculator.php @@ -29,7 +29,7 @@ public function __construct(private readonly PriceCurrencyInterface $priceCurren * @param float|null $value * @return float|null */ - public function calculateDiscount(Product $product, float $value = null): ?float + public function calculateDiscount(Product $product, float $value = null) { if ($value === null) { $value = $product->getPriceInfo()->getPrice(FinalPrice::PRICE_CODE)->getValue(); From 124997d7365962cffbafecf6192197b13981edca Mon Sep 17 00:00:00 2001 From: arnsaha <arnsaha@adobe.com> Date: Fri, 23 Feb 2024 08:34:52 -0600 Subject: [PATCH 24/27] ACP2E-2799: [Cloud] Wrong bundle product price when used with tier prices - With test --- app/code/Magento/Bundle/Pricing/Price/DiscountCalculator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Pricing/Price/DiscountCalculator.php b/app/code/Magento/Bundle/Pricing/Price/DiscountCalculator.php index 1cef9f71eff0..3dcce3db04a8 100644 --- a/app/code/Magento/Bundle/Pricing/Price/DiscountCalculator.php +++ b/app/code/Magento/Bundle/Pricing/Price/DiscountCalculator.php @@ -29,7 +29,7 @@ public function __construct(private readonly PriceCurrencyInterface $priceCurren * @param float|null $value * @return float|null */ - public function calculateDiscount(Product $product, float $value = null) + public function calculateDiscount(Product $product, $value = null) { if ($value === null) { $value = $product->getPriceInfo()->getPrice(FinalPrice::PRICE_CODE)->getValue(); From 9e667db4ac233c9d39861f925f68f08eea4fd9f8 Mon Sep 17 00:00:00 2001 From: Anna Bukatar <abukatar@magento.com> Date: Fri, 23 Feb 2024 14:06:33 -0800 Subject: [PATCH 25/27] ACP2E-2841: Payflow creates new transaction each time we click on fetch button on the view transaction screen --- .../Paypal/Model/Payflow/Transparent.php | 14 ---------- .../Unit/Model/Payflow/TransparentTest.php | 28 ++++++++++++++++++- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Paypal/Model/Payflow/Transparent.php b/app/code/Magento/Paypal/Model/Payflow/Transparent.php index f9233c15a4a7..ff715c271509 100644 --- a/app/code/Magento/Paypal/Model/Payflow/Transparent.php +++ b/app/code/Magento/Paypal/Model/Payflow/Transparent.php @@ -389,20 +389,6 @@ public function denyPayment(InfoInterface $payment) return true; } - /** - * @inheritDoc - */ - public function fetchTransactionInfo(InfoInterface $payment, $transactionId) - { - $result = parent::fetchTransactionInfo($payment, $transactionId); - $this->_canFetchTransactionInfo = false; - if ($payment->getIsTransactionApproved()) { - $this->acceptPayment($payment); - } - - return $result; - } - /** * Marks payment as fraudulent. * diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php index 0e327e5d1854..c3916172aa37 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php @@ -235,7 +235,7 @@ private function getPaymentConfigInterfaceFactory() ->disableOriginalConstructor() ->getMock(); $this->paymentConfig = $this->getMockBuilder(PaymentConfigInterface::class) - ->setMethods(['setStoreId', 'setMethodInstance', 'setMethod', 'getBuildNotationCode']) + ->addMethods(['setStoreId', 'setMethodInstance', 'setMethod', 'getBuildNotationCode', 'getPaymentAction']) ->getMockForAbstractClass(); $paymentConfigInterfaceFactory->method('create')->willReturn($this->paymentConfig); @@ -345,6 +345,23 @@ private function initPayment() { $this->payment = $this->getMockBuilder(Payment::class) ->disableOriginalConstructor() + ->addMethods(['getIsTransactionApproved']) + ->onlyMethods( + [ + 'setTransactionId', + 'setIsTransactionClosed', + 'getCcExpYear', + 'getCcExpMonth', + 'getExtensionAttributes', + 'getOrder', + 'authorize', + 'canFetchTransactionInfo', + 'getParentTransactionId', + 'setParentTransactionId', + 'setAdditionalInformation', + 'getAdditionalInformation' + ] + ) ->getMock(); $this->order = $this->getMockBuilder(Order::class) ->disableOriginalConstructor() @@ -360,7 +377,16 @@ private function initPayment() $this->payment->method('getCcExpYear')->willReturn('2019'); $this->payment->method('getCcExpMonth')->willReturn('05'); $this->payment->method('getExtensionAttributes')->willReturn($this->paymentExtensionAttributes); + $this->payment->method('getIsTransactionApproved')->willReturn(true); return $this->payment; } + + public function testFetchTransactionInfo() + { + $this->payment->method('canFetchTransactionInfo')->willReturn(false); + $this->paymentConfig->method('getPaymentAction')->willReturn('authorize'); + $this->payment->expects($this->never())->method('authorize'); + $this->subject->fetchTransactionInfo($this->payment, '123'); + } } From 296e9d14aaa9a5cdcbe2fb364f651999ad9de142 Mon Sep 17 00:00:00 2001 From: Anna Bukatar <abukatar@magento.com> Date: Fri, 23 Feb 2024 15:55:19 -0800 Subject: [PATCH 26/27] ACP2E-2841: Payflow creates new transaction each time we click on fetch button on the view transaction screen --- .../Test/Unit/Model/Payflow/TransparentTest.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php index c3916172aa37..9ee08135aad8 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php @@ -125,15 +125,21 @@ public function testCaptureCorrectId(string $parentTransactionId) $gatewayToken = 'gateway_token'; $this->payment->expects($this->once())->method('getParentTransactionId')->willReturn($parentTransactionId); $this->payment->expects($this->exactly($setParentTransactionIdCalls))->method('setParentTransactionId'); - $this->payment->expects($this->exactly($setAdditionalInformationCalls))->method('setAdditionalInformation')->with(Payflowpro::PNREF, $gatewayToken); + $this->payment->expects($this->exactly($setAdditionalInformationCalls)) + ->method('setAdditionalInformation') + ->with(Payflowpro::PNREF, $gatewayToken); $this->payment->expects($this->exactly(4))->method('getAdditionalInformation')->withConsecutive( ['result_code'], [Payflowpro::PNREF], [Payflowpro::PNREF], [Payflowpro::PNREF], )->willReturn(0, '', Payflowpro::PNREF, Payflowpro::PNREF); - $this->paymentExtensionAttributes->expects($this->once())->method('getVaultPaymentToken')->willReturn($this->paymentToken); - $this->paymentToken->expects($this->exactly($getGatewayTokenCalls))->method('getGatewayToken')->willReturn($gatewayToken); + $this->paymentExtensionAttributes->expects($this->once()) + ->method('getVaultPaymentToken') + ->willReturn($this->paymentToken); + $this->paymentToken->expects($this->exactly($getGatewayTokenCalls)) + ->method('getGatewayToken') + ->willReturn($gatewayToken); $this->subject->capture($this->payment, 100); } From 9fad9e539e3877803b64436c7ecde5508742ae27 Mon Sep 17 00:00:00 2001 From: Anna Bukatar <abukatar@magento.com> Date: Tue, 20 Feb 2024 19:01:57 -0800 Subject: [PATCH 27/27] ACP2E-2811: Reindexing Catalog Rule Product Indexer throws SQLSTATE[HY000]: General error: 2006 MySQL server has gone away --- .../Model/Indexer/ReindexRuleProduct.php | 2 +- .../Model/Indexer/ReindexRuleProductTest.php | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php index 320eb8a38ba6..295484ca25f3 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php @@ -146,7 +146,7 @@ public function execute(Rule $rule, $batchCount, $useAdditionalTable = false) 'sort_order' => $sortOrder, ]; - if (count($rows) === $batchCount) { + if (count($rows) === (int) $batchCount) { $connection->insertMultiple($indexTable, $rows); $rows = []; } diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php index a2f4ccfad3b5..eb1c9f330fa7 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php @@ -186,6 +186,28 @@ public function testExecute(): void self::assertTrue($this->model->execute($this->ruleMock, 2, true)); } + public function testExecuteWithCustomBatchSize() + { + $websiteId = 3; + $productIds = [ + 4 => [$websiteId => 1], + 5 => [$websiteId => 1], + 6 => [$websiteId => 1] + ]; + + $this->prepareResourceMock(); + $this->prepareRuleMock([3], $productIds, [10]); + + $this->localeDateMock->method('getConfigTimezone') + ->willReturnMap([ + [ScopeInterface::SCOPE_WEBSITE, self::ADMIN_WEBSITE_ID, $this->adminTimeZone], + [ScopeInterface::SCOPE_WEBSITE, $websiteId, $this->websiteTz] + ]); + + $this->connectionMock->expects($this->exactly(2))->method('insertMultiple'); + self::assertTrue($this->model->execute($this->ruleMock, '2', true)); + } + /** * @param array $websitesIds * @param array $productIds