magento2-docker/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTes...

591 lines
24 KiB
PHP
Executable File

<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Quote\Model;
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
use Magento\Catalog\Test\Fixture\Virtual as VirtualProductFixture;
use Magento\Customer\Api\AddressRepositoryInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Api\Data\AddressInterface;
use Magento\Customer\Api\Data\GroupInterface;
use Magento\Customer\Api\GroupRepositoryInterface;
use Magento\Customer\Model\Vat;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\App\Config\MutableScopeConfigInterface;
use Magento\Framework\DataObject;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\ObjectManagerInterface;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Api\Data\AddressInterfaceFactory;
use Magento\Quote\Api\Data\EstimateAddressInterface;
use Magento\Quote\Api\GuestShippingMethodManagementInterface;
use Magento\Quote\Api\ShippingMethodManagementInterface;
use Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver;
use Magento\Quote\Test\Fixture\AddProductToCart as AddProductToCartFixture;
use Magento\Quote\Test\Fixture\GuestCart as GuestCartFixture;
use Magento\Checkout\Test\Fixture\SetBillingAddress as SetBillingAddressFixture;
use Magento\Checkout\Test\Fixture\SetShippingAddress as SetShippingAddressFixture;
use Magento\SalesRule\Test\Fixture\AddressCondition as AddressConditionFixture;
use Magento\SalesRule\Test\Fixture\Rule as RuleFixture;
use Magento\Store\Model\ScopeInterface;
use Magento\Tax\Api\Data\TaxClassInterface;
use Magento\Tax\Api\TaxClassRepositoryInterface;
use Magento\Tax\Model\ClassModel;
use Magento\Tax\Model\Config as TaxConfig;
use Magento\TestFramework\Fixture\Config as ConfigFixture;
use Magento\TestFramework\Fixture\DataFixture;
use Magento\TestFramework\Fixture\DataFixtureStorage;
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId;
use PHPUnit\Framework\TestCase;
use Magento\Quote\Api\CouponManagementInterface;
use Magento\Customer\Model\Session;
/**
* Test for shipping methods management
*
* @magentoDbIsolation enabled
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ShippingMethodManagementTest extends TestCase
{
/** @var ObjectManagerInterface $objectManager */
private $objectManager;
/** @var GroupRepositoryInterface $groupRepository */
private $groupRepository;
/** @var TaxClassRepositoryInterface $taxClassRepository */
private $taxClassRepository;
/**
* @var DataFixtureStorage
*/
private $fixtures;
/**
* @inheritdoc
*/
protected function setUp(): void
{
$this->objectManager = Bootstrap::getObjectManager();
$this->groupRepository = $this->objectManager->get(GroupRepositoryInterface::class);
$this->taxClassRepository = $this->objectManager->get(TaxClassRepositoryInterface::class);
$this->fixtures = $this->objectManager->get(DataFixtureStorageManager::class)->getStorage();
}
/**
* @magentoDataFixture Magento/SalesRule/_files/cart_rule_100_percent_off.php
* @magentoDataFixture Magento/Sales/_files/quote_with_customer.php
* @return void
* @throws NoSuchEntityException
*/
public function testRateAppliedToShipping(): void
{
$objectManager = Bootstrap::getObjectManager();
/** @var CartRepositoryInterface $quoteRepository */
$quoteRepository = $objectManager->create(CartRepositoryInterface::class);
$customerQuote = $quoteRepository->getForCustomer(1);
$this->assertEquals(0, $customerQuote->getBaseGrandTotal());
}
/**
* @magentoConfigFixture current_store carriers/tablerate/active 1
* @magentoConfigFixture current_store carriers/flatrate/active 0
* @magentoConfigFixture current_store carriers/freeshipping/active 0
* @magentoConfigFixture current_store carriers/tablerate/condition_name package_qty
* @magentoDataFixture Magento/SalesRule/_files/cart_rule_free_shipping_by_cart.php
* @magentoDataFixture Magento/Sales/_files/quote.php
* @magentoDataFixture Magento/OfflineShipping/_files/tablerates.php
* @return void
*/
public function testTableRateFreeShipping()
{
$objectManager = Bootstrap::getObjectManager();
/** @var Quote $quote */
$quote = $objectManager->get(Quote::class);
$quote->load('test01', 'reserved_order_id');
$cartId = $quote->getId();
if (!$cartId) {
$this->fail('quote fixture failed');
}
/** @var QuoteIdMask $quoteIdMask */
$quoteIdMask = Bootstrap::getObjectManager()
->create(QuoteIdMaskFactory::class)
->create();
$quoteIdMask->load($cartId, 'quote_id');
//Use masked cart Id
$cartId = $quoteIdMask->getMaskedId();
$data = [
'data' => [
'country_id' => "US",
'postcode' => null,
'region' => null,
'region_id' => null
]
];
/** @var EstimateAddressInterface $address */
$address = $objectManager->create(EstimateAddressInterface::class, $data);
/** @var GuestShippingMethodManagementInterface $shippingEstimation */
$shippingEstimation = $objectManager->get(GuestShippingMethodManagementInterface::class);
$result = $shippingEstimation->estimateByAddress($cartId, $address);
$this->assertNotEmpty($result);
$expectedResult = [
'method_code' => 'bestway',
'amount' => 0
];
foreach ($result as $rate) {
$this->assertEquals($expectedResult['amount'], $rate->getAmount());
$this->assertEquals($expectedResult['method_code'], $rate->getMethodCode());
}
}
/**
* @magentoDataFixture Magento/OfflineShipping/_files/tablerates_price.php
* @return void
* @throws NoSuchEntityException
*/
#[
ConfigFixture('carriers/tablerate/active', '1', 'store', 'default'),
ConfigFixture('carriers/flatrate/active', '0', 'store', 'default'),
ConfigFixture('carriers/tablerate/condition_name', 'package_value_with_discount', 'store'),
ConfigFixture('carriers/tablerate/include_virtual_price', '0', 'store', 'default'),
DataFixture(ProductFixture::class, ['sku' => 'simple', 'special_price' => 5.99], 'p1'),
DataFixture(VirtualProductFixture::class, ['sku' => 'virtual', 'weight' => 0], 'p2'),
DataFixture(GuestCartFixture::class, as: 'cart'),
DataFixture(AddProductToCartFixture::class, ['cart_id' => '$cart.id$', 'product_id' => '$p1.id$']),
DataFixture(AddProductToCartFixture::class, ['cart_id' => '$cart.id$', 'product_id' => '$p2.id$']),
DataFixture(SetBillingAddressFixture::class, ['cart_id' => '$cart.id$']),
DataFixture(SetShippingAddressFixture::class, ['cart_id' => '$cart.id$']),
]
public function testTableRateWithoutIncludingVirtualProduct()
{
$cartId = (int)$this->fixtures->get('cart')->getId();
if (!$cartId) {
$this->fail('quote fixture failed');
}
/** @var QuoteRepository $quoteRepository */
$quoteRepository = $this->objectManager->get(QuoteRepository::class);
$quote = $quoteRepository->get($cartId);
/** @var QuoteIdToMaskedQuoteIdInterface $maskedQuoteId */
$maskedQuoteId = $this->objectManager->get(QuoteIdToMaskedQuoteIdInterface::class)->execute($cartId);
/** @var GuestShippingMethodManagementInterface $shippingEstimation */
$shippingEstimation = $this->objectManager->get(GuestShippingMethodManagementInterface::class);
$result = $shippingEstimation->estimateByExtendedAddress(
$maskedQuoteId,
$quote->getShippingAddress()
);
$this->assertCount(1, $result);
$rate = reset($result);
$expectedResult = [
'method_code' => 'bestway',
'amount' => 15,
];
$this->assertEquals($expectedResult['method_code'], $rate->getMethodCode());
$this->assertEquals($expectedResult['amount'], $rate->getAmount());
}
/**
* Test table rate amount for the cart that contains some items with free shipping applied.
*
* @magentoConfigFixture current_store carriers/tablerate/active 1
* @magentoConfigFixture current_store carriers/flatrate/active 0
* @magentoConfigFixture current_store carriers/freeshipping/active 0
* @magentoConfigFixture current_store carriers/tablerate/condition_name package_value_with_discount
* @magentoDataFixture Magento/Catalog/_files/categories.php
* @magentoDataFixture Magento/SalesRule/_files/cart_rule_free_shipping_by_category.php
* @magentoDataFixture Magento/Sales/_files/quote_with_multiple_products.php
* @magentoDataFixture Magento/OfflineShipping/_files/tablerates_price.php
* @return void
*/
public function testTableRateWithCartRuleForFreeShipping()
{
$objectManager = Bootstrap::getObjectManager();
$quote = $this->getQuote('tableRate');
$cartId = $quote->getId();
if (!$cartId) {
$this->fail('quote fixture failed');
}
/** @var QuoteIdMask $quoteIdMask */
$quoteIdMask = Bootstrap::getObjectManager()
->create(QuoteIdMaskFactory::class)
->create();
$quoteIdMask->load($cartId, 'quote_id');
//Use masked cart Id
$cartId = $quoteIdMask->getMaskedId();
$addressFactory = $this->objectManager->get(AddressInterfaceFactory::class);
/** @var \Magento\Quote\Api\Data\AddressInterface $address */
$address = $addressFactory->create();
$address->setCountryId('US');
/** @var GuestShippingMethodManagementInterface $shippingEstimation */
$shippingEstimation = $objectManager->get(GuestShippingMethodManagementInterface::class);
$result = $shippingEstimation->estimateByExtendedAddress($cartId, $address);
$this->assertCount(1, $result);
$rate = reset($result);
$expectedResult = [
'method_code' => 'bestway',
'amount' => 10
];
$this->assertEquals($expectedResult['method_code'], $rate->getMethodCode());
$this->assertEquals($expectedResult['amount'], $rate->getAmount());
}
/**
* Retrieves quote by reserved order id.
*
* @param string $reservedOrderId
* @return Quote
*/
private function getQuote(string $reservedOrderId): Quote
{
/** @var SearchCriteriaBuilder $searchCriteriaBuilder */
$searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
$searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId)
->create();
/** @var CartRepositoryInterface $quoteRepository */
$quoteRepository = $this->objectManager->get(CartRepositoryInterface::class);
$items = $quoteRepository->getList($searchCriteria)->getItems();
return array_pop($items);
}
/**
* @magentoConfigFixture current_store carriers/tablerate/active 1
* @magentoConfigFixture current_store carriers/tablerate/condition_name package_qty
* @magentoDataFixture Magento/SalesRule/_files/cart_rule_free_shipping.php
* @magentoDataFixture Magento/Sales/_files/quote.php
* @magentoDataFixture Magento/OfflineShipping/_files/tablerates.php
* @return void
*/
public function testEstimateByAddressWithCartPriceRuleByItem()
{
$this->executeTestFlow(0, 0);
}
/**
* @magentoConfigFixture current_store carriers/tablerate/active 1
* @magentoConfigFixture current_store carriers/tablerate/condition_name package_qty
* @magentoDataFixture Magento/SalesRule/_files/cart_rule_free_shipping_by_cart.php
* @magentoDataFixture Magento/Sales/_files/quote.php
* @magentoDataFixture Magento/OfflineShipping/_files/tablerates.php
* @return void
*/
public function testEstimateByAddressWithCartPriceRuleByShipment()
{
$this->markTestSkipped('According to MAGETWO-69940 it is an incorrect behavior');
// Rule applied to entire shipment should not overwrite flat or table rate shipping prices
// Only rules applied to specific items should modify those prices (MAGETWO-63844)
$this->executeTestFlow(5, 10);
}
/**
* @magentoConfigFixture current_store carriers/tablerate/active 1
* @magentoConfigFixture current_store carriers/tablerate/condition_name package_qty
* @magentoDataFixture Magento/Sales/_files/quote.php
* @magentoDataFixture Magento/OfflineShipping/_files/tablerates.php
* @return void
*/
public function testEstimateByAddress()
{
$this->executeTestFlow(5, 10);
}
/**
* Provide testing of shipping method estimation based on address
*
* @param int $flatRateAmount
* @param int $tableRateAmount
* @return void
*/
private function executeTestFlow($flatRateAmount, $tableRateAmount)
{
$objectManager = Bootstrap::getObjectManager();
/** @var Quote $quote */
$quote = $objectManager->get(Quote::class);
$quote->load('test01', 'reserved_order_id');
$cartId = $quote->getId();
if (!$cartId) {
$this->fail('quote fixture failed');
}
/** @var QuoteIdMask $quoteIdMask */
$quoteIdMask = Bootstrap::getObjectManager()
->create(QuoteIdMaskFactory::class)
->create();
$quoteIdMask->load($cartId, 'quote_id');
//Use masked cart Id
$cartId = $quoteIdMask->getMaskedId();
$data = [
'data' => [
'country_id' => "US",
'postcode' => null,
'region' => null,
'region_id' => null
]
];
/** @var EstimateAddressInterface $address */
$address = $objectManager->create(EstimateAddressInterface::class, $data);
/** @var GuestShippingMethodManagementInterface $shippingEstimation */
$shippingEstimation = $objectManager->get(GuestShippingMethodManagementInterface::class);
$result = $shippingEstimation->estimateByAddress($cartId, $address);
$this->assertNotEmpty($result);
$expectedResult = [
'tablerate' => [
'method_code' => 'bestway',
'amount' => $tableRateAmount
],
'flatrate' => [
'method_code' => 'flatrate',
'amount' => $flatRateAmount
]
];
foreach ($result as $rate) {
$this->assertEquals($expectedResult[$rate->getCarrierCode()]['amount'], $rate->getAmount());
$this->assertEquals($expectedResult[$rate->getCarrierCode()]['method_code'], $rate->getMethodCode());
}
}
/**
* Test for estimate shipping with tax and changed VAT customer group
*
* @magentoDbIsolation disabled
* @magentoDataFixture Magento/Tax/_files/tax_classes_de.php
* @magentoDataFixture Magento/Sales/_files/quote_with_customer.php
* @magentoDataFixture Magento/Customer/_files/customer_group.php
* @magentoDataFixture Magento/Customer/_files/customer_address.php
* @magentoConfigFixture current_store customer/create_account/tax_calculation_address_type shipping
* @magentoConfigFixture current_store customer/create_account/default_group 1
* @magentoConfigFixture current_store customer/create_account/auto_group_assign 1
* @magentoConfigFixture current_store tax/calculation/price_includes_tax 1
* @magentoConfigFixture current_store tax/calculation/shipping_includes_tax 1
*/
public function testEstimateByAddressWithInclExclTaxAndVATGroup()
{
/** @var GroupInterface $customerGroup */
$customerGroup = $this->findCustomerGroupByCode('custom_group');
$this->mockCustomerVat((int)$customerGroup->getId());
$customerGroup->setTaxClassId($this->getTaxClass('CustomerTaxClass')->getClassId());
$this->groupRepository->save($customerGroup);
/** @var CustomerRepositoryInterface $customerRepository */
$customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class);
$customer = $customerRepository->get('customer@example.com');
$customer->setGroupId($customerGroup->getId());
$customer->setTaxvat('12');
$customerRepository->save($customer);
$this->setConfig($customerGroup->getId(), $this->getTaxClass('ProductTaxClass')->getClassId());
$this->changeCustomerAddress($customer->getDefaultShipping());
$quote = $this->objectManager->get(GetQuoteByReservedOrderId::class)->execute('test01');
$addressRepository = $this->objectManager->get(AddressRepositoryInterface::class);
$address = $addressRepository->getById(1);
$address->setIsDefaultShipping(true);
$customer->setAddresses([$address]);
$customerSession = $this->objectManager->get(Session::class);
$customerSession->loginById($customer->getId());
/** @var ShippingMethodManagementInterface $shippingEstimation */
$shippingEstimation = $this->objectManager->get(ShippingMethodManagementInterface::class);
$result = $shippingEstimation->estimateByAddressId($quote->getId(), (int)$customer->getDefaultShipping());
$this->assertEquals(6.05, $result[0]->getPriceInclTax());
$this->assertEquals(5.0, $result[0]->getPriceExclTax());
}
/**
* Create a test double fot customer vat class
*
* @param int $customerGroupId
*/
private function mockCustomerVat(int $customerGroupId): void
{
$gatewayResponse = new DataObject([
'is_valid' => false,
'request_date' => '',
'request_identifier' => '123123123',
'request_success' => false,
'request_message' => __('Error during VAT Number verification.'),
]);
$customerVat = $this->createPartialMock(
Vat::class,
[
'checkVatNumber',
'isCountryInEU',
'getCustomerGroupIdBasedOnVatNumber',
'getMerchantCountryCode',
'getMerchantVatNumber'
]
);
$customerVat->method('checkVatNumber')->willReturn($gatewayResponse);
$customerVat->method('isCountryInEU')->willReturn(true);
$customerVat->method('getMerchantCountryCode')->willReturn('GB');
$customerVat->method('getMerchantVatNumber')->willReturn('11111');
$customerVat->method('getCustomerGroupIdBasedOnVatNumber')->willReturn($customerGroupId);
$this->objectManager->removeSharedInstance(Vat::class);
$this->objectManager->addSharedInstance($customerVat, Vat::class);
// Remove instances where the customer vat object is cached
$this->objectManager->removeSharedInstance(CollectTotalsObserver::class);
}
/**
* Find the group with a given code.
*
* @param string $code
* @return GroupInterface
*/
protected function findCustomerGroupByCode(string $code): ?GroupInterface
{
/** @var SearchCriteriaBuilder $searchBuilder */
$searchBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
$searchCriteria = $searchBuilder->addFilter('code', $code)
->create();
$groups = $this->groupRepository->getList($searchCriteria)
->getItems();
return array_shift($groups);
}
/**
* Change customer address
*
* @param int $customerAddressId
*
* @return AddressInterface
*/
private function changeCustomerAddress(int $customerAddressId): AddressInterface
{
$addressRepository = $this->objectManager->get(AddressRepositoryInterface::class);
$address = $addressRepository->getById($customerAddressId);
$address->setVatId(12345);
$address->setCountryId('DE');
$address->setRegionId(0);
$address->setPostcode(10178);
return $addressRepository->save($address);
}
/**
* Get tax class.
*
* @param string $name
*
* @return TaxClassInterface
*/
private function getTaxClass(string $name): ?TaxClassInterface
{
/** @var SearchCriteriaBuilder $searchBuilder */
$searchBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
$searchCriteria = $searchBuilder->addFilter(ClassModel::KEY_NAME, $name)
->create();
$searchResults = $this->taxClassRepository->getList($searchCriteria)
->getItems();
return array_shift($searchResults);
}
/**
* Set the configuration.
*
* @param int $customerGroupId
* @param int $productTaxClassId
*
* @return void
*/
private function setConfig(int $customerGroupId, int $productTaxClassId): void
{
$configData = [
[
'path' => Vat::XML_PATH_CUSTOMER_VIV_INVALID_GROUP,
'value' => $customerGroupId,
'scope' => ScopeInterface::SCOPE_STORE,
],
[
'path' => TaxConfig::CONFIG_XML_PATH_SHIPPING_TAX_CLASS,
'value' => $productTaxClassId,
'scope' => ScopeInterface::SCOPE_STORE,
],
];
$config = $this->objectManager->get(MutableScopeConfigInterface::class);
foreach ($configData as $data) {
$config->setValue($data['path'], $data['value'], $data['scope']);
}
}
/**
*
* Test table rate with zero amount is available for the cart when discount coupon cart price rule to all items
* and freeshipping cart price rule is applied when order subtotal is greater than specified amount.
*
* @magentoConfigFixture default_store carriers/tablerate/active 1
* @magentoConfigFixture default_store carriers/flatrate/active 0
* @magentoConfigFixture default_store carriers/freeshipping/active 0
* @magentoConfigFixture default_store carriers/tablerate/condition_name package_value_with_discount
* @magentoDataFixture Magento/Sales/_files/quote_with_multiple_products.php
* @magentoDataFixture Magento/OfflineShipping/_files/tablerates_price.php
* @return void
*/
#[
DataFixture(
AddressConditionFixture::class,
['attribute' => 'base_subtotal', 'operator' => '>=', 'value' => 30],
'c1'
),
DataFixture(
RuleFixture::class,
['stop_rules_processing' => 0, 'simple_free_shipping' => 1, 'conditions' => ['$c1$']],
'r1'
),
DataFixture(
RuleFixture::class,
['stop_rules_processing' => 0, 'coupon_code' => '123', 'discount_amount' => 20],
'r1'
),
]
public function testTableRateWithZeroPriceShownWhenDiscountCouponAndFreeShippingCartRuleApplied()
{
$objectManager = Bootstrap::getObjectManager();
$quote = $this->getQuote('tableRate');
$cartId = $quote->getId();
if (!$cartId) {
$this->fail('quote fixture failed');
}
/** @var QuoteIdMask $quoteIdMask */
$quoteIdMask = Bootstrap::getObjectManager()
->create(QuoteIdMaskFactory::class)
->create();
$quoteIdMask->load($cartId, 'quote_id');
//Use masked cart Id
$cartId = $quoteIdMask->getMaskedId();
$addressFactory = $this->objectManager->get(AddressInterfaceFactory::class);
/** @var \Magento\Quote\Api\Data\AddressInterface $address */
$address = $addressFactory->create();
$address->setCountryId('US');
/** @var CouponManagementInterface $couponManagement */
$couponManagement = Bootstrap::getObjectManager()->get(CouponManagementInterface::class);
$couponManagement->set($quote->getId(), '123');
/** @var GuestShippingMethodManagementInterface $shippingEstimation */
$shippingEstimation = $objectManager->get(GuestShippingMethodManagementInterface::class);
$result = $shippingEstimation->estimateByExtendedAddress($cartId, $address);
$this->assertCount(1, $result);
$rate = reset($result);
$expectedResult = [
'method_code' => 'bestway',
'amount' => 0
];
$this->assertEquals($expectedResult['method_code'], $rate->getMethodCode());
$this->assertEquals($expectedResult['amount'], $rate->getAmount());
}
}