minor #15738 Use the new State Machine abstraction (jakubtobiasz)

This PR was merged into the 1.13 branch.

Discussion
----------

| Q               | A                                                            |
|-----------------|--------------------------------------------------------------|
| Branch?         | 1.13
| Bug fix?        | no
| New feature?    | kinda
| BC breaks?      | no
| Deprecations?   | no
| Related tickets | based on the #15729
| License         | MIT



Commits
-------
  Use the new State Machine abstraction
  Fix invalid workflow event names
  Fix retrieving the checkout state in the validator
  Reverse the sign next to the Symfony Workflow events listeners priorities
  Remove the non-existing config key
  Fix the StateMachine-related tests
  Fix CI
  Remove a reference to Sylius Resource
  Fix handling transitions inside the WinzouStateMachine
  Add testing the state machine adapter in the CI
  Fix the deprecation trigger descriptions
  Remove redundant phpdocs about thrown exception
  Remove redundant sm.configs parameter passed to the winzou adapter
This commit is contained in:
Jan Góralski 2024-01-31 21:05:51 +01:00 committed by GitHub
commit e688d18fec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
81 changed files with 1744 additions and 357 deletions

View file

@ -44,7 +44,7 @@ jobs:
behat-no-js:
needs: get-matrix
runs-on: ubuntu-latest
name: "Non-JS, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MariaDB ${{ matrix.mariadb }}"
name: "Non-JS, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MariaDB ${{ matrix.mariadb }}, State Machine Adapter ${{ matrix.state_machine_adapter }}"
timeout-minutes: 45
strategy:
fail-fast: false
@ -53,6 +53,7 @@ jobs:
env:
APP_ENV: test_cached
DATABASE_URL: "mysql://root:root@127.0.0.1/sylius?charset=utf8mb4&serverVersion=mariadb-${{ matrix.mariadb }}"
TEST_SYLIUS_STATE_MACHINE_ADAPTER: "${{ matrix.state_machine_adapter }}"
steps:
- name: Set variables

View file

@ -19,13 +19,15 @@
{
"php": "8.0",
"symfony": "^5.4",
"mariadb": "10.4.10"
"mariadb": "10.4.10",
"state_machine_adapter": "winzou_state_machine"
},
{
"php": "8.2",
"symfony": "^6.4",
"mariadb": "10.4.10",
"dbal": "^3.0"
"dbal": "^3.0",
"state_machine_adapter": "symfony_workflow"
}
]
},
@ -105,6 +107,7 @@
"php": [ "8.0", "8.1", "8.2" ],
"symfony": [ "^5.4", "^6.4" ],
"mariadb": [ "10.4.10" ],
"state_machine_adapter": [ "winzou_state_machine", "symfony_workflow" ],
"exclude": [
{
"php": "8.0",

View file

@ -1,5 +1,27 @@
# UPGRADE FROM `v1.12.X` TO `v1.13.0`
1. Starting with Sylius `1.13` we provided a possibility to use the Symfony Workflow as your State Machine. To allow a smooth transition
we created a new package called `sylius/state-machine-abstraction`,
which provides a configurable abstraction,
allowing you to define which adapter should be used (Winzou State Machine or Symfony Workflow) per graph.
Starting with Sylius `1.13` we configure all existing Sylius' graphs to use the Symfony Workflow.
At the same time, all other graphs are using Winzou State Machine as a default state machine,
and must be configured manually to use the Symfony Workflow.
Configuration:
```yaml
sylius_state_machine_abstraction:
graphs_to_adapters_mapping:
<graph_name>: <adapter_name>
# e.g.
sylius_order_checkout: symfony_workflow # available adapters: symfony_workflow, winzou_state_machine
# we can also can the default adapter
default_adapter: symfony_workflow # winzou_state_machine is a default value here
```
> **Note!** Starting with Sylius `2.0` the Symfony Workflow will be the default state machine for all graphs.
1. A new parameter has been added to specify the validation groups for given gateway factory.
If you have any custom validation groups for your factory, you need to add them to your `config/packages/_sylius.yaml` file.
Also, if you have your own gateway factory and want to add your validation groups you can add another entry to the `validation_groups` configuration node.
@ -19,7 +41,7 @@
- 'your_custom_validation_group'
```
1. New parameters have been added to specify the validation groups for given shipping method rules and calculators.
1. There have been a naw parameters added to specify the validation groups for given shipping method rule and calculator.
If you have any custom validation groups for your calculator or rules, you need to add them to your `config/packages/_sylius.yaml` file.
Also, if you have your own shipping method rule or calculator and want to add your validation groups you can add another key to the `validation_groups` parameter.
@ -58,7 +80,7 @@
1. Class `Sylius\Bundle\ProductBundle\Form\Type\ProductOptionChoiceType` has been deprecated.
Use `Sylius\Bundle\ProductBundle\Form\Type\ProductOptionAutocompleteType` instead.
1. Using `parentId` query parameter to generate the slug in `Sylius\Bundle\TaxonomyBundle\Controller\TaxonSlugController` has been deprecated.
1. Using `parentId` query parameter to generate slug in `Sylius\Bundle\TaxonomyBundle\Controller\TaxonSlugController` has been deprecated.
Use the `parentCode` query parameter instead.
1. Starting with Sylius 1.13, the `SyliusPriceHistoryPlugin` is included.
@ -75,10 +97,6 @@
1. The name of the second argument of `Sylius\Bundle\AdminBundle\Action\ResendOrderConfirmationEmailAction` is deprecated and will be renamed to `$resendOrderConfirmationEmailDispatcher`.
1. Passing `Sylius\Bundle\AdminBundle\EmailManager\ShipmentEmailManagerInterface` to `Sylius\Bundle\AdminBundle\Action\ResendShipmentConfirmationEmailAction` as a second argument is deprecated, use `Sylius\Bundle\CoreBundle\MessageDispatcher\ResendShipmentConfirmationEmailDispatcherInterface` instead.
1. The name of the second argument of `Sylius\Bundle\AdminBundle\Action\ResendShipmentConfirmationEmailAction` is deprecated and will be renamed to `$resendShipmentConfirmationEmailDispatcher`.
1. Not passing `Sylius\Bundle\CoreBundle\CatalogPromotion\Announcer\CatalogPromotionRemovalAnnouncerInterface` to `Sylius\Bundle\CoreBundle\CatalogPromotion\Processor\CatalogPromotionRemovalProcessor`
as a second argument is deprecated.

View file

@ -1,5 +1,20 @@
parameters:
test_default_state_machine_adapter: 'symfony_workflow'
test_sylius_state_machine_adapter: '%env(string:default:test_default_state_machine_adapter:TEST_SYLIUS_STATE_MACHINE_ADAPTER)%'
sylius_user:
encoder: plaintext
sylius_api:
enabled: true
sylius_state_machine_abstraction:
graphs_to_adapters_mapping:
sylius_catalog_promotion: '%test_sylius_state_machine_adapter%'
sylius_order: '%test_sylius_state_machine_adapter%'
sylius_order_checkout: '%test_sylius_state_machine_adapter%'
sylius_order_payment: '%test_sylius_state_machine_adapter%'
sylius_order_shipping: '%test_sylius_state_machine_adapter%'
sylius_payment: '%test_sylius_state_machine_adapter%'
sylius_product_review: '%test_sylius_state_machine_adapter%'
sylius_shipment: '%test_sylius_state_machine_adapter%'

View file

@ -26,14 +26,12 @@
<service id="sylius_abstraction.state_machine.adapter.symfony_workflow" class="Sylius\Abstraction\StateMachine\SymfonyWorkflowAdapter">
<argument type="service" id="workflow.registry" />
<tag name="sylius.state_machine" key="symfony_workflow" />
</service>
<service id="Sylius\Abstraction\StateMachine\StateMachineInterface $symfonyWorkflow" alias="sylius_abstraction.state_machine.adapter.symfony_workflow" />
<service id="sylius_abstraction.state_machine.adapter.winzou_state_machine" class="Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter">
<argument type="service" id="sm.factory" />
<tag name="sylius.state_machine" key="winzou_state_machine" />
</service>
<service id="Sylius\Abstraction\StateMachine\StateMachineInterface $winzouStateMachine" alias="sylius_abstraction.state_machine.adapter.winzou_state_machine" />

View file

@ -63,6 +63,16 @@ class CompositeStateMachine implements StateMachineInterface
return $this->getStateMachineAdapter($graphName)->getEnabledTransitions($subject, $graphName);
}
public function getTransitionFromState(object $subject, string $graphName, string $fromState): ?string
{
return $this->getStateMachineAdapter($graphName)->getTransitionFromState($subject, $graphName, $fromState);
}
public function getTransitionToState(object $subject, string $graphName, string $toState): ?string
{
return $this->getStateMachineAdapter($graphName)->getTransitionToState($subject, $graphName, $toState);
}
private function getStateMachineAdapter(string $graphName): StateMachineInterface
{
if (isset($this->graphsToAdaptersMapping[$graphName])) {

View file

@ -21,22 +21,17 @@ final class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('sylius_abstraction');
$treeBuilder = new TreeBuilder('sylius_state_machine_abstraction');
/** @var ArrayNodeDefinition $rootNode */
$rootNode = $treeBuilder->getRootNode();
$rootNode
->addDefaultsIfNotSet()
->children()
->arrayNode('state_machine')
->addDefaultsIfNotSet()
->children()
->scalarNode('default_adapter')->defaultValue('winzou_state_machine')->end()
->arrayNode('graphs_to_adapters_mapping')
->useAttributeAsKey('graph_name')
->scalarPrototype()->end()
->end()
->end()
->scalarNode('default_adapter')->defaultValue('winzou_state_machine')->end()
->arrayNode('graphs_to_adapters_mapping')
->useAttributeAsKey('graph_name')
->scalarPrototype()->end()
->end()
->end()
;

View file

@ -27,7 +27,7 @@ final class SyliusStateMachineAbstractionExtension extends Extension
$loader = new XmlFileLoader($container, new FileLocator(dirname(__DIR__, 2) . '/config/'));
$loader->load('services.xml');
$container->setParameter('sylius_abstraction.state_machine.default_adapter', $config['state_machine']['default_adapter']);
$container->setParameter('sylius_abstraction.state_machine.graphs_to_adapters_mapping', $config['state_machine']['graphs_to_adapters_mapping']);
$container->setParameter('sylius_abstraction.state_machine.default_adapter', $config['default_adapter']);
$container->setParameter('sylius_abstraction.state_machine.graphs_to_adapters_mapping', $config['graphs_to_adapters_mapping']);
}
}

View file

@ -13,6 +13,6 @@ declare(strict_types=1);
namespace Sylius\Abstraction\StateMachine\Exception;
class StateMachineExecutionException extends \Exception
class StateMachineExecutionException extends \RuntimeException
{
}

View file

@ -35,4 +35,14 @@ interface StateMachineInterface
* @return array<TransitionInterface>
*/
public function getEnabledTransitions(object $subject, string $graphName): array;
/**
* @throws StateMachineExecutionException
*/
public function getTransitionFromState(object $subject, string $graphName, string $fromState): ?string;
/**
* @throws StateMachineExecutionException
*/
public function getTransitionToState(object $subject, string $graphName, string $toState): ?string;
}

View file

@ -61,4 +61,26 @@ final class SymfonyWorkflowAdapter implements StateMachineInterface
$enabledTransitions,
);
}
public function getTransitionFromState(object $subject, string $graphName, string $fromState): ?string
{
foreach ($this->getEnabledTransitions($subject, $graphName) as $transition) {
if ($transition->getFroms() !== null && in_array($fromState, $transition->getFroms(), true)) {
return $transition->getName();
}
}
return null;
}
public function getTransitionToState(object $subject, string $graphName, string $toState): ?string
{
foreach ($this->getEnabledTransitions($subject, $graphName) as $transition) {
if ($transition->getTos() !== null && in_array($toState, $transition->getTos(), true)) {
return $transition->getName();
}
}
return null;
}
}

View file

@ -42,21 +42,79 @@ final class WinzouStateMachineAdapter implements StateMachineInterface
}
public function getEnabledTransitions(object $subject, string $graphName): array
{
$stateMachine = $this->getStateMachine($subject, $graphName);
return array_filter(
$this->getAllTransitions($stateMachine),
fn (TransitionInterface $transition) => $this->can($subject, $graphName, $transition->getName()),
);
}
/**
* @return array<TransitionInterface>
*/
private function getAllTransitions(\SM\StateMachine\StateMachineInterface $stateMachine): array
{
try {
$transitions = $this->getStateMachine($subject, $graphName)->getPossibleTransitions();
} catch (SMException $exception) {
$transitionsConfig = $this->getConfig($stateMachine)['transitions'];
} catch (\ReflectionException $exception) {
throw new StateMachineExecutionException($exception->getMessage(), $exception->getCode(), $exception);
}
return array_map(
fn (string $transition) => new Transition($transition, null, null),
$transitions,
);
$transitions = [];
foreach ($transitionsConfig as $transitionName => $transitionConfig) {
$froms = $transitionConfig['from'];
$tos = array($transitionConfig['to']);
$transitions[] = new Transition($transitionName, $froms, $tos);
}
return $transitions;
}
/**
* @throws \ReflectionException
*
* @return array{transitions: array<string, array{from: array<string>, to: string}>}
*/
private function getConfig(\SM\StateMachine\StateMachineInterface $stateMachine): array
{
$reflection = new \ReflectionClass($stateMachine);
$configProperty = $reflection->getProperty('config');
$configProperty->setAccessible(true);
return $configProperty->getValue($stateMachine);
}
public function getTransitionFromState(object $subject, string $graphName, string $fromState): ?string
{
foreach ($this->getEnabledTransitions($subject, $graphName) as $transition) {
if ($transition->getFroms() !== null && in_array($fromState, $transition->getFroms(), true)) {
return $transition->getName();
}
}
return null;
}
public function getTransitionToState(object $subject, string $graphName, string $toState): ?string
{
foreach ($this->getEnabledTransitions($subject, $graphName) as $transition) {
if ($transition->getTos() !== null && in_array($toState, $transition->getTos(), true)) {
return $transition->getName();
}
}
return null;
}
private function getStateMachine(object $subject, string $graphName): \SM\StateMachine\StateMachineInterface
{
return $this->winzouStateMachineFactory->get($subject, $graphName);
try {
return $this->winzouStateMachineFactory->get($subject, $graphName);
} catch (SMException $exception) {
throw new StateMachineExecutionException($exception->getMessage(), $exception->getCode(), $exception);
}
}
}

View file

@ -28,18 +28,13 @@ final class ConfigurationTest extends TestCase
$this->assertProcessedConfigurationEquals(
[
[
'state_machine' => [
'default_adapter' => 'symfony_workflow',
],
'default_adapter' => 'symfony_workflow',
],
],
[
'state_machine' => [
'default_adapter' => 'symfony_workflow',
'graphs_to_adapters_mapping' => [],
],
'default_adapter' => 'symfony_workflow',
'graphs_to_adapters_mapping' => [],
],
'state_machine',
);
}
@ -49,24 +44,19 @@ final class ConfigurationTest extends TestCase
$this->assertProcessedConfigurationEquals(
[
[
'state_machine' => [
'graphs_to_adapters_mapping' => [
'order' => 'symfony_workflow',
'payment' => 'winzou_state_machine',
],
],
],
],
[
'state_machine' => [
'default_adapter' => 'winzou_state_machine',
'graphs_to_adapters_mapping' => [
'order' => 'symfony_workflow',
'payment' => 'winzou_state_machine',
],
],
],
'state_machine',
[
'default_adapter' => 'winzou_state_machine',
'graphs_to_adapters_mapping' => [
'order' => 'symfony_workflow',
'payment' => 'winzou_state_machine',
],
],
);
}

View file

@ -78,6 +78,24 @@ final class CompositeStateMachineTest extends TestCase
$this->assertSame(['some_transition'], $stateMachine->getEnabledTransitions(new \stdClass(), 'another_graph'));
}
public function testItReturnsTransitionFromStateUsingMappedAdapter(): void
{
$this->someStateMachineAdapter->expects($this->never())->method('getTransitionFromState');
$this->anotherStateMachineAdapter->expects($this->once())->method('getTransitionFromState')->willReturn('some_transition');
$stateMachine = $this->createTestSubject();
$this->assertSame('some_transition', $stateMachine->getTransitionFromState(new \stdClass(), 'another_graph', 'some_state'));
}
public function testItReturnsTransitionToStateUsingMappedAdapter(): void
{
$this->someStateMachineAdapter->expects($this->never())->method('getTransitionToState');
$this->anotherStateMachineAdapter->expects($this->once())->method('getTransitionToState')->willReturn('some_transition');
$stateMachine = $this->createTestSubject();
$this->assertSame('some_transition', $stateMachine->getTransitionToState(new \stdClass(), 'another_graph', 'some_state'));
}
private function createTestSubject(mixed ...$arguments): StateMachineInterface
{
return new CompositeStateMachine(...array_replace([

View file

@ -16,7 +16,6 @@ namespace Tests\Sylius\Abstraction\StateMachine\Unit;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Sylius\Abstraction\StateMachine\Exception\StateMachineExecutionException;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\SymfonyWorkflowAdapter;
use Symfony\Component\Workflow\Exception\InvalidArgumentException;
use Symfony\Component\Workflow\Registry;
@ -150,13 +149,126 @@ final class SymfonyWorkflowAdapterTest extends TestCase
$this->createTestSubject()->getEnabledTransitions($subject, $graphName);
}
private function createTestSubject(): StateMachineInterface
/**
* @dataProvider itReturnsTransitionsToForGivenTransitionProvider
*/
public function testItReturnsTransitionsToForGivenTransition(?string $expectedToStateTransition, string $requestedTransition): void
{
$subject = new \stdClass();
$graphName = 'graph_name';
$someTransition = $this->createSampleTransition(
name: 'some_transition',
froms: ['from_1', 'from_2'],
tos: ['to_1'],
);
$anotherTransition = $this->createSampleTransition(
name: 'another_transition',
froms: ['from_1', 'from_3'],
tos: ['to_1', 'to_2'],
);
$workflow = $this->createMock(Workflow::class);
$workflow->method('getEnabledTransitions')->with($subject)->willReturn([$someTransition, $anotherTransition]);
$this->symfonyWorkflowRegistry->method('get')->with($subject, $graphName)->willReturn($workflow);
$testSubject = $this->createTestSubject();
$this->assertSame(
$expectedToStateTransition,
$testSubject->getTransitionToState($subject, $graphName, $requestedTransition),
);
}
/**
* @return iterable<string, mixed>
*/
public static function itReturnsTransitionsToForGivenTransitionProvider(): iterable
{
yield 'it returns first transition for to_1' => [
'expectedToStateTransition' => 'some_transition',
'requestedTransition' => 'to_1',
];
yield ' it returns second transition for to_2' => [
'expectedToStateTransition' => 'another_transition',
'requestedTransition' => 'to_2',
];
yield 'it returns null for to_3' => [
'expectedToStateTransition' => null,
'requestedTransition' => 'to_3',
];
}
/**
* @dataProvider itReturnsTransitionsFromForGivenTransitionProvider
*/
public function testItReturnsTransitionsFromForGivenTransition(?string $expectedFromStateTransition, string $requestedTransition): void
{
$subject = new \stdClass();
$graphName = 'graph_name';
$someTransition = $this->createSampleTransition(
name: 'some_transition',
froms: ['from_1', 'from_2'],
tos: ['to_1'],
);
$anotherTransition = $this->createSampleTransition(
name: 'another_transition',
froms: ['from_1', 'from_3'],
tos: ['to_1', 'to_2'],
);
$workflow = $this->createMock(Workflow::class);
$workflow->method('getEnabledTransitions')->with($subject)->willReturn([$someTransition, $anotherTransition]);
$this->symfonyWorkflowRegistry->method('get')->with($subject, $graphName)->willReturn($workflow);
$testSubject = $this->createTestSubject();
$this->assertSame(
$expectedFromStateTransition,
$testSubject->getTransitionFromState($subject, $graphName, $requestedTransition),
);
}
/**
* @return iterable<string, mixed>
*/
public static function itReturnsTransitionsFromForGivenTransitionProvider(): iterable
{
yield 'it returns first transition for from_1' => [
'expectedFromStateTransition' => 'some_transition',
'requestedTransition' => 'from_1',
];
yield 'it returns first transition for from_2' => [
'expectedFromStateTransition' => 'some_transition',
'requestedTransition' => 'from_2',
];
yield 'it returns second transition for from_3' => [
'expectedFromStateTransition' => 'another_transition',
'requestedTransition' => 'from_3',
];
yield 'it returns null for from_4' => [
'expectedFromStateTransition' => null,
'requestedTransition' => 'from_4',
];
}
private function createTestSubject(): SymfonyWorkflowAdapter
{
return new SymfonyWorkflowAdapter($this->symfonyWorkflowRegistry);
}
private function createSampleTransition(): SymfonyWorkflowTransition
private function createSampleTransition(mixed ...$arguments): SymfonyWorkflowTransition
{
return new SymfonyWorkflowTransition('transition', ['from'], ['to']);
return new SymfonyWorkflowTransition(...array_replace(
[
'name' => 'transition',
'froms' => ['from'],
'tos' => ['to'],
],
$arguments,
));
}
}

View file

@ -17,7 +17,7 @@ use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use SM\Factory\FactoryInterface;
use SM\SMException;
use SM\StateMachine\StateMachineInterface as WinzouStateMachineInterface;
use SM\StateMachine\StateMachine as WinzouStateMachine;
use Sylius\Abstraction\StateMachine\Exception\StateMachineExecutionException;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
@ -27,14 +27,26 @@ final class WinzouStateMachineAdapterTest extends TestCase
/** @var FactoryInterface&MockObject */
private FactoryInterface $winzouStateMachineFactory;
/** @var WinzouStateMachineInterface&MockObject */
private WinzouStateMachineInterface $winzouStateMachine;
/** @var WinzouStateMachine&MockObject */
private WinzouStateMachine $winzouStateMachine;
protected function setUp(): void
{
parent::setUp();
$this->winzouStateMachine = $this->createMock(WinzouStateMachineInterface::class);
$this->winzouStateMachine = $this->createMock(WinzouStateMachine::class);
$this->setStateMachineConfig($this->winzouStateMachine, [
'transitions' => [
'transition' => [
'from' => ['from_state'],
'to' => 'to_state',
],
'another_transition' => [
'from' => ['another_from_state'],
'to' => 'another_to_state',
],
],
]);
$this->winzouStateMachineFactory = $this->createMock(FactoryInterface::class);
$this->winzouStateMachineFactory
@ -67,17 +79,20 @@ final class WinzouStateMachineAdapterTest extends TestCase
public function testItReturnsEnabledTransitions(): void
{
$this->winzouStateMachine->method('getPossibleTransitions')->willReturn(['transition']);
$subject = new \stdClass();
$graphName = 'graph_name';
$this->winzouStateMachine->method('can')->willReturnMap([
['transition', true],
['another_transition', false],
]);
$transitions = $this->createTestSubject()->getEnabledTransitions($subject, $graphName);
$this->assertCount(1, $transitions);
$this->assertSame('transition', $transitions[0]->getName());
$this->assertNull($transitions[0]->getFroms());
$this->assertNull($transitions[0]->getTos());
$this->assertSame(['from_state'], $transitions[0]->getFroms());
$this->assertSame(['to_state'], $transitions[0]->getTos());
}
public function testItConvertsWorkflowExceptionsToCustomOnesOnCan(): void
@ -118,8 +133,67 @@ final class WinzouStateMachineAdapterTest extends TestCase
$this->createTestSubject()->getEnabledTransitions($subject, $graphName);
}
public function testItReturnsTransitionsToForGivenTransition(): void
{
$this->setStateMachineConfig($this->winzouStateMachine, [
'transitions' => [
'transition_to_state' => [
'from' => ['from_state'],
'to' => 'to_state',
],
],
]);
$this->winzouStateMachine->method('can')->willReturn(true);
$this->winzouStateMachineFactory = $this->createMock(FactoryInterface::class);
$this->winzouStateMachineFactory->method('get')->willReturn($this->winzouStateMachine);
$stateMachine = $this->createTestSubject();
$this->assertSame(
'transition_to_state',
$stateMachine->getTransitionToState(new \stdClass(), 'graph_name', 'to_state'),
);
}
public function testItReturnsTransitionsFromForGivenTransition(): void
{
$this->setStateMachineConfig($this->winzouStateMachine, [
'transitions' => [
'transition_from_state' => [
'from' => ['from_state'],
'to' => 'to_state',
],
],
]);
$this->winzouStateMachine->method('can')->willReturn(true);
$this->winzouStateMachineFactory = $this->createMock(FactoryInterface::class);
$this->winzouStateMachineFactory->method('get')->willReturn($this->winzouStateMachine);
$stateMachine = $this->createTestSubject();
$this->assertSame(
'transition_from_state',
$stateMachine->getTransitionFromState(new \stdClass(), 'graph_name', 'from_state'),
);
}
private function createTestSubject(): StateMachineInterface
{
return new WinzouStateMachineAdapter($this->winzouStateMachineFactory);
}
/**
* @param array<string, mixed> $config
*/
private function setStateMachineConfig(WinzouStateMachine $stateMachine, array $config): void
{
$reflection = new \ReflectionClass($stateMachine);
$configProperty = $reflection->getProperty('config');
$configProperty->setAccessible(true);
$configProperty->setValue($stateMachine, $config);
}
}

View file

@ -15,7 +15,8 @@ namespace Sylius\Bundle\AdminBundle\Event;
use Knp\Menu\FactoryInterface;
use Knp\Menu\ItemInterface;
use SM\StateMachine\StateMachineInterface;
use SM\StateMachine\StateMachineInterface as WinzouStateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Bundle\UiBundle\Menu\Event\MenuBuilderEvent;
use Sylius\Component\Core\Model\OrderInterface;
@ -25,9 +26,21 @@ class OrderShowMenuBuilderEvent extends MenuBuilderEvent
FactoryInterface $factory,
ItemInterface $menu,
private OrderInterface $order,
private StateMachineInterface $stateMachine,
private WinzouStateMachineInterface|StateMachineInterface $stateMachine,
) {
parent::__construct($factory, $menu);
if ($this->stateMachine instanceof WinzouStateMachineInterface) {
trigger_deprecation(
'sylius/admin-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the fourth argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
WinzouStateMachineInterface::class,
StateMachineInterface::class,
),
);
}
}
public function getOrder(): OrderInterface
@ -35,7 +48,7 @@ class OrderShowMenuBuilderEvent extends MenuBuilderEvent
return $this->order;
}
public function getStateMachine(): StateMachineInterface
public function getStateMachine(): WinzouStateMachineInterface|StateMachineInterface
{
return $this->stateMachine;
}

View file

@ -16,6 +16,8 @@ namespace Sylius\Bundle\AdminBundle\Menu;
use Knp\Menu\FactoryInterface;
use Knp\Menu\ItemInterface;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\AdminBundle\Event\OrderShowMenuBuilderEvent;
use Sylius\Component\Order\OrderTransitions;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@ -28,9 +30,21 @@ final class OrderShowMenuBuilder
public function __construct(
private FactoryInterface $factory,
private EventDispatcherInterface $eventDispatcher,
private StateMachineFactoryInterface $stateMachineFactory,
private StateMachineFactoryInterface|StateMachineInterface $stateMachineFactory,
private CsrfTokenManagerInterface $csrfTokenManager,
) {
)
{
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
trigger_deprecation(
'sylius/admin-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the third argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
StateMachineFactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function createMenu(array $options): ItemInterface
@ -50,25 +64,24 @@ final class OrderShowMenuBuilder
])
->setAttribute('type', 'link')
->setLabel('sylius.ui.history')
->setLabelAttribute('icon', 'history')
;
->setLabelAttribute('icon', 'history');
$stateMachine = $this->stateMachineFactory->get($order, OrderTransitions::GRAPH);
if ($stateMachine->can(OrderTransitions::TRANSITION_CANCEL)) {
$stateMachine = $this->getStateMachine();
if ($stateMachine->can($order, OrderTransitions::GRAPH, OrderTransitions::TRANSITION_CANCEL)) {
$menu
->addChild('cancel', [
'route' => 'sylius_admin_order_cancel',
'routeParameters' => [
'id' => $order->getId(),
'_csrf_token' => $this->csrfTokenManager->getToken((string) $order->getId())->getValue(),
'_csrf_token' => $this->csrfTokenManager->getToken((string)$order->getId())->getValue(),
],
])
->setAttribute('type', 'transition')
->setAttribute('confirmation', true)
->setLabel('sylius.ui.cancel')
->setLabelAttribute('icon', 'ban')
->setLabelAttribute('color', 'yellow')
;
->setLabelAttribute('color', 'yellow');
}
$this->eventDispatcher->dispatch(
@ -78,4 +91,13 @@ final class OrderShowMenuBuilder
return $menu;
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -36,7 +36,7 @@
<service id="sylius.admin.menu_builder.order.show" class="Sylius\Bundle\AdminBundle\Menu\OrderShowMenuBuilder">
<argument type="service" id="knp_menu.factory" />
<argument type="service" id="event_dispatcher" />
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
<argument type="service" id="security.csrf.token_manager" />
<tag name="knp_menu.menu_builder" method="createMenu" alias="sylius.admin.order.show" />
</service>

View file

@ -16,7 +16,8 @@ namespace spec\Sylius\Bundle\AdminBundle\Event;
use Knp\Menu\FactoryInterface;
use Knp\Menu\ItemInterface;
use PhpSpec\ObjectBehavior;
use SM\StateMachine\StateMachineInterface;
use SM\StateMachine\StateMachineInterface as WinzouStateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Bundle\UiBundle\Menu\Event\MenuBuilderEvent;
use Sylius\Component\Core\Model\OrderInterface;
@ -26,7 +27,7 @@ final class OrderShowMenuBuilderEventSpec extends ObjectBehavior
FactoryInterface $factory,
ItemInterface $menu,
OrderInterface $order,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$this->beConstructedWith($factory, $menu, $order, $stateMachine);
}
@ -41,8 +42,18 @@ final class OrderShowMenuBuilderEventSpec extends ObjectBehavior
$this->getOrder()->shouldReturn($order);
}
function it_has_a_state_machine(StateMachineInterface $stateMachine): void
function it_has_a_state_machine(WinzouStateMachineInterface $stateMachine): void
{
$this->getStateMachine()->shouldReturn($stateMachine);
}
function it_allows_to_pass_the_new_state_machine_abstraction(
FactoryInterface $factory,
ItemInterface $menu,
OrderInterface $order,
StateMachineInterface $newStateMachineAbstraction
): void {
$this->beConstructedWith($factory, $menu, $order, $newStateMachineAbstraction);
$this->getStateMachine()->shouldReturn($newStateMachineAbstraction);
}
}

View file

@ -14,14 +14,27 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Applicator;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Order\OrderTransitions;
/** @experimental */
final class OrderStateMachineTransitionApplicator implements OrderStateMachineTransitionApplicatorInterface
{
public function __construct(private StateMachineFactoryInterface $stateMachineFactory)
public function __construct(private StateMachineFactoryInterface|StateMachineInterface $stateMachineFactory)
{
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
trigger_deprecation(
'sylius/api-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the first argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
StateMachineFactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function cancel(OrderInterface $data): OrderInterface
@ -33,7 +46,15 @@ final class OrderStateMachineTransitionApplicator implements OrderStateMachineTr
private function applyTransition(OrderInterface $order, string $transition): void
{
$stateMachine = $this->stateMachineFactory->get($order, OrderTransitions::GRAPH);
$stateMachine->apply($transition);
$this->getStateMachine()->apply($order, OrderTransitions::GRAPH, $transition);
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -14,14 +14,27 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Applicator;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Component\Payment\Model\PaymentInterface;
use Sylius\Component\Payment\PaymentTransitions;
/** @experimental */
final class PaymentStateMachineTransitionApplicator implements PaymentStateMachineTransitionApplicatorInterface
{
public function __construct(private StateMachineFactoryInterface $stateMachineFactory)
public function __construct(private StateMachineFactoryInterface|StateMachineInterface $stateMachineFactory)
{
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
trigger_deprecation(
'sylius/api-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the first argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
StateMachineFactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function complete(PaymentInterface $data): PaymentInterface
@ -33,7 +46,15 @@ final class PaymentStateMachineTransitionApplicator implements PaymentStateMachi
private function applyTransition(PaymentInterface $payment, string $transition): void
{
$stateMachine = $this->stateMachineFactory->get($payment, PaymentTransitions::GRAPH);
$stateMachine->apply($transition);
$this->getStateMachine()->apply($payment, PaymentTransitions::GRAPH, $transition);
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -14,14 +14,27 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Applicator;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Component\Core\ProductReviewTransitions;
use Sylius\Component\Review\Model\ReviewInterface;
/** @experimental */
final class ProductReviewStateMachineTransitionApplicator implements ProductReviewStateMachineTransitionApplicatorInterface
{
public function __construct(private StateMachineFactoryInterface $stateMachineFactory)
public function __construct(private StateMachineFactoryInterface|StateMachineInterface $stateMachineFactory)
{
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
trigger_deprecation(
'sylius/api-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the first argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
StateMachineFactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function accept(ReviewInterface $data): ReviewInterface
@ -40,7 +53,15 @@ final class ProductReviewStateMachineTransitionApplicator implements ProductRevi
private function applyTransition(ReviewInterface $review, string $transition): void
{
$stateMachine = $this->stateMachineFactory->get($review, ProductReviewTransitions::GRAPH);
$stateMachine->apply($transition);
$this->getStateMachine()->apply($review, ProductReviewTransitions::GRAPH, $transition);
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -14,6 +14,8 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\CommandHandler\Checkout;
use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\ApiBundle\Changer\PaymentMethodChangerInterface;
use Sylius\Bundle\ApiBundle\Command\Checkout\ChoosePaymentMethod;
use Sylius\Bundle\ApiBundle\Exception\PaymentMethodCannotBeChangedException;
@ -33,9 +35,20 @@ final class ChoosePaymentMethodHandler implements MessageHandlerInterface
private OrderRepositoryInterface $orderRepository,
private PaymentMethodRepositoryInterface $paymentMethodRepository,
private PaymentRepositoryInterface $paymentRepository,
private FactoryInterface $stateMachineFactory,
private FactoryInterface|StateMachineInterface $stateMachineFactory,
private PaymentMethodChangerInterface $paymentMethodChanger,
) {
if ($this->stateMachineFactory instanceof FactoryInterface) {
trigger_deprecation(
'sylius/api-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the fourth argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
FactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function __invoke(ChoosePaymentMethod $choosePaymentMethod): OrderInterface
@ -64,21 +77,27 @@ final class ChoosePaymentMethodHandler implements MessageHandlerInterface
Assert::notNull($payment, 'Can not find payment with given identifier.');
if ($cart->getState() === OrderInterface::STATE_CART) {
$stateMachine = $this->stateMachineFactory->get($cart, OrderCheckoutTransitions::GRAPH);
$stateMachine = $this->getStateMachine();
Assert::true(
$stateMachine->can(
OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT,
),
$stateMachine->can($cart, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT),
'Order cannot have payment method assigned.',
);
$payment->setMethod($paymentMethod);
$stateMachine->apply(OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT);
$stateMachine->apply($cart, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT);
return $cart;
}
throw new PaymentMethodCannotBeChangedException();
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -14,6 +14,8 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\CommandHandler\Checkout;
use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\ApiBundle\Command\Checkout\ChooseShippingMethod;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\ShippingMethodInterface;
@ -33,8 +35,19 @@ final class ChooseShippingMethodHandler implements MessageHandlerInterface
private ShippingMethodRepositoryInterface $shippingMethodRepository,
private ShipmentRepositoryInterface $shipmentRepository,
private ShippingMethodEligibilityCheckerInterface $eligibilityChecker,
private FactoryInterface $stateMachineFactory,
private FactoryInterface|StateMachineInterface $stateMachineFactory,
) {
if ($this->stateMachineFactory instanceof FactoryInterface) {
trigger_deprecation(
'sylius/api-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the fifth argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
FactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function __invoke(ChooseShippingMethod $chooseShippingMethod): OrderInterface
@ -44,10 +57,9 @@ final class ChooseShippingMethodHandler implements MessageHandlerInterface
Assert::notNull($cart, 'Cart has not been found.');
$stateMachine = $this->stateMachineFactory->get($cart, OrderCheckoutTransitions::GRAPH);
$stateMachine = $this->getStateMachine();
Assert::true(
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING),
$stateMachine->can($cart, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING),
'Order cannot have shipment method assigned.',
);
@ -66,8 +78,17 @@ final class ChooseShippingMethodHandler implements MessageHandlerInterface
);
$shipment->setMethod($shippingMethod);
$stateMachine->apply(OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING);
$stateMachine->apply($cart, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING);
return $cart;
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -14,7 +14,9 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\CommandHandler\Checkout;
use SM\Factory\FactoryInterface;
use SM\SMException;
use Sylius\Abstraction\StateMachine\Exception\StateMachineExecutionException;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\ApiBundle\Command\Cart\InformAboutCartRecalculation;
use Sylius\Bundle\ApiBundle\Command\Checkout\CompleteOrder;
use Sylius\Bundle\ApiBundle\CommandHandler\Checkout\Exception\OrderTotalHasChangedException;
@ -33,15 +35,26 @@ final class CompleteOrderHandler implements MessageHandlerInterface
{
public function __construct(
private OrderRepositoryInterface $orderRepository,
private FactoryInterface $stateMachineFactory,
private FactoryInterface|StateMachineInterface $stateMachineFactory,
private MessageBusInterface $commandBus,
private MessageBusInterface $eventBus,
private OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker,
) {
if ($this->stateMachineFactory instanceof FactoryInterface) {
trigger_deprecation(
'sylius/api-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the second argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
FactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
/**
* @throws SMException
* @throws StateMachineExecutionException
* @throws OrderTotalHasChangedException
*/
public function __invoke(CompleteOrder $completeOrder): OrderInterface
@ -73,17 +86,25 @@ final class CompleteOrderHandler implements MessageHandlerInterface
throw new OrderTotalHasChangedException();
}
$stateMachine = $this->stateMachineFactory->get($cart, OrderCheckoutTransitions::GRAPH);
$stateMachine = $this->getStateMachine();
Assert::true(
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_COMPLETE),
$stateMachine->can($cart, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_COMPLETE),
sprintf('Order with %s token cannot be completed.', $orderTokenValue),
);
$stateMachine->apply(OrderCheckoutTransitions::TRANSITION_COMPLETE);
$stateMachine->apply($cart, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_COMPLETE);
$this->eventBus->dispatch(new OrderCompleted($cart->getTokenValue()), [new DispatchAfterCurrentBusStamp()]);
return $cart;
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -14,6 +14,8 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\CommandHandler\Checkout;
use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\ApiBundle\Command\Checkout\SendShipmentConfirmationEmail;
use Sylius\Bundle\ApiBundle\Command\Checkout\ShipShipment;
use Sylius\Component\Core\Model\ShipmentInterface;
@ -29,9 +31,20 @@ final class ShipShipmentHandler implements MessageHandlerInterface
{
public function __construct(
private ShipmentRepositoryInterface $shipmentRepository,
private FactoryInterface $stateMachineFactory,
private FactoryInterface|StateMachineInterface $stateMachineFactory,
private MessageBusInterface $eventBus,
) {
if ($this->stateMachineFactory instanceof FactoryInterface) {
trigger_deprecation(
'sylius/api-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the second argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
FactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function __invoke(ShipShipment $shipShipment): ShipmentInterface
@ -45,17 +58,25 @@ final class ShipShipmentHandler implements MessageHandlerInterface
$shipment->setTracking($shipShipment->trackingCode);
}
$stateMachine = $this->stateMachineFactory->get($shipment, ShipmentTransitions::GRAPH);
$stateMachine = $this->getStateMachine();
Assert::true(
$stateMachine->can(ShipmentTransitions::TRANSITION_SHIP),
$stateMachine->can($shipment, ShipmentTransitions::GRAPH, ShipmentTransitions::TRANSITION_SHIP),
'This shipment cannot be completed.',
);
$stateMachine->apply(ShipmentTransitions::TRANSITION_SHIP);
$stateMachine->apply($shipment, ShipmentTransitions::GRAPH, ShipmentTransitions::TRANSITION_SHIP);
$this->eventBus->dispatch(new SendShipmentConfirmationEmail($shipShipment->shipmentId), [new DispatchAfterCurrentBusStamp()]);
return $shipment;
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -14,6 +14,8 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Modifier;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\ApiBundle\Mapper\AddressMapperInterface;
use Sylius\Component\Core\Model\AddressInterface;
use Sylius\Component\Core\Model\OrderInterface;
@ -23,9 +25,20 @@ use Webmozart\Assert\Assert;
final class OrderAddressModifier implements OrderAddressModifierInterface
{
public function __construct(
private StateMachineFactoryInterface $stateMachineFactory,
private StateMachineFactoryInterface|StateMachineInterface $stateMachineFactory,
private AddressMapperInterface $addressMapper,
) {
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
trigger_deprecation(
'sylius/api-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the first argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
StateMachineFactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function modify(
@ -33,10 +46,9 @@ final class OrderAddressModifier implements OrderAddressModifierInterface
?AddressInterface $billingAddress,
?AddressInterface $shippingAddress = null,
): OrderInterface {
$stateMachine = $this->stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH);
$stateMachine = $this->getStateMachine();
Assert::true(
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_ADDRESS),
$stateMachine->can($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_ADDRESS),
sprintf('Order with %s token cannot be addressed.', $order->getTokenValue()),
);
@ -59,7 +71,7 @@ final class OrderAddressModifier implements OrderAddressModifierInterface
$this->setBillingAddress($order, $oldBillingAddress, $billingAddress);
$this->setShippingAddress($order, $oldShippingAddress, $shippingAddress);
$stateMachine->apply(OrderCheckoutTransitions::TRANSITION_ADDRESS);
$stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_ADDRESS);
return $order;
}
@ -91,4 +103,13 @@ final class OrderAddressModifier implements OrderAddressModifierInterface
$order->setShippingAddress($shippingAddress);
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -55,6 +55,7 @@ api_platform:
# Sylius exception to status code mapping
SM\SMException: 422
Sylius\Abstraction\StateMachine\Exception\StateMachineExecutionException: 422
Sylius\Bundle\ApiBundle\Exception\CannotRemoveCurrentlyLoggedInUser: 422
Sylius\Bundle\ApiBundle\Exception\ChannelCannotBeRemoved: 422
Sylius\Bundle\ApiBundle\Exception\ChannelPricingChannelCodeMismatchException: 422

View file

@ -143,7 +143,7 @@
</service>
<service id="Sylius\Bundle\ApiBundle\Modifier\OrderAddressModifierInterface" class="Sylius\Bundle\ApiBundle\Modifier\OrderAddressModifier" public="true">
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
<argument type="service" id="Sylius\Bundle\ApiBundle\Mapper\AddressMapperInterface" />
<argument type="service" id="Sylius\Bundle\CoreBundle\Resolver\CustomerResolverInterface" />
</service>

View file

@ -77,7 +77,7 @@
<argument type="service" id="sylius.repository.shipping_method" />
<argument type="service" id="sylius.repository.shipment" />
<argument type="service" id="sylius.shipping_method_eligibility_checker" />
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
<tag name="messenger.message_handler" bus="sylius.command_bus" />
<tag name="messenger.message_handler" bus="sylius_default.bus" />
</service>
@ -86,7 +86,7 @@
<argument type="service" id="sylius.repository.order" />
<argument type="service" id="sylius.repository.payment_method" />
<argument type="service" id="sylius.repository.payment" />
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
<argument type="service" id="Sylius\Bundle\ApiBundle\Changer\PaymentMethodChangerInterface" />
<tag name="messenger.message_handler" bus="sylius.command_bus" />
<tag name="messenger.message_handler" bus="sylius_default.bus" />
@ -94,7 +94,7 @@
<service id="Sylius\Bundle\ApiBundle\CommandHandler\Checkout\CompleteOrderHandler">
<argument type="service" id="sylius.repository.order" />
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
<argument type="service" id="sylius.command_bus" />
<argument type="service" id="sylius.event_bus" />
<argument type="service" id="Sylius\Bundle\CoreBundle\Order\Checker\OrderPromotionsIntegrityCheckerInterface" />
@ -104,7 +104,7 @@
<service id="Sylius\Bundle\ApiBundle\CommandHandler\Checkout\ShipShipmentHandler">
<argument type="service" id="sylius.repository.shipment" />
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
<argument type="service" id="sylius.command_bus" />
<tag name="messenger.message_handler" bus="sylius.command_bus" />
<tag name="messenger.message_handler" bus="sylius_default.bus" />

View file

@ -48,7 +48,7 @@
<service id="Sylius\Bundle\ApiBundle\Validator\Constraints\CheckoutCompletionValidator">
<argument type="service" id="sylius.repository.order" />
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
<tag name="validator.constraint_validator" alias="sylius_api_validator_checkout_completion" />
</service>

View file

@ -14,6 +14,9 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Validator\Constraints;
use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\TransitionInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\OrderCheckoutTransitions;
@ -30,8 +33,19 @@ class CheckoutCompletionValidator extends ConstraintValidator
*/
public function __construct(
private OrderRepositoryInterface $orderRepository,
private FactoryInterface $stateMachineFactory,
private FactoryInterface|StateMachineInterface $stateMachineFactory,
) {
if ($this->stateMachineFactory instanceof FactoryInterface) {
trigger_deprecation(
'sylius/api-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the second argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
FactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function validate(mixed $value, Constraint $constraint): void
@ -41,20 +55,35 @@ class CheckoutCompletionValidator extends ConstraintValidator
/** @var CheckoutCompletion $constraint */
Assert::isInstanceOf($constraint, CheckoutCompletion::class);
/** @var OrderInterface|null $order */
$order = $this->orderRepository->findOneBy(['tokenValue' => $value->getOrderTokenValue()]);
/** @var OrderInterface $order */
Assert::isInstanceOf($order, OrderInterface::class);
$stateMachine = $this->stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH);
$stateMachine = $this->getStateMachine();
if ($stateMachine->can(OrderCheckoutTransitions::TRANSITION_COMPLETE)) {
if ($stateMachine->can($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_COMPLETE)) {
return;
}
$this->context->addViolation($constraint->message, [
'%currentState%' => $stateMachine->getState(),
'%possibleTransitions%' => implode(', ', $stateMachine->getPossibleTransitions()),
'%currentState%' => $order->getCheckoutState(),
'%possibleTransitions%' => implode(
', ',
array_map(
fn (TransitionInterface $transition) => $transition->getName(),
$stateMachine->getEnabledTransitions($order, OrderCheckoutTransitions::GRAPH),
),
),
]);
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -72,8 +72,8 @@
},
"autoload-dev": {
"psr-4": {
"spec\\Sylius\\Bundle\\ApiBundle\\": "spec/",
"Sylius\\Bundle\\ApiBundle\\Application\\": "Tests/Application/src/",
"Sylius\\Bundle\\ApiBundle\\spec\\": "spec/",
"Sylius\\Bundle\\ApiBundle\\Tests\\": "Tests/"
}
},

View file

@ -15,7 +15,8 @@ namespace spec\Sylius\Bundle\ApiBundle\Applicator;
use PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use SM\StateMachine\StateMachine;
use SM\StateMachine\StateMachine as WinzouStateMachine;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Order\OrderTransitions;
@ -29,11 +30,22 @@ final class OrderStateMachineTransitionApplicatorSpec extends ObjectBehavior
function it_cancels_order(
StateMachineFactoryInterface $stateMachineFactory,
OrderInterface $order,
StateMachine $stateMachine,
WinzouStateMachine $stateMachine,
): void {
$stateMachineFactory->get($order, OrderTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->apply(OrderTransitions::TRANSITION_CANCEL)->shouldBeCalled();
$this->cancel($order);
}
function it_uses_the_new_state_machine_abstraction_if_passed(
StateMachineInterface $stateMachine,
OrderInterface $order,
): void {
$this->beConstructedWith($stateMachine);
$stateMachine->apply($order, OrderTransitions::GRAPH, OrderTransitions::TRANSITION_CANCEL)->shouldBeCalled();
$this->cancel($order);
}
}

View file

@ -15,7 +15,8 @@ namespace spec\Sylius\Bundle\ApiBundle\Applicator;
use PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use SM\StateMachine\StateMachine;
use SM\StateMachine\StateMachine as WinzouStateMachine;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Payment\PaymentTransitions;
@ -29,11 +30,22 @@ final class PaymentStateMachineTransitionApplicatorSpec extends ObjectBehavior
function it_completes_payment(
StateMachineFactoryInterface $stateMachineFactory,
PaymentInterface $payment,
StateMachine $stateMachine,
WinzouStateMachine $stateMachine,
): void {
$stateMachineFactory->get($payment, PaymentTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->apply(PaymentTransitions::TRANSITION_COMPLETE)->shouldBeCalled();
$this->complete($payment);
}
function it_uses_the_new_state_machine_abstraction_if_passed(
StateMachineInterface $stateMachine,
PaymentInterface $payment,
): void {
$this->beConstructedWith($stateMachine);
$stateMachine->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_COMPLETE)->shouldBeCalled();
$this->complete($payment);
}
}

View file

@ -15,7 +15,8 @@ namespace spec\Sylius\Bundle\ApiBundle\Applicator;
use PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use SM\StateMachine\StateMachine;
use SM\StateMachine\StateMachine as WinzouStateMachine;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Component\Core\ProductReviewTransitions;
use Sylius\Component\Review\Model\ReviewInterface;
@ -29,7 +30,7 @@ final class ProductReviewStateMachineTransitionApplicatorSpec extends ObjectBeha
public function it_accepts_product_review(
StateMachineFactoryInterface $stateMachineFactory,
ReviewInterface $review,
StateMachine $stateMachine,
WinzouStateMachine $stateMachine,
): void {
$stateMachineFactory->get($review, ProductReviewTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->apply(ProductReviewTransitions::TRANSITION_ACCEPT)->shouldBeCalled();
@ -37,14 +38,36 @@ final class ProductReviewStateMachineTransitionApplicatorSpec extends ObjectBeha
$this->accept($review);
}
public function it_uses_the_new_state_machine_abstraction_if_passed_while_accepting_a_product_review(
StateMachineInterface $stateMachine,
ReviewInterface $review,
): void {
$this->beConstructedWith($stateMachine);
$stateMachine->apply($review, ProductReviewTransitions::GRAPH, ProductReviewTransitions::TRANSITION_ACCEPT)->shouldBeCalled();
$this->accept($review);
}
public function it_rejects_product_review(
StateMachineFactoryInterface $stateMachineFactory,
ReviewInterface $review,
StateMachine $stateMachine,
WinzouStateMachine $stateMachine,
): void {
$stateMachineFactory->get($review, ProductReviewTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->apply(ProductReviewTransitions::TRANSITION_REJECT)->shouldBeCalled();
$this->reject($review);
}
public function it_uses_the_new_state_machine_abstraction_if_passed_while_rejecting_a_product_review(
StateMachineInterface $stateMachine,
ReviewInterface $review,
): void {
$this->beConstructedWith($stateMachine);
$stateMachine->apply($review, ProductReviewTransitions::GRAPH, ProductReviewTransitions::TRANSITION_REJECT)->shouldBeCalled();
$this->reject($review);
}
}

View file

@ -16,7 +16,8 @@ namespace spec\Sylius\Bundle\ApiBundle\CommandHandler\Checkout;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use SM\Factory\FactoryInterface;
use SM\StateMachine\StateMachineInterface;
use SM\StateMachine\StateMachineInterface as WinzouStateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Bundle\ApiBundle\Changer\PaymentMethodChangerInterface;
use Sylius\Bundle\ApiBundle\Command\Checkout\ChoosePaymentMethod;
use Sylius\Bundle\ApiBundle\Exception\PaymentMethodCannotBeChangedException;
@ -55,7 +56,7 @@ final class ChoosePaymentMethodHandlerSpec extends ObjectBehavior
OrderInterface $cart,
PaymentInterface $payment,
PaymentMethodInterface $paymentMethod,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$choosePaymentMethod = new ChoosePaymentMethod('CASH_ON_DELIVERY_METHOD');
$choosePaymentMethod->setOrderTokenValue('ORDERTOKEN');
@ -82,6 +83,41 @@ final class ChoosePaymentMethodHandlerSpec extends ObjectBehavior
$this($choosePaymentMethod)->shouldReturn($cart);
}
function it_uses_the_new_state_machine_abstraction_if_passed(
OrderRepositoryInterface $orderRepository,
PaymentMethodRepositoryInterface $paymentMethodRepository,
PaymentRepositoryInterface $paymentRepository,
StateMachineInterface $stateMachine,
PaymentMethodChangerInterface $paymentMethodChanger,
OrderInterface $cart,
PaymentInterface $payment,
PaymentMethodInterface $paymentMethod,
): void {
$this->beConstructedWith($orderRepository, $paymentMethodRepository, $paymentRepository, $stateMachine, $paymentMethodChanger);
$choosePaymentMethod = new ChoosePaymentMethod('CASH_ON_DELIVERY_METHOD');
$choosePaymentMethod->setOrderTokenValue('ORDERTOKEN');
$choosePaymentMethod->setSubresourceId('123');
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($cart);
$cart->getCheckoutState()->willReturn(OrderCheckoutStates::STATE_SHIPPING_SELECTED);
$stateMachine->can($cart, OrderCheckoutTransitions::GRAPH, 'select_payment')->willReturn(true);
$stateMachine->apply($cart, OrderCheckoutTransitions::GRAPH, 'select_payment')->shouldBeCalled();
$paymentMethodRepository->findOneBy(['code' => 'CASH_ON_DELIVERY_METHOD'])->willReturn($paymentMethod);
$cart->getId()->willReturn('111');
$paymentRepository->findOneByOrderId('123', '111')->willReturn($payment);
$cart->getState()->willReturn(OrderInterface::STATE_CART);
$payment->setMethod($paymentMethod)->shouldBeCalled();
$this($choosePaymentMethod)->shouldReturn($cart);
}
function it_throws_an_exception_if_order_with_given_token_has_not_been_found(
OrderRepositoryInterface $orderRepository,
PaymentInterface $payment,
@ -105,7 +141,7 @@ final class ChoosePaymentMethodHandlerSpec extends ObjectBehavior
PaymentMethodRepositoryInterface $paymentMethodRepository,
FactoryInterface $stateMachineFactory,
OrderInterface $cart,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
PaymentInterface $payment,
): void {
$choosePaymentMethod = new ChoosePaymentMethod('CASH_ON_DELIVERY_METHOD');
@ -134,7 +170,7 @@ final class ChoosePaymentMethodHandlerSpec extends ObjectBehavior
PaymentMethodRepositoryInterface $paymentMethodRepository,
FactoryInterface $stateMachineFactory,
OrderInterface $cart,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
PaymentInterface $payment,
): void {
$choosePaymentMethod = new ChoosePaymentMethod('CASH_ON_DELIVERY_METHOD');
@ -165,7 +201,7 @@ final class ChoosePaymentMethodHandlerSpec extends ObjectBehavior
FactoryInterface $stateMachineFactory,
OrderInterface $cart,
PaymentMethodInterface $paymentMethod,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$choosePaymentMethod = new ChoosePaymentMethod('CASH_ON_DELIVERY_METHOD');
$choosePaymentMethod->setOrderTokenValue('ORDERTOKEN');

View file

@ -17,7 +17,8 @@ use Doctrine\Common\Collections\ArrayCollection;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use SM\Factory\FactoryInterface;
use SM\StateMachine\StateMachineInterface;
use SM\StateMachine\StateMachineInterface as WinzouStateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Bundle\ApiBundle\Command\Checkout\ChooseShippingMethod;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\ShipmentInterface;
@ -55,7 +56,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
OrderInterface $cart,
ShippingMethodInterface $shippingMethod,
ShipmentInterface $shipment,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD');
$chooseShippingMethod->setOrderTokenValue('ORDERTOKEN');
@ -82,6 +83,48 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
$this($chooseShippingMethod)->shouldReturn($cart);
}
function it_uses_the_new_state_machine_abstraction_if_passed(
OrderRepositoryInterface $orderRepository,
ShippingMethodRepositoryInterface $shippingMethodRepository,
ShipmentRepositoryInterface $shipmentRepository,
ShippingMethodEligibilityCheckerInterface $eligibilityChecker,
StateMachineInterface $stateMachine,
OrderInterface $cart,
ShippingMethodInterface $shippingMethod,
ShipmentInterface $shipment,
): void {
$this->beConstructedWith(
$orderRepository,
$shippingMethodRepository,
$shipmentRepository,
$eligibilityChecker,
$stateMachine,
);
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD');
$chooseShippingMethod->setOrderTokenValue('ORDERTOKEN');
$chooseShippingMethod->setSubresourceId('123');
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($cart);
$stateMachine->can($cart, OrderCheckoutTransitions::GRAPH, 'select_shipping')->willReturn(true);
$shippingMethodRepository->findOneBy(['code' => 'DHL_SHIPPING_METHOD'])->willReturn($shippingMethod);
$cart->getShipments()->willReturn(new ArrayCollection([$shipment->getWrappedObject()]));
$cart->getId()->willReturn('111');
$shipmentRepository->findOneByOrderId('123', '111')->willReturn($shipment);
$eligibilityChecker->isEligible($shipment, $shippingMethod)->willReturn(true);
$shipment->setMethod($shippingMethod)->shouldBeCalled();
$stateMachine->apply($cart, OrderCheckoutTransitions::GRAPH, 'select_shipping')->shouldBeCalled();
$this($chooseShippingMethod)->shouldReturn($cart);
}
function it_throws_an_exception_if_shipping_method_is_not_eligible(
OrderRepositoryInterface $orderRepository,
ShippingMethodRepositoryInterface $shippingMethodRepository,
@ -91,7 +134,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
OrderInterface $cart,
ShippingMethodInterface $shippingMethod,
ShipmentInterface $shipment,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD');
$chooseShippingMethod->setOrderTokenValue('ORDERTOKEN');
@ -143,7 +186,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
ShippingMethodRepositoryInterface $shippingMethodRepository,
FactoryInterface $stateMachineFactory,
OrderInterface $cart,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
ShipmentInterface $shipment,
): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD');
@ -169,7 +212,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
ShippingMethodRepositoryInterface $shippingMethodRepository,
FactoryInterface $stateMachineFactory,
OrderInterface $cart,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
ShipmentInterface $shipment,
): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD');
@ -199,7 +242,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
FactoryInterface $stateMachineFactory,
OrderInterface $cart,
ShippingMethodInterface $shippingMethod,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD');
$chooseShippingMethod->setOrderTokenValue('ORDERTOKEN');

View file

@ -15,7 +15,8 @@ namespace spec\Sylius\Bundle\ApiBundle\CommandHandler\Checkout;
use PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface;
use SM\StateMachine\StateMachineInterface;
use SM\StateMachine\StateMachineInterface as WinzouStateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Bundle\ApiBundle\Command\Cart\InformAboutCartRecalculation;
use Sylius\Bundle\ApiBundle\Command\Checkout\CompleteOrder;
use Sylius\Bundle\ApiBundle\CommandHandler\Checkout\Exception\OrderTotalHasChangedException;
@ -47,7 +48,7 @@ final class CompleteOrderHandlerSpec extends ObjectBehavior
FactoryInterface $stateMachineFactory,
MessageBusInterface $eventBus,
OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
OrderInterface $order,
CustomerInterface $customer,
): void {
@ -80,12 +81,51 @@ final class CompleteOrderHandlerSpec extends ObjectBehavior
$this($completeOrder)->shouldReturn($order);
}
function it_uses_the_new_state_machine_abstraction_if_passed(
OrderRepositoryInterface $orderRepository,
StateMachineInterface $stateMachine,
MessageBusInterface $commandBus,
MessageBusInterface $eventBus,
OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker,
OrderInterface $order,
CustomerInterface $customer,
): void {
$this->beConstructedWith($orderRepository, $stateMachine, $commandBus, $eventBus, $orderPromotionsIntegrityChecker);
$completeOrder = new CompleteOrder();
$completeOrder->setOrderTokenValue('ORDERTOKEN');
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($order);
$order->getCustomer()->willReturn($customer);
$order->getTotal()->willReturn(1500);
$order->setNotes(null)->shouldNotBeCalled();
$orderPromotionsIntegrityChecker->check($order)->willReturn(null);
$stateMachine->can($order, OrderCheckoutTransitions::GRAPH, 'complete')->willReturn(true);
$order->getTokenValue()->willReturn('COMPLETED_ORDER_TOKEN');
$stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, 'complete')->shouldBeCalled();
$orderCompleted = new OrderCompleted('COMPLETED_ORDER_TOKEN');
$eventBus
->dispatch($orderCompleted, [new DispatchAfterCurrentBusStamp()])
->willReturn(new Envelope($orderCompleted))
->shouldBeCalled()
;
$this($completeOrder)->shouldReturn($order);
}
function it_handles_order_completion_with_notes(
OrderRepositoryInterface $orderRepository,
FactoryInterface $stateMachineFactory,
MessageBusInterface $eventBus,
OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
OrderInterface $order,
CustomerInterface $customer,
): void {
@ -190,7 +230,7 @@ final class CompleteOrderHandlerSpec extends ObjectBehavior
OrderRepositoryInterface $orderRepository,
FactoryInterface $stateMachineFactory,
OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
OrderInterface $order,
CustomerInterface $customer,
): void {

View file

@ -15,7 +15,8 @@ namespace spec\Sylius\Bundle\ApiBundle\CommandHandler\Checkout;
use PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface;
use SM\StateMachine\StateMachineInterface;
use SM\StateMachine\StateMachineInterface as WinzouStateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Bundle\ApiBundle\Command\Checkout\SendShipmentConfirmationEmail;
use Sylius\Bundle\ApiBundle\Command\Checkout\ShipShipment;
use Sylius\Component\Core\Model\ShipmentInterface;
@ -37,7 +38,7 @@ final class ShipShipmentHandlerSpec extends ObjectBehavior
function it_handles_shipping_without_tracking_number(
ShipmentRepositoryInterface $shipmentRepository,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
ShipmentInterface $shipment,
FactoryInterface $stateMachineFactory,
MessageBusInterface $eventBus,
@ -67,7 +68,7 @@ final class ShipShipmentHandlerSpec extends ObjectBehavior
function it_handles_shipping_with_tracking_number(
ShipmentRepositoryInterface $shipmentRepository,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
ShipmentInterface $shipment,
FactoryInterface $stateMachineFactory,
MessageBusInterface $eventBus,
@ -95,6 +96,35 @@ final class ShipShipmentHandlerSpec extends ObjectBehavior
$this($shipShipment)->shouldReturn($shipment);
}
function it_uses_the_new_state_machine_abstraction_if_passed(
ShipmentRepositoryInterface $shipmentRepository,
StateMachineInterface $stateMachine,
MessageBusInterface $eventBus,
ShipmentInterface $shipment,
): void {
$this->beConstructedWith($shipmentRepository, $stateMachine, $eventBus);
$shipShipment = new ShipShipment('TRACK');
$shipShipment->setShipmentId(123);
$shipmentRepository->find(123)->willReturn($shipment);
$shipment->setTracking('TRACK')->shouldBeCalled();
$stateMachine->can($shipment, ShipmentTransitions::GRAPH, ShipmentTransitions::TRANSITION_SHIP)->willReturn(true);
$stateMachine->apply($shipment, ShipmentTransitions::GRAPH, ShipmentTransitions::TRANSITION_SHIP)->shouldBeCalled();
$sendShipmentConfirmationEmail = new SendShipmentConfirmationEmail(123);
$eventBus
->dispatch($sendShipmentConfirmationEmail, [new DispatchAfterCurrentBusStamp()])
->willReturn(new Envelope($sendShipmentConfirmationEmail))
->shouldBeCalled()
;
$this($shipShipment)->shouldReturn($shipment);
}
function it_throws_an_exception_if_shipment_does_not_exist(
ShipmentRepositoryInterface $shipmentRepository,
): void {
@ -111,7 +141,7 @@ final class ShipShipmentHandlerSpec extends ObjectBehavior
function it_throws_an_exception_if_shipment_cannot_be_shipped(
ShipmentRepositoryInterface $shipmentRepository,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
ShipmentInterface $shipment,
FactoryInterface $stateMachineFactory,
): void {

View file

@ -16,7 +16,8 @@ namespace spec\Sylius\Bundle\ApiBundle\Modifier;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use SM\StateMachine\StateMachineInterface;
use SM\StateMachine\StateMachineInterface as WinzouStateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Bundle\ApiBundle\Mapper\AddressMapperInterface;
use Sylius\Component\Core\Model\AddressInterface;
use Sylius\Component\Core\Model\ChannelInterface;
@ -37,7 +38,7 @@ final class OrderAddressModifierSpec extends ObjectBehavior
AddressInterface $billingAddress,
OrderInterface $order,
ChannelInterface $channel,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$order->getTokenValue()->willReturn('ORDERTOKEN');
$order->getShippingAddress()->willReturn(null);
@ -61,7 +62,7 @@ final class OrderAddressModifierSpec extends ObjectBehavior
AddressInterface $shippingAddress,
OrderInterface $order,
ChannelInterface $channel,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$order->getTokenValue()->willReturn('ORDERTOKEN');
$order->getShippingAddress()->willReturn(null);
@ -86,7 +87,7 @@ final class OrderAddressModifierSpec extends ObjectBehavior
AddressInterface $shippingAddress,
OrderInterface $order,
ChannelInterface $channel,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$order->getTokenValue()->willReturn('ORDERTOKEN');
$order->getShippingAddress()->willReturn(null);
@ -105,6 +106,32 @@ final class OrderAddressModifierSpec extends ObjectBehavior
$this->modify($order, $billingAddress->getWrappedObject(), $shippingAddress->getWrappedObject(), 'r2d2@droid.com');
}
function it_uses_the_new_state_machine_abstraction_if_provided(
StateMachineInterface $stateMachine,
AddressMapperInterface $addressMapper,
AddressInterface $billingAddress,
AddressInterface $shippingAddress,
OrderInterface $order,
ChannelInterface $channel,
): void {
$this->beConstructedWith($stateMachine, $addressMapper);
$order->getTokenValue()->willReturn('ORDERTOKEN');
$order->getShippingAddress()->willReturn(null);
$order->getBillingAddress()->willReturn(null);
$order->getChannel()->willReturn($channel);
$channel->isShippingAddressInCheckoutRequired()->willReturn(false);
$order->setBillingAddress($billingAddress)->shouldBeCalled();
$order->setShippingAddress($shippingAddress)->shouldBeCalled();
$stateMachine->can($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_ADDRESS)->willReturn(true);
$stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_ADDRESS)->shouldBeCalled();
$this->modify($order, $billingAddress->getWrappedObject(), $shippingAddress->getWrappedObject(), 'r2d2@droid.com');
}
function it_updates_order_addresses(
StateMachineFactoryInterface $stateMachineFactory,
AddressMapperInterface $addressMapper,
@ -114,7 +141,7 @@ final class OrderAddressModifierSpec extends ObjectBehavior
AddressInterface $oldShippingAddress,
OrderInterface $order,
ChannelInterface $channel,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$order->getTokenValue()->willReturn('ORDERTOKEN');
$order->getBillingAddress()->willReturn($oldBillingAddress);
@ -141,7 +168,7 @@ final class OrderAddressModifierSpec extends ObjectBehavior
AddressInterface $billingAddress,
AddressInterface $shippingAddress,
OrderInterface $order,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_ADDRESS)->willReturn(false);

View file

@ -16,7 +16,8 @@ namespace spec\Sylius\Bundle\ApiBundle\Validator\Constraints;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use SM\Factory\FactoryInterface;
use SM\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\Transition;
use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface;
use Sylius\Bundle\ApiBundle\Validator\Constraints\CheckoutCompletion;
use Sylius\Component\Core\Model\OrderInterface;
@ -79,7 +80,7 @@ final class CheckoutCompletionValidatorSpec extends ObjectBehavior
ExecutionContextInterface $executionContext,
OrderInterface $order,
OrderTokenValueAwareInterface $orderTokenValueAware,
StateMachineInterface $stateMachine,
WinzouStateMachineStub $stateMachine,
): void {
$this->initialize($executionContext);
@ -105,8 +106,27 @@ final class CheckoutCompletionValidatorSpec extends ObjectBehavior
ExecutionContextInterface $executionContext,
OrderInterface $order,
OrderTokenValueAwareInterface $orderTokenValueAware,
StateMachineInterface $stateMachine,
WinzouStateMachineStub $stateMachine,
): void {
$stateMachine->can('some_possible_transition')->willReturn(true);
$stateMachine->can('another_possible_transition')->willReturn(true);
$stateMachine->can('yet_another_possible_transition')->willReturn(false);
$this->setPropertyValue($stateMachine, 'config', [
'transitions' => [
'some_possible_transition' => [
'from' => [],
'to' => '',
],
'another_possible_transition' => [
'from' => [],
'to' => '',
],
'yet_another_possible_transition' => [
'from' => [],
'to' => '',
],
]
]);
$this->initialize($executionContext);
$orderTokenValueAware->getOrderTokenValue()->willReturn('xxx');
@ -114,8 +134,8 @@ final class CheckoutCompletionValidatorSpec extends ObjectBehavior
$stateMachineFactory->get($order, 'sylius_order_checkout')->willReturn($stateMachine);
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_COMPLETE)->willReturn(false);
$stateMachine->getState()->willReturn('some_state_that_does_not_allow_to_complete_order');
$stateMachine->getPossibleTransitions()->willReturn(['some_possible_transition', 'another_possible_transition']);
$order->getCheckoutState()->willReturn('some_state_that_does_not_allow_to_complete_order');
$executionContext
->addViolation(
@ -130,4 +150,48 @@ final class CheckoutCompletionValidatorSpec extends ObjectBehavior
$this->validate($orderTokenValueAware, new CheckoutCompletion());
}
function it_uses_the_new_state_machine_abstraction_if_passed(
OrderRepositoryInterface $orderRepository,
StateMachineInterface $stateMachine,
ExecutionContextInterface $executionContext,
OrderInterface $order,
OrderTokenValueAwareInterface $orderTokenValueAware,
): void {
$this->beConstructedWith($orderRepository, $stateMachine);
$this->initialize($executionContext);
$orderTokenValueAware->getOrderTokenValue()->willReturn('xxx');
$orderRepository->findOneBy(['tokenValue' => 'xxx'])->willReturn($order);
$stateMachine->can($order, 'sylius_order_checkout', OrderCheckoutTransitions::TRANSITION_COMPLETE)->willReturn(false);
$stateMachine
->getEnabledTransitions($order, 'sylius_order_checkout')
->willReturn([new Transition('some_possible_transition', null, null), new Transition('another_possible_transition', null, null)])
;
$order->getCheckoutState()->willReturn('some_state_that_does_not_allow_to_complete_order');
$executionContext
->addViolation(
'sylius.order.invalid_state_transition',
[
'%currentState%' => 'some_state_that_does_not_allow_to_complete_order',
'%possibleTransitions%' => 'some_possible_transition, another_possible_transition',
],
)
->shouldBeCalled()
;
$this->validate($orderTokenValueAware, new CheckoutCompletion());
}
private function setPropertyValue(object $object, string $property, mixed $value): void
{
$reflection = new \ReflectionClass(WinzouStateMachineStub::class);
$reflectionProperty = $reflection->getProperty($property);
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($object, $value);
}
}

View file

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace spec\Sylius\Bundle\ApiBundle\Validator\Constraints;
use SM\StateMachine\StateMachine;
class WinzouStateMachineStub extends StateMachine
{
public $config = [];
}

View file

@ -14,6 +14,8 @@ declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\CatalogPromotion\Processor;
use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\CoreBundle\CatalogPromotion\Checker\CatalogPromotionEligibilityCheckerInterface;
use Sylius\Component\Core\Model\CatalogPromotionInterface;
use Sylius\Component\Promotion\Model\CatalogPromotionTransitions;
@ -22,30 +24,50 @@ final class CatalogPromotionStateProcessor implements CatalogPromotionStateProce
{
public function __construct(
private CatalogPromotionEligibilityCheckerInterface $catalogPromotionEligibilityChecker,
private FactoryInterface $stateMachineFactory,
private FactoryInterface|StateMachineInterface $stateMachineFactory,
) {
if ($this->stateMachineFactory instanceof FactoryInterface) {
trigger_deprecation(
'sylius/core-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the second argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
FactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function process(CatalogPromotionInterface $catalogPromotion): void
{
$stateMachine = $this->stateMachineFactory->get($catalogPromotion, CatalogPromotionTransitions::GRAPH);
$stateMachine = $this->getStateMachine();
if ($stateMachine->can(CatalogPromotionTransitions::TRANSITION_PROCESS)) {
$stateMachine->apply(CatalogPromotionTransitions::TRANSITION_PROCESS);
if ($stateMachine->can($catalogPromotion, CatalogPromotionTransitions::GRAPH, CatalogPromotionTransitions::TRANSITION_PROCESS)) {
$stateMachine->apply($catalogPromotion, CatalogPromotionTransitions::GRAPH, CatalogPromotionTransitions::TRANSITION_PROCESS);
return;
}
if (!$this->catalogPromotionEligibilityChecker->isCatalogPromotionEligible($catalogPromotion)) {
if ($stateMachine->can(CatalogPromotionTransitions::TRANSITION_DEACTIVATE)) {
$stateMachine->apply(CatalogPromotionTransitions::TRANSITION_DEACTIVATE);
if ($stateMachine->can($catalogPromotion, CatalogPromotionTransitions::GRAPH, CatalogPromotionTransitions::TRANSITION_DEACTIVATE)) {
$stateMachine->apply($catalogPromotion, CatalogPromotionTransitions::GRAPH, CatalogPromotionTransitions::TRANSITION_DEACTIVATE);
}
return;
}
if ($stateMachine->can(CatalogPromotionTransitions::TRANSITION_ACTIVATE)) {
$stateMachine->apply(CatalogPromotionTransitions::TRANSITION_ACTIVATE);
if ($stateMachine->can($catalogPromotion, CatalogPromotionTransitions::GRAPH, CatalogPromotionTransitions::TRANSITION_ACTIVATE)) {
$stateMachine->apply($catalogPromotion, CatalogPromotionTransitions::GRAPH, CatalogPromotionTransitions::TRANSITION_ACTIVATE);
}
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -14,6 +14,8 @@ declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\Checkout;
use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Order\Context\CartContextInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@ -29,8 +31,19 @@ final class CheckoutResolver implements EventSubscriberInterface
private CartContextInterface $cartContext,
private CheckoutStateUrlGeneratorInterface $urlGenerator,
private RequestMatcherInterface $requestMatcher,
private FactoryInterface $stateMachineFactory,
private FactoryInterface|StateMachineInterface $stateMachineFactory,
) {
if ($this->stateMachineFactory instanceof FactoryInterface) {
trigger_deprecation(
'sylius/core-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the fourth argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
FactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public static function getSubscribedEvents(): array
@ -73,9 +86,7 @@ final class CheckoutResolver implements EventSubscriberInterface
return;
}
$stateMachine = $this->stateMachineFactory->get($order, $graph);
if ($stateMachine->can($transition)) {
if ($this->getStateMachine()->can($order, $graph, $transition)) {
return;
}
@ -91,4 +102,13 @@ final class CheckoutResolver implements EventSubscriberInterface
{
return $request->attributes->get('_sylius', [])['state_machine']['transition'] ?? null;
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -17,6 +17,8 @@ use Doctrine\Persistence\ObjectManager;
use Faker\Factory;
use Faker\Generator;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\CoreBundle\Fixture\OptionsResolver\LazyOption;
use Sylius\Component\Addressing\Model\CountryInterface;
use Sylius\Component\Core\Checker\OrderPaymentMethodSelectionRequirementCheckerInterface;
@ -61,13 +63,25 @@ class OrderExampleFactory extends AbstractExampleFactory implements ExampleFacto
protected PaymentMethodRepositoryInterface $paymentMethodRepository,
protected ShippingMethodRepositoryInterface $shippingMethodRepository,
protected FactoryInterface $addressFactory,
protected StateMachineFactoryInterface $stateMachineFactory,
protected StateMachineFactoryInterface|StateMachineInterface $stateMachineFactory,
protected OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
protected OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
) {
$this->optionsResolver = new OptionsResolver();
$this->faker = Factory::create();
$this->configureOptions($this->optionsResolver);
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
trigger_deprecation(
'sylius/core-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the twelfth argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
StateMachineFactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function create(array $options = []): OrderInterface
@ -251,7 +265,7 @@ class OrderExampleFactory extends AbstractExampleFactory implements ExampleFacto
protected function applyCheckoutStateTransition(OrderInterface $order, string $transition): void
{
$this->stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->apply($transition);
$this->getStateMachine()->apply($order, OrderCheckoutTransitions::GRAPH, $transition);
}
protected function generateInvalidSkipMessage(string $type, string $channelCode): string
@ -281,21 +295,32 @@ class OrderExampleFactory extends AbstractExampleFactory implements ExampleFacto
protected function completePayments(OrderInterface $order): void
{
$stateMachine = $this->getStateMachine();
foreach ($order->getPayments() as $payment) {
$stateMachine = $this->stateMachineFactory->get($payment, PaymentTransitions::GRAPH);
if ($stateMachine->can(PaymentTransitions::TRANSITION_COMPLETE)) {
$stateMachine->apply(PaymentTransitions::TRANSITION_COMPLETE);
if ($stateMachine->can($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_COMPLETE)) {
$stateMachine->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_COMPLETE);
}
}
}
protected function completeShipments(OrderInterface $order): void
{
$stateMachine = $this->getStateMachine();
foreach ($order->getShipments() as $shipment) {
$stateMachine = $this->stateMachineFactory->get($shipment, ShipmentTransitions::GRAPH);
if ($stateMachine->can(ShipmentTransitions::TRANSITION_SHIP)) {
$stateMachine->apply(ShipmentTransitions::TRANSITION_SHIP);
if ($stateMachine->can($shipment, ShipmentTransitions::GRAPH, ShipmentTransitions::TRANSITION_SHIP)) {
$stateMachine->apply($shipment, ShipmentTransitions::GRAPH, ShipmentTransitions::TRANSITION_SHIP);
}
}
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -16,10 +16,11 @@ namespace Sylius\Bundle\CoreBundle\Fixture\Factory;
use Faker\Factory;
use Faker\Generator;
use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\CoreBundle\Fixture\OptionsResolver\LazyOption;
use Sylius\Component\Core\Repository\CustomerRepositoryInterface;
use Sylius\Component\Core\Repository\ProductRepositoryInterface;
use Sylius\Component\Resource\StateMachine\StateMachineInterface;
use Sylius\Component\Review\Factory\ReviewFactoryInterface;
use Sylius\Component\Review\Model\ReviewInterface;
use Symfony\Component\OptionsResolver\Options;
@ -35,12 +36,24 @@ class ProductReviewExampleFactory extends AbstractExampleFactory implements Exam
private ReviewFactoryInterface $productReviewFactory,
private ProductRepositoryInterface $productRepository,
private CustomerRepositoryInterface $customerRepository,
private FactoryInterface $stateMachineFactory,
private FactoryInterface|StateMachineInterface $stateMachineFactory,
) {
$this->faker = Factory::create();
$this->optionsResolver = new OptionsResolver();
$this->configureOptions($this->optionsResolver);
if ($this->stateMachineFactory instanceof FactoryInterface) {
trigger_deprecation(
'sylius/core-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the fourth argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
FactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function create(array $options = []): ReviewInterface
@ -95,12 +108,21 @@ class ProductReviewExampleFactory extends AbstractExampleFactory implements Exam
private function applyReviewTransition(ReviewInterface $productReview, string $targetState): void
{
/** @var StateMachineInterface $stateMachine */
$stateMachine = $this->stateMachineFactory->get($productReview, 'sylius_product_review');
$transition = $stateMachine->getTransitionToState($targetState);
$stateMachine = $this->getStateMachine();
$transition = $stateMachine->getTransitionToState($productReview, 'sylius_product_review', $targetState);
if (null !== $transition) {
$stateMachine->apply($transition);
$stateMachine->apply($productReview, 'sylius_product_review', $transition);
}
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -17,6 +17,7 @@ use Doctrine\Persistence\ObjectManager;
use Faker\Factory;
use Faker\Generator;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Bundle\CoreBundle\Fixture\Factory\OrderExampleFactory;
use Sylius\Bundle\FixturesBundle\Fixture\AbstractFixture;
use Sylius\Component\Core\Checker\OrderPaymentMethodSelectionRequirementCheckerInterface;
@ -52,7 +53,7 @@ class OrderFixture extends AbstractFixture
PaymentMethodRepositoryInterface $paymentMethodRepository,
ShippingMethodRepositoryInterface $shippingMethodRepository,
FactoryInterface $addressFactory,
StateMachineFactoryInterface $stateMachineFactory,
StateMachineFactoryInterface|StateMachineInterface $stateMachineFactory,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderExampleFactory $orderExampleFactory = null,

View file

@ -17,6 +17,8 @@ use Doctrine\Persistence\ObjectManager;
use Faker\Factory;
use Faker\Generator;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\FixturesBundle\Fixture\AbstractFixture;
use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Core\Repository\PaymentRepositoryInterface;
@ -45,13 +47,25 @@ class PaymentFixture extends AbstractFixture
*/
public function __construct(
private PaymentRepositoryInterface $paymentRepository,
private StateMachineFactoryInterface $stateMachineFactory,
private StateMachineFactoryInterface|StateMachineInterface $stateMachineFactory,
private ObjectManager $paymentManager,
) {
$this->faker = Factory::create();
$this->optionsResolver = new OptionsResolver();
$this->configureOptions($this->optionsResolver);
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
trigger_deprecation(
'sylius/core-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the second argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
StateMachineFactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function getName(): string
@ -88,11 +102,7 @@ class PaymentFixture extends AbstractFixture
private function completePayment(PaymentInterface $payment): void
{
$this
->stateMachineFactory
->get($payment, PaymentTransitions::GRAPH)
->apply(PaymentTransitions::TRANSITION_COMPLETE)
;
$this->getStateMachine()->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_COMPLETE);
$this->paymentManager->persist($payment);
}
@ -104,4 +114,13 @@ class PaymentFixture extends AbstractFixture
->setAllowedTypes('percentage_completed', 'int')
;
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -156,3 +156,14 @@ sonata_block:
resources: ~
statistics: ~
taxon: ~
sylius_state_machine_abstraction:
graphs_to_adapters_mapping:
sylius_catalog_promotion: symfony_workflow
sylius_order: symfony_workflow
sylius_order_checkout: symfony_workflow
sylius_order_payment: symfony_workflow
sylius_order_shipping: symfony_workflow
sylius_payment: symfony_workflow
sylius_product_review: symfony_workflow
sylius_shipment: symfony_workflow

View file

@ -205,7 +205,7 @@
<service id="sylius.unpaid_orders_state_updater" class="Sylius\Component\Core\Updater\UnpaidOrdersStateUpdater">
<argument type="service" id="sylius.repository.order" />
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
<argument>%sylius_order.order_expiration_period%</argument>
<argument type="service" id="logger" />
<argument type="service" id="sylius.manager.order" />
@ -215,7 +215,7 @@
<service id="sylius.order_payment_provider" class="Sylius\Component\Core\Payment\Provider\OrderPaymentProvider">
<argument type="service" id="sylius.payment_method_resolver.default" />
<argument type="service" id="sylius.factory.payment" />
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
</service>
<service id="Sylius\Component\Core\Payment\Provider\OrderPaymentProviderInterface" alias="sylius.order_payment_provider" />

View file

@ -199,7 +199,7 @@
<argument type="service" id="sylius.repository.payment_method" />
<argument type="service" id="sylius.repository.shipping_method" />
<argument type="service" id="sylius.factory.address" />
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
<argument type="service" id="sylius.checker.order_shipping_method_selection_requirement" />
<argument type="service" id="sylius.checker.order_payment_method_selection_requirement" />
<argument type="service" id="sylius.fixture.example_factory.order" />

View file

@ -121,7 +121,7 @@
<argument type="service" id="sylius.factory.product_review" />
<argument type="service" id="sylius.repository.product" />
<argument type="service" id="sylius.repository.customer" />
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
</service>
<service id="sylius.fixture.example_factory.tax_category" class="Sylius\Bundle\CoreBundle\Fixture\Factory\TaxCategoryExampleFactory">
@ -178,14 +178,14 @@
<argument type="service" id="sylius.repository.payment_method" />
<argument type="service" id="sylius.repository.shipping_method" />
<argument type="service" id="sylius.factory.address" />
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
<argument type="service" id="sylius.checker.order_shipping_method_selection_requirement" />
<argument type="service" id="sylius.checker.order_payment_method_selection_requirement" />
</service>
<service id="Sylius\Bundle\CoreBundle\Fixture\PaymentFixture">
<argument type="service" id="sylius.repository.payment" />
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
<argument type="service" id="sylius.manager.payment" />
<tag name="sylius_fixtures.fixture" />
</service>

View file

@ -15,82 +15,82 @@
<services>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\AssignOrderNumberListener">
<argument type="service" id="Sylius\Bundle\OrderBundle\NumberAssigner\OrderNumberAssignerInterface"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.transition.create" priority="-200"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.transition.create" priority="200"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\AssignOrderTokenListener">
<argument type="service" id="Sylius\Component\Core\TokenAssigner\OrderTokenAssignerInterface" />
<tag name="kernel.event_listener" event="workflow.sylius_order.transition.create" priority="-100"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.transition.create" priority="100"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CreatePaymentListener">
<argument type="service" id="sylius_abstraction.state_machine" />
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.cancel" priority="-600"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.create" priority="600"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CreateShipmentListener">
<argument type="service" id="sylius_abstraction.state_machine" />
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.cancel" priority="-500"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.create" priority="500"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\DecrementPromotionUsagesListener">
<argument type="service" id="Sylius\Component\Core\Promotion\Modifier\OrderPromotionsUsageModifierInterface"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.cancel" priority="-100"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.cancel" priority="100"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\IncrementPromotionUsagesListener">
<argument type="service" id="Sylius\Component\Core\Promotion\Modifier\OrderPromotionsUsageModifierInterface"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.create" priority="-300"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.create" priority="300"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\HoldInventoryListener">
<argument type="service" id="sylius.custom_inventory.order_inventory_operator"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.create" priority="-400"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.create" priority="400"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\GiveBackInventoryListener">
<argument type="service" id="Sylius\Component\Core\Inventory\Operator\OrderInventoryOperatorInterface"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.cancel" priority="-200"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.cancel" priority="200"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\RequestOrderPaymentListener">
<argument type="service" id="sylius_abstraction.state_machine" />
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.create" priority="-700"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.create" priority="700"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\RequestOrderShippingListener">
<argument type="service" id="sylius_abstraction.state_machine" />
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.create" priority="-800"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.create" priority="800"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\SaveCustomerAddressesListener">
<argument type="service" id="Sylius\Component\Core\Customer\OrderAddressesSaverInterface" />
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.create" priority="-200"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.create" priority="200"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\SetImmutableNamesListener">
<argument type="service" id="Sylius\Component\Core\Order\OrderItemNamesSetterInterface" />
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.create" priority="-100"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.create" priority="100"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CancelOrderPaymentListener">
<argument type="service" id="sylius_abstraction.state_machine" />
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.cancel" priority="-400"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.cancel" priority="400"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CancelOrderShippingListener">
<argument type="service" id="sylius_abstraction.state_machine" />
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.cancel" priority="-300"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.cancel" priority="300"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CancelPaymentListener">
<argument type="service" id="sylius_abstraction.state_machine" />
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.cancel" priority="-600"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.cancel" priority="600"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CancelShipmentListener">
<argument type="service" id="sylius_abstraction.state_machine" />
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.cancel" priority="-500"/>
<tag name="kernel.event_listener" event="workflow.sylius_order.completed.cancel" priority="500"/>
</service>
</services>
</container>

View file

@ -15,36 +15,36 @@
<services>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\ProcessCartListener">
<argument type="service" id="sylius.order_processing.order_processor" />
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.address" priority="-200"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.select_shipping" priority="-200"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.skip_shipping" priority="-200"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.select_payment" priority="-200"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.skip_payment" priority="-200"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.address" priority="200"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.select_shipping" priority="200"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.skip_shipping" priority="200"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.select_payment" priority="200"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.skip_payment" priority="200"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\ApplyCreateTransitionOnOrderListener">
<argument type="service" id="sylius_abstraction.state_machine" />
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.complete" priority="-400"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.complete" priority="400"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\SaveCheckoutCompletionDateListener">
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.complete" priority="-300"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.complete" priority="300"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\ResolveOrderCheckoutStateListener">
<argument type="service" id="sylius.state_resolver.order_checkout" />
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.address" priority="-100"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.select_shipping" priority="-100"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.address" priority="100"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.select_shipping" priority="100"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\ResolveOrderPaymentStateListener">
<argument type="service" id="sylius.state_resolver.order_payment" />
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.complete" priority="-200"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.complete" priority="200"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\ResolveOrderShippingStateListener">
<argument type="service" id="sylius.state_resolver.order_shipping" />
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.complete" priority="-100"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_checkout.completed.complete" priority="100"/>
</service>
</services>
</container>

View file

@ -17,12 +17,12 @@
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderPayment\SellOrderInventoryListener">
<argument type="service" id="sylius.inventory.order_inventory_operator" />
<tag name="kernel.event_listener" event="workflow.sylius_order_payment.completed.pay" priority="-200"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_payment.completed.pay" priority="200"/>
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderPayment\ResolveOrderStateListener">
<argument type="service" id="sylius.state_resolver.order" />
<tag name="kernel.event_listener" event="workflow.sylius_order_payment.completed.pay" priority="-100"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_payment.completed.pay" priority="100"/>
</service>
</services>
</container>

View file

@ -17,7 +17,7 @@
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderShipping\ResolveOrderStateListener">
<argument type="service" id="sylius.state_resolver.order" />
<tag name="kernel.event_listener" event="workflow.sylius_order_shipping.completed.ship" priority="-100"/>
<tag name="kernel.event_listener" event="workflow.sylius_order_shipping.completed.ship" priority="100"/>
</service>
</services>
</container>

View file

@ -17,16 +17,16 @@
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Payment\ProcessOrderListener">
<argument type="service" id="sylius.order_processing.order_payment_processor.after_checkout" />
<tag name="kernel.event_listener" event="workflow.sylius_payment.completed.fail" priority="-100" />
<tag name="kernel.event_listener" event="workflow.sylius_payment.completed.cancel" priority="-100" />
<tag name="kernel.event_listener" event="workflow.sylius_payment.completed.fail" priority="100" />
<tag name="kernel.event_listener" event="workflow.sylius_payment.completed.cancel" priority="100" />
</service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Payment\ResolveOrderPaymentStateListener">
<argument type="service" id="sylius.state_resolver.order_payment" />
<tag name="kernel.event_listener" event="workflow.sylius_payment.completed.complete" priority="-100" />
<tag name="kernel.event_listener" event="workflow.sylius_payment.completed.process" priority="-100" />
<tag name="kernel.event_listener" event="workflow.sylius_payment.completed.refund" priority="-100" />
<tag name="kernel.event_listener" event="workflow.sylius_payment.completed.authorize" priority="-100" />
<tag name="kernel.event_listener" event="workflow.sylius_payment.completed.complete" priority="100" />
<tag name="kernel.event_listener" event="workflow.sylius_payment.completed.process" priority="100" />
<tag name="kernel.event_listener" event="workflow.sylius_payment.completed.refund" priority="100" />
<tag name="kernel.event_listener" event="workflow.sylius_payment.completed.authorize" priority="100" />
</service>
</services>
</container>

View file

@ -19,7 +19,7 @@
<argument type="service" id="sylius.state_resolver.order_shipping" />
<tag name="kernel.event_listener"
event="workflow.sylius_shipment.completed.ship"
priority="-100"
priority="100"
/>
</service>
@ -27,7 +27,7 @@
<argument type="service" id="sylius.shipping_date_assigner" />
<tag name="kernel.event_listener"
event="workflow.sylius_shipment.transition.ship"
priority="-100"
priority="100"
/>
</service>
</services>

View file

@ -16,18 +16,18 @@
<defaults public="true" />
<service id="sylius.state_resolver.order" class="Sylius\Component\Core\StateResolver\OrderStateResolver">
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
</service>
<service id="sylius.state_resolver.order_checkout" class="Sylius\Component\Core\StateResolver\CheckoutStateResolver">
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
<argument type="service" id="sylius.checker.order_payment_method_selection_requirement" />
<argument type="service" id="sylius.checker.order_shipping_method_selection_requirement" />
</service>
<service id="sylius.state_resolver.order_payment" class="Sylius\Component\Core\StateResolver\OrderPaymentStateResolver">
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
</service>
<service id="sylius.state_resolver.order_shipping" class="Sylius\Component\Core\StateResolver\OrderShippingStateResolver">
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
</service>
</services>
</container>

View file

@ -21,7 +21,6 @@ use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\Customer;
use Sylius\Component\Core\Model\Order;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\OrderShippingStates;
use Sylius\Component\Core\Repository\PromotionRepositoryInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
@ -216,7 +215,6 @@ final class OrderCheckoutWorkflowTest extends KernelTestCase
$order->setChannel($channel);
$order->setCustomer($customer);
$order->setCheckoutState($checkoutState);
$order->setShippingState(OrderShippingStates::STATE_READY);
return $order;
}

View file

@ -15,7 +15,8 @@ namespace spec\Sylius\Bundle\CoreBundle\CatalogPromotion\Processor;
use PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface;
use SM\StateMachine\StateMachineInterface;
use SM\StateMachine\StateMachineInterface as WinzouStateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Bundle\CoreBundle\CatalogPromotion\Checker\CatalogPromotionEligibilityCheckerInterface;
use Sylius\Bundle\CoreBundle\CatalogPromotion\Processor\CatalogPromotionStateProcessorInterface;
use Sylius\Component\Core\Model\CatalogPromotionInterface;
@ -39,7 +40,7 @@ final class CatalogPromotionStateProcessorSpec extends ObjectBehavior
CatalogPromotionEligibilityCheckerInterface $catalogPromotionEligibilityChecker,
CatalogPromotionInterface $catalogPromotion,
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$stateMachineFactory->get($catalogPromotion, CatalogPromotionTransitions::GRAPH)->willReturn($stateMachine);
@ -54,11 +55,29 @@ final class CatalogPromotionStateProcessorSpec extends ObjectBehavior
$this->process($catalogPromotion);
}
function it_uses_the_new_state_machine_adapter_if_passed(
CatalogPromotionEligibilityCheckerInterface $catalogPromotionEligibilityChecker,
CatalogPromotionInterface $catalogPromotion,
StateMachineInterface $stateMachine,
): void {
$this->beConstructedWith($catalogPromotionEligibilityChecker, $stateMachine);
$catalogPromotionEligibilityChecker->isCatalogPromotionEligible($catalogPromotion)->willReturn(true);
$stateMachine->can($catalogPromotion, CatalogPromotionTransitions::GRAPH, CatalogPromotionTransitions::TRANSITION_PROCESS)->willReturn(true);
$stateMachine->apply($catalogPromotion, CatalogPromotionTransitions::GRAPH, CatalogPromotionTransitions::TRANSITION_PROCESS)->shouldBeCalled();
$stateMachine->apply($catalogPromotion, CatalogPromotionTransitions::GRAPH, CatalogPromotionTransitions::TRANSITION_ACTIVATE)->shouldNotBeCalled();
$stateMachine->apply($catalogPromotion, CatalogPromotionTransitions::GRAPH, CatalogPromotionTransitions::TRANSITION_DEACTIVATE)->shouldNotBeCalled();
$this->process($catalogPromotion);
}
function it_activates_a_catalog_promotion(
CatalogPromotionEligibilityCheckerInterface $catalogPromotionEligibilityChecker,
CatalogPromotionInterface $catalogPromotion,
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$stateMachineFactory->get($catalogPromotion, CatalogPromotionTransitions::GRAPH)->willReturn($stateMachine);
@ -78,7 +97,7 @@ final class CatalogPromotionStateProcessorSpec extends ObjectBehavior
CatalogPromotionEligibilityCheckerInterface $catalogPromotionEligibilityChecker,
CatalogPromotionInterface $catalogPromotion,
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$stateMachineFactory->get($catalogPromotion, CatalogPromotionTransitions::GRAPH)->willReturn($stateMachine);

View file

@ -16,7 +16,8 @@ namespace spec\Sylius\Bundle\CoreBundle\Checkout;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use SM\Factory\FactoryInterface;
use SM\StateMachine\StateMachineInterface;
use SM\StateMachine\StateMachineInterface as WinzouStateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Bundle\CoreBundle\Checkout\CheckoutStateUrlGeneratorInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Order\Context\CartContextInterface;
@ -160,7 +161,7 @@ final class CheckoutResolverSpec extends ObjectBehavior
CartContextInterface $cartContext,
OrderInterface $order,
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$request = new Request([], [], [
'_sylius' => ['state_machine' => ['graph' => 'test_graph', 'transition' => 'test_transition']],
@ -183,7 +184,7 @@ final class CheckoutResolverSpec extends ObjectBehavior
CartContextInterface $cartContext,
OrderInterface $order,
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
CheckoutStateUrlGeneratorInterface $urlGenerator,
): void {
$request = new Request([], [], [
@ -201,4 +202,29 @@ final class CheckoutResolverSpec extends ObjectBehavior
$this->onKernelRequest($event);
}
function it_uses_the_new_state_machine_abstraction_if_passed(
CartContextInterface $cartContext,
CheckoutStateUrlGeneratorInterface $urlGenerator,
RequestMatcherInterface $requestMatcher,
StateMachineInterface $stateMachine,
RequestEvent $event,
OrderInterface $order,
): void {
$this->beConstructedWith($cartContext, $urlGenerator, $requestMatcher, $stateMachine);
$request = new Request([], [], [
'_sylius' => ['state_machine' => ['graph' => 'test_graph', 'transition' => 'test_transition']],
]);
$event->isMainRequest()->willReturn(true);
$event->getRequest()->willReturn($request);
$requestMatcher->matches($request)->willReturn(true);
$cartContext->getCart()->willReturn($order);
$order->isEmpty()->willReturn(false);
$stateMachine->can($order, 'test_graph', 'test_transition')->willReturn(false);
$urlGenerator->generateForOrderCheckoutState($order)->willReturn('/target-url');
$event->setResponse(Argument::type(RedirectResponse::class))->shouldBeCalled();
$this->onKernelRequest($event);
}
}

View file

@ -55,7 +55,6 @@ framework:
to: unpublished
sylius_state_machine_abstraction:
state_machine:
default_adapter: 'winzou_state_machine'
graphs_to_adapters_mapping:
app_blog_post: 'symfony_workflow'
default_adapter: 'winzou_state_machine'
graphs_to_adapters_mapping:
app_blog_post: 'symfony_workflow'

View file

@ -19,16 +19,25 @@ use Payum\Core\Request\Generic;
use Payum\Core\Request\GetStatusInterface;
use Payum\Core\Request\Notify;
use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\PayumBundle\Request\GetStatus;
use Sylius\Component\Payment\Model\PaymentInterface;
use Sylius\Component\Payment\PaymentTransitions;
use Sylius\Component\Resource\StateMachine\StateMachineInterface;
use Webmozart\Assert\Assert;
final class UpdatePaymentStateExtension implements ExtensionInterface
{
public function __construct(private FactoryInterface $factory)
public function __construct(private FactoryInterface|StateMachineInterface $factory)
{
trigger_deprecation(
'sylius/payum-bundle',
'1.13',
sprintf(
'Passing an instance of "%s" as the first argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
FactoryInterface::class,
StateMachineInterface::class,
),
);
}
public function onPreExecute(Context $context): void
@ -84,13 +93,19 @@ final class UpdatePaymentStateExtension implements ExtensionInterface
private function updatePaymentState(PaymentInterface $payment, string $nextState): void
{
$stateMachine = $this->factory->get($payment, PaymentTransitions::GRAPH);
$stateMachine = $this->getStateMachine();
/** @var StateMachineInterface $stateMachine */
Assert::isInstanceOf($stateMachine, StateMachineInterface::class);
if (null !== $transition = $stateMachine->getTransitionToState($nextState)) {
$stateMachine->apply($transition);
if (null !== $transition = $stateMachine->getTransitionToState($payment, PaymentTransitions::GRAPH, $nextState)) {
$stateMachine->apply($payment, PaymentTransitions::GRAPH, $transition);
}
}
private function getStateMachine(): StateMachineInterface
{
if ($this->factory instanceof FactoryInterface) {
return new WinzouStateMachineAdapter($this->factory);
}
return $this->factory;
}
}

View file

@ -8,7 +8,7 @@
<defaults public="true" />
<service id="sylius.payum_extension.update_payment_state" class="Sylius\Bundle\PayumBundle\Extension\UpdatePaymentStateExtension">
<argument type="service" id="sm.factory" />
<argument type="service" id="sylius_abstraction.state_machine" />
<tag name="payum.extension" all="true" prepend="true" />
</service>
</services>

View file

@ -14,6 +14,8 @@ declare(strict_types=1);
namespace Sylius\Component\Core\Payment\Provider;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Core\Model\PaymentMethodInterface;
@ -22,7 +24,6 @@ use Sylius\Component\Payment\Exception\UnresolvedDefaultPaymentMethodException;
use Sylius\Component\Payment\Factory\PaymentFactoryInterface;
use Sylius\Component\Payment\PaymentTransitions;
use Sylius\Component\Payment\Resolver\DefaultPaymentMethodResolverInterface;
use Sylius\Component\Resource\StateMachine\StateMachineInterface;
use Webmozart\Assert\Assert;
final class OrderPaymentProvider implements OrderPaymentProviderInterface
@ -30,8 +31,19 @@ final class OrderPaymentProvider implements OrderPaymentProviderInterface
public function __construct(
private DefaultPaymentMethodResolverInterface $defaultPaymentMethodResolver,
private PaymentFactoryInterface $paymentFactory,
private StateMachineFactoryInterface $stateMachineFactory,
private StateMachineFactoryInterface|StateMachineInterface $stateMachineFactory,
) {
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
trigger_deprecation(
'sylius/core',
'1.13',
sprintf(
'Passing an instance of "%s" as the third argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
StateMachineFactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function provideOrderPayment(OrderInterface $order, string $targetState): ?PaymentInterface
@ -88,12 +100,20 @@ final class OrderPaymentProvider implements OrderPaymentProviderInterface
return;
}
/** @var StateMachineInterface $stateMachine */
$stateMachine = $this->stateMachineFactory->get($payment, PaymentTransitions::GRAPH);
$stateMachine = $this->getStateMachine();
$targetTransition = $stateMachine->getTransitionToState($targetState);
$targetTransition = $stateMachine->getTransitionToState($payment, PaymentTransitions::GRAPH, $targetState);
if (null !== $targetTransition) {
$stateMachine->apply($targetTransition);
$stateMachine->apply($payment, PaymentTransitions::GRAPH, $targetTransition);
}
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof StateMachineFactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -14,6 +14,8 @@ declare(strict_types=1);
namespace Sylius\Component\Core\StateResolver;
use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Component\Core\Checker\OrderPaymentMethodSelectionRequirementCheckerInterface;
use Sylius\Component\Core\Checker\OrderShippingMethodSelectionRequirementCheckerInterface;
use Sylius\Component\Core\Model\OrderInterface;
@ -25,29 +27,47 @@ use Webmozart\Assert\Assert;
final class CheckoutStateResolver implements StateResolverInterface
{
public function __construct(
private FactoryInterface $stateMachineFactory,
private FactoryInterface|StateMachineInterface $stateMachineFactory,
private OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
private OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
) {
trigger_deprecation(
'sylius/core',
'1.13',
sprintf(
'Passing an instance of "%s" as the first argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
FactoryInterface::class,
StateMachineInterface::class,
),
);
}
public function resolve(BaseOrderInterface $order): void
{
Assert::isInstanceOf($order, OrderInterface::class);
$stateMachine = $this->stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH);
$stateMachine = $this->getStateMachine();
if (
!$this->orderShippingMethodSelectionRequirementChecker->isShippingMethodSelectionRequired($order) &&
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_SKIP_SHIPPING)
$stateMachine->can($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SKIP_SHIPPING)
) {
$stateMachine->apply(OrderCheckoutTransitions::TRANSITION_SKIP_SHIPPING);
$stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SKIP_SHIPPING);
}
if (
!$this->orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order) &&
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_SKIP_PAYMENT)
$stateMachine->can($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SKIP_PAYMENT)
) {
$stateMachine->apply(OrderCheckoutTransitions::TRANSITION_SKIP_PAYMENT);
$stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SKIP_PAYMENT);
}
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -15,7 +15,8 @@ namespace Sylius\Component\Core\StateResolver;
use Doctrine\Common\Collections\Collection;
use SM\Factory\FactoryInterface;
use SM\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Core\OrderPaymentTransitions;
@ -26,8 +27,19 @@ use Webmozart\Assert\Assert;
final class OrderPaymentStateResolver implements StateResolverInterface
{
public function __construct(private FactoryInterface $stateMachineFactory)
public function __construct(private FactoryInterface|StateMachineInterface $stateMachineFactory)
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
trigger_deprecation(
'sylius/core',
'1.13',
sprintf(
'Passing an instance of "%s" as the first argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
FactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function resolve(BaseOrderInterface $order): void
@ -35,18 +47,13 @@ final class OrderPaymentStateResolver implements StateResolverInterface
/** @var OrderInterface $order */
Assert::isInstanceOf($order, OrderInterface::class);
$stateMachine = $this->stateMachineFactory->get($order, OrderPaymentTransitions::GRAPH);
$targetTransition = $this->getTargetTransition($order);
$stateMachine = $this->getStateMachine();
if (null !== $targetTransition) {
$this->applyTransition($stateMachine, $targetTransition);
}
}
private function applyTransition(StateMachineInterface $stateMachine, string $transition): void
{
if ($stateMachine->can($transition)) {
$stateMachine->apply($transition);
if ($stateMachine->can($order, OrderPaymentTransitions::GRAPH, $targetTransition)) {
$stateMachine->apply($order, OrderPaymentTransitions::GRAPH, $targetTransition);
}
}
}
@ -129,4 +136,13 @@ final class OrderPaymentStateResolver implements StateResolverInterface
return $payments;
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -14,7 +14,8 @@ declare(strict_types=1);
namespace Sylius\Component\Core\StateResolver;
use SM\Factory\FactoryInterface;
use SM\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\ShipmentInterface;
use Sylius\Component\Core\OrderShippingStates;
@ -25,8 +26,19 @@ use Webmozart\Assert\Assert;
final class OrderShippingStateResolver implements StateResolverInterface
{
public function __construct(private FactoryInterface $stateMachineFactory)
public function __construct(private FactoryInterface|StateMachineInterface $stateMachineFactory)
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
trigger_deprecation(
'sylius/core',
'1.13',
sprintf(
'Passing an instance of "%s" as the first argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
FactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function resolve(BaseOrderInterface $order): void
@ -37,15 +49,15 @@ final class OrderShippingStateResolver implements StateResolverInterface
if (OrderShippingStates::STATE_SHIPPED === $order->getShippingState()) {
return;
}
/** @var StateMachineInterface $stateMachine */
$stateMachine = $this->stateMachineFactory->get($order, OrderShippingTransitions::GRAPH);
$stateMachine = $this->getStateMachine();
if ($this->allShipmentsInStateButOrderStateNotUpdated($order, ShipmentInterface::STATE_SHIPPED, OrderShippingStates::STATE_SHIPPED)) {
$stateMachine->apply(OrderShippingTransitions::TRANSITION_SHIP);
$stateMachine->apply($order, OrderShippingTransitions::GRAPH, OrderShippingTransitions::TRANSITION_SHIP);
}
if ($this->isPartiallyShippedButOrderStateNotUpdated($order)) {
$stateMachine->apply(OrderShippingTransitions::TRANSITION_PARTIALLY_SHIP);
$stateMachine->apply($order, OrderShippingTransitions::GRAPH, OrderShippingTransitions::TRANSITION_PARTIALLY_SHIP);
}
}
@ -83,4 +95,13 @@ final class OrderShippingStateResolver implements StateResolverInterface
OrderShippingStates::STATE_PARTIALLY_SHIPPED !== $order->getShippingState()
;
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -14,6 +14,8 @@ declare(strict_types=1);
namespace Sylius\Component\Core\StateResolver;
use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\OrderPaymentStates;
use Sylius\Component\Core\OrderShippingStates;
@ -24,17 +26,28 @@ use Webmozart\Assert\Assert;
final class OrderStateResolver implements StateResolverInterface
{
public function __construct(private FactoryInterface $stateMachineFactory)
public function __construct(private FactoryInterface|StateMachineInterface $stateMachineFactory)
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
trigger_deprecation(
'sylius/core',
'1.13',
sprintf(
'Passing an instance of "%s" as the first argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
FactoryInterface::class,
StateMachineInterface::class,
),
);
}
}
public function resolve(BaseOrderInterface $order): void
{
Assert::isInstanceOf($order, OrderInterface::class);
$stateMachine = $this->stateMachineFactory->get($order, OrderTransitions::GRAPH);
$stateMachine = $this->getStateMachine();
if ($this->canOrderBeFulfilled($order) && $stateMachine->can(OrderTransitions::TRANSITION_FULFILL)) {
$stateMachine->apply(OrderTransitions::TRANSITION_FULFILL);
if ($this->canOrderBeFulfilled($order) && $stateMachine->can($order, OrderTransitions::GRAPH, OrderTransitions::TRANSITION_FULFILL)) {
$stateMachine->apply($order, OrderTransitions::GRAPH, OrderTransitions::TRANSITION_FULFILL);
}
}
@ -46,4 +59,13 @@ final class OrderStateResolver implements StateResolverInterface
OrderShippingStates::STATE_SHIPPED === $order->getShippingState()
;
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -16,7 +16,10 @@ namespace Sylius\Component\Core\Updater;
use Doctrine\Persistence\ObjectManager;
use Psr\Log\LoggerInterface;
use SM\Factory\Factory;
use SM\SMException;
use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\Exception\StateMachineExecutionException;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\Component\Order\Model\OrderInterface;
use Sylius\Component\Order\OrderTransitions;
@ -25,7 +28,7 @@ final class UnpaidOrdersStateUpdater implements UnpaidOrdersStateUpdaterInterfac
{
public function __construct(
private OrderRepositoryInterface $orderRepository,
private Factory $stateMachineFactory,
private Factory|StateMachineInterface $stateMachineFactory,
private string $expirationPeriod,
private ?LoggerInterface $logger = null,
private ?ObjectManager $orderManager = null,
@ -47,6 +50,18 @@ final class UnpaidOrdersStateUpdater implements UnpaidOrdersStateUpdaterInterfac
);
}
if ($this->stateMachineFactory instanceof FactoryInterface) {
trigger_deprecation(
'sylius/core',
'1.13',
sprintf(
'Passing an instance of "%s" as the second argument is deprecated. It will accept only instances of "%s" in Sylius 2.0.',
FactoryInterface::class,
StateMachineInterface::class,
),
);
}
$this->logger = $logger;
}
@ -79,13 +94,22 @@ final class UnpaidOrdersStateUpdater implements UnpaidOrdersStateUpdaterInterfac
private function cancelOrder(OrderInterface $expiredUnpaidOrder): void
{
try {
$stateMachine = $this->stateMachineFactory->get($expiredUnpaidOrder, OrderTransitions::GRAPH);
$stateMachine->apply(OrderTransitions::TRANSITION_CANCEL);
} catch (SMException $e) {
$stateMachine = $this->getStateMachine();
$stateMachine->apply($expiredUnpaidOrder, OrderTransitions::GRAPH, OrderTransitions::TRANSITION_CANCEL);
} catch (StateMachineExecutionException $e) {
$this->logger?->error(
sprintf('An error occurred while cancelling unpaid order #%s', $expiredUnpaidOrder->getId()),
['exception' => $e, 'message' => $e->getMessage()],
);
}
}
private function getStateMachine(): StateMachineInterface
{
if ($this->stateMachineFactory instanceof FactoryInterface) {
return new WinzouStateMachineAdapter($this->stateMachineFactory);
}
return $this->stateMachineFactory;
}
}

View file

@ -49,6 +49,7 @@
"sylius/resource": "^1.9",
"sylius/review": "^1.13",
"sylius/shipping": "^1.13",
"sylius/state-machine-abstraction": "^1.13",
"sylius/taxation": "^1.13",
"sylius/taxonomy": "^1.13",
"sylius/user": "^1.13",
@ -82,7 +83,7 @@
},
"autoload-dev": {
"psr-4": {
"Sylius\\Component\\Core\\spec\\": "spec/"
"spec\\Sylius\\Component\\Core\\": "spec/"
}
},
"repositories": [

View file

@ -16,6 +16,7 @@ namespace spec\Sylius\Component\Core\Payment\Provider;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Core\Model\PaymentMethodInterface;
@ -25,7 +26,6 @@ use Sylius\Component\Payment\Exception\UnresolvedDefaultPaymentMethodException;
use Sylius\Component\Payment\Factory\PaymentFactoryInterface;
use Sylius\Component\Payment\PaymentTransitions;
use Sylius\Component\Payment\Resolver\DefaultPaymentMethodResolverInterface;
use Sylius\Component\Resource\StateMachine\StateMachineInterface;
final class OrderPaymentProviderSpec extends ObjectBehavior
{
@ -54,8 +54,18 @@ final class OrderPaymentProviderSpec extends ObjectBehavior
PaymentInterface $newPayment,
PaymentMethodInterface $paymentMethod,
StateMachineFactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineStub $stateMachine,
): void {
$stateMachine->can(PaymentTransitions::TRANSITION_CREATE)->willReturn(true);
$this->setPropertyValue($stateMachine, 'config', [
'transitions' => [
PaymentTransitions::TRANSITION_CREATE => [
'from' => [PaymentInterface::STATE_CART],
'to' => PaymentInterface::STATE_NEW,
],
],
]);
$order->getTotal()->willReturn(1000);
$order->getCurrencyCode()->willReturn('USD');
$order->getLastPayment(PaymentInterface::STATE_CANCELLED)->willReturn($lastCancelledPayment);
@ -70,12 +80,45 @@ final class OrderPaymentProviderSpec extends ObjectBehavior
$newPayment->setOrder($order)->shouldBeCalled();
$stateMachineFactory->get($newPayment, PaymentTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->getTransitionToState(PaymentInterface::STATE_NEW)->willReturn(PaymentTransitions::TRANSITION_CREATE);
$stateMachine->apply(PaymentTransitions::TRANSITION_CREATE)->shouldBeCalled();
$this->provideOrderPayment($order, PaymentInterface::STATE_NEW)->shouldReturn($newPayment);
}
function it_uses_the_new_state_machine_abstraction_if_passed(
DefaultPaymentMethodResolverInterface $defaultPaymentMethodResolver,
PaymentFactoryInterface $paymentFactory,
StateMachineInterface $stateMachine,
OrderInterface $order,
PaymentInterface $lastCancelledPayment,
PaymentInterface $newPayment,
PaymentMethodInterface $paymentMethod,
): void {
$this->beConstructedWith(
$defaultPaymentMethodResolver,
$paymentFactory,
$stateMachine,
);
$order->getTotal()->willReturn(1000);
$order->getCurrencyCode()->willReturn('USD');
$order->getLastPayment(PaymentInterface::STATE_CANCELLED)->willReturn($lastCancelledPayment);
$lastCancelledPayment->getMethod()->willReturn($paymentMethod);
$paymentFactory->createWithAmountAndCurrencyCode(1000, 'USD')->willReturn($newPayment);
$defaultPaymentMethodResolver->getDefaultPaymentMethod($newPayment)->willReturn($paymentMethod);
$newPayment->setMethod($paymentMethod)->shouldBeCalled();
$newPayment->getState()->willReturn(PaymentInterface::STATE_CART);
$newPayment->setOrder($order)->shouldBeCalled();
$stateMachine->getTransitionToState($newPayment, PaymentTransitions::GRAPH, PaymentInterface::STATE_NEW)->willReturn(PaymentTransitions::TRANSITION_CREATE);
$stateMachine->apply($newPayment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_CREATE)->shouldBeCalled();
$this->provideOrderPayment($order, PaymentInterface::STATE_NEW)->shouldReturn($newPayment);
}
function it_provides_payment_in_configured_state_with_payment_method_from_last_failed_payment(
DefaultPaymentMethodResolverInterface $defaultPaymentMethodResolver,
OrderInterface $order,
@ -84,8 +127,18 @@ final class OrderPaymentProviderSpec extends ObjectBehavior
PaymentInterface $newPayment,
PaymentMethodInterface $paymentMethod,
StateMachineFactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineStub $stateMachine,
): void {
$stateMachine->can(PaymentTransitions::TRANSITION_CREATE)->willReturn(true);
$this->setPropertyValue($stateMachine, 'config', [
'transitions' => [
PaymentTransitions::TRANSITION_CREATE => [
'from' => [PaymentInterface::STATE_CART],
'to' => PaymentInterface::STATE_NEW,
],
],
]);
$order->getTotal()->willReturn(1000);
$order->getCurrencyCode()->willReturn('USD');
$order->getLastPayment(PaymentInterface::STATE_CANCELLED)->willReturn(null);
@ -101,7 +154,6 @@ final class OrderPaymentProviderSpec extends ObjectBehavior
$newPayment->setOrder($order)->shouldBeCalled();
$stateMachineFactory->get($newPayment, PaymentTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->getTransitionToState(PaymentInterface::STATE_NEW)->willReturn(PaymentTransitions::TRANSITION_CREATE);
$stateMachine->apply(PaymentTransitions::TRANSITION_CREATE)->shouldBeCalled();
$this->provideOrderPayment($order, PaymentInterface::STATE_NEW)->shouldReturn($newPayment);
@ -114,8 +166,18 @@ final class OrderPaymentProviderSpec extends ObjectBehavior
PaymentInterface $newPayment,
PaymentMethodInterface $paymentMethod,
StateMachineFactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineStub $stateMachine,
): void {
$stateMachine->can(PaymentTransitions::TRANSITION_CREATE)->willReturn(true);
$this->setPropertyValue($stateMachine, 'config', [
'transitions' => [
PaymentTransitions::TRANSITION_CREATE => [
'from' => [PaymentInterface::STATE_CART],
'to' => PaymentInterface::STATE_NEW,
],
],
]);
$order->getTotal()->willReturn(1000);
$order->getCurrencyCode()->willReturn('USD');
$order->getLastPayment(PaymentInterface::STATE_CANCELLED)->willReturn(null);
@ -130,7 +192,6 @@ final class OrderPaymentProviderSpec extends ObjectBehavior
$newPayment->getState()->willReturn(PaymentInterface::STATE_CART);
$stateMachineFactory->get($newPayment, PaymentTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->getTransitionToState(PaymentInterface::STATE_NEW)->willReturn(PaymentTransitions::TRANSITION_CREATE);
$stateMachine->apply(PaymentTransitions::TRANSITION_CREATE)->shouldBeCalled();
$this->provideOrderPayment($order, PaymentInterface::STATE_NEW)->shouldReturn($newPayment);
@ -192,4 +253,12 @@ final class OrderPaymentProviderSpec extends ObjectBehavior
->during('provideOrderPayment', [$order, PaymentInterface::STATE_NEW])
;
}
private function setPropertyValue(object $object, string $property, mixed $value): void
{
$reflection = new \ReflectionClass(WinzouStateMachineStub::class);
$reflectionProperty = $reflection->getProperty($property);
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($object, $value);
}
}

View file

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace spec\Sylius\Component\Core\Payment\Provider;
use SM\StateMachine\StateMachine;
class WinzouStateMachineStub extends StateMachine
{
public $config = [];
}

View file

@ -15,7 +15,8 @@ namespace spec\Sylius\Component\Core\StateResolver;
use PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface;
use SM\StateMachine\StateMachineInterface;
use SM\StateMachine\StateMachineInterface as WinzouStateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Component\Core\Checker\OrderPaymentMethodSelectionRequirementCheckerInterface;
use Sylius\Component\Core\Checker\OrderShippingMethodSelectionRequirementCheckerInterface;
use Sylius\Component\Core\Model\OrderInterface;
@ -46,7 +47,7 @@ final class CheckoutStateResolverSpec extends ObjectBehavior
OrderInterface $order,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(false);
@ -63,12 +64,33 @@ final class CheckoutStateResolverSpec extends ObjectBehavior
$this->resolve($order);
}
function it_uses_the_new_state_machine_abstraction_if_passed(
StateMachineInterface $stateMachine,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
OrderInterface $order,
): void {
$this->beConstructedWith($stateMachine, $orderPaymentMethodSelectionRequirementChecker, $orderShippingMethodSelectionRequirementChecker);
$orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(false);
$orderShippingMethodSelectionRequirementChecker->isShippingMethodSelectionRequired($order)->willReturn(false);
$stateMachine->can($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SKIP_SHIPPING)->willReturn(true);
$stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SKIP_SHIPPING)->shouldBeCalled();
$stateMachine->can($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SKIP_PAYMENT)->willReturn(true);
$stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SKIP_PAYMENT)->shouldBeCalled();
$this->resolve($order);
}
function it_applies_transition_skip_shipping_if_shipping_method_selection_is_not_required_and_this_transition_is_possible(
FactoryInterface $stateMachineFactory,
OrderInterface $order,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(true);
@ -90,7 +112,7 @@ final class CheckoutStateResolverSpec extends ObjectBehavior
OrderInterface $order,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(true);
@ -112,7 +134,7 @@ final class CheckoutStateResolverSpec extends ObjectBehavior
OrderInterface $order,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(true);
@ -134,7 +156,7 @@ final class CheckoutStateResolverSpec extends ObjectBehavior
OrderInterface $order,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(false);
@ -156,7 +178,7 @@ final class CheckoutStateResolverSpec extends ObjectBehavior
OrderInterface $order,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(true);
@ -178,7 +200,7 @@ final class CheckoutStateResolverSpec extends ObjectBehavior
OrderInterface $order,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(true);

View file

@ -16,7 +16,8 @@ namespace spec\Sylius\Component\Core\StateResolver;
use Doctrine\Common\Collections\ArrayCollection;
use PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface;
use SM\StateMachine\StateMachineInterface;
use SM\StateMachine\StateMachineInterface as WinzouStateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Core\OrderPaymentTransitions;
@ -36,7 +37,7 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_refunded_if_all_its_payments_are_refunded(
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
OrderInterface $order,
PaymentInterface $firstPayment,
PaymentInterface $secondPayment,
@ -59,9 +60,34 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
$this->resolve($order);
}
function it_uses_the_new_state_machine_if_passed(
StateMachineInterface $stateMachine,
OrderInterface $order,
PaymentInterface $firstPayment,
PaymentInterface $secondPayment,
): void {
$this->beConstructedWith($stateMachine);
$firstPayment->getAmount()->willReturn(6000);
$firstPayment->getState()->willReturn(PaymentInterface::STATE_REFUNDED);
$secondPayment->getAmount()->willReturn(4000);
$secondPayment->getState()->willReturn(PaymentInterface::STATE_REFUNDED);
$order
->getPayments()
->willReturn(new ArrayCollection([$firstPayment->getWrappedObject(), $secondPayment->getWrappedObject()]))
;
$order->getTotal()->willReturn(10000);
$stateMachine->can($order, OrderPaymentTransitions::GRAPH, OrderPaymentTransitions::TRANSITION_REFUND)->willReturn(true);
$stateMachine->apply($order, OrderPaymentTransitions::GRAPH, OrderPaymentTransitions::TRANSITION_REFUND)->shouldBeCalled();
$this->resolve($order);
}
function it_marks_an_order_as_refunded_if_its_payments_are_refunded_or_failed_but_at_least_one_is_refunded(
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
OrderInterface $order,
PaymentInterface $firstPayment,
PaymentInterface $secondPayment,
@ -86,7 +112,7 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_paid_if_fully_paid(
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
OrderInterface $order,
PaymentInterface $payment,
): void {
@ -107,7 +133,7 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_paid_if_it_does_not_have_any_payments(
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
OrderInterface $order,
) {
$order->getPayments()->willReturn(new ArrayCollection([]));
@ -122,7 +148,7 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_paid_if_fully_paid_even_if_previous_payment_was_failed(
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
OrderInterface $order,
PaymentInterface $firstPayment,
PaymentInterface $secondPayment,
@ -146,7 +172,7 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_partially_refunded_if_one_of_the_payment_is_refunded(
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
OrderInterface $order,
PaymentInterface $firstPayment,
PaymentInterface $secondPayment,
@ -171,7 +197,7 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_completed_if_fully_paid_multiple_payments(
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
OrderInterface $order,
PaymentInterface $firstPayment,
PaymentInterface $secondPayment,
@ -196,7 +222,7 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_partially_paid_if_one_of_the_payment_is_processing(
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
OrderInterface $order,
PaymentInterface $firstPayment,
PaymentInterface $secondPayment,
@ -221,7 +247,7 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_authorized_if_all_its_payments_are_authorized(
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
OrderInterface $order,
PaymentInterface $firstPayment,
PaymentInterface $secondPayment,
@ -246,7 +272,7 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_partially_authorized_if_one_of_the_payments_is_processing_and_one_of_the_payments_is_authorized(
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
OrderInterface $order,
PaymentInterface $firstPayment,
PaymentInterface $secondPayment,
@ -271,7 +297,7 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_awaiting_payment_if_payments_is_processing(
FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
OrderInterface $order,
PaymentInterface $firstPayment,
): void {

View file

@ -16,7 +16,8 @@ namespace spec\Sylius\Component\Core\StateResolver;
use Doctrine\Common\Collections\ArrayCollection;
use PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface;
use SM\StateMachine\StateMachineInterface;
use SM\StateMachine\StateMachineInterface as WinzouStateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\ShipmentInterface;
use Sylius\Component\Core\OrderShippingStates;
@ -40,7 +41,7 @@ final class OrderShippingStateResolverSpec extends ObjectBehavior
OrderInterface $order,
ShipmentInterface $shipment1,
ShipmentInterface $shipment2,
StateMachineInterface $orderStateMachine,
WinzouStateMachineInterface $orderStateMachine,
): void {
$shipments = new ArrayCollection();
$shipments->add($shipment1->getWrappedObject());
@ -58,10 +59,33 @@ final class OrderShippingStateResolverSpec extends ObjectBehavior
$this->resolve($order);
}
function it_uses_the_new_state_machine_abstraction_if_passed(
StateMachineInterface $orderStateMachine,
OrderInterface $order,
ShipmentInterface $shipment1,
ShipmentInterface $shipment2,
): void {
$this->beConstructedWith($orderStateMachine);
$shipments = new ArrayCollection();
$shipments->add($shipment1->getWrappedObject());
$shipments->add($shipment2->getWrappedObject());
$order->getShipments()->willReturn($shipments);
$order->getShippingState()->willReturn(OrderShippingStates::STATE_READY);
$shipment1->getState()->willReturn(ShipmentInterface::STATE_SHIPPED);
$shipment2->getState()->willReturn(ShipmentInterface::STATE_SHIPPED);
$orderStateMachine->apply($order, OrderShippingTransitions::GRAPH, OrderShippingTransitions::TRANSITION_SHIP)->shouldBeCalled();
$this->resolve($order);
}
function it_marks_an_order_as_shipped_if_there_are_no_shipments_to_deliver(
FactoryInterface $stateMachineFactory,
OrderInterface $order,
StateMachineInterface $orderStateMachine,
WinzouStateMachineInterface $orderStateMachine,
): void {
$order->getShipments()->willReturn(new ArrayCollection());
$order->getShippingState()->willReturn(OrderShippingStates::STATE_READY);
@ -77,7 +101,7 @@ final class OrderShippingStateResolverSpec extends ObjectBehavior
OrderInterface $order,
ShipmentInterface $shipment1,
ShipmentInterface $shipment2,
StateMachineInterface $orderStateMachine,
WinzouStateMachineInterface $orderStateMachine,
): void {
$shipments = new ArrayCollection();
$shipments->add($shipment1->getWrappedObject());
@ -100,7 +124,7 @@ final class OrderShippingStateResolverSpec extends ObjectBehavior
OrderInterface $order,
ShipmentInterface $shipment1,
ShipmentInterface $shipment2,
StateMachineInterface $orderStateMachine,
WinzouStateMachineInterface $orderStateMachine,
): void {
$shipments = new ArrayCollection();
$shipments->add($shipment1->getWrappedObject());

View file

@ -16,7 +16,8 @@ namespace spec\Sylius\Component\Core\StateResolver;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use SM\Factory\FactoryInterface;
use SM\StateMachine\StateMachineInterface;
use SM\StateMachine\StateMachineInterface as WinzouStateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\OrderPaymentStates;
use Sylius\Component\Core\OrderShippingStates;
@ -38,7 +39,7 @@ final class OrderStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_fulfilled_when_its_paid_for_and_has_been_shipped(
FactoryInterface $stateMachineFactory,
OrderInterface $order,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$order->getShippingState()->willReturn(OrderShippingStates::STATE_SHIPPED);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_PAID);
@ -51,10 +52,25 @@ final class OrderStateResolverSpec extends ObjectBehavior
$this->resolve($order);
}
function it_uses_the_new_state_machine_abstraction_if_passed(
StateMachineInterface $stateMachine,
OrderInterface $order,
): void {
$this->beConstructedWith($stateMachine);
$order->getShippingState()->willReturn(OrderShippingStates::STATE_SHIPPED);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_PAID);
$stateMachine->can($order, OrderTransitions::GRAPH, OrderTransitions::TRANSITION_FULFILL)->willReturn(true);
$stateMachine->apply($order, OrderTransitions::GRAPH, OrderTransitions::TRANSITION_FULFILL)->shouldBeCalled();
$this->resolve($order);
}
function it_marks_an_order_as_fulfilled_when_its_partially_refunded_and_has_been_shipped(
FactoryInterface $stateMachineFactory,
OrderInterface $order,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$order->getShippingState()->willReturn(OrderShippingStates::STATE_SHIPPED);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_PARTIALLY_REFUNDED);
@ -70,7 +86,7 @@ final class OrderStateResolverSpec extends ObjectBehavior
function it_does_not_mark_an_order_as_fulfilled_when_it_has_been_paid_but_not_shipped(
FactoryInterface $stateMachineFactory,
OrderInterface $order,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$order->getShippingState()->willReturn(Argument::not(OrderShippingStates::STATE_SHIPPED));
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_PAID);
@ -86,7 +102,7 @@ final class OrderStateResolverSpec extends ObjectBehavior
function it_does_not_mark_an_order_as_fulfilled_when_it_has_been_shipped_but_not_paid(
FactoryInterface $stateMachineFactory,
OrderInterface $order,
StateMachineInterface $stateMachine,
WinzouStateMachineInterface $stateMachine,
): void {
$order->getShippingState()->willReturn(OrderShippingStates::STATE_SHIPPED);
$order->getPaymentState()->willReturn(Argument::notIn([OrderPaymentStates::STATE_PAID, OrderPaymentStates::STATE_PARTIALLY_REFUNDED]));

View file

@ -19,7 +19,8 @@ use Prophecy\Argument;
use Psr\Log\LoggerInterface;
use SM\Factory\Factory;
use SM\SMException;
use SM\StateMachine\StateMachineInterface;
use SM\StateMachine\StateMachineInterface as WinzouStateMachineInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\Component\Core\Updater\UnpaidOrdersStateUpdaterInterface;
use Sylius\Component\Order\Model\OrderInterface;
@ -55,9 +56,9 @@ final class UnpaidOrdersStateUpdaterSpec extends ObjectBehavior
OrderInterface $secondOrder,
OrderInterface $thirdOrder,
OrderRepositoryInterface $orderRepository,
StateMachineInterface $firstOrderStateMachine,
StateMachineInterface $secondOrderStateMachine,
StateMachineInterface $thirdOrderStateMachine,
WinzouStateMachineInterface $firstOrderStateMachine,
WinzouStateMachineInterface $secondOrderStateMachine,
WinzouStateMachineInterface $thirdOrderStateMachine,
): void {
$orderRepository->findOrdersUnpaidSince(Argument::type(\DateTimeInterface::class), 100)->willReturn(
[$firstOrder, $secondOrder],
@ -79,14 +80,47 @@ final class UnpaidOrdersStateUpdaterSpec extends ObjectBehavior
$this->cancel();
}
function it_uses_the_new_state_machine_abstraction_if_passed(
StateMachineInterface $stateMachine,
ObjectManager $objectManager,
OrderInterface $firstOrder,
OrderInterface $secondOrder,
OrderInterface $thirdOrder,
OrderRepositoryInterface $orderRepository,
): void {
$this->beConstructedWith(
$orderRepository,
$stateMachine,
'10 months',
null,
$objectManager,
100,
);
$orderRepository->findOrdersUnpaidSince(Argument::type(\DateTimeInterface::class), 100)->willReturn(
[$firstOrder, $secondOrder],
[$thirdOrder],
[],
);
$objectManager->flush()->shouldBeCalledTimes(2);
$objectManager->clear()->shouldBeCalledTimes(2);
$stateMachine->apply($firstOrder, 'sylius_order', OrderTransitions::TRANSITION_CANCEL)->shouldBeCalled();
$stateMachine->apply($secondOrder, 'sylius_order', OrderTransitions::TRANSITION_CANCEL)->shouldBeCalled();
$stateMachine->apply($thirdOrder, 'sylius_order', OrderTransitions::TRANSITION_CANCEL)->shouldBeCalled();
$this->cancel();
}
function it_wont_stop_cancelling_unpaid_orders_on_exception_for_a_single_order_and_logs_error(
Factory $stateMachineFactory,
ObjectManager $objectManager,
OrderInterface $firstOrder,
OrderInterface $secondOrder,
OrderRepositoryInterface $orderRepository,
StateMachineInterface $firstOrderStateMachine,
StateMachineInterface $secondOrderStateMachine,
WinzouStateMachineInterface $firstOrderStateMachine,
WinzouStateMachineInterface $secondOrderStateMachine,
LoggerInterface $logger,
): void {
$orderRepository->findOrdersUnpaidSince(Argument::type(\DateTimeInterface::class), 100)->willReturn(
@ -121,8 +155,8 @@ final class UnpaidOrdersStateUpdaterSpec extends ObjectBehavior
OrderInterface $firstOrder,
OrderInterface $secondOrder,
OrderRepositoryInterface $orderRepository,
StateMachineInterface $firstOrderStateMachine,
StateMachineInterface $secondOrderStateMachine,
WinzouStateMachineInterface $firstOrderStateMachine,
WinzouStateMachineInterface $secondOrderStateMachine,
): void {
$this->beConstructedWith($orderRepository, $stateMachineFactory, '10 months', null, $objectManager);
@ -150,8 +184,8 @@ final class UnpaidOrdersStateUpdaterSpec extends ObjectBehavior
OrderInterface $firstOrder,
OrderInterface $secondOrder,
OrderRepositoryInterface $orderRepository,
StateMachineInterface $firstOrderStateMachine,
StateMachineInterface $secondOrderStateMachine,
WinzouStateMachineInterface $firstOrderStateMachine,
WinzouStateMachineInterface $secondOrderStateMachine,
): void {
$this->beConstructedWith($orderRepository, $stateMachineFactory, '10 months', null, null, 100);