Skip to content

Commit

Permalink
feature #14944 Cover inform customer about order total changes scenar…
Browse files Browse the repository at this point in the history
…ios in API (jakubtobiasz)

This PR was merged into the 1.13 branch.

Discussion
----------

| Q               | A                                                            |
|-----------------|--------------------------------------------------------------|
| Branch?         | 1.13
| Bug fix?        | no
| New feature?    | no
| BC breaks?      | no
| Deprecations?   | no
| Related tickets | contains fixes from #14941 
| License         | MIT


Commits
-------

b0c17b1 Cover inform_customer_about_order_total_changes scenarios in API
c126bf3 Remove dead code
48a4bdf Add a past tense for "I proceed through checkout process" step
2a77873 Fix CI
  • Loading branch information
GSadee committed May 18, 2023
2 parents 44a54e6 + 2a77873 commit 18211a7
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,39 @@ Feature: Inform customer about any order total changes during checkout process
And the store ships everywhere for Free
And the store allows paying Offline

@ui
@ui @api
Scenario: Inform customer about order total change due to product price change
Given I am a logged in customer
And I added product "PHP T-Shirt" to the cart
And I have proceeded selecting "Offline" payment method
And I proceeded through checkout process
And this product price has been changed to "$25.00"
When I confirm my order
Then I should be informed that order total has been changed
And I should not see the thank you page
Then my order should not be placed due to changed order total

@ui
@ui @api
Scenario: Be able to confirm order after information appears
Given I am a logged in customer
And I added product "PHP T-Shirt" to the cart
And I have proceeded selecting "Offline" payment method
And I proceeded through checkout process
And this product price has been changed to "$25.00"
And I have confirmed order
When I confirm my order
Then I should see the thank you page
Then my order should not be placed due to changed order total

@ui
@ui @api
Scenario: Inform customer about order total change due to tax change
Given I am a logged in customer
And I added product "PHP T-Shirt" to the cart
And I have proceeded selecting "Offline" payment method
And I proceeded through checkout process
And the "NA VAT" tax rate has changed to 10%
When I confirm my order
Then I should be informed that order total has been changed
And I should not see the thank you page
Then my order should not be placed due to changed order total

@ui
@ui @api
Scenario: Inform customer about order total change due to shipping method fee change
Given the store has "UPS" shipping method with "$20.00" fee
And I added product "PHP T-Shirt" to the cart
And I have completed addressing step with email "guest@example.com" and "United States" based billing address
And I have proceeded order with "UPS" shipping method and "Offline" payment
And the shipping fee for "UPS" shipping method has been changed to "$30.00"
When I confirm my order
Then I should be informed that order total has been changed
And I should not see the thank you page
Then my order should not be placed due to changed order total
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,15 @@ public function iShouldBeInformedThatThisVariantHasBeenDisabled(ProductVariantIn
Assert::string($lastResponseContent);
Assert::contains($lastResponseContent, sprintf('This product %s has been disabled.', $productVariant->getName()));
}

/**
* @Then my order should not be placed due to changed order total
*/
public function myOrderShouldNotBePlacedDueToChangedOrderTotal(): void
{
$lastResponseContent = $this->client->getLastResponse()->getContent();

Assert::string($lastResponseContent);
Assert::contains($lastResponseContent, 'Order total has changed during checkout process');
}
}
5 changes: 3 additions & 2 deletions src/Sylius/Behat/Context/Api/Shop/CheckoutContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ public function iConfirmMyOrder(): void
{
$response = $this->completeOrder();

if ($response->getStatusCode() === 422) {
if ($response->getStatusCode() > 299) {
return;
}

Expand Down Expand Up @@ -477,7 +477,6 @@ public function iCompleteTheShippingStepWithFirstShippingMethod(): void
* @Given I completed the payment step with :paymentMethod payment method
* @When I choose :paymentMethod payment method
* @When I select :paymentMethod payment method
* @When I proceed selecting :paymentMethod payment method
* @When /^the (?:customer|visitor) proceed with ("[^"]+" payment)$/
* @Given /^the (?:customer|visitor) has proceeded ("[^"]+" payment)$/
* @When I try to change payment method to :paymentMethod payment
Expand All @@ -500,6 +499,7 @@ public function iChoosePaymentMethod(PaymentMethodInterface $paymentMethod): voi

/**
* @When I proceed through checkout process
* @When I proceeded through checkout process
*/
public function iProceedThroughCheckoutProcess(): void
{
Expand All @@ -513,6 +513,7 @@ public function iProceedThroughCheckoutProcess(): void
}

/**
* @When I proceed selecting :paymentMethod payment method
* @When I have proceeded selecting :paymentMethod payment method
*/
public function iHaveProceededSelectingPaymentMethod(PaymentMethodInterface $paymentMethod): void
Expand Down
11 changes: 8 additions & 3 deletions src/Sylius/Behat/Context/Setup/ProductContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -778,10 +778,15 @@ public function theProductVariantIsTrackedByTheInventory(ProductVariantInterface
* @Given /^the (product "[^"]+") changed its price to ("[^"]+")$/
* @Given /^(this product) price has been changed to ("[^"]+")$/
*/
public function theProductChangedItsPriceTo(ProductInterface $product, int $price)
public function theProductChangedItsPriceTo(ProductInterface $product, int $price): void
{
/** @var ProductVariantInterface $productVariant */
$productVariant = $this->defaultVariantResolver->getVariant($product);
/** @var false|ProductInterface $productVariant */
$productVariant = $product->getVariants()->first();
Assert::isInstanceOf($productVariant, ProductVariantInterface::class);

$productVariantId = $productVariant->getId();

$productVariant = $this->productVariantRepository->find($productVariantId);
$channelPricing = $productVariant->getChannelPricingForChannel($this->sharedStorage->get('channel'));
$channelPricing->setPrice($price);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use FriendsOfBehat\PageObjectExtension\Page\UnexpectedPageException;
use Sylius\Behat\NotificationType;
use Sylius\Behat\Page\Shop\Checkout\CompletePageInterface;
use Sylius\Behat\Page\Shop\Order\ThankYouPageInterface;
use Sylius\Behat\Service\NotificationCheckerInterface;
use Sylius\Behat\Service\SharedStorageInterface;
use Sylius\Component\Core\Formatter\StringInflector;
Expand All @@ -35,6 +36,7 @@ public function __construct(
private SharedStorageInterface $sharedStorage,
private CompletePageInterface $completePage,
private NotificationCheckerInterface $notificationChecker,
private ThankYouPageInterface $thankYouPage,
) {
}

Expand Down Expand Up @@ -321,14 +323,15 @@ public function iShouldBeInformedThatThisProductHasBeenDisabled(ProductInterface
}

/**
* @Then I should be informed that order total has been changed
* @Then my order should not be placed due to changed order total
*/
public function iShouldBeInformedThatOrderTotalHasBeenChanged()
public function myOrderShouldNotBePlacedDueToChangedOrderTotal(): void
{
$this->notificationChecker->checkNotification(
'Your order total has been changed, check your order information and confirm it again.',
NotificationType::failure(),
);
Assert::false($this->thankYouPage->isOpen());
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/Sylius/Behat/Context/Ui/Shop/CheckoutContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public function iProceedOrderWithShippingMethodAndPayment(string $shippingMethod
* @Given I have proceeded through checkout process in the :localeCode locale with email :email
* @Given I have proceeded through checkout process
* @When I proceed through checkout process
* @When I proceeded through checkout process
* @When I proceed through checkout process in the :localeCode locale
* @When I proceed through checkout process in the :localeCode locale with email :email
*/
Expand Down
1 change: 1 addition & 0 deletions src/Sylius/Behat/Resources/config/services/contexts/ui.xml
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@
<argument type="service" id="sylius.behat.shared_storage" />
<argument type="service" id="sylius.behat.page.shop.checkout.complete" />
<argument type="service" id="sylius.behat.notification_checker" />
<argument type="service" id="sylius.behat.page.shop.order.thank_you" />
</service>

<service id="sylius.behat.context.ui.shop.checkout.registration_after_checkout" class="Sylius\Behat\Context\Ui\Shop\Checkout\RegistrationAfterCheckoutContext">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ default:
- sylius.behat.context.transform.product_variant
- sylius.behat.context.transform.shared_storage
- sylius.behat.context.transform.shipping_method
- sylius.behat.context.transform.tax_category
- sylius.behat.context.transform.tax_rate
- sylius.behat.context.transform.zone

- sylius.behat.context.setup.cart
Expand All @@ -29,6 +31,7 @@ default:
- sylius.behat.context.setup.product
- sylius.behat.context.setup.shipping
- sylius.behat.context.setup.shop_api_security
- sylius.behat.context.setup.taxation
- sylius.behat.context.setup.zone

- sylius.behat.context.api.shop.cart
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
namespace Sylius\Bundle\ApiBundle\CommandHandler\Checkout;

use SM\Factory\FactoryInterface;
use SM\SMException;
use Sylius\Bundle\ApiBundle\Command\Cart\InformAboutCartRecalculation;
use Sylius\Bundle\ApiBundle\Command\Checkout\CompleteOrder;
use Sylius\Bundle\ApiBundle\CommandHandler\Checkout\Exception\OrderTotalHasChangedException;
use Sylius\Bundle\ApiBundle\Event\OrderCompleted;
use Sylius\Bundle\CoreBundle\Order\Checker\OrderPromotionsIntegrityCheckerInterface;
use Sylius\Component\Core\Model\OrderInterface;
Expand All @@ -38,6 +40,10 @@ public function __construct(
) {
}

/**
* @throws SMException
* @throws OrderTotalHasChangedException
*/
public function __invoke(CompleteOrder $completeOrder): OrderInterface
{
$orderTokenValue = $completeOrder->orderTokenValue;
Expand All @@ -52,6 +58,8 @@ public function __invoke(CompleteOrder $completeOrder): OrderInterface
$cart->setNotes($completeOrder->notes);
}

$oldTotal = $cart->getTotal();

if ($promotion = $this->orderPromotionsIntegrityChecker->check($cart)) {
$this->commandBus->dispatch(
new InformAboutCartRecalculation($promotion->getName()),
Expand All @@ -61,6 +69,10 @@ public function __invoke(CompleteOrder $completeOrder): OrderInterface
return $cart;
}

if ($oldTotal !== $cart->getTotal()) {
throw new OrderTotalHasChangedException();
}

$stateMachine = $this->stateMachineFactory->get($cart, OrderCheckoutTransitions::GRAPH);

Assert::true(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Bundle\ApiBundle\CommandHandler\Checkout\Exception;

use Exception;
use Throwable;

class OrderTotalHasChangedException extends Exception
{
public function __construct (
string $message = 'Order total has changed during checkout process.',
int $code = 0,
Throwable $previous = null,
) {
parent::__construct($message, $code, $previous);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ api_platform:
Sylius\Bundle\UserBundle\Exception\UserNotFoundException: 404
Symfony\Component\Serializer\Exception\UnexpectedValueException: 400
Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException: 400
Sylius\Bundle\ApiBundle\CommandHandler\Checkout\Exception\OrderTotalHasChangedException: 409
collection:
pagination:
client_items_per_page: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use SM\StateMachine\StateMachineInterface;
use Sylius\Bundle\ApiBundle\Command\Cart\InformAboutCartRecalculation;
use Sylius\Bundle\ApiBundle\Command\Checkout\CompleteOrder;
use Sylius\Bundle\ApiBundle\CommandHandler\Checkout\Exception\OrderTotalHasChangedException;
use Sylius\Bundle\ApiBundle\Event\OrderCompleted;
use Sylius\Bundle\CoreBundle\Order\Checker\OrderPromotionsIntegrityCheckerInterface;
use Sylius\Component\Core\Model\CustomerInterface;
Expand All @@ -43,19 +44,20 @@ function let(

function it_handles_order_completion_without_notes(
OrderRepositoryInterface $orderRepository,
StateMachineInterface $stateMachine,
OrderInterface $order,
FactoryInterface $stateMachineFactory,
MessageBusInterface $eventBus,
CustomerInterface $customer,
OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker,
StateMachineInterface $stateMachine,
OrderInterface $order,
CustomerInterface $customer,
): void {
$completeOrder = new CompleteOrder();
$completeOrder->setOrderTokenValue('ORDERTOKEN');

$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($order);

$order->getCustomer()->willReturn($customer);
$order->getTotal()->willReturn(1500);

$order->setNotes(null)->shouldNotBeCalled();

Expand All @@ -80,17 +82,18 @@ function it_handles_order_completion_without_notes(

function it_handles_order_completion_with_notes(
OrderRepositoryInterface $orderRepository,
StateMachineInterface $stateMachine,
OrderInterface $order,
FactoryInterface $stateMachineFactory,
MessageBusInterface $eventBus,
CustomerInterface $customer,
OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker,
StateMachineInterface $stateMachine,
OrderInterface $order,
CustomerInterface $customer,
): void {
$completeOrder = new CompleteOrder('ThankYou');
$completeOrder->setOrderTokenValue('ORDERTOKEN');

$order->getCustomer()->willReturn($customer);
$order->getTotal()->willReturn(1500);

$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($order);

Expand All @@ -117,16 +120,17 @@ function it_handles_order_completion_with_notes(

function it_delays_an_information_about_cart_recalculate(
OrderRepositoryInterface $orderRepository,
OrderInterface $order,
MessageBusInterface $commandBus,
OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker,
OrderInterface $order,
CustomerInterface $customer,
PromotionInterface $promotion,
OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker,
): void {
$completeOrder = new CompleteOrder('ThankYou');
$completeOrder->setOrderTokenValue('ORDERTOKEN');

$order->getCustomer()->willReturn($customer);
$order->getTotal()->willReturn(1000);

$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($order);

Expand Down Expand Up @@ -160,20 +164,43 @@ function it_throws_an_exception_if_order_does_not_exist(
;
}

function it_throws_an_exception_if_order_total_has_changed(
OrderRepositoryInterface $orderRepository,
OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker,
OrderInterface $order,
CustomerInterface $customer,
): void {
$completeOrder = new CompleteOrder();
$completeOrder->setOrderTokenValue('ORDERTOKEN');

$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($order);

$order->getCustomer()->willReturn($customer);
$order->getTotal()->willReturn(1500, 2000);

$orderPromotionsIntegrityChecker->check($order)->willReturn(null);

$this
->shouldThrow(OrderTotalHasChangedException::class)
->during('__invoke', [$completeOrder])
;
}

function it_throws_an_exception_if_order_cannot_be_completed(
OrderRepositoryInterface $orderRepository,
FactoryInterface $stateMachineFactory,
OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker,
StateMachineInterface $stateMachine,
OrderInterface $order,
FactoryInterface $stateMachineFactory,
CustomerInterface $customer,
OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker,
): void {
$completeOrder = new CompleteOrder();
$completeOrder->setOrderTokenValue('ORDERTOKEN');

$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($order);

$order->getCustomer()->willReturn($customer);
$order->getTotal()->willReturn(1500);

$orderPromotionsIntegrityChecker->check($order)->willReturn(null);

Expand Down

0 comments on commit 18211a7

Please sign in to comment.