Merge pull request #8703 from magento-l3/PR_10_JAN_2024

[Tier 4] Bug fix delivery
This commit is contained in:
internal-magento-queue-manager[bot] authored and GitHub committed 2024-03-22 06:01:22 +00:00
commit 57a32313c4
17 files changed
+491 -99

No files matched your search

@@ -0,0 +1,21 @@
<?php
/**
* 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.
*/
declare(strict_types=1);
namespace Magento\Catalog\Model\Category\Attribute\Backend;
class DefaultSortby extends Sortby
{
}
@@ -0,0 +1,83 @@
<?php
/**
* 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.
*/
declare(strict_types=1);
namespace Magento\Catalog\Setup\Patch\Data;
use Magento\Catalog\Setup\CategorySetup;
use Magento\Catalog\Setup\CategorySetupFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Framework\Setup\Patch\PatchInterface;
class UpdateDefaultSortyByBackendType implements DataPatchInterface
{
/**
* @var ModuleDataSetupInterface
*/
private $moduleDataSetup;
/**
* @var CategorySetupFactory
*/
private $categorySetupFactory;
/**
* PatchInitial constructor.
* @param ModuleDataSetupInterface $moduleDataSetup
* @param CategorySetupFactory $categorySetupFactory
*/
public function __construct(
ModuleDataSetupInterface $moduleDataSetup,
CategorySetupFactory $categorySetupFactory
) {
$this->moduleDataSetup = $moduleDataSetup;
$this->categorySetupFactory = $categorySetupFactory;
}
/**
* @inheritdoc
*/
public function apply(): UpdateDefaultSortyByBackendType
{
$mediaBackendModel = \Magento\Catalog\Model\Category\Attribute\Backend\DefaultSortby::class;
/** @var CategorySetup $categorySetup */
$categorySetup = $this->categorySetupFactory->create(['setup' => $this->moduleDataSetup]);
$categorySetup->updateAttribute(
'catalog_category',
'default_sort_by',
'backend_model',
$mediaBackendModel
);
return $this;
}
/**
* @inheritdoc
*/
public static function getDependencies()
{
return [];
}
/**
* @inheritdoc
*/
public function getAliases()
{
return [];
}
}
@@ -27,7 +27,7 @@ class Category implements RevertibleDataFixtureInterface
'include_in_menu' => true,
'available_sort_by' => [],
'custom_attributes' => [
'default_sort_by' => ['name']
'default_sort_by' => 'position'
],
'extension_attributes' => [],
'created_at' => null,
+1
View File
@@ -740,6 +740,7 @@
<item name="Magento\Catalog\Model\Product\Attribute\Backend\Category" xsi:type="string">int[]</item>
<item name="Magento\Catalog\Model\Product\Attribute\Backend\Stock" xsi:type="string">Magento\CatalogInventory\Api\Data\StockItemInterface[]</item>
<item name="Magento\Catalog\Model\Category\Attribute\Backend\Sortby" xsi:type="string">string[]</item>
<item name="Magento\Catalog\Model\Category\Attribute\Backend\DefaultSortby" xsi:type="string">string</item>
</argument>
</arguments>
</type>
@@ -6,7 +6,17 @@
namespace Magento\CheckoutAgreements\Model\Checkout\Plugin;
use Magento\Checkout\Api\AgreementsValidatorInterface;
use Magento\Checkout\Api\GuestPaymentInformationManagementInterface;
use Magento\CheckoutAgreements\Api\CheckoutAgreementsListInterface;
use Magento\CheckoutAgreements\Model\AgreementsProvider;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Quote\Api\Data\AddressInterface;
use Magento\Quote\Api\Data\PaymentInterface;
use Magento\Quote\Api\GuestCartRepositoryInterface;
use Magento\Store\Model\App\Emulation;
use Magento\Store\Model\ScopeInterface;
use Magento\CheckoutAgreements\Model\Api\SearchCriteria\ActiveStoreAgreementsFilter;
@@ -40,62 +50,85 @@ class GuestValidation
private $activeStoreAgreementsFilter;
/**
* @param \Magento\Checkout\Api\AgreementsValidatorInterface $agreementsValidator
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfiguration
* @param \Magento\CheckoutAgreements\Api\CheckoutAgreementsListInterface $checkoutAgreementsList
* @var GuestCartRepositoryInterface
*/
private GuestCartRepositoryInterface $quoteRepository;
/**
* @var Emulation
*/
private Emulation $storeEmulation;
/**
* @param AgreementsValidatorInterface $agreementsValidator
* @param ScopeConfigInterface $scopeConfiguration
* @param CheckoutAgreementsListInterface $checkoutAgreementsList
* @param ActiveStoreAgreementsFilter $activeStoreAgreementsFilter
* @param GuestCartRepositoryInterface $quoteRepository
* @param Emulation $storeEmulation
*/
public function __construct(
\Magento\Checkout\Api\AgreementsValidatorInterface $agreementsValidator,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfiguration,
\Magento\CheckoutAgreements\Api\CheckoutAgreementsListInterface $checkoutAgreementsList,
\Magento\CheckoutAgreements\Model\Api\SearchCriteria\ActiveStoreAgreementsFilter $activeStoreAgreementsFilter
\Magento\CheckoutAgreements\Model\Api\SearchCriteria\ActiveStoreAgreementsFilter $activeStoreAgreementsFilter,
GuestCartRepositoryInterface $quoteRepository,
Emulation $storeEmulation
) {
$this->agreementsValidator = $agreementsValidator;
$this->scopeConfiguration = $scopeConfiguration;
$this->checkoutAgreementsList = $checkoutAgreementsList;
$this->activeStoreAgreementsFilter = $activeStoreAgreementsFilter;
$this->quoteRepository = $quoteRepository;
$this->storeEmulation = $storeEmulation;
}
/**
* Validates agreements before save payment information and order placing.
*
* @param \Magento\Checkout\Api\GuestPaymentInformationManagementInterface $subject
* @param GuestPaymentInformationManagementInterface $subject
* @param string $cartId
* @param string $email
* @param \Magento\Quote\Api\Data\PaymentInterface $paymentMethod
* @param \Magento\Quote\Api\Data\AddressInterface|null $billingAddress
* @throws \Magento\Framework\Exception\CouldNotSaveException
* @param PaymentInterface $paymentMethod
* @param AddressInterface|null $billingAddress
* @return void
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
* @throws CouldNotSaveException|NoSuchEntityException
*/
public function beforeSavePaymentInformationAndPlaceOrder(
\Magento\Checkout\Api\GuestPaymentInformationManagementInterface $subject,
GuestPaymentInformationManagementInterface $subject,
$cartId,
$email,
\Magento\Quote\Api\Data\PaymentInterface $paymentMethod,
\Magento\Quote\Api\Data\AddressInterface $billingAddress = null
PaymentInterface $paymentMethod,
AddressInterface $billingAddress = null
) {
if ($this->isAgreementEnabled()) {
$this->validateAgreements($paymentMethod);
$quote = $this->quoteRepository->get($cartId);
$storeId = $quote->getStoreId();
$this->validateAgreements($paymentMethod, $storeId);
}
}
/**
* Validates agreements.
*
* @param \Magento\Quote\Api\Data\PaymentInterface $paymentMethod
* @throws \Magento\Framework\Exception\CouldNotSaveException
* @param PaymentInterface $paymentMethod
* @param int $storeId
* @return void
* @throws CouldNotSaveException
*/
private function validateAgreements(\Magento\Quote\Api\Data\PaymentInterface $paymentMethod)
private function validateAgreements(PaymentInterface $paymentMethod, int $storeId)
{
$agreements = $paymentMethod->getExtensionAttributes() === null
? []
: $paymentMethod->getExtensionAttributes()->getAgreementIds();
if (!$this->agreementsValidator->isValid($agreements)) {
throw new \Magento\Framework\Exception\CouldNotSaveException(
$this->storeEmulation->startEnvironmentEmulation($storeId);
$isValid = $this->agreementsValidator->isValid($agreements);
$this->storeEmulation->stopEnvironmentEmulation();
if (!$isValid) {
throw new CouldNotSaveException(
__(
"The order wasn't placed. "
. "First, agree to the terms and conditions, then try placing your order again."
@@ -6,9 +6,16 @@
namespace Magento\CheckoutAgreements\Model\Checkout\Plugin;
use Magento\Checkout\Api\AgreementsValidatorInterface;
use Magento\CheckoutAgreements\Api\CheckoutAgreementsListInterface;
use Magento\CheckoutAgreements\Model\AgreementsProvider;
use Magento\CheckoutAgreements\Model\Api\SearchCriteria\ActiveStoreAgreementsFilter;
use Magento\CheckoutAgreements\Model\EmulateStore;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Api\Data\PaymentInterface;
use Magento\Store\Model\App\Emulation;
use Magento\Store\Model\ScopeInterface;
/**
@@ -37,31 +44,37 @@ class Validation
private $activeStoreAgreementsFilter;
/**
* Quote repository.
*
* @var \Magento\Quote\Api\CartRepositoryInterface
*/
private $quoteRepository;
/**
* @param \Magento\Checkout\Api\AgreementsValidatorInterface $agreementsValidator
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfiguration
* @param \Magento\CheckoutAgreements\Api\CheckoutAgreementsListInterface $checkoutAgreementsList
* @var Emulation
*/
private Emulation $storeEmulation;
/**
* @param AgreementsValidatorInterface $agreementsValidator
* @param ScopeConfigInterface $scopeConfiguration
* @param CheckoutAgreementsListInterface $checkoutAgreementsList
* @param ActiveStoreAgreementsFilter $activeStoreAgreementsFilter
* @param CartRepositoryInterface $quoteRepository
* @param Emulation $storeEmulation
*/
public function __construct(
\Magento\Checkout\Api\AgreementsValidatorInterface $agreementsValidator,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfiguration,
\Magento\CheckoutAgreements\Api\CheckoutAgreementsListInterface $checkoutAgreementsList,
\Magento\CheckoutAgreements\Model\Api\SearchCriteria\ActiveStoreAgreementsFilter $activeStoreAgreementsFilter,
CartRepositoryInterface $quoteRepository
CartRepositoryInterface $quoteRepository,
Emulation $storeEmulation
) {
$this->agreementsValidator = $agreementsValidator;
$this->scopeConfiguration = $scopeConfiguration;
$this->checkoutAgreementsList = $checkoutAgreementsList;
$this->activeStoreAgreementsFilter = $activeStoreAgreementsFilter;
$this->quoteRepository = $quoteRepository;
$this->storeEmulation = $storeEmulation;
}
/**
@@ -82,24 +95,31 @@ class Validation
\Magento\Quote\Api\Data\AddressInterface $billingAddress = null
) {
if ($this->isAgreementEnabled()) {
$this->validateAgreements($paymentMethod);
$quote = $this->quoteRepository->get($cartId);
$storeId = $quote->getStoreId();
$this->validateAgreements($paymentMethod, $storeId);
}
}
/**
* Validate agreements base on the payment method
*
* @param \Magento\Quote\Api\Data\PaymentInterface $paymentMethod
* @throws \Magento\Framework\Exception\CouldNotSaveException
* @param PaymentInterface $paymentMethod
* @param int $storeId
* @return void
* @throws CouldNotSaveException
*/
protected function validateAgreements(\Magento\Quote\Api\Data\PaymentInterface $paymentMethod)
private function validateAgreements(\Magento\Quote\Api\Data\PaymentInterface $paymentMethod, int $storeId)
{
$agreements = $paymentMethod->getExtensionAttributes() === null
? []
: $paymentMethod->getExtensionAttributes()->getAgreementIds();
if (!$this->agreementsValidator->isValid($agreements)) {
$this->storeEmulation->startEnvironmentEmulation($storeId);
$isValid = $this->agreementsValidator->isValid($agreements);
$this->storeEmulation->stopEnvironmentEmulation();
if (!$isValid) {
throw new \Magento\Framework\Exception\CouldNotSaveException(
__(
"The order wasn't placed. "
@@ -15,9 +15,14 @@ use Magento\CheckoutAgreements\Model\Api\SearchCriteria\ActiveStoreAgreementsFil
use Magento\CheckoutAgreements\Model\Checkout\Plugin\GuestValidation;
use Magento\Framework\Api\SearchCriteria;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Api\Data\AddressInterface;
use Magento\Quote\Api\Data\PaymentExtension;
use Magento\Quote\Api\Data\PaymentExtensionInterface;
use Magento\Quote\Api\Data\PaymentInterface;
use Magento\Quote\Api\GuestCartRepositoryInterface;
use Magento\Quote\Model\MaskedQuoteIdToQuoteId;
use Magento\Quote\Model\Quote;
use Magento\Store\Model\App\Emulation;
use Magento\Store\Model\ScopeInterface;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\MockObject\RuntimeException;
@@ -73,6 +78,26 @@ class GuestValidationTest extends TestCase
*/
private $agreementsFilterMock;
/**
* @var Quote|MockObject
*/
private Quote|MockObject $quoteMock;
/**
* @var MaskedQuoteIdToQuoteId|MockObject
*/
private MaskedQuoteIdToQuoteId|MockObject $maskedQuoteIdToQuoteIdMock;
/**
* @var CartRepositoryInterface|MockObject
*/
private CartRepositoryInterface|MockObject $cartRepositoryMock;
/**
* @var Emulation|MockObject
*/
private Emulation|MockObject $storeEmulationMock;
protected function setUp(): void
{
$this->agreementsValidatorMock = $this->getMockForAbstractClass(AgreementsValidatorInterface::class);
@@ -87,18 +112,34 @@ class GuestValidationTest extends TestCase
$this->agreementsFilterMock = $this->createMock(
ActiveStoreAgreementsFilter::class
);
$this->quoteMock = $this->createMock(Quote::class);
$this->maskedQuoteIdToQuoteIdMock = $this->createMock(MaskedQuoteIdToQuoteId::class);
$this->cartRepositoryMock = $this->createMock(GuestCartRepositoryInterface::class);
$this->storeEmulationMock = $this->createMock(Emulation::class);
$storeId = 1;
$this->quoteMock->expects($this->once())
->method('getStoreId')
->willReturn($storeId);
$this->cartRepositoryMock->expects($this->once())
->method('get')
->with('0CQwCntNHR4yN9P5PUAzbxatvDvBXOce')
->willReturn($this->quoteMock);
$this->model = new GuestValidation(
$this->agreementsValidatorMock,
$this->scopeConfigMock,
$this->checkoutAgreementsListMock,
$this->agreementsFilterMock
$this->agreementsFilterMock,
$this->cartRepositoryMock,
$this->storeEmulationMock
);
}
public function testBeforeSavePaymentInformationAndPlaceOrder()
{
$cartId = '100';
$storeId = 1;
$cartId = '0CQwCntNHR4yN9P5PUAzbxatvDvBXOce';
$email = 'email@example.com';
$agreements = [1, 2, 3];
$this->scopeConfigMock
@@ -119,6 +160,11 @@ class GuestValidationTest extends TestCase
$this->paymentMock->expects(static::atLeastOnce())
->method('getExtensionAttributes')
->willReturn($this->extensionAttributesMock);
$this->storeEmulationMock->expects($this->once())
->method('startEnvironmentEmulation')
->with($storeId);
$this->storeEmulationMock->expects($this->once())
->method('stopEnvironmentEmulation');
$this->model->beforeSavePaymentInformationAndPlaceOrder(
$this->subjectMock,
$cartId,
@@ -131,7 +177,8 @@ class GuestValidationTest extends TestCase
public function testBeforeSavePaymentInformationAndPlaceOrderIfAgreementsNotValid()
{
$this->expectException('Magento\Framework\Exception\CouldNotSaveException');
$cartId = 100;
$storeId = 1;
$cartId = '0CQwCntNHR4yN9P5PUAzbxatvDvBXOce';
$email = 'email@example.com';
$agreements = [1, 2, 3];
$this->scopeConfigMock
@@ -152,6 +199,11 @@ class GuestValidationTest extends TestCase
$this->paymentMock->expects(static::atLeastOnce())
->method('getExtensionAttributes')
->willReturn($this->extensionAttributesMock);
$this->storeEmulationMock->expects($this->once())
->method('startEnvironmentEmulation')
->with($storeId);
$this->storeEmulationMock->expects($this->once())
->method('stopEnvironmentEmulation');
$this->model->beforeSavePaymentInformationAndPlaceOrder(
$this->subjectMock,
$cartId,
@@ -172,9 +224,10 @@ class GuestValidationTest extends TestCase
*/
private function getPaymentExtension(): MockObject
{
$mockBuilder = $this->getMockBuilder(PaymentExtension::class);
$mockBuilder = $this->getMockBuilder(PaymentExtensionInterface::class)
->disableOriginalConstructor();
try {
$mockBuilder->addMethods(['getAgreementIds']);
$mockBuilder->addMethods(['getAgreementIds', 'setAgreementIds']);
} catch (RuntimeException $e) {
// Payment extension already generated.
}
@@ -13,13 +13,15 @@ use Magento\CheckoutAgreements\Api\CheckoutAgreementsListInterface;
use Magento\CheckoutAgreements\Model\AgreementsProvider;
use Magento\CheckoutAgreements\Model\Api\SearchCriteria\ActiveStoreAgreementsFilter;
use Magento\CheckoutAgreements\Model\Checkout\Plugin\Validation;
use Magento\CheckoutAgreements\Model\EmulateStore;
use Magento\Framework\Api\SearchCriteria;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Api\Data\AddressInterface;
use Magento\Quote\Api\Data\PaymentExtension;
use Magento\Quote\Api\Data\PaymentExtensionInterface;
use Magento\Quote\Api\Data\PaymentInterface;
use Magento\Quote\Model\Quote;
use Magento\Store\Model\App\Emulation;
use Magento\Store\Model\ScopeInterface;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\MockObject\RuntimeException;
@@ -86,6 +88,11 @@ class ValidationTest extends TestCase
*/
private $quoteRepositoryMock;
/**
* @var Emulation|MockObject
*/
private Emulation|MockObject $storeEmulationMock;
protected function setUp(): void
{
$this->agreementsValidatorMock = $this->getMockForAbstractClass(AgreementsValidatorInterface::class);
@@ -94,6 +101,7 @@ class ValidationTest extends TestCase
$this->addressMock = $this->getMockForAbstractClass(AddressInterface::class);
$this->quoteMock = $this->getMockBuilder(Quote::class)
->addMethods(['getIsMultiShipping'])
->onlyMethods(['getStoreId'])
->disableOriginalConstructor()
->getMock();
$this->quoteRepositoryMock = $this->getMockForAbstractClass(CartRepositoryInterface::class);
@@ -105,18 +113,29 @@ class ValidationTest extends TestCase
$this->agreementsFilterMock = $this->createMock(
ActiveStoreAgreementsFilter::class
);
$this->storeEmulationMock = $this->createMock(Emulation::class);
$storeId = 1;
$this->quoteMock->expects($this->once())
->method('getStoreId')
->willReturn($storeId);
$this->quoteRepositoryMock->expects($this->once())
->method('get')
->willReturn($this->quoteMock);
$this->model = new Validation(
$this->agreementsValidatorMock,
$this->scopeConfigMock,
$this->checkoutAgreementsListMock,
$this->agreementsFilterMock,
$this->quoteRepositoryMock
$this->quoteRepositoryMock,
$this->storeEmulationMock
);
}
public function testBeforeSavePaymentInformationAndPlaceOrder()
{
$storeId = 1;
$cartId = 100;
$agreements = [1, 2, 3];
$this->scopeConfigMock
@@ -144,6 +163,11 @@ class ValidationTest extends TestCase
$this->paymentMock->expects(static::atLeastOnce())
->method('getExtensionAttributes')
->willReturn($this->extensionAttributesMock);
$this->storeEmulationMock->expects($this->once())
->method('startEnvironmentEmulation')
->with($storeId);
$this->storeEmulationMock->expects($this->once())
->method('stopEnvironmentEmulation');
$this->model->beforeSavePaymentInformationAndPlaceOrder(
$this->subjectMock,
$cartId,
@@ -155,6 +179,7 @@ class ValidationTest extends TestCase
public function testBeforeSavePaymentInformationAndPlaceOrderIfAgreementsNotValid()
{
$this->expectException('Magento\Framework\Exception\CouldNotSaveException');
$storeId = 1;
$cartId = 100;
$agreements = [1, 2, 3];
$this->scopeConfigMock
@@ -182,13 +207,17 @@ class ValidationTest extends TestCase
$this->paymentMock->expects(static::atLeastOnce())
->method('getExtensionAttributes')
->willReturn($this->extensionAttributesMock);
$this->storeEmulationMock->expects($this->once())
->method('startEnvironmentEmulation')
->with($storeId);
$this->storeEmulationMock->expects($this->once())
->method('stopEnvironmentEmulation');
$this->model->beforeSavePaymentInformationAndPlaceOrder(
$this->subjectMock,
$cartId,
$this->paymentMock,
$this->addressMock
);
$this->expectExceptionMessage(
"The order wasn't placed. First, agree to the terms and conditions, then try placing your order again."
);
@@ -201,9 +230,10 @@ class ValidationTest extends TestCase
*/
private function getPaymentExtension(): MockObject
{
$mockBuilder = $this->getMockBuilder(PaymentExtension::class);
$mockBuilder = $this->getMockBuilder(PaymentExtensionInterface::class)
->disableOriginalConstructor();
try {
$mockBuilder->addMethods(['getAgreementIds']);
$mockBuilder->addMethods(['getAgreementIds', 'setAgreementIds']);
} catch (RuntimeException $e) {
// Payment extension already generated.
}
@@ -28,7 +28,7 @@ class PopupDeliveryDate
public function afterFormatDeliveryDateTime(Popup $subject, $result, $date, $time)
{
if ($this->getCarrier($subject) === Carrier::CODE) {
$result = $subject->formatDeliveryDate($date);
$result = $subject->formatDeliveryDate($date. ' ' . $time);
}
return $result;
}
@@ -21,9 +21,9 @@ use PHPUnit\Framework\TestCase;
*/
class PopupDeliveryDateTest extends TestCase
{
const STUB_CARRIER_CODE_NOT_FEDEX = 'not-fedex';
const STUB_DELIVERY_DATE = '2020-02-02';
const STUB_DELIVERY_TIME = '12:00';
public const STUB_CARRIER_CODE_NOT_FEDEX = 'not-fedex';
public const STUB_DELIVERY_DATE = '2020-02-02';
public const STUB_DELIVERY_TIME = '12:00';
/**
* @var MockObject|PopupDeliveryDate
@@ -68,6 +68,30 @@ class PopupDeliveryDateTest extends TestCase
$this->executeOriginalMethod();
}
/**
* Test the method with Fedex carrier with timezone impact
* @dataProvider getDates
*/
public function testAfterFormatDeliveryDateTimeWithFedexCarrierWithTimezone(
$date,
$currentTimezone,
$convertedTimezone,
$expected
) {
$this->trackingStatusMock->expects($this::once())
->method('getCarrier')
->willReturn(Carrier::CODE);
$date = new \DateTime($date, new \DateTimeZone($currentTimezone));
$date->setTimezone(new \DateTimeZone($convertedTimezone));
$this->subjectMock->expects($this->once())->method('formatDeliveryDate')
->willReturn($date->format('Y-m-d'));
$result = $this->executeOriginalMethodWithTimezone();
$this->assertEquals($expected, $result);
}
/**
* Test the method with a different carrier
*/
@@ -119,4 +143,40 @@ class PopupDeliveryDateTest extends TestCase
self::STUB_DELIVERY_TIME
);
}
/**
* Run plugin's original method taking into account timezone
*/
private function executeOriginalMethodWithTimezone()
{
return $this->plugin->afterFormatDeliveryDateTime(
$this->subjectMock,
'Test Result',
self::STUB_DELIVERY_DATE,
'00:00:00'
);
}
/**
* Data provider for testAfterFormatDeliveryDateTimeWithFedexCarrierWithTimezone
*
* @return array[]
*/
public function getDates(): array
{
return [
'same day' => [
'date' => '2024-01-07 06:00:00',
'current_timezone' => 'US/Eastern',
'converted_timezone' => 'America/Chicago',
'expected' => '2024-01-07'
],
'previous day' => [
'date' => '2024-01-07 00:00:00',
'current_timezone' => 'US/Eastern',
'converted_timezone' => 'America/Chicago',
'expected' => '2024-01-06'
]
];
}
}
@@ -3,12 +3,18 @@
* See COPYING.txt for license details.
*/
/**
* @codingStandardsIgnoreStart
*/
@import 'source/lib/_lib.less'; // Global lib
@import 'source/_theme.less'; // Theme overrides
@import 'source/_variables.less'; // Local theme variables
@baseDir: '../'; // Default
// @codingStandardsIgnoreEnd
// Magento/blank
.page-print {
.logo {
@@ -27,7 +33,6 @@
text-shadow: none !important;
}
// Black prints faster:h5bp.com/s
a,
a:visited {
text-decoration: underline !important;
@@ -76,18 +81,18 @@
p,
h2,
h3 {
orphans: 3;
widows: 3;
break-inside: avoid;
}
.block-content {
page-break-before: avoid;
break-before: avoid;
}
.block-title,
h2,
h3 {
page-break-after: avoid;
break-after: avoid;
break-inside: avoid;
}
.nav-toggle {
@@ -269,12 +269,13 @@ class CategoryRepositoryTest extends WebapiAbstract
$this->createdCategories = [$categoryId];
}
/**
* @magentoApiDataFixture Magento/Catalog/_files/category.php
*/
#[
DataFixture(CategoryFixture::class, as: 'category')
]
public function testUpdateWithDefaultSortByAttribute()
{
$categoryId = 333;
$category = $this->fixtures->get('category');
$categoryId = $category->getId();
$categoryData = [
'name' => 'Update Category Test With default_sort_by Attribute',
'is_active' => true,
@@ -282,7 +283,7 @@ class CategoryRepositoryTest extends WebapiAbstract
'custom_attributes' => [
[
'attribute_code' => 'default_sort_by',
'value' => ["name"],
'value' => "price"
],
],
];
@@ -293,7 +294,7 @@ class CategoryRepositoryTest extends WebapiAbstract
$category = $model->load($categoryId);
$this->assertTrue((bool)$category->getIsActive(), 'Category "is_active" must equal to true');
$this->assertEquals("Update Category Test With default_sort_by Attribute", $category->getName());
$this->assertEquals("name", $category->getDefaultSortBy());
$this->assertEquals("price", $category->getDefaultSortBy());
$this->createdCategories = [$categoryId];
}
@@ -156,7 +156,7 @@ QUERY;
'catalog_product'
];
$attributeTypes = [
'String[]',
'String',
'String[]',
'Int',
'CatalogInventoryDataStockItemInterface[]',
@@ -314,6 +314,7 @@ return [
Magento\Catalog\Model\Category\Attribute\Backend\Image\Interceptor::class => null,
Magento\Catalog\Model\Attribute\Backend\Startdate\Interceptor::class => null,
Magento\Eav\Model\Entity\Attribute\Backend\Datetime\Interceptor::class => null,
Magento\Catalog\Model\Category\Attribute\Backend\DefaultSortby\Interceptor::class => null,
Magento\Catalog\Model\Category\Attribute\Backend\Sortby\Interceptor::class => null,
Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate\Interceptor::class => null,
Magento\Catalog\Model\Attribute\Backend\Customlayoutupdate\Interceptor::class => null,
@@ -193,7 +193,7 @@ class TypeLocatorTest extends \PHPUnit\Framework\TestCase
'custom_design_to' => 'string',
'available_sort_by' => 'string[]',
'page_layout' => 'string',
'default_sort_by' => 'string[]',
'default_sort_by' => 'string',
'filter_price_range' => 'double',
'custom_layout_update' => 'string',
]
@@ -5,11 +5,24 @@
*/
namespace Magento\Framework\Model;
use Exception;
use Laminas\Validator\ValidatorChain;
use Laminas\Validator\ValidatorInterface;
use Magento\Framework\App\CacheInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\State;
use Magento\Framework\DataObject;
use Magento\Framework\Event\ManagerInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Message\Error;
use Magento\Framework\Model\ActionValidator\RemoveAction;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
use Magento\Framework\Data\Collection\AbstractDb as AbstractDbCollection;
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
use Magento\Framework\Phrase;
use Magento\Framework\Registry;
use Psr\Log\LoggerInterface;
/**
* Abstract model class
@@ -71,12 +84,12 @@ abstract class AbstractModel extends DataObject
/**
* Resource model instance
*
* @var \Magento\Framework\Model\ResourceModel\Db\AbstractDb
* @var AbstractDb
*/
protected $_resource;
/**
* @var \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
* @var AbstractCollection
*/
protected $_resourceCollection;
@@ -129,34 +142,34 @@ abstract class AbstractModel extends DataObject
/**
* Application Event Dispatcher
*
* @var \Magento\Framework\Event\ManagerInterface
* @var ManagerInterface
*/
protected $_eventManager;
/**
* Application Cache Manager
*
* @var \Magento\Framework\App\CacheInterface
* @var CacheInterface
*/
protected $_cacheManager;
/**
* @var \Magento\Framework\Registry
* @var Registry
*/
protected $_registry;
/**
* @var \Psr\Log\LoggerInterface
* @var LoggerInterface
*/
protected $_logger;
/**
* @var \Magento\Framework\App\State
* @var State
*/
protected $_appState;
/**
* @var \Magento\Framework\Model\ActionValidator\RemoveAction
* @var RemoveAction
*/
protected $_actionValidator;
@@ -168,18 +181,19 @@ abstract class AbstractModel extends DataObject
protected $storedData = [];
/**
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
* @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
* @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
* @param Context $context
* @param Registry $registry
* @param AbstractResource|null $resource
* @param AbstractDbCollection|null $resourceCollection
* @param array $data
* @throws LocalizedException
*/
public function __construct(
\Magento\Framework\Model\Context $context,
\Magento\Framework\Registry $registry,
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
array $data = []
Context $context,
Registry $registry,
AbstractResource $resource = null,
AbstractDbCollection $resourceCollection = null,
array $data = []
) {
$this->_registry = $registry;
$this->_appState = $context->getAppState();
@@ -214,6 +228,7 @@ abstract class AbstractModel extends DataObject
*
* @param string $resourceModel
* @return void
* @throws LocalizedException
*/
protected function _init($resourceModel)
{
@@ -252,11 +267,11 @@ abstract class AbstractModel extends DataObject
*/
public function __wakeup()
{
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_registry = $objectManager->get(\Magento\Framework\Registry::class);
$objectManager = ObjectManager::getInstance();
$this->_registry = $objectManager->get(Registry::class);
$context = $objectManager->get(\Magento\Framework\Model\Context::class);
if ($context instanceof \Magento\Framework\Model\Context) {
$context = $objectManager->get(Context::class);
if ($context instanceof Context) {
$this->_appState = $context->getAppState();
$this->_eventManager = $context->getEventDispatcher();
$this->_cacheManager = $context->getCacheManager();
@@ -357,6 +372,7 @@ abstract class AbstractModel extends DataObject
}
$this->_data = $key;
} else {
$this->checkAndConvertNumericValue($key, $value);
if (!array_key_exists($key, $this->_data) || $this->_data[$key] !== $value) {
$this->_hasDataChanges = true;
}
@@ -470,19 +486,19 @@ abstract class AbstractModel extends DataObject
/**
* Get resource instance
*
* @throws \Magento\Framework\Exception\LocalizedException
* @return \Magento\Framework\Model\ResourceModel\Db\AbstractDb
* @return AbstractDb
* @throws LocalizedException
* @deprecated 101.0.0 because resource models should be used directly
* @see we don't recommend this approach anymore
*/
protected function _getResource()
{
if (empty($this->_resourceName) && empty($this->_resource)) {
throw new \Magento\Framework\Exception\LocalizedException(
new \Magento\Framework\Phrase('The resource isn\'t set.')
throw new LocalizedException(
new Phrase('The resource isn\'t set.')
);
}
return $this->_resource ?: \Magento\Framework\App\ObjectManager::getInstance()->get($this->_resourceName);
return $this->_resource ?: ObjectManager::getInstance()->get($this->_resourceName);
}
/**
@@ -499,20 +515,20 @@ abstract class AbstractModel extends DataObject
* Get collection instance
*
* @TODO MAGETWO-23541: Incorrect dependencies between Model\AbstractModel and Data\Collection\Db from Framework
* @throws \Magento\Framework\Exception\LocalizedException
* @return \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
* @throws LocalizedException
* @return AbstractCollection
* @deprecated 101.0.0 because collections should be used directly via factory
* @see we don't recommend this approach anymore
*/
public function getResourceCollection()
{
if (empty($this->_resourceCollection) && empty($this->_collectionName)) {
throw new \Magento\Framework\Exception\LocalizedException(
new \Magento\Framework\Phrase('Model collection resource name is not defined.')
throw new LocalizedException(
new Phrase('Model collection resource name is not defined.')
);
}
return !$this->_collectionName ? clone $this
->_resourceCollection : \Magento\Framework\App\ObjectManager::getInstance()
->_resourceCollection : ObjectManager::getInstance()
->create(
$this->_collectionName
);
@@ -522,9 +538,10 @@ abstract class AbstractModel extends DataObject
* Retrieve collection instance
*
* @TODO MAGETWO-23541: Incorrect dependencies between Model\AbstractModel and Data\Collection\Db from Framework
* @return \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
* @deprecated 101.0.0 because collections should be used directly via factory
* @return AbstractCollection
* @throws LocalizedException
* @see we don't recommend this approach anymore
* @deprecated 101.0.0 because collections should be used directly via factory
*/
public function getCollection()
{
@@ -537,10 +554,11 @@ abstract class AbstractModel extends DataObject
* @param integer $modelId
* @param null|string $field
* @return $this
* @throws LocalizedException
* @see we don't recommend this approach anymore
* @deprecated 100.1.0 because entities must not be responsible for their own loading.
* Service contracts should persist entities. Use resource model "load" or collections to implement
* service contract model loading operations.
* @see we don't recommend this approach anymore
*/
public function load($modelId, $field = null)
{
@@ -651,7 +669,7 @@ abstract class AbstractModel extends DataObject
* Save object data
*
* @return $this
* @throws \Exception
* @throws Exception
*
* @deprecated 100.1.0 because entities must not be responsible for their own persistence.
* Service contracts should persist entities. Use resource model "save" to implement
@@ -726,7 +744,7 @@ abstract class AbstractModel extends DataObject
new Phrase(implode(PHP_EOL, $errors))
);
foreach ($errors as $errorMessage) {
$exception->addMessage(new \Magento\Framework\Message\Error($errorMessage));
$exception->addMessage(new Error($errorMessage));
}
throw $exception;
}
@@ -739,6 +757,7 @@ abstract class AbstractModel extends DataObject
* Returns FALSE, if no validation rules exist.
*
* @return ValidatorInterface|false
* @throws LocalizedException
*/
protected function _getValidatorBeforeSave()
{
@@ -782,7 +801,7 @@ abstract class AbstractModel extends DataObject
*/
private function getValidator(): ValidatorChain
{
return \Magento\Framework\App\ObjectManager::getInstance()->create(ValidatorChain::class);
return ObjectManager::getInstance()->create(ValidatorChain::class);
}
/**
@@ -852,7 +871,7 @@ abstract class AbstractModel extends DataObject
* Delete object from database
*
* @return $this
* @throws \Exception
* @throws Exception
* @deprecated 100.1.0 because entities must not be responsible for their own deletion.
* Service contracts should delete entities. Use resource model "delete" method to implement
* service contract persistence operations.
@@ -868,13 +887,13 @@ abstract class AbstractModel extends DataObject
* Processing object before delete data
*
* @return $this
* @throws \Magento\Framework\Exception\LocalizedException
* @throws LocalizedException
*/
public function beforeDelete()
{
if (!$this->_actionValidator->isAllowed($this)) {
throw new \Magento\Framework\Exception\LocalizedException(
new \Magento\Framework\Phrase('Delete operation is forbidden for current area')
throw new LocalizedException(
new Phrase('Delete operation is forbidden for current area')
);
}
@@ -913,7 +932,7 @@ abstract class AbstractModel extends DataObject
/**
* Retrieve model resource
*
* @return \Magento\Framework\Model\ResourceModel\Db\AbstractDb
* @return AbstractDb
* @deprecated 101.0.0 because resource models should be used directly
* @see we don't recommend this approach anymore
*/
@@ -1010,4 +1029,24 @@ abstract class AbstractModel extends DataObject
{
return $this->_eventPrefix;
}
/**
* Check and Convert Numeric Value for Proper Type Matching
*
* @param mixed $key
* @param mixed $value
* @return void
*/
private function checkAndConvertNumericValue(mixed $key, mixed $value): void
{
if (array_key_exists($key, $this->_data) && is_numeric($this->_data[$key])
&& $value != null
) {
if (is_int($value)) {
$this->_data[$key] = (int) $this->_data[$key];
} elseif (is_float($value)) {
$this->_data[$key] = (float) $this->_data[$key];
}
}
}
}
@@ -190,4 +190,49 @@ class AbstractModelTest extends TestCase
$this->model->setDataChanges(true);
$this->assertTrue($this->model->hasDataChanges());
}
/**
* Test case for checking setData function is working for all possible key value pairs
*
* @dataProvider getKeyValueDataPairs
*/
public function testSetDataWithDifferentKeyValuePairs(
array $data,
mixed $testKey,
mixed $testValue,
bool $hasDataChangedFor
): void {
$this->model->setData($data);
$this->model->setOrigData();
$this->model->setData($testKey, $testValue);
$this->assertEquals($data, $this->model->getOrigData());
$this->assertEquals($hasDataChangedFor, $this->model->dataHasChangedFor($testKey));
}
/**
* Data provider for testSetDataWithDifferentKeyValuePairs
*
* @return array
*/
public function getKeyValueDataPairs(): array
{
return [
'when test data and compare data are string' => [['key' => 'value'], 'key', 'value', false],
'when test data and compare data are different' => [['key' => 'value'], 'key', 10, true],
'when test data string and compare data is null' => [['key' => 'value'], 'key', null, true],
'when test data and compare data both null' => [['key' => null], 'key', null, false],
'when test data empty string and compare data is null' => [['key' => ''], 'key', null, false],
'when test data and compare data are empty string' => [['key' => ''], 'key', '', false],
'when test data is null and compare data is empty string' => [['key' => null], 'key', '', false],
'when test data and compare data are int' => [['key' => 1], 'key', 1, false],
'when test data is int and compare data is float' => [['key' => 1.0], 'key', 1, false],
'when test data is string and compare data is float' => [['key' => '1.0'], 'key', 1.0, false],
'when test data is string and compare data is int' => [['key' => '1'], 'key', 1, false],
'when test data is float and compare data is string' => [['key' => 1.0], 'key', '1.0', false],
'when test data is int and compare data is string' => [['key' => 1], 'key', '1', false],
'when test data and compare data are float' => [['key' => 1.0], 'key', 1.0, false],
'when test data is 0 and compare data is null' => [['key' => 0], 'key', null, false],
'when test data is null and compare data is 0' => [['key' => null], 'key', 0, false],
];
}
}