Skip to content

Commit

Permalink
Merge pull request #39 from Setono/do-not-touched-payment-on-edited-o…
Browse files Browse the repository at this point in the history
…rders

Do not touch payments on edited orders
  • Loading branch information
loevgaard authored Aug 21, 2024
2 parents b91f44b + 8004c4f commit 52fac92
Show file tree
Hide file tree
Showing 9 changed files with 275 additions and 29 deletions.
28 changes: 28 additions & 0 deletions src/Checker/OrderPaymentEditionChecker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusOrderEditPlugin\Checker;

use Sylius\Component\Order\Model\OrderInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Webmozart\Assert\Assert;

final class OrderPaymentEditionChecker implements OrderPaymentEditionCheckerInterface
{
public function __construct(private readonly RequestStack $requestStack)
{
}

public function shouldOrderPaymentBeEdited(OrderInterface $order): bool
{
Assert::isInstanceOf($order, \Sylius\Component\Core\Model\OrderInterface::class);

$request = $this->requestStack->getCurrentRequest();
if (null === $request) {
return true;
}

return 'setono_sylius_order_edit_admin_update' !== $request->attributes->get('_route');
}
}
12 changes: 12 additions & 0 deletions src/Checker/OrderPaymentEditionCheckerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusOrderEditPlugin\Checker;

use Sylius\Component\Order\Model\OrderInterface;

interface OrderPaymentEditionCheckerInterface
{
public function shouldOrderPaymentBeEdited(OrderInterface $order): bool;
}
33 changes: 33 additions & 0 deletions src/Checker/PostCheckoutOrderPaymentEditionChecker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusOrderEditPlugin\Checker;

use Sylius\Component\Core\OrderPaymentStates;
use Sylius\Component\Order\Model\OrderInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Webmozart\Assert\Assert;

final class PostCheckoutOrderPaymentEditionChecker implements OrderPaymentEditionCheckerInterface
{
public function __construct(private readonly RequestStack $requestStack)
{
}

public function shouldOrderPaymentBeEdited(OrderInterface $order): bool
{
Assert::isInstanceOf($order, \Sylius\Component\Core\Model\OrderInterface::class);

$request = $this->requestStack->getCurrentRequest();
if (null === $request) {
return true;
}

if ('setono_sylius_order_edit_admin_update' !== $request->attributes->get('_route')) {
return true;
}

return $order->getPaymentState() === OrderPaymentStates::STATE_AWAITING_PAYMENT;
}
}
11 changes: 3 additions & 8 deletions src/OrderProcessing/OrderPaymentProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,21 @@

namespace Setono\SyliusOrderEditPlugin\OrderProcessing;

use Setono\SyliusOrderEditPlugin\Checker\OrderPaymentEditionCheckerInterface;
use Sylius\Component\Order\Model\OrderInterface;
use Sylius\Component\Order\Processor\OrderProcessorInterface;
use Symfony\Component\HttpFoundation\RequestStack;

final class OrderPaymentProcessor implements OrderProcessorInterface
{
public function __construct(
private readonly OrderProcessorInterface $decorated,
private readonly RequestStack $requestStack,
private readonly OrderPaymentEditionCheckerInterface $orderPaymentEditionChecker,
) {
}

public function process(OrderInterface $order): void
{
/** @var mixed $route */
$route = $this->requestStack->getCurrentRequest()?->attributes->get('_route');

// This disables the \Sylius\Component\Core\OrderProcessing\OrderPaymentProcessor if the route is 'setono_sylius_order_edit_admin_update'
// which means we are editing the order in the admin panel
if ('setono_sylius_order_edit_admin_update' === $route) {
if (!$this->orderPaymentEditionChecker->shouldOrderPaymentBeEdited($order)) {
return;
}

Expand Down
33 changes: 29 additions & 4 deletions src/Resources/config/services/order_processing.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,36 @@
<container xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="setono_sylius_order_edit.order_processing.order_payment_processor"
class="Setono\SyliusOrderEditPlugin\OrderProcessing\OrderPaymentProcessor"
decorates="sylius.order_processing.order_payment_processor.checkout" decoration-priority="64">
<service
id="setono_sylius_order_edit.order_processing.order_payment_processor"
class="Setono\SyliusOrderEditPlugin\OrderProcessing\OrderPaymentProcessor"
decorates="sylius.order_processing.order_payment_processor.checkout" decoration-priority="64"
>
<argument type="service" id="setono_sylius_order_edit.order_processing.order_payment_processor.inner"/>
<argument type="service" id="request_stack"/>
<argument type="service" id="setono_sylius_order_edit.checker.order_payment_edition"/>
</service>

<service
id="setono_sylius_order_edit.order_processing.order_payment_processor.after_checkout"
class="Setono\SyliusOrderEditPlugin\OrderProcessing\OrderPaymentProcessor"
decorates="sylius.order_processing.order_payment_processor.after_checkout" decoration-priority="64"
>
<argument type="service" id="setono_sylius_order_edit.order_processing.order_payment_processor.after_checkout.inner"/>
<argument type="service" id="setono_sylius_order_edit.checker.order_payment_edition.post_checkout"/>
</service>

<service
id="setono_sylius_order_edit.checker.order_payment_edition"
class="Setono\SyliusOrderEditPlugin\Checker\OrderPaymentEditionChecker"
>
<argument type="service" id="request_stack" />
</service>

<service
id="setono_sylius_order_edit.checker.order_payment_edition.post_checkout"
class="Setono\SyliusOrderEditPlugin\Checker\PostCheckoutOrderPaymentEditionChecker"
>
<argument type="service" id="request_stack" />
</service>

<service
Expand Down
33 changes: 33 additions & 0 deletions tests/Functional/OrderUpdateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Setono\SyliusOrderEditPlugin\Entity\EditableOrderInterface;
use Setono\SyliusOrderEditPlugin\Model\AdjustmentTypes;
use Setono\SyliusOrderEditPlugin\Tests\Application\Entity\Order;
use SM\Factory\FactoryInterface;
use Sylius\Bundle\ApiBundle\Command\Cart\AddItemToCart;
use Sylius\Bundle\ApiBundle\Command\Cart\PickupCart;
use Sylius\Bundle\ApiBundle\Command\Checkout\ChoosePaymentMethod;
Expand All @@ -17,9 +18,11 @@
use Sylius\Component\Core\Model\Address;
use Sylius\Component\Core\Model\AdjustmentInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Core\Model\ProductVariantInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\Component\Core\Repository\ProductVariantRepositoryInterface;
use Sylius\Component\Payment\PaymentTransitions;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\Messenger\MessageBusInterface;
Expand Down Expand Up @@ -95,8 +98,28 @@ public function testItAllowsToAddDiscountsForTheWholeOrder(): void

self::assertResponseStatusCodeSame(302);

/** @var OrderInterface $order */
$order = $this->getOrderRepository()->findOneBy(['tokenValue' => 'TOKEN']);
self::assertSame($initialOrderTotalWithoutTaxes - 100, $this->getResultTotal($order));
self::assertCount(1, $order->getPayments()->toArray());
}

public function testItDoesNotChangePaymentsForAlreadyPaidOrders(): void
{
$order = $this->placeOrderProgrammatically(quantity: 5, paid: true);
$initialOrderTotalWithoutTaxes = $this->getInitialTotal($order);
$initialPaymentTotal = $order->getPayments()->first()->getAmount();

$this->loginAsAdmin();
$this->addDiscountsToOrder($order->getId(), [1]);

self::assertResponseStatusCodeSame(302);

/** @var OrderInterface $order */
$order = $this->getOrderRepository()->findOneBy(['tokenValue' => 'TOKEN']);
self::assertSame($initialOrderTotalWithoutTaxes - 100, $this->getResultTotal($order));
self::assertCount(1, $order->getPayments()->toArray());
self::assertSame($initialPaymentTotal, $order->getPayments()->first()->getAmount());
}

public function testItAllowsToAddAndRemoveDiscountsForTheWholeOrderMultipleTimes(): void
Expand Down Expand Up @@ -209,6 +232,7 @@ public function testItAllowsToAddStoreNotes(): void
private function placeOrderProgrammatically(
string $variantCode = '000F_office_grey_jeans-variant-0',
int $quantity = 1,
bool $paid = false,
): EditableOrderInterface {
/** @var MessageBusInterface $commandBus */
$commandBus = self::getContainer()->get('sylius.command_bus');
Expand Down Expand Up @@ -252,6 +276,15 @@ private function placeOrderProgrammatically(
$completeOrder->setOrderTokenValue('TOKEN');
$commandBus->dispatch($completeOrder);

if ($paid) {
/** @var FactoryInterface $stateMachineFactory */
$stateMachineFactory = self::getContainer()->get('sm.factory');
/** @var PaymentInterface $payment */
$payment = $order->getPayments()->first();
$stateMachineFactory->get($payment, PaymentTransitions::GRAPH)->apply(PaymentTransitions::TRANSITION_COMPLETE);
self::getEntityManager()->flush();
}

return $this->getOrderRepository()->findOneBy(['tokenValue' => 'TOKEN']);
}

Expand Down
52 changes: 52 additions & 0 deletions tests/Unit/Checker/OrderPaymentEditionCheckerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusOrderEditPlugin\Tests\Unit\Checker;

use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Setono\SyliusOrderEditPlugin\Checker\OrderPaymentEditionChecker;
use Sylius\Component\Core\Model\Order;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

final class OrderPaymentEditionCheckerTest extends TestCase
{
use ProphecyTrait;

public function testItSaysOrderPaymentsShouldNotBeEditedIfTheRouteIsOrderEdit(): void
{
$requestStack = $this->prophesize(RequestStack::class);
$request = new Request([], [], ['_route' => 'setono_sylius_order_edit_admin_update']);
$requestStack->getCurrentRequest()->willReturn($request);

$checker = new OrderPaymentEditionChecker($requestStack->reveal());
$order = new Order();

self::assertFalse($checker->shouldOrderPaymentBeEdited($order));
}

public function testItSaysOrderPaymentCanBeEditedIfItsNotOrderEditRoute(): void
{
$requestStack = $this->prophesize(RequestStack::class);
$request = new Request([], [], ['_route' => 'sylius_admin_order_any_other_route']);
$requestStack->getCurrentRequest()->willReturn($request);

$checker = new OrderPaymentEditionChecker($requestStack->reveal());
$order = new Order();

self::assertTrue($checker->shouldOrderPaymentBeEdited($order));
}

public function testItSaysOrderPaymentCanBeEditedIfThereIsNoCurrentRequest(): void
{
$requestStack = $this->prophesize(RequestStack::class);
$requestStack->getCurrentRequest()->willReturn(null);

$checker = new OrderPaymentEditionChecker($requestStack->reveal());
$order = new Order();

self::assertTrue($checker->shouldOrderPaymentBeEdited($order));
}
}
71 changes: 71 additions & 0 deletions tests/Unit/Checker/PostCheckoutOrderPaymentEditionCheckerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusOrderEditPlugin\Tests\Unit\Checker;

use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Setono\SyliusOrderEditPlugin\Checker\PostCheckoutOrderPaymentEditionChecker;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\OrderPaymentStates;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

final class PostCheckoutOrderPaymentEditionCheckerTest extends TestCase
{
use ProphecyTrait;

public function testItSaysOrderPaymentsShouldNotBeEditedIfTheRouteIsOrderEditAndThePaymentIsAlreadyProcessed(): void
{
$requestStack = $this->prophesize(RequestStack::class);
$request = new Request([], [], ['_route' => 'setono_sylius_order_edit_admin_update']);
$requestStack->getCurrentRequest()->willReturn($request);

$checker = new PostCheckoutOrderPaymentEditionChecker($requestStack->reveal());

$order = $this->prophesize(OrderInterface::class);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_AUTHORIZED);

self::assertFalse($checker->shouldOrderPaymentBeEdited($order->reveal()));
}

public function testItSaysOrderPaymentShouldBeEditedItTheRouteIsOrderEditButOrderIsAwaitingPayment(): void
{
$requestStack = $this->prophesize(RequestStack::class);
$request = new Request([], [], ['_route' => 'setono_sylius_order_edit_admin_update']);
$requestStack->getCurrentRequest()->willReturn($request);

$checker = new PostCheckoutOrderPaymentEditionChecker($requestStack->reveal());

$order = $this->prophesize(OrderInterface::class);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_AWAITING_PAYMENT);

self::assertTrue($checker->shouldOrderPaymentBeEdited($order->reveal()));
}

public function testItSaysOrderPaymentShouldBeEditedIfItsNotOrderEditRoute(): void
{
$requestStack = $this->prophesize(RequestStack::class);
$request = new Request([], [], ['_route' => 'sylius_admin_order_any_other_route']);
$requestStack->getCurrentRequest()->willReturn($request);

$checker = new PostCheckoutOrderPaymentEditionChecker($requestStack->reveal());

$order = $this->prophesize(OrderInterface::class);

self::assertTrue($checker->shouldOrderPaymentBeEdited($order->reveal()));
}

public function testItSaysOrderPaymentShouldBeEditedIfThereIsNoCurrentRequest(): void
{
$requestStack = $this->prophesize(RequestStack::class);
$requestStack->getCurrentRequest()->willReturn(null);

$checker = new PostCheckoutOrderPaymentEditionChecker($requestStack->reveal());

$order = $this->prophesize(OrderInterface::class);

self::assertTrue($checker->shouldOrderPaymentBeEdited($order->reveal()));
}
}
Loading

0 comments on commit 52fac92

Please sign in to comment.