diff --git a/src/Spryker/Zed/Oms/Business/Lock/TriggerLocker.php b/src/Spryker/Zed/Oms/Business/Lock/TriggerLocker.php index 44d8f2cd..67b80d2b 100644 --- a/src/Spryker/Zed/Oms/Business/Lock/TriggerLocker.php +++ b/src/Spryker/Zed/Oms/Business/Lock/TriggerLocker.php @@ -135,7 +135,7 @@ protected function acquireBatch(array $identifiers, ?string $details = null): bo } try { - $isCommitSuccess = $this->commit(); + $isCommitSuccess = $this->commitIdentical(); } catch (Exception $exception) { throw new LockException( sprintf( diff --git a/src/Spryker/Zed/Oms/Business/OrderStateMachine/OrderStateMachine.php b/src/Spryker/Zed/Oms/Business/OrderStateMachine/OrderStateMachine.php index b83efff9..99b132e7 100644 --- a/src/Spryker/Zed/Oms/Business/OrderStateMachine/OrderStateMachine.php +++ b/src/Spryker/Zed/Oms/Business/OrderStateMachine/OrderStateMachine.php @@ -1119,17 +1119,8 @@ protected function getStateByEvent(array $orderItems) protected function saveOrderItemsTimeout(array $orderItems, array $processes, array $sourceStateBuffer) { $currentTime = new DateTime('now'); - foreach ($orderItems as $orderItem) { - $process = $processes[$orderItem->getProcess()->getName()]; - - $sourceStateId = $sourceStateBuffer[$orderItem->getIdSalesOrderItem()]; - $targetStateId = $orderItem->getState()->getName(); - $targetState = $process->getStateFromAllProcesses($targetStateId); - if ($targetState->hasTimeoutEvent()) { - $this->timeout->dropOldTimeout($process, $sourceStateId, $orderItem); - $this->timeout->setNewTimeout($process, $orderItem, $currentTime); - } - } + $this->timeout->dropOldTimeouts($orderItems, $processes, $sourceStateBuffer); + $this->timeout->setNewTimeouts($orderItems, $currentTime, $processes); } } diff --git a/src/Spryker/Zed/Oms/Business/OrderStateMachine/PersistenceManager.php b/src/Spryker/Zed/Oms/Business/OrderStateMachine/PersistenceManager.php index 28e0e543..2416d46b 100644 --- a/src/Spryker/Zed/Oms/Business/OrderStateMachine/PersistenceManager.php +++ b/src/Spryker/Zed/Oms/Business/OrderStateMachine/PersistenceManager.php @@ -24,6 +24,16 @@ class PersistenceManager implements PersistenceManagerInterface */ protected $omsConfig; + /** + * @var array + */ + protected static $stateCache = []; + + /** + * @var array + */ + protected static $processCache = []; + /** * @param \Spryker\Zed\Oms\OmsConfig $omsConfig */ @@ -39,7 +49,11 @@ public function __construct(OmsConfig $omsConfig) */ protected function getStateEntity($stateName) { - return $this->getTransactionHandler()->handleTransaction(function () use ($stateName): SpyOmsOrderItemState { + if (isset(static::$stateCache[$stateName])) { + return static::$stateCache[$stateName]; + } + + $stateEntity = $this->getTransactionHandler()->handleTransaction(function () use ($stateName): SpyOmsOrderItemState { $stateEntity = SpyOmsOrderItemStateQuery::create() ->filterByName($stateName) ->findOneOrCreate(); @@ -50,6 +64,10 @@ protected function getStateEntity($stateName) return $stateEntity; }); + + static::$stateCache[$stateName] = $stateEntity; + + return $stateEntity; } /** @@ -68,7 +86,11 @@ public function getProcessEntity($processName) )); } - return $this->getTransactionHandler()->handleTransaction(function () use ($processName): SpyOmsOrderProcess { + if (isset(static::$processCache[$processName])) { + return static::$processCache[$processName]; + } + + $processEntity = $this->getTransactionHandler()->handleTransaction(function () use ($processName): SpyOmsOrderProcess { $processEntity = SpyOmsOrderProcessQuery::create() ->filterByName($processName) ->findOneOrCreate(); @@ -79,6 +101,10 @@ public function getProcessEntity($processName) return $processEntity; }); + + static::$processCache[$processName] = $processEntity; + + return $processEntity; } /** diff --git a/src/Spryker/Zed/Oms/Business/OrderStateMachine/Timeout.php b/src/Spryker/Zed/Oms/Business/OrderStateMachine/Timeout.php index c97c966a..556c9353 100644 --- a/src/Spryker/Zed/Oms/Business/OrderStateMachine/Timeout.php +++ b/src/Spryker/Zed/Oms/Business/OrderStateMachine/Timeout.php @@ -23,9 +23,12 @@ use Spryker\Zed\Oms\Business\Util\TimeoutProcessorCollectionInterface; use Spryker\Zed\Oms\OmsConfig; use Spryker\Zed\Oms\Persistence\OmsQueryContainerInterface; +use Spryker\Zed\Propel\Persistence\BatchProcessor\ActiveRecordBatchProcessorTrait; class Timeout implements TimeoutInterface { + use ActiveRecordBatchProcessorTrait; + /** * @var \Spryker\Zed\Oms\Persistence\OmsQueryContainerInterface */ @@ -100,29 +103,10 @@ public function checkTimeouts( */ public function setNewTimeout(ProcessInterface $process, SpySalesOrderItem $orderItem, DateTime $currentTime) { - $targetStateEntity = $orderItem->getState(); - - $targetState = $this->getStateFromProcess($targetStateEntity->getName(), $process); + $newOmsEventTimeoutEntities = $this->getNewOmsEventTimeoutEntities($orderItem, $process, $currentTime); - if ($targetState->hasTimeoutEvent()) { - $events = $targetState->getTimeoutEvents(); - - $handledEvents = []; - foreach ($events as $event) { - if (in_array($event->getName(), $handledEvents)) { - continue; - } - - $handledEvents[] = $event->getName(); - $timeoutDate = $this->calculateTimeoutDateFromEvent($currentTime, $event, $orderItem); - - (new SpyOmsEventTimeout()) - ->setTimeout($timeoutDate) - ->setOrderItem($orderItem) - ->setState($targetStateEntity) - ->setEvent($event->getName()) - ->save(); - } + foreach ($newOmsEventTimeoutEntities as $newOmsEventTimeoutEntity) { + $newOmsEventTimeoutEntity->save(); } } @@ -144,6 +128,64 @@ public function dropOldTimeout(ProcessInterface $process, $stateId, SpySalesOrde } } + /** + * @param array<\Orm\Zed\Sales\Persistence\SpySalesOrderItem> $orderItems + * @param \DateTime $currentTime + * @param array<\Spryker\Zed\Oms\Business\Process\ProcessInterface> $processes + * + * @return void + */ + public function setNewTimeouts(array $orderItems, DateTime $currentTime, array $processes): void + { + $newOmsEventTimeoutEntities = []; + + foreach ($orderItems as $orderItem) { + $process = $processes[$orderItem->getProcess()->getName()]; + + $newOmsEventTimeoutEntities = array_merge( + $newOmsEventTimeoutEntities, + $this->getNewOmsEventTimeoutEntities($orderItem, $process, $currentTime), + ); + } + + if ($newOmsEventTimeoutEntities !== []) { + foreach ($newOmsEventTimeoutEntities as $newOmsEventTimeoutEntity) { + $this->persist($newOmsEventTimeoutEntity); + } + + $this->commitIdentical(); + } + } + + /** + * @param array<\Orm\Zed\Sales\Persistence\SpySalesOrderItem> $orderItems + * @param array<\Spryker\Zed\Oms\Business\Process\ProcessInterface> $processes + * @param array $sourceStateBuffer + * + * @return void + */ + public function dropOldTimeouts(array $orderItems, array $processes, array $sourceStateBuffer): void + { + $orderItemIdsForRemoving = []; + + foreach ($orderItems as $orderItem) { + $process = $processes[$orderItem->getProcess()->getName()]; + + $sourceStateId = $sourceStateBuffer[$orderItem->getIdSalesOrderItem()]; + $targetStateId = $orderItem->getState()->getName(); + $targetState = $this->getStateFromProcess($targetStateId, $process); + $sourceState = $this->getStateFromProcess($sourceStateId, $process); + + if ($targetState->hasTimeoutEvent() && $sourceState->hasTimeoutEvent()) { + $orderItemIdsForRemoving[] = $orderItem->getIdSalesOrderItem(); + } + } + + SpyOmsEventTimeoutQuery::create() + ->filterByFkSalesOrderItem_In($orderItemIdsForRemoving) + ->delete(); + } + /** * @param \DateTime $currentTime * @param \Spryker\Zed\Oms\Business\Process\EventInterface $event @@ -294,4 +336,45 @@ protected function calculateTimeoutFromTimeoutProcessor( return (new DateTime())->setTimestamp($timeoutProcessorTimeoutResponseTransfer->getTimeoutTimestamp()); } + + /** + * @param \Orm\Zed\Sales\Persistence\SpySalesOrderItem $orderItem + * @param \Spryker\Zed\Oms\Business\Process\ProcessInterface $process + * @param \DateTime $currentTime + * + * @return array<\Orm\Zed\Oms\Persistence\SpyOmsEventTimeout> + */ + protected function getNewOmsEventTimeoutEntities( + SpySalesOrderItem $orderItem, + ProcessInterface $process, + DateTime $currentTime + ): array { + $targetStateEntity = $orderItem->getState(); + + $targetState = $this->getStateFromProcess($targetStateEntity->getName(), $process); + + $omsEventTimeoutEntities = []; + + if ($targetState->hasTimeoutEvent()) { + $events = $targetState->getTimeoutEvents(); + + $handledEvents = []; + foreach ($events as $event) { + if (in_array($event->getName(), $handledEvents)) { + continue; + } + + $handledEvents[] = $event->getName(); + $timeoutDate = $this->calculateTimeoutDateFromEvent($currentTime, $event, $orderItem); + + $omsEventTimeoutEntities[] = (new SpyOmsEventTimeout()) + ->setTimeout($timeoutDate) + ->setOrderItem($orderItem) + ->setState($targetStateEntity) + ->setEvent($event->getName()); + } + } + + return $omsEventTimeoutEntities; + } } diff --git a/src/Spryker/Zed/Oms/Business/OrderStateMachine/TimeoutInterface.php b/src/Spryker/Zed/Oms/Business/OrderStateMachine/TimeoutInterface.php index 01e4e1d7..ce35486b 100644 --- a/src/Spryker/Zed/Oms/Business/OrderStateMachine/TimeoutInterface.php +++ b/src/Spryker/Zed/Oms/Business/OrderStateMachine/TimeoutInterface.php @@ -48,4 +48,28 @@ public function setNewTimeout(ProcessInterface $process, SpySalesOrderItem $orde * @return void */ public function dropOldTimeout(ProcessInterface $process, $stateId, SpySalesOrderItem $orderItem); + + /** + * @param array<\Orm\Zed\Sales\Persistence\SpySalesOrderItem> $orderItems + * @param \DateTime $currentTime + * @param array<\Spryker\Zed\Oms\Business\Process\ProcessInterface> $processes + * + * @throws \Exception + * @throws \Propel\Runtime\Exception\PropelException + * + * @return void + */ + public function setNewTimeouts(array $orderItems, DateTime $currentTime, array $processes); + + /** + * @param array<\Orm\Zed\Sales\Persistence\SpySalesOrderItem> $orderItems + * @param array<\Spryker\Zed\Oms\Business\Process\ProcessInterface> $processes + * @param array $sourceStateBuffer + * + * @throws \Exception + * @throws \Propel\Runtime\Exception\PropelException + * + * @return void + */ + public function dropOldTimeouts(array $orderItems, array $processes, array $sourceStateBuffer); } diff --git a/tests/SprykerTest/Zed/Oms/_support/Helper/OmsHelper.php b/tests/SprykerTest/Zed/Oms/_support/Helper/OmsHelper.php index 3002156b..197bd092 100644 --- a/tests/SprykerTest/Zed/Oms/_support/Helper/OmsHelper.php +++ b/tests/SprykerTest/Zed/Oms/_support/Helper/OmsHelper.php @@ -17,8 +17,10 @@ use Orm\Zed\Oms\Persistence\SpyOmsOrderItemStateQuery; use Orm\Zed\Oms\Persistence\SpyOmsProductReservation; use Orm\Zed\Sales\Persistence\SpySalesOrderItemQuery; +use ReflectionProperty; use Spryker\Shared\Oms\OmsConstants; use Spryker\Zed\Oms\Business\OmsFacade; +use Spryker\Zed\Oms\Business\OrderStateMachine\PersistenceManager; use SprykerTest\Shared\Testify\Helper\ConfigHelperTrait; use SprykerTest\Shared\Testify\Helper\DataCleanupHelperTrait; use Symfony\Component\Process\Process; @@ -119,6 +121,8 @@ protected function runCommand(string $command): void */ public function configureTestStateMachine(array $activeProcesses, ?string $xmlFolder = null): void { + $this->clearPersistenceManagerCache(); + if (!$xmlFolder) { $xmlFolder = realpath(__DIR__ . '/../../../../../_data/state-machine/'); } @@ -199,4 +203,17 @@ public function haveOmsEventTimeoutEntity(array $seed): SpyOmsEventTimeout return $omsEventTimeout; } + + /** + * @return void + */ + protected function clearPersistenceManagerCache(): void + { + $stateCacheProperty = new ReflectionProperty(PersistenceManager::class, 'stateCache'); + $stateCacheProperty->setAccessible(true); + $stateCacheProperty->setValue([]); + $processCacheProperty = new ReflectionProperty(PersistenceManager::class, 'processCache'); + $processCacheProperty->setAccessible(true); + $processCacheProperty->setValue([]); + } }