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: behat-no-js:
needs: get-matrix needs: get-matrix
runs-on: ubuntu-latest 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 timeout-minutes: 45
strategy: strategy:
fail-fast: false fail-fast: false
@ -53,6 +53,7 @@ jobs:
env: env:
APP_ENV: test_cached APP_ENV: test_cached
DATABASE_URL: "mysql://root:root@127.0.0.1/sylius?charset=utf8mb4&serverVersion=mariadb-${{ matrix.mariadb }}" 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: steps:
- name: Set variables - name: Set variables

View file

@ -19,13 +19,15 @@
{ {
"php": "8.0", "php": "8.0",
"symfony": "^5.4", "symfony": "^5.4",
"mariadb": "10.4.10" "mariadb": "10.4.10",
"state_machine_adapter": "winzou_state_machine"
}, },
{ {
"php": "8.2", "php": "8.2",
"symfony": "^6.4", "symfony": "^6.4",
"mariadb": "10.4.10", "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" ], "php": [ "8.0", "8.1", "8.2" ],
"symfony": [ "^5.4", "^6.4" ], "symfony": [ "^5.4", "^6.4" ],
"mariadb": [ "10.4.10" ], "mariadb": [ "10.4.10" ],
"state_machine_adapter": [ "winzou_state_machine", "symfony_workflow" ],
"exclude": [ "exclude": [
{ {
"php": "8.0", "php": "8.0",

View file

@ -1,5 +1,27 @@
# UPGRADE FROM `v1.12.X` TO `v1.13.0` # 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. 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. 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. 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' - '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. 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. 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. 1. Class `Sylius\Bundle\ProductBundle\Form\Type\ProductOptionChoiceType` has been deprecated.
Use `Sylius\Bundle\ProductBundle\Form\Type\ProductOptionAutocompleteType` instead. 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. Use the `parentCode` query parameter instead.
1. Starting with Sylius 1.13, the `SyliusPriceHistoryPlugin` is included. 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. 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` 1. Not passing `Sylius\Bundle\CoreBundle\CatalogPromotion\Announcer\CatalogPromotionRemovalAnnouncerInterface` to `Sylius\Bundle\CoreBundle\CatalogPromotion\Processor\CatalogPromotionRemovalProcessor`
as a second argument is deprecated. 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: sylius_user:
encoder: plaintext encoder: plaintext
sylius_api: sylius_api:
enabled: true 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"> <service id="sylius_abstraction.state_machine.adapter.symfony_workflow" class="Sylius\Abstraction\StateMachine\SymfonyWorkflowAdapter">
<argument type="service" id="workflow.registry" /> <argument type="service" id="workflow.registry" />
<tag name="sylius.state_machine" key="symfony_workflow" /> <tag name="sylius.state_machine" key="symfony_workflow" />
</service> </service>
<service id="Sylius\Abstraction\StateMachine\StateMachineInterface $symfonyWorkflow" alias="sylius_abstraction.state_machine.adapter.symfony_workflow" /> <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"> <service id="sylius_abstraction.state_machine.adapter.winzou_state_machine" class="Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter">
<argument type="service" id="sm.factory" /> <argument type="service" id="sm.factory" />
<tag name="sylius.state_machine" key="winzou_state_machine" /> <tag name="sylius.state_machine" key="winzou_state_machine" />
</service> </service>
<service id="Sylius\Abstraction\StateMachine\StateMachineInterface $winzouStateMachine" alias="sylius_abstraction.state_machine.adapter.winzou_state_machine" /> <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); 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 private function getStateMachineAdapter(string $graphName): StateMachineInterface
{ {
if (isset($this->graphsToAdaptersMapping[$graphName])) { if (isset($this->graphsToAdaptersMapping[$graphName])) {

View file

@ -21,22 +21,17 @@ final class Configuration implements ConfigurationInterface
{ {
public function getConfigTreeBuilder(): TreeBuilder public function getConfigTreeBuilder(): TreeBuilder
{ {
$treeBuilder = new TreeBuilder('sylius_abstraction'); $treeBuilder = new TreeBuilder('sylius_state_machine_abstraction');
/** @var ArrayNodeDefinition $rootNode */ /** @var ArrayNodeDefinition $rootNode */
$rootNode = $treeBuilder->getRootNode(); $rootNode = $treeBuilder->getRootNode();
$rootNode $rootNode
->addDefaultsIfNotSet() ->addDefaultsIfNotSet()
->children() ->children()
->arrayNode('state_machine') ->scalarNode('default_adapter')->defaultValue('winzou_state_machine')->end()
->addDefaultsIfNotSet() ->arrayNode('graphs_to_adapters_mapping')
->children() ->useAttributeAsKey('graph_name')
->scalarNode('default_adapter')->defaultValue('winzou_state_machine')->end() ->scalarPrototype()->end()
->arrayNode('graphs_to_adapters_mapping')
->useAttributeAsKey('graph_name')
->scalarPrototype()->end()
->end()
->end()
->end() ->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 = new XmlFileLoader($container, new FileLocator(dirname(__DIR__, 2) . '/config/'));
$loader->load('services.xml'); $loader->load('services.xml');
$container->setParameter('sylius_abstraction.state_machine.default_adapter', $config['state_machine']['default_adapter']); $container->setParameter('sylius_abstraction.state_machine.default_adapter', $config['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.graphs_to_adapters_mapping', $config['graphs_to_adapters_mapping']);
} }
} }

View file

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

View file

@ -35,4 +35,14 @@ interface StateMachineInterface
* @return array<TransitionInterface> * @return array<TransitionInterface>
*/ */
public function getEnabledTransitions(object $subject, string $graphName): array; 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, $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 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 { try {
$transitions = $this->getStateMachine($subject, $graphName)->getPossibleTransitions(); $transitionsConfig = $this->getConfig($stateMachine)['transitions'];
} catch (SMException $exception) { } catch (\ReflectionException $exception) {
throw new StateMachineExecutionException($exception->getMessage(), $exception->getCode(), $exception); throw new StateMachineExecutionException($exception->getMessage(), $exception->getCode(), $exception);
} }
return array_map( $transitions = [];
fn (string $transition) => new Transition($transition, null, null),
$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 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( $this->assertProcessedConfigurationEquals(
[ [
[ [
'state_machine' => [ 'default_adapter' => 'symfony_workflow',
'default_adapter' => 'symfony_workflow',
],
], ],
], ],
[ [
'state_machine' => [ 'default_adapter' => 'symfony_workflow',
'default_adapter' => 'symfony_workflow', 'graphs_to_adapters_mapping' => [],
'graphs_to_adapters_mapping' => [],
],
], ],
'state_machine',
); );
} }
@ -49,24 +44,19 @@ final class ConfigurationTest extends TestCase
$this->assertProcessedConfigurationEquals( $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' => [ 'graphs_to_adapters_mapping' => [
'order' => 'symfony_workflow', 'order' => 'symfony_workflow',
'payment' => 'winzou_state_machine', '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')); $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 private function createTestSubject(mixed ...$arguments): StateMachineInterface
{ {
return new CompositeStateMachine(...array_replace([ 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\MockObject\MockObject;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Sylius\Abstraction\StateMachine\Exception\StateMachineExecutionException; use Sylius\Abstraction\StateMachine\Exception\StateMachineExecutionException;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\SymfonyWorkflowAdapter; use Sylius\Abstraction\StateMachine\SymfonyWorkflowAdapter;
use Symfony\Component\Workflow\Exception\InvalidArgumentException; use Symfony\Component\Workflow\Exception\InvalidArgumentException;
use Symfony\Component\Workflow\Registry; use Symfony\Component\Workflow\Registry;
@ -150,13 +149,126 @@ final class SymfonyWorkflowAdapterTest extends TestCase
$this->createTestSubject()->getEnabledTransitions($subject, $graphName); $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); 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 PHPUnit\Framework\TestCase;
use SM\Factory\FactoryInterface; use SM\Factory\FactoryInterface;
use SM\SMException; 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\Exception\StateMachineExecutionException;
use Sylius\Abstraction\StateMachine\StateMachineInterface; use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter; use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
@ -27,14 +27,26 @@ final class WinzouStateMachineAdapterTest extends TestCase
/** @var FactoryInterface&MockObject */ /** @var FactoryInterface&MockObject */
private FactoryInterface $winzouStateMachineFactory; private FactoryInterface $winzouStateMachineFactory;
/** @var WinzouStateMachineInterface&MockObject */ /** @var WinzouStateMachine&MockObject */
private WinzouStateMachineInterface $winzouStateMachine; private WinzouStateMachine $winzouStateMachine;
protected function setUp(): void protected function setUp(): void
{ {
parent::setUp(); 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 = $this->createMock(FactoryInterface::class);
$this->winzouStateMachineFactory $this->winzouStateMachineFactory
@ -67,17 +79,20 @@ final class WinzouStateMachineAdapterTest extends TestCase
public function testItReturnsEnabledTransitions(): void public function testItReturnsEnabledTransitions(): void
{ {
$this->winzouStateMachine->method('getPossibleTransitions')->willReturn(['transition']);
$subject = new \stdClass(); $subject = new \stdClass();
$graphName = 'graph_name'; $graphName = 'graph_name';
$this->winzouStateMachine->method('can')->willReturnMap([
['transition', true],
['another_transition', false],
]);
$transitions = $this->createTestSubject()->getEnabledTransitions($subject, $graphName); $transitions = $this->createTestSubject()->getEnabledTransitions($subject, $graphName);
$this->assertCount(1, $transitions); $this->assertCount(1, $transitions);
$this->assertSame('transition', $transitions[0]->getName()); $this->assertSame('transition', $transitions[0]->getName());
$this->assertNull($transitions[0]->getFroms()); $this->assertSame(['from_state'], $transitions[0]->getFroms());
$this->assertNull($transitions[0]->getTos()); $this->assertSame(['to_state'], $transitions[0]->getTos());
} }
public function testItConvertsWorkflowExceptionsToCustomOnesOnCan(): void public function testItConvertsWorkflowExceptionsToCustomOnesOnCan(): void
@ -118,8 +133,67 @@ final class WinzouStateMachineAdapterTest extends TestCase
$this->createTestSubject()->getEnabledTransitions($subject, $graphName); $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 private function createTestSubject(): StateMachineInterface
{ {
return new WinzouStateMachineAdapter($this->winzouStateMachineFactory); 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\FactoryInterface;
use Knp\Menu\ItemInterface; 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\Bundle\UiBundle\Menu\Event\MenuBuilderEvent;
use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderInterface;
@ -25,9 +26,21 @@ class OrderShowMenuBuilderEvent extends MenuBuilderEvent
FactoryInterface $factory, FactoryInterface $factory,
ItemInterface $menu, ItemInterface $menu,
private OrderInterface $order, private OrderInterface $order,
private StateMachineInterface $stateMachine, private WinzouStateMachineInterface|StateMachineInterface $stateMachine,
) { ) {
parent::__construct($factory, $menu); 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 public function getOrder(): OrderInterface
@ -35,7 +48,7 @@ class OrderShowMenuBuilderEvent extends MenuBuilderEvent
return $this->order; return $this->order;
} }
public function getStateMachine(): StateMachineInterface public function getStateMachine(): WinzouStateMachineInterface|StateMachineInterface
{ {
return $this->stateMachine; return $this->stateMachine;
} }

View file

@ -16,6 +16,8 @@ namespace Sylius\Bundle\AdminBundle\Menu;
use Knp\Menu\FactoryInterface; use Knp\Menu\FactoryInterface;
use Knp\Menu\ItemInterface; use Knp\Menu\ItemInterface;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface; use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\AdminBundle\Event\OrderShowMenuBuilderEvent; use Sylius\Bundle\AdminBundle\Event\OrderShowMenuBuilderEvent;
use Sylius\Component\Order\OrderTransitions; use Sylius\Component\Order\OrderTransitions;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@ -28,9 +30,21 @@ final class OrderShowMenuBuilder
public function __construct( public function __construct(
private FactoryInterface $factory, private FactoryInterface $factory,
private EventDispatcherInterface $eventDispatcher, private EventDispatcherInterface $eventDispatcher,
private StateMachineFactoryInterface $stateMachineFactory, private StateMachineFactoryInterface|StateMachineInterface $stateMachineFactory,
private CsrfTokenManagerInterface $csrfTokenManager, 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 public function createMenu(array $options): ItemInterface
@ -50,25 +64,24 @@ final class OrderShowMenuBuilder
]) ])
->setAttribute('type', 'link') ->setAttribute('type', 'link')
->setLabel('sylius.ui.history') ->setLabel('sylius.ui.history')
->setLabelAttribute('icon', 'history') ->setLabelAttribute('icon', 'history');
;
$stateMachine = $this->stateMachineFactory->get($order, OrderTransitions::GRAPH); $stateMachine = $this->getStateMachine();
if ($stateMachine->can(OrderTransitions::TRANSITION_CANCEL)) {
if ($stateMachine->can($order, OrderTransitions::GRAPH, OrderTransitions::TRANSITION_CANCEL)) {
$menu $menu
->addChild('cancel', [ ->addChild('cancel', [
'route' => 'sylius_admin_order_cancel', 'route' => 'sylius_admin_order_cancel',
'routeParameters' => [ 'routeParameters' => [
'id' => $order->getId(), '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('type', 'transition')
->setAttribute('confirmation', true) ->setAttribute('confirmation', true)
->setLabel('sylius.ui.cancel') ->setLabel('sylius.ui.cancel')
->setLabelAttribute('icon', 'ban') ->setLabelAttribute('icon', 'ban')
->setLabelAttribute('color', 'yellow') ->setLabelAttribute('color', 'yellow');
;
} }
$this->eventDispatcher->dispatch( $this->eventDispatcher->dispatch(
@ -78,4 +91,13 @@ final class OrderShowMenuBuilder
return $menu; 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"> <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="knp_menu.factory" />
<argument type="service" id="event_dispatcher" /> <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" /> <argument type="service" id="security.csrf.token_manager" />
<tag name="knp_menu.menu_builder" method="createMenu" alias="sylius.admin.order.show" /> <tag name="knp_menu.menu_builder" method="createMenu" alias="sylius.admin.order.show" />
</service> </service>

View file

@ -16,7 +16,8 @@ namespace spec\Sylius\Bundle\AdminBundle\Event;
use Knp\Menu\FactoryInterface; use Knp\Menu\FactoryInterface;
use Knp\Menu\ItemInterface; use Knp\Menu\ItemInterface;
use PhpSpec\ObjectBehavior; 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\Bundle\UiBundle\Menu\Event\MenuBuilderEvent;
use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderInterface;
@ -26,7 +27,7 @@ final class OrderShowMenuBuilderEventSpec extends ObjectBehavior
FactoryInterface $factory, FactoryInterface $factory,
ItemInterface $menu, ItemInterface $menu,
OrderInterface $order, OrderInterface $order,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$this->beConstructedWith($factory, $menu, $order, $stateMachine); $this->beConstructedWith($factory, $menu, $order, $stateMachine);
} }
@ -41,8 +42,18 @@ final class OrderShowMenuBuilderEventSpec extends ObjectBehavior
$this->getOrder()->shouldReturn($order); $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); $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; namespace Sylius\Bundle\ApiBundle\Applicator;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface; 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\OrderInterface;
use Sylius\Component\Order\OrderTransitions; use Sylius\Component\Order\OrderTransitions;
/** @experimental */ /** @experimental */
final class OrderStateMachineTransitionApplicator implements OrderStateMachineTransitionApplicatorInterface 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 public function cancel(OrderInterface $data): OrderInterface
@ -33,7 +46,15 @@ final class OrderStateMachineTransitionApplicator implements OrderStateMachineTr
private function applyTransition(OrderInterface $order, string $transition): void private function applyTransition(OrderInterface $order, string $transition): void
{ {
$stateMachine = $this->stateMachineFactory->get($order, OrderTransitions::GRAPH); $this->getStateMachine()->apply($order, OrderTransitions::GRAPH, $transition);
$stateMachine->apply($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; namespace Sylius\Bundle\ApiBundle\Applicator;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface; 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\Model\PaymentInterface;
use Sylius\Component\Payment\PaymentTransitions; use Sylius\Component\Payment\PaymentTransitions;
/** @experimental */ /** @experimental */
final class PaymentStateMachineTransitionApplicator implements PaymentStateMachineTransitionApplicatorInterface 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 public function complete(PaymentInterface $data): PaymentInterface
@ -33,7 +46,15 @@ final class PaymentStateMachineTransitionApplicator implements PaymentStateMachi
private function applyTransition(PaymentInterface $payment, string $transition): void private function applyTransition(PaymentInterface $payment, string $transition): void
{ {
$stateMachine = $this->stateMachineFactory->get($payment, PaymentTransitions::GRAPH); $this->getStateMachine()->apply($payment, PaymentTransitions::GRAPH, $transition);
$stateMachine->apply($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; namespace Sylius\Bundle\ApiBundle\Applicator;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface; use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Component\Core\ProductReviewTransitions; use Sylius\Component\Core\ProductReviewTransitions;
use Sylius\Component\Review\Model\ReviewInterface; use Sylius\Component\Review\Model\ReviewInterface;
/** @experimental */ /** @experimental */
final class ProductReviewStateMachineTransitionApplicator implements ProductReviewStateMachineTransitionApplicatorInterface 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 public function accept(ReviewInterface $data): ReviewInterface
@ -40,7 +53,15 @@ final class ProductReviewStateMachineTransitionApplicator implements ProductRevi
private function applyTransition(ReviewInterface $review, string $transition): void private function applyTransition(ReviewInterface $review, string $transition): void
{ {
$stateMachine = $this->stateMachineFactory->get($review, ProductReviewTransitions::GRAPH); $this->getStateMachine()->apply($review, ProductReviewTransitions::GRAPH, $transition);
$stateMachine->apply($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; namespace Sylius\Bundle\ApiBundle\CommandHandler\Checkout;
use SM\Factory\FactoryInterface; use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\ApiBundle\Changer\PaymentMethodChangerInterface; use Sylius\Bundle\ApiBundle\Changer\PaymentMethodChangerInterface;
use Sylius\Bundle\ApiBundle\Command\Checkout\ChoosePaymentMethod; use Sylius\Bundle\ApiBundle\Command\Checkout\ChoosePaymentMethod;
use Sylius\Bundle\ApiBundle\Exception\PaymentMethodCannotBeChangedException; use Sylius\Bundle\ApiBundle\Exception\PaymentMethodCannotBeChangedException;
@ -33,9 +35,20 @@ final class ChoosePaymentMethodHandler implements MessageHandlerInterface
private OrderRepositoryInterface $orderRepository, private OrderRepositoryInterface $orderRepository,
private PaymentMethodRepositoryInterface $paymentMethodRepository, private PaymentMethodRepositoryInterface $paymentMethodRepository,
private PaymentRepositoryInterface $paymentRepository, private PaymentRepositoryInterface $paymentRepository,
private FactoryInterface $stateMachineFactory, private FactoryInterface|StateMachineInterface $stateMachineFactory,
private PaymentMethodChangerInterface $paymentMethodChanger, 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 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.'); Assert::notNull($payment, 'Can not find payment with given identifier.');
if ($cart->getState() === OrderInterface::STATE_CART) { if ($cart->getState() === OrderInterface::STATE_CART) {
$stateMachine = $this->stateMachineFactory->get($cart, OrderCheckoutTransitions::GRAPH); $stateMachine = $this->getStateMachine();
Assert::true( Assert::true(
$stateMachine->can( $stateMachine->can($cart, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT),
OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT,
),
'Order cannot have payment method assigned.', 'Order cannot have payment method assigned.',
); );
$payment->setMethod($paymentMethod); $payment->setMethod($paymentMethod);
$stateMachine->apply(OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT); $stateMachine->apply($cart, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT);
return $cart; return $cart;
} }
throw new PaymentMethodCannotBeChangedException(); 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; namespace Sylius\Bundle\ApiBundle\CommandHandler\Checkout;
use SM\Factory\FactoryInterface; use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\ApiBundle\Command\Checkout\ChooseShippingMethod; use Sylius\Bundle\ApiBundle\Command\Checkout\ChooseShippingMethod;
use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\ShippingMethodInterface; use Sylius\Component\Core\Model\ShippingMethodInterface;
@ -33,8 +35,19 @@ final class ChooseShippingMethodHandler implements MessageHandlerInterface
private ShippingMethodRepositoryInterface $shippingMethodRepository, private ShippingMethodRepositoryInterface $shippingMethodRepository,
private ShipmentRepositoryInterface $shipmentRepository, private ShipmentRepositoryInterface $shipmentRepository,
private ShippingMethodEligibilityCheckerInterface $eligibilityChecker, 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 public function __invoke(ChooseShippingMethod $chooseShippingMethod): OrderInterface
@ -44,10 +57,9 @@ final class ChooseShippingMethodHandler implements MessageHandlerInterface
Assert::notNull($cart, 'Cart has not been found.'); Assert::notNull($cart, 'Cart has not been found.');
$stateMachine = $this->stateMachineFactory->get($cart, OrderCheckoutTransitions::GRAPH); $stateMachine = $this->getStateMachine();
Assert::true( Assert::true(
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING), $stateMachine->can($cart, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING),
'Order cannot have shipment method assigned.', 'Order cannot have shipment method assigned.',
); );
@ -66,8 +78,17 @@ final class ChooseShippingMethodHandler implements MessageHandlerInterface
); );
$shipment->setMethod($shippingMethod); $shipment->setMethod($shippingMethod);
$stateMachine->apply(OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING); $stateMachine->apply($cart, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING);
return $cart; 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; namespace Sylius\Bundle\ApiBundle\CommandHandler\Checkout;
use SM\Factory\FactoryInterface; 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\Cart\InformAboutCartRecalculation;
use Sylius\Bundle\ApiBundle\Command\Checkout\CompleteOrder; use Sylius\Bundle\ApiBundle\Command\Checkout\CompleteOrder;
use Sylius\Bundle\ApiBundle\CommandHandler\Checkout\Exception\OrderTotalHasChangedException; use Sylius\Bundle\ApiBundle\CommandHandler\Checkout\Exception\OrderTotalHasChangedException;
@ -33,15 +35,26 @@ final class CompleteOrderHandler implements MessageHandlerInterface
{ {
public function __construct( public function __construct(
private OrderRepositoryInterface $orderRepository, private OrderRepositoryInterface $orderRepository,
private FactoryInterface $stateMachineFactory, private FactoryInterface|StateMachineInterface $stateMachineFactory,
private MessageBusInterface $commandBus, private MessageBusInterface $commandBus,
private MessageBusInterface $eventBus, private MessageBusInterface $eventBus,
private OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker, 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 * @throws OrderTotalHasChangedException
*/ */
public function __invoke(CompleteOrder $completeOrder): OrderInterface public function __invoke(CompleteOrder $completeOrder): OrderInterface
@ -73,17 +86,25 @@ final class CompleteOrderHandler implements MessageHandlerInterface
throw new OrderTotalHasChangedException(); throw new OrderTotalHasChangedException();
} }
$stateMachine = $this->stateMachineFactory->get($cart, OrderCheckoutTransitions::GRAPH); $stateMachine = $this->getStateMachine();
Assert::true( Assert::true(
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_COMPLETE), $stateMachine->can($cart, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_COMPLETE),
sprintf('Order with %s token cannot be completed.', $orderTokenValue), 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()]); $this->eventBus->dispatch(new OrderCompleted($cart->getTokenValue()), [new DispatchAfterCurrentBusStamp()]);
return $cart; 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; namespace Sylius\Bundle\ApiBundle\CommandHandler\Checkout;
use SM\Factory\FactoryInterface; 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\SendShipmentConfirmationEmail;
use Sylius\Bundle\ApiBundle\Command\Checkout\ShipShipment; use Sylius\Bundle\ApiBundle\Command\Checkout\ShipShipment;
use Sylius\Component\Core\Model\ShipmentInterface; use Sylius\Component\Core\Model\ShipmentInterface;
@ -29,9 +31,20 @@ final class ShipShipmentHandler implements MessageHandlerInterface
{ {
public function __construct( public function __construct(
private ShipmentRepositoryInterface $shipmentRepository, private ShipmentRepositoryInterface $shipmentRepository,
private FactoryInterface $stateMachineFactory, private FactoryInterface|StateMachineInterface $stateMachineFactory,
private MessageBusInterface $eventBus, 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 public function __invoke(ShipShipment $shipShipment): ShipmentInterface
@ -45,17 +58,25 @@ final class ShipShipmentHandler implements MessageHandlerInterface
$shipment->setTracking($shipShipment->trackingCode); $shipment->setTracking($shipShipment->trackingCode);
} }
$stateMachine = $this->stateMachineFactory->get($shipment, ShipmentTransitions::GRAPH); $stateMachine = $this->getStateMachine();
Assert::true( Assert::true(
$stateMachine->can(ShipmentTransitions::TRANSITION_SHIP), $stateMachine->can($shipment, ShipmentTransitions::GRAPH, ShipmentTransitions::TRANSITION_SHIP),
'This shipment cannot be completed.', '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()]); $this->eventBus->dispatch(new SendShipmentConfirmationEmail($shipShipment->shipmentId), [new DispatchAfterCurrentBusStamp()]);
return $shipment; 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; namespace Sylius\Bundle\ApiBundle\Modifier;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface; use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\ApiBundle\Mapper\AddressMapperInterface; use Sylius\Bundle\ApiBundle\Mapper\AddressMapperInterface;
use Sylius\Component\Core\Model\AddressInterface; use Sylius\Component\Core\Model\AddressInterface;
use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderInterface;
@ -23,9 +25,20 @@ use Webmozart\Assert\Assert;
final class OrderAddressModifier implements OrderAddressModifierInterface final class OrderAddressModifier implements OrderAddressModifierInterface
{ {
public function __construct( public function __construct(
private StateMachineFactoryInterface $stateMachineFactory, private StateMachineFactoryInterface|StateMachineInterface $stateMachineFactory,
private AddressMapperInterface $addressMapper, 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( public function modify(
@ -33,10 +46,9 @@ final class OrderAddressModifier implements OrderAddressModifierInterface
?AddressInterface $billingAddress, ?AddressInterface $billingAddress,
?AddressInterface $shippingAddress = null, ?AddressInterface $shippingAddress = null,
): OrderInterface { ): OrderInterface {
$stateMachine = $this->stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH); $stateMachine = $this->getStateMachine();
Assert::true( 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()), 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->setBillingAddress($order, $oldBillingAddress, $billingAddress);
$this->setShippingAddress($order, $oldShippingAddress, $shippingAddress); $this->setShippingAddress($order, $oldShippingAddress, $shippingAddress);
$stateMachine->apply(OrderCheckoutTransitions::TRANSITION_ADDRESS); $stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_ADDRESS);
return $order; return $order;
} }
@ -91,4 +103,13 @@ final class OrderAddressModifier implements OrderAddressModifierInterface
$order->setShippingAddress($shippingAddress); $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 # Sylius exception to status code mapping
SM\SMException: 422 SM\SMException: 422
Sylius\Abstraction\StateMachine\Exception\StateMachineExecutionException: 422
Sylius\Bundle\ApiBundle\Exception\CannotRemoveCurrentlyLoggedInUser: 422 Sylius\Bundle\ApiBundle\Exception\CannotRemoveCurrentlyLoggedInUser: 422
Sylius\Bundle\ApiBundle\Exception\ChannelCannotBeRemoved: 422 Sylius\Bundle\ApiBundle\Exception\ChannelCannotBeRemoved: 422
Sylius\Bundle\ApiBundle\Exception\ChannelPricingChannelCodeMismatchException: 422 Sylius\Bundle\ApiBundle\Exception\ChannelPricingChannelCodeMismatchException: 422

View file

@ -143,7 +143,7 @@
</service> </service>
<service id="Sylius\Bundle\ApiBundle\Modifier\OrderAddressModifierInterface" class="Sylius\Bundle\ApiBundle\Modifier\OrderAddressModifier" public="true"> <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\ApiBundle\Mapper\AddressMapperInterface" />
<argument type="service" id="Sylius\Bundle\CoreBundle\Resolver\CustomerResolverInterface" /> <argument type="service" id="Sylius\Bundle\CoreBundle\Resolver\CustomerResolverInterface" />
</service> </service>

View file

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

View file

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

View file

@ -14,6 +14,9 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Validator\Constraints; namespace Sylius\Bundle\ApiBundle\Validator\Constraints;
use SM\Factory\FactoryInterface; 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\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface;
use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\OrderCheckoutTransitions; use Sylius\Component\Core\OrderCheckoutTransitions;
@ -30,8 +33,19 @@ class CheckoutCompletionValidator extends ConstraintValidator
*/ */
public function __construct( public function __construct(
private OrderRepositoryInterface $orderRepository, 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 public function validate(mixed $value, Constraint $constraint): void
@ -41,20 +55,35 @@ class CheckoutCompletionValidator extends ConstraintValidator
/** @var CheckoutCompletion $constraint */ /** @var CheckoutCompletion $constraint */
Assert::isInstanceOf($constraint, CheckoutCompletion::class); Assert::isInstanceOf($constraint, CheckoutCompletion::class);
/** @var OrderInterface|null $order */
$order = $this->orderRepository->findOneBy(['tokenValue' => $value->getOrderTokenValue()]); $order = $this->orderRepository->findOneBy(['tokenValue' => $value->getOrderTokenValue()]);
/** @var OrderInterface $order */
Assert::isInstanceOf($order, OrderInterface::class); 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; return;
} }
$this->context->addViolation($constraint->message, [ $this->context->addViolation($constraint->message, [
'%currentState%' => $stateMachine->getState(), '%currentState%' => $order->getCheckoutState(),
'%possibleTransitions%' => implode(', ', $stateMachine->getPossibleTransitions()), '%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": { "autoload-dev": {
"psr-4": { "psr-4": {
"spec\\Sylius\\Bundle\\ApiBundle\\": "spec/",
"Sylius\\Bundle\\ApiBundle\\Application\\": "Tests/Application/src/", "Sylius\\Bundle\\ApiBundle\\Application\\": "Tests/Application/src/",
"Sylius\\Bundle\\ApiBundle\\spec\\": "spec/",
"Sylius\\Bundle\\ApiBundle\\Tests\\": "Tests/" "Sylius\\Bundle\\ApiBundle\\Tests\\": "Tests/"
} }
}, },

View file

@ -15,7 +15,8 @@ namespace spec\Sylius\Bundle\ApiBundle\Applicator;
use PhpSpec\ObjectBehavior; use PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface; 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\Core\Model\OrderInterface;
use Sylius\Component\Order\OrderTransitions; use Sylius\Component\Order\OrderTransitions;
@ -29,11 +30,22 @@ final class OrderStateMachineTransitionApplicatorSpec extends ObjectBehavior
function it_cancels_order( function it_cancels_order(
StateMachineFactoryInterface $stateMachineFactory, StateMachineFactoryInterface $stateMachineFactory,
OrderInterface $order, OrderInterface $order,
StateMachine $stateMachine, WinzouStateMachine $stateMachine,
): void { ): void {
$stateMachineFactory->get($order, OrderTransitions::GRAPH)->willReturn($stateMachine); $stateMachineFactory->get($order, OrderTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->apply(OrderTransitions::TRANSITION_CANCEL)->shouldBeCalled(); $stateMachine->apply(OrderTransitions::TRANSITION_CANCEL)->shouldBeCalled();
$this->cancel($order); $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 PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface; 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\Core\Model\PaymentInterface;
use Sylius\Component\Payment\PaymentTransitions; use Sylius\Component\Payment\PaymentTransitions;
@ -29,11 +30,22 @@ final class PaymentStateMachineTransitionApplicatorSpec extends ObjectBehavior
function it_completes_payment( function it_completes_payment(
StateMachineFactoryInterface $stateMachineFactory, StateMachineFactoryInterface $stateMachineFactory,
PaymentInterface $payment, PaymentInterface $payment,
StateMachine $stateMachine, WinzouStateMachine $stateMachine,
): void { ): void {
$stateMachineFactory->get($payment, PaymentTransitions::GRAPH)->willReturn($stateMachine); $stateMachineFactory->get($payment, PaymentTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->apply(PaymentTransitions::TRANSITION_COMPLETE)->shouldBeCalled(); $stateMachine->apply(PaymentTransitions::TRANSITION_COMPLETE)->shouldBeCalled();
$this->complete($payment); $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 PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface; 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\Core\ProductReviewTransitions;
use Sylius\Component\Review\Model\ReviewInterface; use Sylius\Component\Review\Model\ReviewInterface;
@ -29,7 +30,7 @@ final class ProductReviewStateMachineTransitionApplicatorSpec extends ObjectBeha
public function it_accepts_product_review( public function it_accepts_product_review(
StateMachineFactoryInterface $stateMachineFactory, StateMachineFactoryInterface $stateMachineFactory,
ReviewInterface $review, ReviewInterface $review,
StateMachine $stateMachine, WinzouStateMachine $stateMachine,
): void { ): void {
$stateMachineFactory->get($review, ProductReviewTransitions::GRAPH)->willReturn($stateMachine); $stateMachineFactory->get($review, ProductReviewTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->apply(ProductReviewTransitions::TRANSITION_ACCEPT)->shouldBeCalled(); $stateMachine->apply(ProductReviewTransitions::TRANSITION_ACCEPT)->shouldBeCalled();
@ -37,14 +38,36 @@ final class ProductReviewStateMachineTransitionApplicatorSpec extends ObjectBeha
$this->accept($review); $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( public function it_rejects_product_review(
StateMachineFactoryInterface $stateMachineFactory, StateMachineFactoryInterface $stateMachineFactory,
ReviewInterface $review, ReviewInterface $review,
StateMachine $stateMachine, WinzouStateMachine $stateMachine,
): void { ): void {
$stateMachineFactory->get($review, ProductReviewTransitions::GRAPH)->willReturn($stateMachine); $stateMachineFactory->get($review, ProductReviewTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->apply(ProductReviewTransitions::TRANSITION_REJECT)->shouldBeCalled(); $stateMachine->apply(ProductReviewTransitions::TRANSITION_REJECT)->shouldBeCalled();
$this->reject($review); $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 PhpSpec\ObjectBehavior;
use Prophecy\Argument; use Prophecy\Argument;
use SM\Factory\FactoryInterface; 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\Changer\PaymentMethodChangerInterface;
use Sylius\Bundle\ApiBundle\Command\Checkout\ChoosePaymentMethod; use Sylius\Bundle\ApiBundle\Command\Checkout\ChoosePaymentMethod;
use Sylius\Bundle\ApiBundle\Exception\PaymentMethodCannotBeChangedException; use Sylius\Bundle\ApiBundle\Exception\PaymentMethodCannotBeChangedException;
@ -55,7 +56,7 @@ final class ChoosePaymentMethodHandlerSpec extends ObjectBehavior
OrderInterface $cart, OrderInterface $cart,
PaymentInterface $payment, PaymentInterface $payment,
PaymentMethodInterface $paymentMethod, PaymentMethodInterface $paymentMethod,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$choosePaymentMethod = new ChoosePaymentMethod('CASH_ON_DELIVERY_METHOD'); $choosePaymentMethod = new ChoosePaymentMethod('CASH_ON_DELIVERY_METHOD');
$choosePaymentMethod->setOrderTokenValue('ORDERTOKEN'); $choosePaymentMethod->setOrderTokenValue('ORDERTOKEN');
@ -82,6 +83,41 @@ final class ChoosePaymentMethodHandlerSpec extends ObjectBehavior
$this($choosePaymentMethod)->shouldReturn($cart); $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( function it_throws_an_exception_if_order_with_given_token_has_not_been_found(
OrderRepositoryInterface $orderRepository, OrderRepositoryInterface $orderRepository,
PaymentInterface $payment, PaymentInterface $payment,
@ -105,7 +141,7 @@ final class ChoosePaymentMethodHandlerSpec extends ObjectBehavior
PaymentMethodRepositoryInterface $paymentMethodRepository, PaymentMethodRepositoryInterface $paymentMethodRepository,
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
OrderInterface $cart, OrderInterface $cart,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
PaymentInterface $payment, PaymentInterface $payment,
): void { ): void {
$choosePaymentMethod = new ChoosePaymentMethod('CASH_ON_DELIVERY_METHOD'); $choosePaymentMethod = new ChoosePaymentMethod('CASH_ON_DELIVERY_METHOD');
@ -134,7 +170,7 @@ final class ChoosePaymentMethodHandlerSpec extends ObjectBehavior
PaymentMethodRepositoryInterface $paymentMethodRepository, PaymentMethodRepositoryInterface $paymentMethodRepository,
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
OrderInterface $cart, OrderInterface $cart,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
PaymentInterface $payment, PaymentInterface $payment,
): void { ): void {
$choosePaymentMethod = new ChoosePaymentMethod('CASH_ON_DELIVERY_METHOD'); $choosePaymentMethod = new ChoosePaymentMethod('CASH_ON_DELIVERY_METHOD');
@ -165,7 +201,7 @@ final class ChoosePaymentMethodHandlerSpec extends ObjectBehavior
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
OrderInterface $cart, OrderInterface $cart,
PaymentMethodInterface $paymentMethod, PaymentMethodInterface $paymentMethod,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$choosePaymentMethod = new ChoosePaymentMethod('CASH_ON_DELIVERY_METHOD'); $choosePaymentMethod = new ChoosePaymentMethod('CASH_ON_DELIVERY_METHOD');
$choosePaymentMethod->setOrderTokenValue('ORDERTOKEN'); $choosePaymentMethod->setOrderTokenValue('ORDERTOKEN');

View file

@ -17,7 +17,8 @@ use Doctrine\Common\Collections\ArrayCollection;
use PhpSpec\ObjectBehavior; use PhpSpec\ObjectBehavior;
use Prophecy\Argument; use Prophecy\Argument;
use SM\Factory\FactoryInterface; 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\Bundle\ApiBundle\Command\Checkout\ChooseShippingMethod;
use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\ShipmentInterface; use Sylius\Component\Core\Model\ShipmentInterface;
@ -55,7 +56,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
OrderInterface $cart, OrderInterface $cart,
ShippingMethodInterface $shippingMethod, ShippingMethodInterface $shippingMethod,
ShipmentInterface $shipment, ShipmentInterface $shipment,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD'); $chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD');
$chooseShippingMethod->setOrderTokenValue('ORDERTOKEN'); $chooseShippingMethod->setOrderTokenValue('ORDERTOKEN');
@ -82,6 +83,48 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
$this($chooseShippingMethod)->shouldReturn($cart); $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( function it_throws_an_exception_if_shipping_method_is_not_eligible(
OrderRepositoryInterface $orderRepository, OrderRepositoryInterface $orderRepository,
ShippingMethodRepositoryInterface $shippingMethodRepository, ShippingMethodRepositoryInterface $shippingMethodRepository,
@ -91,7 +134,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
OrderInterface $cart, OrderInterface $cart,
ShippingMethodInterface $shippingMethod, ShippingMethodInterface $shippingMethod,
ShipmentInterface $shipment, ShipmentInterface $shipment,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD'); $chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD');
$chooseShippingMethod->setOrderTokenValue('ORDERTOKEN'); $chooseShippingMethod->setOrderTokenValue('ORDERTOKEN');
@ -143,7 +186,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
ShippingMethodRepositoryInterface $shippingMethodRepository, ShippingMethodRepositoryInterface $shippingMethodRepository,
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
OrderInterface $cart, OrderInterface $cart,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
ShipmentInterface $shipment, ShipmentInterface $shipment,
): void { ): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD'); $chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD');
@ -169,7 +212,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
ShippingMethodRepositoryInterface $shippingMethodRepository, ShippingMethodRepositoryInterface $shippingMethodRepository,
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
OrderInterface $cart, OrderInterface $cart,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
ShipmentInterface $shipment, ShipmentInterface $shipment,
): void { ): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD'); $chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD');
@ -199,7 +242,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
OrderInterface $cart, OrderInterface $cart,
ShippingMethodInterface $shippingMethod, ShippingMethodInterface $shippingMethod,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD'); $chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD');
$chooseShippingMethod->setOrderTokenValue('ORDERTOKEN'); $chooseShippingMethod->setOrderTokenValue('ORDERTOKEN');

View file

@ -15,7 +15,8 @@ namespace spec\Sylius\Bundle\ApiBundle\CommandHandler\Checkout;
use PhpSpec\ObjectBehavior; use PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface; 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\Cart\InformAboutCartRecalculation;
use Sylius\Bundle\ApiBundle\Command\Checkout\CompleteOrder; use Sylius\Bundle\ApiBundle\Command\Checkout\CompleteOrder;
use Sylius\Bundle\ApiBundle\CommandHandler\Checkout\Exception\OrderTotalHasChangedException; use Sylius\Bundle\ApiBundle\CommandHandler\Checkout\Exception\OrderTotalHasChangedException;
@ -47,7 +48,7 @@ final class CompleteOrderHandlerSpec extends ObjectBehavior
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
MessageBusInterface $eventBus, MessageBusInterface $eventBus,
OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker, OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
OrderInterface $order, OrderInterface $order,
CustomerInterface $customer, CustomerInterface $customer,
): void { ): void {
@ -80,12 +81,51 @@ final class CompleteOrderHandlerSpec extends ObjectBehavior
$this($completeOrder)->shouldReturn($order); $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( function it_handles_order_completion_with_notes(
OrderRepositoryInterface $orderRepository, OrderRepositoryInterface $orderRepository,
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
MessageBusInterface $eventBus, MessageBusInterface $eventBus,
OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker, OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
OrderInterface $order, OrderInterface $order,
CustomerInterface $customer, CustomerInterface $customer,
): void { ): void {
@ -190,7 +230,7 @@ final class CompleteOrderHandlerSpec extends ObjectBehavior
OrderRepositoryInterface $orderRepository, OrderRepositoryInterface $orderRepository,
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker, OrderPromotionsIntegrityCheckerInterface $orderPromotionsIntegrityChecker,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
OrderInterface $order, OrderInterface $order,
CustomerInterface $customer, CustomerInterface $customer,
): void { ): void {

View file

@ -15,7 +15,8 @@ namespace spec\Sylius\Bundle\ApiBundle\CommandHandler\Checkout;
use PhpSpec\ObjectBehavior; use PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface; 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\SendShipmentConfirmationEmail;
use Sylius\Bundle\ApiBundle\Command\Checkout\ShipShipment; use Sylius\Bundle\ApiBundle\Command\Checkout\ShipShipment;
use Sylius\Component\Core\Model\ShipmentInterface; use Sylius\Component\Core\Model\ShipmentInterface;
@ -37,7 +38,7 @@ final class ShipShipmentHandlerSpec extends ObjectBehavior
function it_handles_shipping_without_tracking_number( function it_handles_shipping_without_tracking_number(
ShipmentRepositoryInterface $shipmentRepository, ShipmentRepositoryInterface $shipmentRepository,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
ShipmentInterface $shipment, ShipmentInterface $shipment,
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
MessageBusInterface $eventBus, MessageBusInterface $eventBus,
@ -67,7 +68,7 @@ final class ShipShipmentHandlerSpec extends ObjectBehavior
function it_handles_shipping_with_tracking_number( function it_handles_shipping_with_tracking_number(
ShipmentRepositoryInterface $shipmentRepository, ShipmentRepositoryInterface $shipmentRepository,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
ShipmentInterface $shipment, ShipmentInterface $shipment,
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
MessageBusInterface $eventBus, MessageBusInterface $eventBus,
@ -95,6 +96,35 @@ final class ShipShipmentHandlerSpec extends ObjectBehavior
$this($shipShipment)->shouldReturn($shipment); $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( function it_throws_an_exception_if_shipment_does_not_exist(
ShipmentRepositoryInterface $shipmentRepository, ShipmentRepositoryInterface $shipmentRepository,
): void { ): void {
@ -111,7 +141,7 @@ final class ShipShipmentHandlerSpec extends ObjectBehavior
function it_throws_an_exception_if_shipment_cannot_be_shipped( function it_throws_an_exception_if_shipment_cannot_be_shipped(
ShipmentRepositoryInterface $shipmentRepository, ShipmentRepositoryInterface $shipmentRepository,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
ShipmentInterface $shipment, ShipmentInterface $shipment,
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
): void { ): void {

View file

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

View file

@ -16,7 +16,8 @@ namespace spec\Sylius\Bundle\ApiBundle\Validator\Constraints;
use PhpSpec\ObjectBehavior; use PhpSpec\ObjectBehavior;
use Prophecy\Argument; use Prophecy\Argument;
use SM\Factory\FactoryInterface; 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\Command\OrderTokenValueAwareInterface;
use Sylius\Bundle\ApiBundle\Validator\Constraints\CheckoutCompletion; use Sylius\Bundle\ApiBundle\Validator\Constraints\CheckoutCompletion;
use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderInterface;
@ -79,7 +80,7 @@ final class CheckoutCompletionValidatorSpec extends ObjectBehavior
ExecutionContextInterface $executionContext, ExecutionContextInterface $executionContext,
OrderInterface $order, OrderInterface $order,
OrderTokenValueAwareInterface $orderTokenValueAware, OrderTokenValueAwareInterface $orderTokenValueAware,
StateMachineInterface $stateMachine, WinzouStateMachineStub $stateMachine,
): void { ): void {
$this->initialize($executionContext); $this->initialize($executionContext);
@ -105,8 +106,27 @@ final class CheckoutCompletionValidatorSpec extends ObjectBehavior
ExecutionContextInterface $executionContext, ExecutionContextInterface $executionContext,
OrderInterface $order, OrderInterface $order,
OrderTokenValueAwareInterface $orderTokenValueAware, OrderTokenValueAwareInterface $orderTokenValueAware,
StateMachineInterface $stateMachine, WinzouStateMachineStub $stateMachine,
): void { ): 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); $this->initialize($executionContext);
$orderTokenValueAware->getOrderTokenValue()->willReturn('xxx'); $orderTokenValueAware->getOrderTokenValue()->willReturn('xxx');
@ -114,8 +134,8 @@ final class CheckoutCompletionValidatorSpec extends ObjectBehavior
$stateMachineFactory->get($order, 'sylius_order_checkout')->willReturn($stateMachine); $stateMachineFactory->get($order, 'sylius_order_checkout')->willReturn($stateMachine);
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_COMPLETE)->willReturn(false); $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 $executionContext
->addViolation( ->addViolation(
@ -130,4 +150,48 @@ final class CheckoutCompletionValidatorSpec extends ObjectBehavior
$this->validate($orderTokenValueAware, new CheckoutCompletion()); $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; namespace Sylius\Bundle\CoreBundle\CatalogPromotion\Processor;
use SM\Factory\FactoryInterface; use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\CoreBundle\CatalogPromotion\Checker\CatalogPromotionEligibilityCheckerInterface; use Sylius\Bundle\CoreBundle\CatalogPromotion\Checker\CatalogPromotionEligibilityCheckerInterface;
use Sylius\Component\Core\Model\CatalogPromotionInterface; use Sylius\Component\Core\Model\CatalogPromotionInterface;
use Sylius\Component\Promotion\Model\CatalogPromotionTransitions; use Sylius\Component\Promotion\Model\CatalogPromotionTransitions;
@ -22,30 +24,50 @@ final class CatalogPromotionStateProcessor implements CatalogPromotionStateProce
{ {
public function __construct( public function __construct(
private CatalogPromotionEligibilityCheckerInterface $catalogPromotionEligibilityChecker, 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 public function process(CatalogPromotionInterface $catalogPromotion): void
{ {
$stateMachine = $this->stateMachineFactory->get($catalogPromotion, CatalogPromotionTransitions::GRAPH); $stateMachine = $this->getStateMachine();
if ($stateMachine->can(CatalogPromotionTransitions::TRANSITION_PROCESS)) { if ($stateMachine->can($catalogPromotion, CatalogPromotionTransitions::GRAPH, CatalogPromotionTransitions::TRANSITION_PROCESS)) {
$stateMachine->apply(CatalogPromotionTransitions::TRANSITION_PROCESS); $stateMachine->apply($catalogPromotion, CatalogPromotionTransitions::GRAPH, CatalogPromotionTransitions::TRANSITION_PROCESS);
return; return;
} }
if (!$this->catalogPromotionEligibilityChecker->isCatalogPromotionEligible($catalogPromotion)) { if (!$this->catalogPromotionEligibilityChecker->isCatalogPromotionEligible($catalogPromotion)) {
if ($stateMachine->can(CatalogPromotionTransitions::TRANSITION_DEACTIVATE)) { if ($stateMachine->can($catalogPromotion, CatalogPromotionTransitions::GRAPH, CatalogPromotionTransitions::TRANSITION_DEACTIVATE)) {
$stateMachine->apply(CatalogPromotionTransitions::TRANSITION_DEACTIVATE); $stateMachine->apply($catalogPromotion, CatalogPromotionTransitions::GRAPH, CatalogPromotionTransitions::TRANSITION_DEACTIVATE);
} }
return; return;
} }
if ($stateMachine->can(CatalogPromotionTransitions::TRANSITION_ACTIVATE)) { if ($stateMachine->can($catalogPromotion, CatalogPromotionTransitions::GRAPH, CatalogPromotionTransitions::TRANSITION_ACTIVATE)) {
$stateMachine->apply(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; namespace Sylius\Bundle\CoreBundle\Checkout;
use SM\Factory\FactoryInterface; use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Order\Context\CartContextInterface; use Sylius\Component\Order\Context\CartContextInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@ -29,8 +31,19 @@ final class CheckoutResolver implements EventSubscriberInterface
private CartContextInterface $cartContext, private CartContextInterface $cartContext,
private CheckoutStateUrlGeneratorInterface $urlGenerator, private CheckoutStateUrlGeneratorInterface $urlGenerator,
private RequestMatcherInterface $requestMatcher, 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 public static function getSubscribedEvents(): array
@ -73,9 +86,7 @@ final class CheckoutResolver implements EventSubscriberInterface
return; return;
} }
$stateMachine = $this->stateMachineFactory->get($order, $graph); if ($this->getStateMachine()->can($order, $graph, $transition)) {
if ($stateMachine->can($transition)) {
return; return;
} }
@ -91,4 +102,13 @@ final class CheckoutResolver implements EventSubscriberInterface
{ {
return $request->attributes->get('_sylius', [])['state_machine']['transition'] ?? null; 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\Factory;
use Faker\Generator; use Faker\Generator;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface; 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\Bundle\CoreBundle\Fixture\OptionsResolver\LazyOption;
use Sylius\Component\Addressing\Model\CountryInterface; use Sylius\Component\Addressing\Model\CountryInterface;
use Sylius\Component\Core\Checker\OrderPaymentMethodSelectionRequirementCheckerInterface; use Sylius\Component\Core\Checker\OrderPaymentMethodSelectionRequirementCheckerInterface;
@ -61,13 +63,25 @@ class OrderExampleFactory extends AbstractExampleFactory implements ExampleFacto
protected PaymentMethodRepositoryInterface $paymentMethodRepository, protected PaymentMethodRepositoryInterface $paymentMethodRepository,
protected ShippingMethodRepositoryInterface $shippingMethodRepository, protected ShippingMethodRepositoryInterface $shippingMethodRepository,
protected FactoryInterface $addressFactory, protected FactoryInterface $addressFactory,
protected StateMachineFactoryInterface $stateMachineFactory, protected StateMachineFactoryInterface|StateMachineInterface $stateMachineFactory,
protected OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker, protected OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
protected OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker, protected OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
) { ) {
$this->optionsResolver = new OptionsResolver(); $this->optionsResolver = new OptionsResolver();
$this->faker = Factory::create(); $this->faker = Factory::create();
$this->configureOptions($this->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 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 public function create(array $options = []): OrderInterface
@ -251,7 +265,7 @@ class OrderExampleFactory extends AbstractExampleFactory implements ExampleFacto
protected function applyCheckoutStateTransition(OrderInterface $order, string $transition): void 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 protected function generateInvalidSkipMessage(string $type, string $channelCode): string
@ -281,21 +295,32 @@ class OrderExampleFactory extends AbstractExampleFactory implements ExampleFacto
protected function completePayments(OrderInterface $order): void protected function completePayments(OrderInterface $order): void
{ {
$stateMachine = $this->getStateMachine();
foreach ($order->getPayments() as $payment) { foreach ($order->getPayments() as $payment) {
$stateMachine = $this->stateMachineFactory->get($payment, PaymentTransitions::GRAPH); if ($stateMachine->can($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_COMPLETE)) {
if ($stateMachine->can(PaymentTransitions::TRANSITION_COMPLETE)) { $stateMachine->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_COMPLETE);
$stateMachine->apply(PaymentTransitions::TRANSITION_COMPLETE);
} }
} }
} }
protected function completeShipments(OrderInterface $order): void protected function completeShipments(OrderInterface $order): void
{ {
$stateMachine = $this->getStateMachine();
foreach ($order->getShipments() as $shipment) { foreach ($order->getShipments() as $shipment) {
$stateMachine = $this->stateMachineFactory->get($shipment, ShipmentTransitions::GRAPH); if ($stateMachine->can($shipment, ShipmentTransitions::GRAPH, ShipmentTransitions::TRANSITION_SHIP)) {
if ($stateMachine->can(ShipmentTransitions::TRANSITION_SHIP)) { $stateMachine->apply($shipment, ShipmentTransitions::GRAPH, ShipmentTransitions::TRANSITION_SHIP);
$stateMachine->apply(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\Factory;
use Faker\Generator; use Faker\Generator;
use SM\Factory\FactoryInterface; use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\CoreBundle\Fixture\OptionsResolver\LazyOption; use Sylius\Bundle\CoreBundle\Fixture\OptionsResolver\LazyOption;
use Sylius\Component\Core\Repository\CustomerRepositoryInterface; use Sylius\Component\Core\Repository\CustomerRepositoryInterface;
use Sylius\Component\Core\Repository\ProductRepositoryInterface; use Sylius\Component\Core\Repository\ProductRepositoryInterface;
use Sylius\Component\Resource\StateMachine\StateMachineInterface;
use Sylius\Component\Review\Factory\ReviewFactoryInterface; use Sylius\Component\Review\Factory\ReviewFactoryInterface;
use Sylius\Component\Review\Model\ReviewInterface; use Sylius\Component\Review\Model\ReviewInterface;
use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\Options;
@ -35,12 +36,24 @@ class ProductReviewExampleFactory extends AbstractExampleFactory implements Exam
private ReviewFactoryInterface $productReviewFactory, private ReviewFactoryInterface $productReviewFactory,
private ProductRepositoryInterface $productRepository, private ProductRepositoryInterface $productRepository,
private CustomerRepositoryInterface $customerRepository, private CustomerRepositoryInterface $customerRepository,
private FactoryInterface $stateMachineFactory, private FactoryInterface|StateMachineInterface $stateMachineFactory,
) { ) {
$this->faker = Factory::create(); $this->faker = Factory::create();
$this->optionsResolver = new OptionsResolver(); $this->optionsResolver = new OptionsResolver();
$this->configureOptions($this->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 public function create(array $options = []): ReviewInterface
@ -95,12 +108,21 @@ class ProductReviewExampleFactory extends AbstractExampleFactory implements Exam
private function applyReviewTransition(ReviewInterface $productReview, string $targetState): void private function applyReviewTransition(ReviewInterface $productReview, string $targetState): void
{ {
/** @var StateMachineInterface $stateMachine */ $stateMachine = $this->getStateMachine();
$stateMachine = $this->stateMachineFactory->get($productReview, 'sylius_product_review');
$transition = $stateMachine->getTransitionToState($targetState); $transition = $stateMachine->getTransitionToState($productReview, 'sylius_product_review', $targetState);
if (null !== $transition) { 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\Factory;
use Faker\Generator; use Faker\Generator;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface; use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Bundle\CoreBundle\Fixture\Factory\OrderExampleFactory; use Sylius\Bundle\CoreBundle\Fixture\Factory\OrderExampleFactory;
use Sylius\Bundle\FixturesBundle\Fixture\AbstractFixture; use Sylius\Bundle\FixturesBundle\Fixture\AbstractFixture;
use Sylius\Component\Core\Checker\OrderPaymentMethodSelectionRequirementCheckerInterface; use Sylius\Component\Core\Checker\OrderPaymentMethodSelectionRequirementCheckerInterface;
@ -52,7 +53,7 @@ class OrderFixture extends AbstractFixture
PaymentMethodRepositoryInterface $paymentMethodRepository, PaymentMethodRepositoryInterface $paymentMethodRepository,
ShippingMethodRepositoryInterface $shippingMethodRepository, ShippingMethodRepositoryInterface $shippingMethodRepository,
FactoryInterface $addressFactory, FactoryInterface $addressFactory,
StateMachineFactoryInterface $stateMachineFactory, StateMachineFactoryInterface|StateMachineInterface $stateMachineFactory,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker, OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker, OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderExampleFactory $orderExampleFactory = null, OrderExampleFactory $orderExampleFactory = null,

View file

@ -17,6 +17,8 @@ use Doctrine\Persistence\ObjectManager;
use Faker\Factory; use Faker\Factory;
use Faker\Generator; use Faker\Generator;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface; use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\FixturesBundle\Fixture\AbstractFixture; use Sylius\Bundle\FixturesBundle\Fixture\AbstractFixture;
use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Core\Repository\PaymentRepositoryInterface; use Sylius\Component\Core\Repository\PaymentRepositoryInterface;
@ -45,13 +47,25 @@ class PaymentFixture extends AbstractFixture
*/ */
public function __construct( public function __construct(
private PaymentRepositoryInterface $paymentRepository, private PaymentRepositoryInterface $paymentRepository,
private StateMachineFactoryInterface $stateMachineFactory, private StateMachineFactoryInterface|StateMachineInterface $stateMachineFactory,
private ObjectManager $paymentManager, private ObjectManager $paymentManager,
) { ) {
$this->faker = Factory::create(); $this->faker = Factory::create();
$this->optionsResolver = new OptionsResolver(); $this->optionsResolver = new OptionsResolver();
$this->configureOptions($this->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 public function getName(): string
@ -88,11 +102,7 @@ class PaymentFixture extends AbstractFixture
private function completePayment(PaymentInterface $payment): void private function completePayment(PaymentInterface $payment): void
{ {
$this $this->getStateMachine()->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_COMPLETE);
->stateMachineFactory
->get($payment, PaymentTransitions::GRAPH)
->apply(PaymentTransitions::TRANSITION_COMPLETE)
;
$this->paymentManager->persist($payment); $this->paymentManager->persist($payment);
} }
@ -104,4 +114,13 @@ class PaymentFixture extends AbstractFixture
->setAllowedTypes('percentage_completed', 'int') ->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: ~ resources: ~
statistics: ~ statistics: ~
taxon: ~ 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"> <service id="sylius.unpaid_orders_state_updater" class="Sylius\Component\Core\Updater\UnpaidOrdersStateUpdater">
<argument type="service" id="sylius.repository.order" /> <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>%sylius_order.order_expiration_period%</argument>
<argument type="service" id="logger" /> <argument type="service" id="logger" />
<argument type="service" id="sylius.manager.order" /> <argument type="service" id="sylius.manager.order" />
@ -215,7 +215,7 @@
<service id="sylius.order_payment_provider" class="Sylius\Component\Core\Payment\Provider\OrderPaymentProvider"> <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.payment_method_resolver.default" />
<argument type="service" id="sylius.factory.payment" /> <argument type="service" id="sylius.factory.payment" />
<argument type="service" id="sm.factory" /> <argument type="service" id="sylius_abstraction.state_machine" />
</service> </service>
<service id="Sylius\Component\Core\Payment\Provider\OrderPaymentProviderInterface" alias="sylius.order_payment_provider" /> <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.payment_method" />
<argument type="service" id="sylius.repository.shipping_method" /> <argument type="service" id="sylius.repository.shipping_method" />
<argument type="service" id="sylius.factory.address" /> <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_shipping_method_selection_requirement" />
<argument type="service" id="sylius.checker.order_payment_method_selection_requirement" /> <argument type="service" id="sylius.checker.order_payment_method_selection_requirement" />
<argument type="service" id="sylius.fixture.example_factory.order" /> <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.factory.product_review" />
<argument type="service" id="sylius.repository.product" /> <argument type="service" id="sylius.repository.product" />
<argument type="service" id="sylius.repository.customer" /> <argument type="service" id="sylius.repository.customer" />
<argument type="service" id="sm.factory" /> <argument type="service" id="sylius_abstraction.state_machine" />
</service> </service>
<service id="sylius.fixture.example_factory.tax_category" class="Sylius\Bundle\CoreBundle\Fixture\Factory\TaxCategoryExampleFactory"> <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.payment_method" />
<argument type="service" id="sylius.repository.shipping_method" /> <argument type="service" id="sylius.repository.shipping_method" />
<argument type="service" id="sylius.factory.address" /> <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_shipping_method_selection_requirement" />
<argument type="service" id="sylius.checker.order_payment_method_selection_requirement" /> <argument type="service" id="sylius.checker.order_payment_method_selection_requirement" />
</service> </service>
<service id="Sylius\Bundle\CoreBundle\Fixture\PaymentFixture"> <service id="Sylius\Bundle\CoreBundle\Fixture\PaymentFixture">
<argument type="service" id="sylius.repository.payment" /> <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" /> <argument type="service" id="sylius.manager.payment" />
<tag name="sylius_fixtures.fixture" /> <tag name="sylius_fixtures.fixture" />
</service> </service>

View file

@ -15,82 +15,82 @@
<services> <services>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\AssignOrderNumberListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\AssignOrderNumberListener">
<argument type="service" id="Sylius\Bundle\OrderBundle\NumberAssigner\OrderNumberAssignerInterface"/> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\AssignOrderTokenListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\AssignOrderTokenListener">
<argument type="service" id="Sylius\Component\Core\TokenAssigner\OrderTokenAssignerInterface" /> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CreatePaymentListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CreatePaymentListener">
<argument type="service" id="sylius_abstraction.state_machine" /> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CreateShipmentListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CreateShipmentListener">
<argument type="service" id="sylius_abstraction.state_machine" /> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\DecrementPromotionUsagesListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\DecrementPromotionUsagesListener">
<argument type="service" id="Sylius\Component\Core\Promotion\Modifier\OrderPromotionsUsageModifierInterface"/> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\IncrementPromotionUsagesListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\IncrementPromotionUsagesListener">
<argument type="service" id="Sylius\Component\Core\Promotion\Modifier\OrderPromotionsUsageModifierInterface"/> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\HoldInventoryListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\HoldInventoryListener">
<argument type="service" id="sylius.custom_inventory.order_inventory_operator"/> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\GiveBackInventoryListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\GiveBackInventoryListener">
<argument type="service" id="Sylius\Component\Core\Inventory\Operator\OrderInventoryOperatorInterface"/> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\RequestOrderPaymentListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\RequestOrderPaymentListener">
<argument type="service" id="sylius_abstraction.state_machine" /> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\RequestOrderShippingListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\RequestOrderShippingListener">
<argument type="service" id="sylius_abstraction.state_machine" /> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\SaveCustomerAddressesListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\SaveCustomerAddressesListener">
<argument type="service" id="Sylius\Component\Core\Customer\OrderAddressesSaverInterface" /> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\SetImmutableNamesListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\SetImmutableNamesListener">
<argument type="service" id="Sylius\Component\Core\Order\OrderItemNamesSetterInterface" /> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CancelOrderPaymentListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CancelOrderPaymentListener">
<argument type="service" id="sylius_abstraction.state_machine" /> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CancelOrderShippingListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CancelOrderShippingListener">
<argument type="service" id="sylius_abstraction.state_machine" /> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CancelPaymentListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CancelPaymentListener">
<argument type="service" id="sylius_abstraction.state_machine" /> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CancelShipmentListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Order\CancelShipmentListener">
<argument type="service" id="sylius_abstraction.state_machine" /> <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> </service>
</services> </services>
</container> </container>

View file

@ -15,36 +15,36 @@
<services> <services>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\ProcessCartListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\ProcessCartListener">
<argument type="service" id="sylius.order_processing.order_processor" /> <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.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.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.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.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.skip_payment" priority="200"/>
</service> </service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\ApplyCreateTransitionOnOrderListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\ApplyCreateTransitionOnOrderListener">
<argument type="service" id="sylius_abstraction.state_machine" /> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\SaveCheckoutCompletionDateListener"> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\ResolveOrderCheckoutStateListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\ResolveOrderCheckoutStateListener">
<argument type="service" id="sylius.state_resolver.order_checkout" /> <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.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.select_shipping" priority="100"/>
</service> </service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\ResolveOrderPaymentStateListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\ResolveOrderPaymentStateListener">
<argument type="service" id="sylius.state_resolver.order_payment" /> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\ResolveOrderShippingStateListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderCheckout\ResolveOrderShippingStateListener">
<argument type="service" id="sylius.state_resolver.order_shipping" /> <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> </service>
</services> </services>
</container> </container>

View file

@ -17,12 +17,12 @@
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderPayment\SellOrderInventoryListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderPayment\SellOrderInventoryListener">
<argument type="service" id="sylius.inventory.order_inventory_operator" /> <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>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderPayment\ResolveOrderStateListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderPayment\ResolveOrderStateListener">
<argument type="service" id="sylius.state_resolver.order" /> <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> </service>
</services> </services>
</container> </container>

View file

@ -17,7 +17,7 @@
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderShipping\ResolveOrderStateListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\OrderShipping\ResolveOrderStateListener">
<argument type="service" id="sylius.state_resolver.order" /> <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> </service>
</services> </services>
</container> </container>

View file

@ -17,16 +17,16 @@
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Payment\ProcessOrderListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Payment\ProcessOrderListener">
<argument type="service" id="sylius.order_processing.order_payment_processor.after_checkout" /> <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.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.cancel" priority="100" />
</service> </service>
<service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Payment\ResolveOrderPaymentStateListener"> <service id="Sylius\Bundle\CoreBundle\EventListener\Workflow\Payment\ResolveOrderPaymentStateListener">
<argument type="service" id="sylius.state_resolver.order_payment" /> <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.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.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.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.authorize" priority="100" />
</service> </service>
</services> </services>
</container> </container>

View file

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

View file

@ -16,18 +16,18 @@
<defaults public="true" /> <defaults public="true" />
<service id="sylius.state_resolver.order" class="Sylius\Component\Core\StateResolver\OrderStateResolver"> <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>
<service id="sylius.state_resolver.order_checkout" class="Sylius\Component\Core\StateResolver\CheckoutStateResolver"> <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_payment_method_selection_requirement" />
<argument type="service" id="sylius.checker.order_shipping_method_selection_requirement" /> <argument type="service" id="sylius.checker.order_shipping_method_selection_requirement" />
</service> </service>
<service id="sylius.state_resolver.order_payment" class="Sylius\Component\Core\StateResolver\OrderPaymentStateResolver"> <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>
<service id="sylius.state_resolver.order_shipping" class="Sylius\Component\Core\StateResolver\OrderShippingStateResolver"> <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> </service>
</services> </services>
</container> </container>

View file

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

View file

@ -15,7 +15,8 @@ namespace spec\Sylius\Bundle\CoreBundle\CatalogPromotion\Processor;
use PhpSpec\ObjectBehavior; use PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface; 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\Checker\CatalogPromotionEligibilityCheckerInterface;
use Sylius\Bundle\CoreBundle\CatalogPromotion\Processor\CatalogPromotionStateProcessorInterface; use Sylius\Bundle\CoreBundle\CatalogPromotion\Processor\CatalogPromotionStateProcessorInterface;
use Sylius\Component\Core\Model\CatalogPromotionInterface; use Sylius\Component\Core\Model\CatalogPromotionInterface;
@ -39,7 +40,7 @@ final class CatalogPromotionStateProcessorSpec extends ObjectBehavior
CatalogPromotionEligibilityCheckerInterface $catalogPromotionEligibilityChecker, CatalogPromotionEligibilityCheckerInterface $catalogPromotionEligibilityChecker,
CatalogPromotionInterface $catalogPromotion, CatalogPromotionInterface $catalogPromotion,
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$stateMachineFactory->get($catalogPromotion, CatalogPromotionTransitions::GRAPH)->willReturn($stateMachine); $stateMachineFactory->get($catalogPromotion, CatalogPromotionTransitions::GRAPH)->willReturn($stateMachine);
@ -54,11 +55,29 @@ final class CatalogPromotionStateProcessorSpec extends ObjectBehavior
$this->process($catalogPromotion); $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( function it_activates_a_catalog_promotion(
CatalogPromotionEligibilityCheckerInterface $catalogPromotionEligibilityChecker, CatalogPromotionEligibilityCheckerInterface $catalogPromotionEligibilityChecker,
CatalogPromotionInterface $catalogPromotion, CatalogPromotionInterface $catalogPromotion,
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$stateMachineFactory->get($catalogPromotion, CatalogPromotionTransitions::GRAPH)->willReturn($stateMachine); $stateMachineFactory->get($catalogPromotion, CatalogPromotionTransitions::GRAPH)->willReturn($stateMachine);
@ -78,7 +97,7 @@ final class CatalogPromotionStateProcessorSpec extends ObjectBehavior
CatalogPromotionEligibilityCheckerInterface $catalogPromotionEligibilityChecker, CatalogPromotionEligibilityCheckerInterface $catalogPromotionEligibilityChecker,
CatalogPromotionInterface $catalogPromotion, CatalogPromotionInterface $catalogPromotion,
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$stateMachineFactory->get($catalogPromotion, CatalogPromotionTransitions::GRAPH)->willReturn($stateMachine); $stateMachineFactory->get($catalogPromotion, CatalogPromotionTransitions::GRAPH)->willReturn($stateMachine);

View file

@ -16,7 +16,8 @@ namespace spec\Sylius\Bundle\CoreBundle\Checkout;
use PhpSpec\ObjectBehavior; use PhpSpec\ObjectBehavior;
use Prophecy\Argument; use Prophecy\Argument;
use SM\Factory\FactoryInterface; 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\Bundle\CoreBundle\Checkout\CheckoutStateUrlGeneratorInterface;
use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Order\Context\CartContextInterface; use Sylius\Component\Order\Context\CartContextInterface;
@ -160,7 +161,7 @@ final class CheckoutResolverSpec extends ObjectBehavior
CartContextInterface $cartContext, CartContextInterface $cartContext,
OrderInterface $order, OrderInterface $order,
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$request = new Request([], [], [ $request = new Request([], [], [
'_sylius' => ['state_machine' => ['graph' => 'test_graph', 'transition' => 'test_transition']], '_sylius' => ['state_machine' => ['graph' => 'test_graph', 'transition' => 'test_transition']],
@ -183,7 +184,7 @@ final class CheckoutResolverSpec extends ObjectBehavior
CartContextInterface $cartContext, CartContextInterface $cartContext,
OrderInterface $order, OrderInterface $order,
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
CheckoutStateUrlGeneratorInterface $urlGenerator, CheckoutStateUrlGeneratorInterface $urlGenerator,
): void { ): void {
$request = new Request([], [], [ $request = new Request([], [], [
@ -201,4 +202,29 @@ final class CheckoutResolverSpec extends ObjectBehavior
$this->onKernelRequest($event); $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 to: unpublished
sylius_state_machine_abstraction: sylius_state_machine_abstraction:
state_machine: default_adapter: 'winzou_state_machine'
default_adapter: 'winzou_state_machine' graphs_to_adapters_mapping:
graphs_to_adapters_mapping: app_blog_post: 'symfony_workflow'
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\GetStatusInterface;
use Payum\Core\Request\Notify; use Payum\Core\Request\Notify;
use SM\Factory\FactoryInterface; use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Bundle\PayumBundle\Request\GetStatus; use Sylius\Bundle\PayumBundle\Request\GetStatus;
use Sylius\Component\Payment\Model\PaymentInterface; use Sylius\Component\Payment\Model\PaymentInterface;
use Sylius\Component\Payment\PaymentTransitions; use Sylius\Component\Payment\PaymentTransitions;
use Sylius\Component\Resource\StateMachine\StateMachineInterface;
use Webmozart\Assert\Assert;
final class UpdatePaymentStateExtension implements ExtensionInterface 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 public function onPreExecute(Context $context): void
@ -84,13 +93,19 @@ final class UpdatePaymentStateExtension implements ExtensionInterface
private function updatePaymentState(PaymentInterface $payment, string $nextState): void private function updatePaymentState(PaymentInterface $payment, string $nextState): void
{ {
$stateMachine = $this->factory->get($payment, PaymentTransitions::GRAPH); $stateMachine = $this->getStateMachine();
/** @var StateMachineInterface $stateMachine */ if (null !== $transition = $stateMachine->getTransitionToState($payment, PaymentTransitions::GRAPH, $nextState)) {
Assert::isInstanceOf($stateMachine, StateMachineInterface::class); $stateMachine->apply($payment, PaymentTransitions::GRAPH, $transition);
if (null !== $transition = $stateMachine->getTransitionToState($nextState)) {
$stateMachine->apply($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" /> <defaults public="true" />
<service id="sylius.payum_extension.update_payment_state" class="Sylius\Bundle\PayumBundle\Extension\UpdatePaymentStateExtension"> <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" /> <tag name="payum.extension" all="true" prepend="true" />
</service> </service>
</services> </services>

View file

@ -14,6 +14,8 @@ declare(strict_types=1);
namespace Sylius\Component\Core\Payment\Provider; namespace Sylius\Component\Core\Payment\Provider;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface; 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\OrderInterface;
use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Core\Model\PaymentMethodInterface; 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\Factory\PaymentFactoryInterface;
use Sylius\Component\Payment\PaymentTransitions; use Sylius\Component\Payment\PaymentTransitions;
use Sylius\Component\Payment\Resolver\DefaultPaymentMethodResolverInterface; use Sylius\Component\Payment\Resolver\DefaultPaymentMethodResolverInterface;
use Sylius\Component\Resource\StateMachine\StateMachineInterface;
use Webmozart\Assert\Assert; use Webmozart\Assert\Assert;
final class OrderPaymentProvider implements OrderPaymentProviderInterface final class OrderPaymentProvider implements OrderPaymentProviderInterface
@ -30,8 +31,19 @@ final class OrderPaymentProvider implements OrderPaymentProviderInterface
public function __construct( public function __construct(
private DefaultPaymentMethodResolverInterface $defaultPaymentMethodResolver, private DefaultPaymentMethodResolverInterface $defaultPaymentMethodResolver,
private PaymentFactoryInterface $paymentFactory, 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 public function provideOrderPayment(OrderInterface $order, string $targetState): ?PaymentInterface
@ -88,12 +100,20 @@ final class OrderPaymentProvider implements OrderPaymentProviderInterface
return; return;
} }
/** @var StateMachineInterface $stateMachine */ $stateMachine = $this->getStateMachine();
$stateMachine = $this->stateMachineFactory->get($payment, PaymentTransitions::GRAPH);
$targetTransition = $stateMachine->getTransitionToState($targetState); $targetTransition = $stateMachine->getTransitionToState($payment, PaymentTransitions::GRAPH, $targetState);
if (null !== $targetTransition) { 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; namespace Sylius\Component\Core\StateResolver;
use SM\Factory\FactoryInterface; 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\OrderPaymentMethodSelectionRequirementCheckerInterface;
use Sylius\Component\Core\Checker\OrderShippingMethodSelectionRequirementCheckerInterface; use Sylius\Component\Core\Checker\OrderShippingMethodSelectionRequirementCheckerInterface;
use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderInterface;
@ -25,29 +27,47 @@ use Webmozart\Assert\Assert;
final class CheckoutStateResolver implements StateResolverInterface final class CheckoutStateResolver implements StateResolverInterface
{ {
public function __construct( public function __construct(
private FactoryInterface $stateMachineFactory, private FactoryInterface|StateMachineInterface $stateMachineFactory,
private OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker, private OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
private OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker, 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 public function resolve(BaseOrderInterface $order): void
{ {
Assert::isInstanceOf($order, OrderInterface::class); Assert::isInstanceOf($order, OrderInterface::class);
$stateMachine = $this->stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH); $stateMachine = $this->getStateMachine();
if ( if (
!$this->orderShippingMethodSelectionRequirementChecker->isShippingMethodSelectionRequired($order) && !$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 ( if (
!$this->orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order) && !$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 Doctrine\Common\Collections\Collection;
use SM\Factory\FactoryInterface; 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\OrderInterface;
use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Core\OrderPaymentTransitions; use Sylius\Component\Core\OrderPaymentTransitions;
@ -26,8 +27,19 @@ use Webmozart\Assert\Assert;
final class OrderPaymentStateResolver implements StateResolverInterface 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 public function resolve(BaseOrderInterface $order): void
@ -35,18 +47,13 @@ final class OrderPaymentStateResolver implements StateResolverInterface
/** @var OrderInterface $order */ /** @var OrderInterface $order */
Assert::isInstanceOf($order, OrderInterface::class); Assert::isInstanceOf($order, OrderInterface::class);
$stateMachine = $this->stateMachineFactory->get($order, OrderPaymentTransitions::GRAPH);
$targetTransition = $this->getTargetTransition($order); $targetTransition = $this->getTargetTransition($order);
$stateMachine = $this->getStateMachine();
if (null !== $targetTransition) { if (null !== $targetTransition) {
$this->applyTransition($stateMachine, $targetTransition); if ($stateMachine->can($order, OrderPaymentTransitions::GRAPH, $targetTransition)) {
} $stateMachine->apply($order, OrderPaymentTransitions::GRAPH, $targetTransition);
} }
private function applyTransition(StateMachineInterface $stateMachine, string $transition): void
{
if ($stateMachine->can($transition)) {
$stateMachine->apply($transition);
} }
} }
@ -129,4 +136,13 @@ final class OrderPaymentStateResolver implements StateResolverInterface
return $payments; 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; namespace Sylius\Component\Core\StateResolver;
use SM\Factory\FactoryInterface; 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\OrderInterface;
use Sylius\Component\Core\Model\ShipmentInterface; use Sylius\Component\Core\Model\ShipmentInterface;
use Sylius\Component\Core\OrderShippingStates; use Sylius\Component\Core\OrderShippingStates;
@ -25,8 +26,19 @@ use Webmozart\Assert\Assert;
final class OrderShippingStateResolver implements StateResolverInterface 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 public function resolve(BaseOrderInterface $order): void
@ -37,15 +49,15 @@ final class OrderShippingStateResolver implements StateResolverInterface
if (OrderShippingStates::STATE_SHIPPED === $order->getShippingState()) { if (OrderShippingStates::STATE_SHIPPED === $order->getShippingState()) {
return; return;
} }
/** @var StateMachineInterface $stateMachine */
$stateMachine = $this->stateMachineFactory->get($order, OrderShippingTransitions::GRAPH); $stateMachine = $this->getStateMachine();
if ($this->allShipmentsInStateButOrderStateNotUpdated($order, ShipmentInterface::STATE_SHIPPED, OrderShippingStates::STATE_SHIPPED)) { 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)) { 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() 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; namespace Sylius\Component\Core\StateResolver;
use SM\Factory\FactoryInterface; use SM\Factory\FactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter;
use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\OrderPaymentStates; use Sylius\Component\Core\OrderPaymentStates;
use Sylius\Component\Core\OrderShippingStates; use Sylius\Component\Core\OrderShippingStates;
@ -24,17 +26,28 @@ use Webmozart\Assert\Assert;
final class OrderStateResolver implements StateResolverInterface 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 public function resolve(BaseOrderInterface $order): void
{ {
Assert::isInstanceOf($order, OrderInterface::class); Assert::isInstanceOf($order, OrderInterface::class);
$stateMachine = $this->stateMachineFactory->get($order, OrderTransitions::GRAPH); $stateMachine = $this->getStateMachine();
if ($this->canOrderBeFulfilled($order) && $stateMachine->can(OrderTransitions::TRANSITION_FULFILL)) { if ($this->canOrderBeFulfilled($order) && $stateMachine->can($order, OrderTransitions::GRAPH, OrderTransitions::TRANSITION_FULFILL)) {
$stateMachine->apply(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() 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 Doctrine\Persistence\ObjectManager;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use SM\Factory\Factory; 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\Core\Repository\OrderRepositoryInterface;
use Sylius\Component\Order\Model\OrderInterface; use Sylius\Component\Order\Model\OrderInterface;
use Sylius\Component\Order\OrderTransitions; use Sylius\Component\Order\OrderTransitions;
@ -25,7 +28,7 @@ final class UnpaidOrdersStateUpdater implements UnpaidOrdersStateUpdaterInterfac
{ {
public function __construct( public function __construct(
private OrderRepositoryInterface $orderRepository, private OrderRepositoryInterface $orderRepository,
private Factory $stateMachineFactory, private Factory|StateMachineInterface $stateMachineFactory,
private string $expirationPeriod, private string $expirationPeriod,
private ?LoggerInterface $logger = null, private ?LoggerInterface $logger = null,
private ?ObjectManager $orderManager = 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; $this->logger = $logger;
} }
@ -79,13 +94,22 @@ final class UnpaidOrdersStateUpdater implements UnpaidOrdersStateUpdaterInterfac
private function cancelOrder(OrderInterface $expiredUnpaidOrder): void private function cancelOrder(OrderInterface $expiredUnpaidOrder): void
{ {
try { try {
$stateMachine = $this->stateMachineFactory->get($expiredUnpaidOrder, OrderTransitions::GRAPH); $stateMachine = $this->getStateMachine();
$stateMachine->apply(OrderTransitions::TRANSITION_CANCEL); $stateMachine->apply($expiredUnpaidOrder, OrderTransitions::GRAPH, OrderTransitions::TRANSITION_CANCEL);
} catch (SMException $e) { } catch (StateMachineExecutionException $e) {
$this->logger?->error( $this->logger?->error(
sprintf('An error occurred while cancelling unpaid order #%s', $expiredUnpaidOrder->getId()), sprintf('An error occurred while cancelling unpaid order #%s', $expiredUnpaidOrder->getId()),
['exception' => $e, 'message' => $e->getMessage()], ['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/resource": "^1.9",
"sylius/review": "^1.13", "sylius/review": "^1.13",
"sylius/shipping": "^1.13", "sylius/shipping": "^1.13",
"sylius/state-machine-abstraction": "^1.13",
"sylius/taxation": "^1.13", "sylius/taxation": "^1.13",
"sylius/taxonomy": "^1.13", "sylius/taxonomy": "^1.13",
"sylius/user": "^1.13", "sylius/user": "^1.13",
@ -82,7 +83,7 @@
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"Sylius\\Component\\Core\\spec\\": "spec/" "spec\\Sylius\\Component\\Core\\": "spec/"
} }
}, },
"repositories": [ "repositories": [

View file

@ -16,6 +16,7 @@ namespace spec\Sylius\Component\Core\Payment\Provider;
use PhpSpec\ObjectBehavior; use PhpSpec\ObjectBehavior;
use Prophecy\Argument; use Prophecy\Argument;
use SM\Factory\FactoryInterface as StateMachineFactoryInterface; use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Core\Model\PaymentMethodInterface; 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\Factory\PaymentFactoryInterface;
use Sylius\Component\Payment\PaymentTransitions; use Sylius\Component\Payment\PaymentTransitions;
use Sylius\Component\Payment\Resolver\DefaultPaymentMethodResolverInterface; use Sylius\Component\Payment\Resolver\DefaultPaymentMethodResolverInterface;
use Sylius\Component\Resource\StateMachine\StateMachineInterface;
final class OrderPaymentProviderSpec extends ObjectBehavior final class OrderPaymentProviderSpec extends ObjectBehavior
{ {
@ -54,8 +54,18 @@ final class OrderPaymentProviderSpec extends ObjectBehavior
PaymentInterface $newPayment, PaymentInterface $newPayment,
PaymentMethodInterface $paymentMethod, PaymentMethodInterface $paymentMethod,
StateMachineFactoryInterface $stateMachineFactory, StateMachineFactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineStub $stateMachine,
): void { ): 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->getTotal()->willReturn(1000);
$order->getCurrencyCode()->willReturn('USD'); $order->getCurrencyCode()->willReturn('USD');
$order->getLastPayment(PaymentInterface::STATE_CANCELLED)->willReturn($lastCancelledPayment); $order->getLastPayment(PaymentInterface::STATE_CANCELLED)->willReturn($lastCancelledPayment);
@ -70,12 +80,45 @@ final class OrderPaymentProviderSpec extends ObjectBehavior
$newPayment->setOrder($order)->shouldBeCalled(); $newPayment->setOrder($order)->shouldBeCalled();
$stateMachineFactory->get($newPayment, PaymentTransitions::GRAPH)->willReturn($stateMachine); $stateMachineFactory->get($newPayment, PaymentTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->getTransitionToState(PaymentInterface::STATE_NEW)->willReturn(PaymentTransitions::TRANSITION_CREATE);
$stateMachine->apply(PaymentTransitions::TRANSITION_CREATE)->shouldBeCalled(); $stateMachine->apply(PaymentTransitions::TRANSITION_CREATE)->shouldBeCalled();
$this->provideOrderPayment($order, PaymentInterface::STATE_NEW)->shouldReturn($newPayment); $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( function it_provides_payment_in_configured_state_with_payment_method_from_last_failed_payment(
DefaultPaymentMethodResolverInterface $defaultPaymentMethodResolver, DefaultPaymentMethodResolverInterface $defaultPaymentMethodResolver,
OrderInterface $order, OrderInterface $order,
@ -84,8 +127,18 @@ final class OrderPaymentProviderSpec extends ObjectBehavior
PaymentInterface $newPayment, PaymentInterface $newPayment,
PaymentMethodInterface $paymentMethod, PaymentMethodInterface $paymentMethod,
StateMachineFactoryInterface $stateMachineFactory, StateMachineFactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineStub $stateMachine,
): void { ): 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->getTotal()->willReturn(1000);
$order->getCurrencyCode()->willReturn('USD'); $order->getCurrencyCode()->willReturn('USD');
$order->getLastPayment(PaymentInterface::STATE_CANCELLED)->willReturn(null); $order->getLastPayment(PaymentInterface::STATE_CANCELLED)->willReturn(null);
@ -101,7 +154,6 @@ final class OrderPaymentProviderSpec extends ObjectBehavior
$newPayment->setOrder($order)->shouldBeCalled(); $newPayment->setOrder($order)->shouldBeCalled();
$stateMachineFactory->get($newPayment, PaymentTransitions::GRAPH)->willReturn($stateMachine); $stateMachineFactory->get($newPayment, PaymentTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->getTransitionToState(PaymentInterface::STATE_NEW)->willReturn(PaymentTransitions::TRANSITION_CREATE);
$stateMachine->apply(PaymentTransitions::TRANSITION_CREATE)->shouldBeCalled(); $stateMachine->apply(PaymentTransitions::TRANSITION_CREATE)->shouldBeCalled();
$this->provideOrderPayment($order, PaymentInterface::STATE_NEW)->shouldReturn($newPayment); $this->provideOrderPayment($order, PaymentInterface::STATE_NEW)->shouldReturn($newPayment);
@ -114,8 +166,18 @@ final class OrderPaymentProviderSpec extends ObjectBehavior
PaymentInterface $newPayment, PaymentInterface $newPayment,
PaymentMethodInterface $paymentMethod, PaymentMethodInterface $paymentMethod,
StateMachineFactoryInterface $stateMachineFactory, StateMachineFactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineStub $stateMachine,
): void { ): 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->getTotal()->willReturn(1000);
$order->getCurrencyCode()->willReturn('USD'); $order->getCurrencyCode()->willReturn('USD');
$order->getLastPayment(PaymentInterface::STATE_CANCELLED)->willReturn(null); $order->getLastPayment(PaymentInterface::STATE_CANCELLED)->willReturn(null);
@ -130,7 +192,6 @@ final class OrderPaymentProviderSpec extends ObjectBehavior
$newPayment->getState()->willReturn(PaymentInterface::STATE_CART); $newPayment->getState()->willReturn(PaymentInterface::STATE_CART);
$stateMachineFactory->get($newPayment, PaymentTransitions::GRAPH)->willReturn($stateMachine); $stateMachineFactory->get($newPayment, PaymentTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->getTransitionToState(PaymentInterface::STATE_NEW)->willReturn(PaymentTransitions::TRANSITION_CREATE);
$stateMachine->apply(PaymentTransitions::TRANSITION_CREATE)->shouldBeCalled(); $stateMachine->apply(PaymentTransitions::TRANSITION_CREATE)->shouldBeCalled();
$this->provideOrderPayment($order, PaymentInterface::STATE_NEW)->shouldReturn($newPayment); $this->provideOrderPayment($order, PaymentInterface::STATE_NEW)->shouldReturn($newPayment);
@ -192,4 +253,12 @@ final class OrderPaymentProviderSpec extends ObjectBehavior
->during('provideOrderPayment', [$order, PaymentInterface::STATE_NEW]) ->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 PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface; 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\OrderPaymentMethodSelectionRequirementCheckerInterface;
use Sylius\Component\Core\Checker\OrderShippingMethodSelectionRequirementCheckerInterface; use Sylius\Component\Core\Checker\OrderShippingMethodSelectionRequirementCheckerInterface;
use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderInterface;
@ -46,7 +47,7 @@ final class CheckoutStateResolverSpec extends ObjectBehavior
OrderInterface $order, OrderInterface $order,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker, OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker, OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(false); $orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(false);
@ -63,12 +64,33 @@ final class CheckoutStateResolverSpec extends ObjectBehavior
$this->resolve($order); $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( function it_applies_transition_skip_shipping_if_shipping_method_selection_is_not_required_and_this_transition_is_possible(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
OrderInterface $order, OrderInterface $order,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker, OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker, OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(true); $orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(true);
@ -90,7 +112,7 @@ final class CheckoutStateResolverSpec extends ObjectBehavior
OrderInterface $order, OrderInterface $order,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker, OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker, OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(true); $orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(true);
@ -112,7 +134,7 @@ final class CheckoutStateResolverSpec extends ObjectBehavior
OrderInterface $order, OrderInterface $order,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker, OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker, OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(true); $orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(true);
@ -134,7 +156,7 @@ final class CheckoutStateResolverSpec extends ObjectBehavior
OrderInterface $order, OrderInterface $order,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker, OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker, OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(false); $orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(false);
@ -156,7 +178,7 @@ final class CheckoutStateResolverSpec extends ObjectBehavior
OrderInterface $order, OrderInterface $order,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker, OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker, OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(true); $orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(true);
@ -178,7 +200,7 @@ final class CheckoutStateResolverSpec extends ObjectBehavior
OrderInterface $order, OrderInterface $order,
OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker, OrderPaymentMethodSelectionRequirementCheckerInterface $orderPaymentMethodSelectionRequirementChecker,
OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker, OrderShippingMethodSelectionRequirementCheckerInterface $orderShippingMethodSelectionRequirementChecker,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(true); $orderPaymentMethodSelectionRequirementChecker->isPaymentMethodSelectionRequired($order)->willReturn(true);

View file

@ -16,7 +16,8 @@ namespace spec\Sylius\Component\Core\StateResolver;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use PhpSpec\ObjectBehavior; use PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface; 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\OrderInterface;
use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Core\OrderPaymentTransitions; 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( function it_marks_an_order_as_refunded_if_all_its_payments_are_refunded(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
OrderInterface $order, OrderInterface $order,
PaymentInterface $firstPayment, PaymentInterface $firstPayment,
PaymentInterface $secondPayment, PaymentInterface $secondPayment,
@ -59,9 +60,34 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
$this->resolve($order); $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( function it_marks_an_order_as_refunded_if_its_payments_are_refunded_or_failed_but_at_least_one_is_refunded(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
OrderInterface $order, OrderInterface $order,
PaymentInterface $firstPayment, PaymentInterface $firstPayment,
PaymentInterface $secondPayment, PaymentInterface $secondPayment,
@ -86,7 +112,7 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_paid_if_fully_paid( function it_marks_an_order_as_paid_if_fully_paid(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
OrderInterface $order, OrderInterface $order,
PaymentInterface $payment, PaymentInterface $payment,
): void { ): void {
@ -107,7 +133,7 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_paid_if_it_does_not_have_any_payments( function it_marks_an_order_as_paid_if_it_does_not_have_any_payments(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
OrderInterface $order, OrderInterface $order,
) { ) {
$order->getPayments()->willReturn(new ArrayCollection([])); $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( function it_marks_an_order_as_paid_if_fully_paid_even_if_previous_payment_was_failed(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
OrderInterface $order, OrderInterface $order,
PaymentInterface $firstPayment, PaymentInterface $firstPayment,
PaymentInterface $secondPayment, 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( function it_marks_an_order_as_partially_refunded_if_one_of_the_payment_is_refunded(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
OrderInterface $order, OrderInterface $order,
PaymentInterface $firstPayment, PaymentInterface $firstPayment,
PaymentInterface $secondPayment, PaymentInterface $secondPayment,
@ -171,7 +197,7 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_completed_if_fully_paid_multiple_payments( function it_marks_an_order_as_completed_if_fully_paid_multiple_payments(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
OrderInterface $order, OrderInterface $order,
PaymentInterface $firstPayment, PaymentInterface $firstPayment,
PaymentInterface $secondPayment, 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( function it_marks_an_order_as_partially_paid_if_one_of_the_payment_is_processing(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
OrderInterface $order, OrderInterface $order,
PaymentInterface $firstPayment, PaymentInterface $firstPayment,
PaymentInterface $secondPayment, PaymentInterface $secondPayment,
@ -221,7 +247,7 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_authorized_if_all_its_payments_are_authorized( function it_marks_an_order_as_authorized_if_all_its_payments_are_authorized(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
OrderInterface $order, OrderInterface $order,
PaymentInterface $firstPayment, PaymentInterface $firstPayment,
PaymentInterface $secondPayment, 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( function it_marks_an_order_as_partially_authorized_if_one_of_the_payments_is_processing_and_one_of_the_payments_is_authorized(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
OrderInterface $order, OrderInterface $order,
PaymentInterface $firstPayment, PaymentInterface $firstPayment,
PaymentInterface $secondPayment, PaymentInterface $secondPayment,
@ -271,7 +297,7 @@ final class OrderPaymentStateResolverSpec extends ObjectBehavior
function it_marks_an_order_as_awaiting_payment_if_payments_is_processing( function it_marks_an_order_as_awaiting_payment_if_payments_is_processing(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
OrderInterface $order, OrderInterface $order,
PaymentInterface $firstPayment, PaymentInterface $firstPayment,
): void { ): void {

View file

@ -16,7 +16,8 @@ namespace spec\Sylius\Component\Core\StateResolver;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use PhpSpec\ObjectBehavior; use PhpSpec\ObjectBehavior;
use SM\Factory\FactoryInterface; 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\OrderInterface;
use Sylius\Component\Core\Model\ShipmentInterface; use Sylius\Component\Core\Model\ShipmentInterface;
use Sylius\Component\Core\OrderShippingStates; use Sylius\Component\Core\OrderShippingStates;
@ -40,7 +41,7 @@ final class OrderShippingStateResolverSpec extends ObjectBehavior
OrderInterface $order, OrderInterface $order,
ShipmentInterface $shipment1, ShipmentInterface $shipment1,
ShipmentInterface $shipment2, ShipmentInterface $shipment2,
StateMachineInterface $orderStateMachine, WinzouStateMachineInterface $orderStateMachine,
): void { ): void {
$shipments = new ArrayCollection(); $shipments = new ArrayCollection();
$shipments->add($shipment1->getWrappedObject()); $shipments->add($shipment1->getWrappedObject());
@ -58,10 +59,33 @@ final class OrderShippingStateResolverSpec extends ObjectBehavior
$this->resolve($order); $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( function it_marks_an_order_as_shipped_if_there_are_no_shipments_to_deliver(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
OrderInterface $order, OrderInterface $order,
StateMachineInterface $orderStateMachine, WinzouStateMachineInterface $orderStateMachine,
): void { ): void {
$order->getShipments()->willReturn(new ArrayCollection()); $order->getShipments()->willReturn(new ArrayCollection());
$order->getShippingState()->willReturn(OrderShippingStates::STATE_READY); $order->getShippingState()->willReturn(OrderShippingStates::STATE_READY);
@ -77,7 +101,7 @@ final class OrderShippingStateResolverSpec extends ObjectBehavior
OrderInterface $order, OrderInterface $order,
ShipmentInterface $shipment1, ShipmentInterface $shipment1,
ShipmentInterface $shipment2, ShipmentInterface $shipment2,
StateMachineInterface $orderStateMachine, WinzouStateMachineInterface $orderStateMachine,
): void { ): void {
$shipments = new ArrayCollection(); $shipments = new ArrayCollection();
$shipments->add($shipment1->getWrappedObject()); $shipments->add($shipment1->getWrappedObject());
@ -100,7 +124,7 @@ final class OrderShippingStateResolverSpec extends ObjectBehavior
OrderInterface $order, OrderInterface $order,
ShipmentInterface $shipment1, ShipmentInterface $shipment1,
ShipmentInterface $shipment2, ShipmentInterface $shipment2,
StateMachineInterface $orderStateMachine, WinzouStateMachineInterface $orderStateMachine,
): void { ): void {
$shipments = new ArrayCollection(); $shipments = new ArrayCollection();
$shipments->add($shipment1->getWrappedObject()); $shipments->add($shipment1->getWrappedObject());

View file

@ -16,7 +16,8 @@ namespace spec\Sylius\Component\Core\StateResolver;
use PhpSpec\ObjectBehavior; use PhpSpec\ObjectBehavior;
use Prophecy\Argument; use Prophecy\Argument;
use SM\Factory\FactoryInterface; 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\OrderInterface;
use Sylius\Component\Core\OrderPaymentStates; use Sylius\Component\Core\OrderPaymentStates;
use Sylius\Component\Core\OrderShippingStates; 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( function it_marks_an_order_as_fulfilled_when_its_paid_for_and_has_been_shipped(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
OrderInterface $order, OrderInterface $order,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$order->getShippingState()->willReturn(OrderShippingStates::STATE_SHIPPED); $order->getShippingState()->willReturn(OrderShippingStates::STATE_SHIPPED);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_PAID); $order->getPaymentState()->willReturn(OrderPaymentStates::STATE_PAID);
@ -51,10 +52,25 @@ final class OrderStateResolverSpec extends ObjectBehavior
$this->resolve($order); $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( function it_marks_an_order_as_fulfilled_when_its_partially_refunded_and_has_been_shipped(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
OrderInterface $order, OrderInterface $order,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$order->getShippingState()->willReturn(OrderShippingStates::STATE_SHIPPED); $order->getShippingState()->willReturn(OrderShippingStates::STATE_SHIPPED);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_PARTIALLY_REFUNDED); $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( function it_does_not_mark_an_order_as_fulfilled_when_it_has_been_paid_but_not_shipped(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
OrderInterface $order, OrderInterface $order,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$order->getShippingState()->willReturn(Argument::not(OrderShippingStates::STATE_SHIPPED)); $order->getShippingState()->willReturn(Argument::not(OrderShippingStates::STATE_SHIPPED));
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_PAID); $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( function it_does_not_mark_an_order_as_fulfilled_when_it_has_been_shipped_but_not_paid(
FactoryInterface $stateMachineFactory, FactoryInterface $stateMachineFactory,
OrderInterface $order, OrderInterface $order,
StateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$order->getShippingState()->willReturn(OrderShippingStates::STATE_SHIPPED); $order->getShippingState()->willReturn(OrderShippingStates::STATE_SHIPPED);
$order->getPaymentState()->willReturn(Argument::notIn([OrderPaymentStates::STATE_PAID, OrderPaymentStates::STATE_PARTIALLY_REFUNDED])); $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 Psr\Log\LoggerInterface;
use SM\Factory\Factory; use SM\Factory\Factory;
use SM\SMException; 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\Repository\OrderRepositoryInterface;
use Sylius\Component\Core\Updater\UnpaidOrdersStateUpdaterInterface; use Sylius\Component\Core\Updater\UnpaidOrdersStateUpdaterInterface;
use Sylius\Component\Order\Model\OrderInterface; use Sylius\Component\Order\Model\OrderInterface;
@ -55,9 +56,9 @@ final class UnpaidOrdersStateUpdaterSpec extends ObjectBehavior
OrderInterface $secondOrder, OrderInterface $secondOrder,
OrderInterface $thirdOrder, OrderInterface $thirdOrder,
OrderRepositoryInterface $orderRepository, OrderRepositoryInterface $orderRepository,
StateMachineInterface $firstOrderStateMachine, WinzouStateMachineInterface $firstOrderStateMachine,
StateMachineInterface $secondOrderStateMachine, WinzouStateMachineInterface $secondOrderStateMachine,
StateMachineInterface $thirdOrderStateMachine, WinzouStateMachineInterface $thirdOrderStateMachine,
): void { ): void {
$orderRepository->findOrdersUnpaidSince(Argument::type(\DateTimeInterface::class), 100)->willReturn( $orderRepository->findOrdersUnpaidSince(Argument::type(\DateTimeInterface::class), 100)->willReturn(
[$firstOrder, $secondOrder], [$firstOrder, $secondOrder],
@ -79,14 +80,47 @@ final class UnpaidOrdersStateUpdaterSpec extends ObjectBehavior
$this->cancel(); $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( function it_wont_stop_cancelling_unpaid_orders_on_exception_for_a_single_order_and_logs_error(
Factory $stateMachineFactory, Factory $stateMachineFactory,
ObjectManager $objectManager, ObjectManager $objectManager,
OrderInterface $firstOrder, OrderInterface $firstOrder,
OrderInterface $secondOrder, OrderInterface $secondOrder,
OrderRepositoryInterface $orderRepository, OrderRepositoryInterface $orderRepository,
StateMachineInterface $firstOrderStateMachine, WinzouStateMachineInterface $firstOrderStateMachine,
StateMachineInterface $secondOrderStateMachine, WinzouStateMachineInterface $secondOrderStateMachine,
LoggerInterface $logger, LoggerInterface $logger,
): void { ): void {
$orderRepository->findOrdersUnpaidSince(Argument::type(\DateTimeInterface::class), 100)->willReturn( $orderRepository->findOrdersUnpaidSince(Argument::type(\DateTimeInterface::class), 100)->willReturn(
@ -121,8 +155,8 @@ final class UnpaidOrdersStateUpdaterSpec extends ObjectBehavior
OrderInterface $firstOrder, OrderInterface $firstOrder,
OrderInterface $secondOrder, OrderInterface $secondOrder,
OrderRepositoryInterface $orderRepository, OrderRepositoryInterface $orderRepository,
StateMachineInterface $firstOrderStateMachine, WinzouStateMachineInterface $firstOrderStateMachine,
StateMachineInterface $secondOrderStateMachine, WinzouStateMachineInterface $secondOrderStateMachine,
): void { ): void {
$this->beConstructedWith($orderRepository, $stateMachineFactory, '10 months', null, $objectManager); $this->beConstructedWith($orderRepository, $stateMachineFactory, '10 months', null, $objectManager);
@ -150,8 +184,8 @@ final class UnpaidOrdersStateUpdaterSpec extends ObjectBehavior
OrderInterface $firstOrder, OrderInterface $firstOrder,
OrderInterface $secondOrder, OrderInterface $secondOrder,
OrderRepositoryInterface $orderRepository, OrderRepositoryInterface $orderRepository,
StateMachineInterface $firstOrderStateMachine, WinzouStateMachineInterface $firstOrderStateMachine,
StateMachineInterface $secondOrderStateMachine, WinzouStateMachineInterface $secondOrderStateMachine,
): void { ): void {
$this->beConstructedWith($orderRepository, $stateMachineFactory, '10 months', null, null, 100); $this->beConstructedWith($orderRepository, $stateMachineFactory, '10 months', null, null, 100);