-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PISHPS-310: added OrderEditSubscriber
- Loading branch information
1 parent
ca01cee
commit 1d6865c
Showing
8 changed files
with
306 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Kiener\MolliePayments\Service\Order; | ||
|
||
use DateTime; | ||
use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity; | ||
use Shopware\Core\Checkout\Order\OrderEntity; | ||
|
||
class OrderTimeService | ||
{ | ||
/** | ||
* @var DateTime | ||
*/ | ||
private $now; | ||
|
||
public function __construct(?DateTime $now = null) | ||
{ | ||
$this->now = $now ?? new DateTime(); | ||
} | ||
|
||
/** | ||
* Checks if the age of the last transaction of the order is greater than the specified number of hours. | ||
* | ||
* @param OrderEntity $order The order entity to check. | ||
* @param int $hours The number of hours to compare against. | ||
* | ||
* @return bool Returns true if the order is older than the specified number of hours, false otherwise. | ||
*/ | ||
public function isOrderAgeGreaterThan(OrderEntity $order, int $hours): bool | ||
{ | ||
$transactions = $order->getTransactions(); | ||
|
||
if ($transactions === null || count($transactions) === 0) { | ||
return false; | ||
} | ||
|
||
/** @var ?OrderTransactionEntity $lastTransaction */ | ||
$lastTransaction = $transactions->last(); | ||
|
||
if ($lastTransaction === null) { | ||
return false; | ||
} | ||
|
||
$transitionDate = $lastTransaction->getCreatedAt(); | ||
|
||
if ($transitionDate === null) { | ||
return false; | ||
} | ||
|
||
$interval = $this->now->diff($transitionDate); | ||
$diffInHours = $interval->h + ($interval->days * 24); | ||
|
||
return $diffInHours > $hours; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Kiener\MolliePayments\Subscriber; | ||
|
||
use Closure; | ||
use Kiener\MolliePayments\Handler\Method\BankTransferPayment; | ||
use Kiener\MolliePayments\Service\Mollie\MolliePaymentStatus; | ||
use Kiener\MolliePayments\Service\Order\OrderStatusUpdater; | ||
use Kiener\MolliePayments\Service\Order\OrderTimeService; | ||
use Kiener\MolliePayments\Service\SettingsService; | ||
use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity; | ||
use Shopware\Core\Checkout\Order\OrderEntity; | ||
use Shopware\Core\Checkout\Order\OrderStates; | ||
use Shopware\Storefront\Page\Account\Order\AccountOrderPageLoadedEvent; | ||
use Symfony\Component\EventDispatcher\EventSubscriberInterface; | ||
|
||
class OrderEditSubscriber implements EventSubscriberInterface | ||
{ | ||
/** | ||
* @var OrderStatusUpdater | ||
*/ | ||
private $orderStatusUpdater; | ||
|
||
/** | ||
* @var OrderTimeService | ||
*/ | ||
private $orderTimeService; | ||
|
||
/** | ||
* @var SettingsService | ||
*/ | ||
private $settingsService; | ||
|
||
public function __construct( | ||
OrderStatusUpdater $orderStatusUpdater, | ||
OrderTimeService $orderTimeService, | ||
SettingsService $settingsService | ||
) { | ||
$this->orderStatusUpdater = $orderStatusUpdater; | ||
$this->orderTimeService = $orderTimeService; | ||
$this->settingsService = $settingsService; | ||
} | ||
|
||
public static function getSubscribedEvents(): array | ||
{ | ||
return [ | ||
AccountOrderPageLoadedEvent::class => 'accountOrderDetailPageLoaded' | ||
]; | ||
} | ||
|
||
public function accountOrderDetailPageLoaded(AccountOrderPageLoadedEvent $event): void | ||
{ | ||
$orders = $event->getPage()->getOrders(); | ||
|
||
foreach ($orders as $order) { | ||
if (!$order instanceof OrderEntity || $this->isMolliePayment($order) === false) { | ||
continue; | ||
} | ||
|
||
$transactions = $order->getTransactions(); | ||
|
||
if ($transactions === null || $transactions->count() === 0) { | ||
continue; | ||
} | ||
|
||
$lastTransaction = $transactions->filter(Closure::fromCallable([$this, 'sortTransactionsByDate']))->last(); | ||
|
||
$lastStatus = $lastTransaction->getStateMachineState()->getTechnicalName(); | ||
|
||
// disregard any orders that are not in progress | ||
if ($lastStatus !== OrderStates::STATE_IN_PROGRESS) { | ||
continue; | ||
} | ||
|
||
$settings = $this->settingsService->getSettings(); | ||
$finalizeTransactionTimeInMinutes = $settings->getPaymentFinalizeTransactionTime(); | ||
$finalizeTransactionTimeInHours = (int) ceil($finalizeTransactionTimeInMinutes / 60); | ||
|
||
if ($this->orderUsesSepaPayment($order)) { | ||
$finalizeTransactionTimeInHours = (int) ceil($settings->getPaymentMethodBankTransferDueDateDays() / 24); | ||
} | ||
|
||
if ($this->orderTimeService->isOrderAgeGreaterThan($order, $finalizeTransactionTimeInHours) === false) { | ||
continue; | ||
} | ||
|
||
// orderStatusUpdater needs the order to be set on the transaction | ||
$lastTransaction->setOrder($order); | ||
$context = $event->getContext(); | ||
// this forces the order to be open again | ||
$context->addState(OrderStatusUpdater::ORDER_STATE_FORCE_OPEN); | ||
try { | ||
$this->orderStatusUpdater->updatePaymentStatus($lastTransaction, MolliePaymentStatus::MOLLIE_PAYMENT_CANCELED, $context); | ||
} catch (\Exception $exception) { | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @param OrderEntity $order | ||
* @return bool | ||
* @todo refactor once php8.0 is minimum version. Use Null-safe operator | ||
*/ | ||
private function orderUsesSepaPayment(OrderEntity $order): bool | ||
{ | ||
$transactions = $order->getTransactions(); | ||
|
||
if ($transactions === null || count($transactions) === 0) { | ||
return false; | ||
} | ||
|
||
$lastTransaction = $transactions->last(); | ||
|
||
if ($lastTransaction instanceof OrderTransactionEntity === false) { | ||
return false; | ||
} | ||
|
||
$paymentMethod = $lastTransaction->getPaymentMethod(); | ||
|
||
if ($paymentMethod === null) { | ||
return false; | ||
} | ||
|
||
return $paymentMethod->getHandlerIdentifier() === BankTransferPayment::class; | ||
} | ||
|
||
private function isMolliePayment(OrderEntity $order): bool | ||
{ | ||
$customFields = $order->getCustomFields(); | ||
|
||
return is_array($customFields) && count($customFields) && isset($customFields['mollie_payments']); | ||
} | ||
|
||
/** | ||
* @param OrderTransactionEntity $a | ||
* @param OrderTransactionEntity $b | ||
* @return int | ||
*/ | ||
private function sortTransactionsByDate(OrderTransactionEntity $a, OrderTransactionEntity $b): int | ||
{ | ||
return $a->getCreatedAt() <=> $b->getCreatedAt(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace MolliePayments\Tests\Service\Order; | ||
|
||
|
||
use Kiener\MolliePayments\Service\Order\OrderTimeService; | ||
use PHPUnit\Framework\TestCase; | ||
use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionCollection; | ||
use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity; | ||
use Shopware\Core\Checkout\Order\OrderEntity; | ||
use Shopware\Core\System\StateMachine\Aggregation\StateMachineState\StateMachineStateEntity; | ||
|
||
class OrderTimeServiceTest extends TestCase | ||
{ | ||
/** | ||
* @param \DateTime $now | ||
* @param \DateTime $orderDate | ||
* @param bool $expected | ||
* | ||
* @dataProvider dateComparisonLogicProvider | ||
*/ | ||
public function testDateComparisonLogic(\DateTime $now, \DateTime $orderDate, bool $expected): void | ||
{ | ||
$order = $this->orderMockWithLastTransactionTimestamp($orderDate); | ||
|
||
$result = (new OrderTimeService($now))->isOrderAgeGreaterThan($order, 1); | ||
|
||
$this->assertSame($expected, $result); | ||
} | ||
|
||
private function orderMockWithLastTransactionTimestamp(\DateTime $time): OrderEntity | ||
{ | ||
$entity = $this->createMock(OrderEntity::class); | ||
$transaction = $this->createMock(OrderTransactionEntity::class); | ||
$transactions = new OrderTransactionCollection([$transaction]); | ||
|
||
$entity->method('getTransactions')->willReturn($transactions); | ||
|
||
$transaction->method('getCreatedAt')->willReturn($time); | ||
|
||
return $entity; | ||
} | ||
|
||
public function dateComparisonLogicProvider() | ||
{ | ||
return [ | ||
'order is older than 1 hour' => [ | ||
new \DateTime('2021-01-01 12:00:00'), | ||
new \DateTime('2021-01-01 10:00:00'), | ||
true | ||
], | ||
'order is not older than 1 hour' => [ | ||
new \DateTime('2021-01-01 12:00:00'), | ||
new \DateTime('2021-01-01 11:00:00'), | ||
false | ||
], | ||
'order is not older than 1 hour, but 1 second' => [ | ||
new \DateTime('2021-01-01 12:00:00'), | ||
new \DateTime('2021-01-01 11:59:59'), | ||
false | ||
], | ||
'order is older than a year' => [ | ||
new \DateTime('2021-01-01 12:00:00'), | ||
new \DateTime('2020-01-01 12:00:00'), | ||
true | ||
], | ||
'order is 2 months old' => [ | ||
new \DateTime('2021-01-01 12:00:00'), | ||
new \DateTime('2020-11-01 12:00:00'), | ||
true | ||
], | ||
]; | ||
} | ||
} |