diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..81d9a5433 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,32 @@ +**/*.log +**/*.md +**/*.php~ +**/._* +**/.dockerignore +**/.DS_Store +**/.git/ +**/.gitattributes +**/.github +**/.gitignore +**/.gitkeep +**/.gitmodules +**/.idea +**/Dockerfile +**/Thumbs.db +**/docker-compose*.yaml +**/docker-compose*.yml +.editorconfig +.php_cs.cache +.travis.yml +composer.phar +docker/mysql/data/ +etc/build/* +node_modules/ +var/* +vendor/ +public/assets/ +public/build/ +public/bundles/ +public/css/ +public/js/ +public/media/ diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..b5f72a57e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,82 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] +# Change these settings to your own preference +indent_style = space +indent_size = 4 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.feature] +indent_style = space +indent_size = 4 + +[*.js] +indent_style = space +indent_size = 2 + +[*.json] +indent_style = space +indent_size = 2 + +[*.md] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = false + +[*.neon] +indent_style = space +indent_size = 4 + +[*.php] +indent_style = space +indent_size = 4 + +[*.sh] +indent_style = space +indent_size = 4 + +[*.{yaml,yml}] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = false + +[.babelrc] +indent_style = space +indent_size = 2 + +[.gitmodules] +indent_style = tab +indent_size = 4 + +[.php_cs{,.dist}] +indent_style = space +indent_size = 4 + +[composer.json] +indent_style = space +indent_size = 4 + +[package.json] +indent_style = space +indent_size = 2 + +[phpspec.yml{,.dist}] +indent_style = space +indent_size = 4 + +[phpstan.neon] +indent_style = space +indent_size = 4 + +[phpunit.xml{,.dist}] +indent_style = space +indent_size = 4 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 540e18449..a7b5a2089 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,7 +1,9 @@ name: Build on: - push: ~ + push: + branches-ignore: + - 'dependabot/**' pull_request: ~ release: types: [created] @@ -19,32 +21,33 @@ jobs: strategy: fail-fast: false matrix: - php: ["8.0", "8.1"] - symfony: ["^4.4", "^5.4"] - sylius: ["~1.10.11", "~1.11.2"] - node: ["14.x"] - mysql: ["5.7", "8.0"] + php: ["8.4"] + symfony: ["^7.3"] + sylius: ["~2.2.0"] + node: ["22.x"] + mysql: ["8.4"] env: APP_ENV: test + BEHAT_BASE_URL: "https://127.0.0.1:8080/" DATABASE_URL: "mysql://root:root@127.0.0.1/sylius?serverVersion=${{ matrix.mysql }}" steps: - - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: "${{ matrix.php }}" - extensions: intl, gd - tools: symfony + extensions: intl + tools: flex,symfony coverage: none - name: Setup Node - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: node-version: "${{ matrix.node }}" @@ -64,25 +67,17 @@ jobs: run: php -v | head -n 1 | awk '{ print $2 }' > .php-version - - name: Install certificates - run: symfony server:ca:install - - - - name: Run Chrome Headless - run: google-chrome-stable --enable-automation --disable-background-networking --no-default-browser-check --no-first-run --disable-popup-blocking --disable-default-apps --allow-insecure-localhost --disable-translate --disable-extensions --no-sandbox --enable-features=Metal --headless --remote-debugging-port=9222 --window-size=2880,1800 --proxy-server='direct://' --proxy-bypass-list='*' http://127.0.0.1 > /dev/null 2>&1 & - - - - name: Run webserver - run: (cd tests/Application && symfony server:start --port=8080 --dir=public --daemon) + name: Validate composer.json + run: composer validate --ansi --strict - name: Get Composer cache directory id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache Composer - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-php-${{ matrix.php }}-composer-${{ hashFiles('**/composer.json **/composer.lock') }} @@ -93,7 +88,8 @@ jobs: name: Restrict Symfony version if: matrix.symfony != '' run: | - composer global require --no-progress --no-scripts --no-plugins "symfony/flex:^1.10" + composer global require --no-progress --no-scripts --no-plugins "symfony/flex:^2.4" + composer global config --no-plugins allow-plugins.symfony/flex true composer config extra.symfony.require "${{ matrix.symfony }}" - @@ -103,16 +99,40 @@ jobs: - name: Install PHP dependencies - run: composer update --no-interaction + run: composer install --no-interaction + + - + name: Validate container + run: vendor/bin/console lint:container + + - + name: Run ECS + run: vendor/bin/ecs check + + - + name: Run PHPStan + run: vendor/bin/phpstan analyse -c phpstan.neon -l max src/ + + - + name: Run Psalm + run: vendor/bin/psalm + + - + name: Run PHPSpec + run: vendor/bin/phpspec run --ansi -f progress --no-interaction + + - + name: Run unit tests + run: vendor/bin/phpunit --colors=always --testsuite=unit - name: Get Yarn cache directory id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" + run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - name: Cache Yarn - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ${{ steps.yarn-cache.outputs.dir }} key: ${{ runner.os }}-node-${{ matrix.node }}-yarn-${{ hashFiles('**/package.json **/yarn.lock') }} @@ -121,61 +141,88 @@ jobs: - name: Install JS dependencies - run: (cd tests/Application && yarn install) + run: (cd vendor/sylius/test-application && yarn install) - name: Prepare test application database run: | - (cd tests/Application && bin/console doctrine:database:create -vvv) - (cd tests/Application && bin/console doctrine:migrations:migrate -n -vvv -q) + vendor/bin/console doctrine:database:create -vvv + vendor/bin/console doctrine:schema:create -vvv + + - + name: Validate database schema + run: vendor/bin/console doctrine:schema:validate - name: Prepare test application assets run: | - (cd tests/Application && bin/console assets:install public -vvv) - (cd tests/Application && yarn build) + vendor/bin/console assets:install -vvv + (cd vendor/sylius/test-application && yarn build) - name: Prepare test application cache - run: (cd tests/Application && bin/console cache:warmup -vvv) + run: vendor/bin/console cache:warmup -vvv - name: Load fixtures in test application - run: (cd tests/Application && bin/console sylius:fixtures:load -n) - - - - name: Validate composer.json - run: composer validate --ansi --strict - - - - name: Validate database schema - run: (cd tests/Application && bin/console doctrine:schema:validate) + run: vendor/bin/console sylius:fixtures:load -n - - name: Run security check - run: symfony security:check + name: Run Non-unit PHPUnit tests + run: vendor/bin/phpunit --colors=always --testsuite=non-unit - - name: Run PHPStan - run: vendor/bin/phpstan analyse -c phpstan.neon -l max src/ + name: Install certificates + run: symfony server:ca:install || true - - name: Run PHPSpec - run: vendor/bin/phpspec run --ansi -f progress --no-interaction + name: Run Chrome Headless + run: google-chrome-stable --enable-automation --disable-background-networking --no-default-browser-check --no-first-run --disable-popup-blocking --disable-default-apps --allow-insecure-localhost --disable-translate --disable-extensions --no-sandbox --enable-features=Metal --headless --remote-debugging-port=9222 --window-size=2880,1800 --proxy-server='direct://' --proxy-bypass-list='*' http://127.0.0.1 > /dev/null 2>&1 & - - name: Run PHPUnit - run: vendor/bin/phpunit --colors=always + name: Run webserver + run: symfony server:start --port=8080 --daemon - name: Run Behat - run: vendor/bin/behat --colors --strict -vvv --no-interaction -f progress || vendor/bin/behat --colors --strict -vvv --no-interaction -f progress --rerun + run: vendor/bin/behat --colors --strict -vvv --no-interaction || vendor/bin/behat --colors --strict -vvv --no-interaction --rerun - name: Upload Behat logs - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 if: failure() with: - name: Behat logs + name: "Behat logs - ${{ matrix.sylius }}-${{ github.run_id }}-${{ github.run_number }}" path: etc/build/ if-no-files-found: ignore + compression-level: 6 + overwrite: true + + roave_bc_check: + name: Roave BC Check + runs-on: ubuntu-latest + env: + PHP_VERSION: 8.4 + steps: + - + name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - + name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ env.PHP_VERSION }}" + extensions: intl + tools: flex,symfony + coverage: none + + - + name: Install roave/backward-compatibility-check. + run: composer require --dev roave/backward-compatibility-check --no-plugins + + - + name: Run roave/backward-compatibility-check. + run: vendor/bin/roave-backward-compatibility-check --format=github-actions diff --git a/.gitignore b/.gitignore index 2fba55ec9..1ec4f2ccd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,23 @@ -/bin/* -!/bin/.gitkeep - /vendor/ +/var/ /node_modules/ +/drivers/ /composer.lock +/docker/ /etc/build/* -!/etc/build/.gitkeep +!/etc/build/.gitignore -/tests/Application/yarn.lock +/.phpunit.result.cache +/behat.yml +/behat.sh +/phpunit.xml +/compose.override.yml +/compose.test.yml -/web/media +# Symfony CLI https://symfony.com/doc/current/setup/symfony_server.html#different-php-settings-per-project +/.php-version +/php.ini -/.phpunit.result.cache +/tests/TestApplication/.env.local +/tests/TestApplication/.env.*.local diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..bc118d807 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,94 @@ +# Agent Instructions for SyliusAdminOrderCreationPlugin + +## What this is + +A Sylius 2.x plugin that lets an Administrator create (or reorder) an Order on behalf of a Customer directly from the admin panel — choosing channel/locale/currency, adding items with custom price adjustments, picking shipping/payment, previewing before confirming, and optionally generating/sending a payment link. + +- Plugin namespace: `Webgriffe\SyliusAdminOrderCreationPlugin` +- Test namespace: `Tests\Webgriffe\SyliusAdminOrderCreationPlugin` +- Requires PHP 8.2+, Sylius ^2.0, Symfony ^7.4 + +## Commands + +### Linting & Static Analysis +```bash +vendor/bin/ecs check # coding standard (fix: add --fix) +vendor/bin/phpstan analyse # level max, baseline in phpstan-baseline.neon +vendor/bin/psalm --no-cache # errorLevel 4, baseline in psalm-baseline.xml +``` + +### Tests +```bash +vendor/bin/phpspec run # unit specs (spec/) +vendor/bin/phpunit # all PHPUnit testsuites +vendor/bin/phpunit --testsuite=unit # tests/Unit +vendor/bin/phpunit --testsuite=functional # tests/Functional +vendor/bin/phpunit --testsuite=integration # tests/Integration +vendor/bin/phpunit --testsuite=non-unit # functional + integration +vendor/bin/behat --strict -vvv --no-interaction # Behat acceptance suite +``` + +Or run everything as CI does: `composer suite` (ecs → phpstan → psalm → phpunit → phpspec → behat). + +`vendor/bin/console` / `bin/console` are symlinks created by `bin/create_console_symlink.php`, which runs automatically post-install/post-update. + +## Architecture + +``` +src/ Plugin source, loaded via WebgriffeSyliusAdminOrderCreationExtension + Controller/ Order create/preview/customer-selection/ajax shipping-method actions + Factory/ Order factory + Preparator/ New-order preparation logic + Provider/ Shipping method / customer / payment token providers + EventListener/ Order creation, payment-link creation + ReorderProcessing/ Composite reorder processor + item/payment/shipment/data processors + Sender/ Order payment-link sender + Doctrine/ORM/ Repository traits/interfaces the host app must wire in + Form/ Form types + Migrations/ Doctrine migrations + DependencyInjection/ Bundle extension + configuration tree + +config/ + config.yaml Main plugin config + services.xml, services/ Service definitions + ajax.yaml, routing.yaml Routes + +templates/ Twig templates (flat `templates/` dir, Sylius 2 convention — not src/Resources/views) + +tests/ + TestApplication/ Full Symfony app (sylius/test-application) used by PHPUnit and Behat + config/bundles.php Registers only this plugin bundle + config/config.yaml Imports plugin config + host-app overrides (repositories, etc.) + Unit/, Integration/, Functional/ PHPUnit suites + Behat/ Contexts, pages, elements, feature files + +spec/ phpspec specs, mirrors src/ structure +``` + +## Key Conventions + +### Language +All code must be in English — class names, method names, variable names, comments, log messages, and exception messages. Italian is only acceptable in user-facing content (Twig templates, translation files under `translations/`). + +### PHP +- All PHP files: `declare(strict_types=1);` — no exceptions. +- Classes default to `final` unless extension is explicitly needed. +- Only add inline comments when the implementation logic is genuinely complex and requires explanation. Keep code self-documenting through clear naming. + +### Static Analysis & Style +- PHPStan at level max (baseline in `phpstan-baseline.neon`); `src/DependencyInjection/Configuration.php` is excluded (crashes PHPStan). +- Psalm at error level 4 (baseline in `psalm-baseline.xml`). +- Coding standard: `sylius-labs/coding-standard` ECS ruleset applied to `src/`, `spec/`, `tests/Behat/`, and `ecs.php`. + +### Tests +- **Unit logic** (mappers, factories, preparators, providers, processors with few dependencies): phpspec, under `spec/`, mirroring `src/` structure — this is the primary unit-testing tool in this plugin. +- **Integration/Functional** (`tests/Integration`, `tests/Functional`): `KernelTestCase`-based, boot the real `sylius/test-application` kernel — use for anything touching the container, Doctrine, or the HTTP layer. +- **Behat** (`tests/Behat`): admin-panel acceptance flows (order creation, reorder, payment link). Contexts under `tests/Behat/Context`, pages under `tests/Behat/Page`. + +### Configuration & Services +- New services go in `config/services/` (or `config/services.xml` for the top-level list). +- `composer.lock` is gitignored — dependency versions float on every `composer install`. `symfony/type-info` is constrained to `>=7.2 <7.4` in `composer.json`: 7.4 added stricter construction validation for union/collection/generic types that breaks Sylius's API Bundle routing loader (`Cannot create union with both "object" and class type`, thrown while parsing `Sylius/Bundle/ApiBundle/Resources/config/routing.yml`) — not an `api-platform/symfony` version issue, that package resolves fine on its own. Don't loosen this constraint without re-verifying `bin/console debug:router` still works. +- After any change to the plugin's namespace, class map, or `composer.json` autoload section, run `composer dump-autoload` **inside the Docker container** too, not just locally — the container's autoload map is what the running app actually uses. + +### Git +- This plugin was migrated from Sylius 1.x to Sylius 2.x (branch `sylius-2`): infrastructure (composer deps, DI/config layout, state machine, test harness, Behat, CI) and the Twig/UI layer (Bootstrap/Tabler admin UI, all templates registered as Twig Hooks under `config/twig_hooks/`) are both done. See the README's "Extension points" section for the hook names in use. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..43c994c2d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/README.md b/README.md index f35172096..1319a3757 100644 --- a/README.md +++ b/README.md @@ -34,28 +34,44 @@ After creating an Order via Admin panel, this new Order is listed like any other ## Installation -#### Beware! - -> This installation instruction assumes that you're using Symfony Flex. If you don't, take a look at the -[legacy installation instruction](docs/legacy_installation.md). However, we strongly encourage you to use -Symfony Flex, it's much quicker! :) +Requires Sylius `~2.2.0` and PHP `^8.2`. 1. Require plugin with composer: ```bash - composer require sylius/admin-order-creation-plugin + composer require webgriffe/sylius-admin-order-creation-plugin ``` - + > Remember to allow community recipes with `composer config extra.symfony.allow-contrib true` or during plugin installation process -2. Copy Sylius templates overridden in plugin to your templates directory (e.g `templates/bundles/`): +2. Register the bundle in `config/bundles.php`: + + ```php + Webgriffe\SyliusAdminOrderCreationPlugin\WebgriffeSyliusAdminOrderCreationPlugin::class => ['all' => true], + ``` + +3. Import plugin configuration in `config/packages/sylius_admin_order_creation_plugin.yaml`: + + ```yaml + imports: + - { resource: "@WebgriffeSyliusAdminOrderCreationPlugin/config/config.yaml" } + ``` + +4. Import plugin routes in `config/routes/sylius_admin_order_creation_plugin.yaml`: + + ```yaml + sylius_admin_order_creation_plugin: + resource: "@WebgriffeSyliusAdminOrderCreationPlugin/config/routing.yaml" + ``` + +5. Copy Sylius templates overridden in plugin to your templates directory (e.g `templates/bundles/`): ```bash mkdir -p templates/bundles/SyliusAdminBundle/ - cp -R vendor/sylius/admin-order-creation-plugin/src/Resources/views/SyliusAdminBundle/* templates/bundles/SyliusAdminBundle/ + cp -R vendor/webgriffe/sylius-admin-order-creation-plugin/templates/bundles/SyliusAdminBundle/* templates/bundles/SyliusAdminBundle/ ``` -3. Override repositories +6. Override repositories 1. Create repository classes ```bash @@ -71,8 +87,8 @@ Symfony Flex, it's much quicker! :) namespace App\Repository; - use Sylius\AdminOrderCreationPlugin\Doctrine\ORM\CustomerRepositoryInterface; - use Sylius\AdminOrderCreationPlugin\Doctrine\ORM\CustomerRepositoryTrait; + use Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM\CustomerRepositoryInterface; + use Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM\CustomerRepositoryTrait; use Sylius\Bundle\CoreBundle\Doctrine\ORM\CustomerRepository as BaseCustomerRepository; final class CustomerRepository extends BaseCustomerRepository implements CustomerRepositoryInterface @@ -88,8 +104,8 @@ Symfony Flex, it's much quicker! :) namespace App\Repository; - use Sylius\AdminOrderCreationPlugin\Doctrine\ORM\ProductVariantRepositoryInterface; - use Sylius\AdminOrderCreationPlugin\Doctrine\ORM\ProductVariantRepositoryTrait; + use Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM\ProductVariantRepositoryInterface; + use Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM\ProductVariantRepositoryTrait; use Sylius\Bundle\CoreBundle\Doctrine\ORM\ProductVariantRepository as BaseProductVariantRepository; final class ProductVariantRepository extends BaseProductVariantRepository implements ProductVariantRepositoryInterface @@ -117,22 +133,115 @@ Symfony Flex, it's much quicker! :) ## Extension points -Admin Order Creation Plugin makes it possible to add custom discount during order creation - thus some of Order -Show templates need to be replaced with those placed in `Resources/views` package. +### Configuration + +The plugin exposes semantic configuration under `sylius_admin_order_creation_plugin`: + +```yaml +sylius_admin_order_creation_plugin: + # Gateway names for which no payment link is generated after an order is created from the admin panel. + offline_gateway_names: ['offline'] + # Whether to generate (and optionally send) a payment link at all after an order is created from the admin panel. + payment_link_generation_enabled: true +``` + +### Events + +In addition to the generic Sylius core `sylius.order.pre_admin_create` / `sylius.order.post_admin_create` events the +plugin itself listens to, it dispatches its own typed events at the points a host application is most likely to +need a hook. Listen to any of them with a plain `#[AsEventListener]` instead of decorating or replacing the +plugin's own services: + +- `Event\OrderCreationInitializedEvent`, dispatched by `OrderFactory` whenever an order is initialized for admin + creation or reorder (carries the `OrderInterface`). Use it to guard/veto (e.g. throw an `AccessDeniedException`) + or enrich the order before it's shown or further processed. +- `Event\OrderCreatedByAdminEvent`, dispatched right after an order is created from the admin panel (carries the + created `OrderInterface`). Use it for side effects that only make sense once the order actually exists + (notifications, audit logging, ...). +- `Event\PaymentLinkGeneratedEvent`, dispatched right after a payment link is generated for a payment (carries the + `PaymentInterface`), independently of whether the "send by email" checkbox was ticked. + +### Payment link generation + +Payment link generation and sending is based on logic placed in the `PaymentLinkCreationListener` class. It can be +turned off entirely via the `payment_link_generation_enabled` configuration flag, or replaced altogether by +decorating/replacing the service for more advanced needs. + +### Templates (Twig Hooks) + +All the plugin's admin pages are registered as [Twig Hooks](https://docs.sylius.com/the-book/customization/twig-hooks). +Override or add your own hookable template at the same hook name (with a different priority, or `enabled: false` to +remove a default one) instead of copying the whole page template. See the corresponding `config/twig_hooks/*.yaml` +file for the exact hook names in use: -Payment link generation and sending process is based on logic placed in the PaymentLinkCreationListener class. Thus, it can -be easily replaced with suitable implementation. +- `config/twig_hooks/order_show.yaml` - Order Show sections related to this plugin (discount rows, payment-link + action, ...), hooked into Sylius core's own `sylius_admin.order.show...` hook tree. +- `config/twig_hooks/order_create.yaml` / `order_preview.yaml` - the `sylius_admin_order_creation.order.create.content` + / `...order.preview.content` hooks, each with a single `form` hookable wrapping the order creation/preview Live + Component. Order creation and preview aren't Sylius resource CRUD routes, so unlike Order Show these hooks are + defined by the plugin itself rather than plugged into a pre-existing Sylius hook tree. +- `config/twig_hooks/order_select_customer.yaml` - the `sylius_admin_order_creation.order.select_customer.content` + hook, with two independent hookables: `existing_customer` and `new_customer` (one card each). Disable + `new_customer` (`enabled: false`) if your application always creates orders for existing customers, for example. -Adjustments set is not closed and strictly defined - adding custom adjustment means defining a new constant in the -AdjustmentType class. +The page shells (`templates/order/create.html.twig`, `preview.html.twig`, `select_customer.html.twig`) still extend +`@SyliusAdmin/shared/layout/base.html.twig` and include the standard sidebar/navbar/flashes/footer by hand, since +these are plain controller-rendered pages, not Sylius resource CRUD routes with their own generic hookable layout. + +### Adjustments + +The set of order/item adjustment types is not closed - adding a custom adjustment means defining a new constant on +your own adjustment-type class; `AdjustmentType`'s own constants (`ORDER_DISCOUNT_ADJUSTMENT`, +`ORDER_ITEM_DISCOUNT_ADJUSTMENT`) are not extensible themselves, since the class is `final`. + +### Reorder processing Significant part of Reorder Processing is inspired by official Sylius [Customer Reorder Plugin](https://github.com/Sylius/CustomerReorderPlugin/). In case of the need for more processors, just add new class implementing `ReorderProcessor` interface, declare it in `reorder_processing.xml` file and match it with a proper tag. +### Forms + Admin Order Creation process is based on Symfony Forms. To find out more about Symfony Forms extension possibilities, check out -[Symfony Docs](https://symfony.com/doc/current/form/create_form_type_extension.html). +[Symfony Docs](https://symfony.com/doc/current/form/create_form_type_extension.html). + +### Factory + +`Factory\OrderFactoryInterface` is aliased as a service, and routes resolve their order factory through that alias +rather than the concrete `OrderFactory` class, so a host application can decorate `OrderFactoryInterface` and have +its decorator picked up wherever the plugin creates an order (e.g. to attach the placing administrator to the +order, or to reuse an in-progress order from session storage). + +## Development + +### Docker + +1. Copy `compose.override.dist.yml` to `compose.override.yml` and adjust it to your needs. + +2. Start the containers: + + ```bash + docker compose up -d + ``` + +3. Install PHP dependencies and initialize the test application: + + ```bash + docker compose exec php composer install + docker compose exec php composer test-app-init + ``` + +4. The test application is available at `http://localhost`. + +### Running the test suite + +```bash +docker compose exec php composer suite +``` + +runs ECS, PHPStan, Psalm, PHPSpec, PHPUnit and Behat in sequence (each can also be run individually via `composer ecs`, +`composer phpstan`, `composer psalm`, `composer phpspec`, `composer phpunit`, `composer behat`). ## Security issues diff --git a/UPGRADE.md b/UPGRADE.md index b9429eff0..3cd51577e 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -32,8 +32,8 @@ declare(strict_types=1); namespace App\Doctrine\ORM; -use Sylius\AdminOrderCreationPlugin\Doctrine\ORM\CustomerRepositoryInterface; -use Sylius\AdminOrderCreationPlugin\Doctrine\ORM\CustomerRepositoryTrait; +use Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM\CustomerRepositoryInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM\CustomerRepositoryTrait; use Sylius\Bundle\CoreBundle\Doctrine\ORM\CustomerRepository as BaseCustomerRepository; final class CustomerRepository extends BaseCustomerRepository implements CustomerRepositoryInterface @@ -50,8 +50,8 @@ declare(strict_types=1); namespace App\Doctrine\ORM; -use Sylius\AdminOrderCreationPlugin\Doctrine\ORM\ProductVariantRepositoryInterface; -use Sylius\AdminOrderCreationPlugin\Doctrine\ORM\ProductVariantRepositoryTrait; +use Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM\ProductVariantRepositoryInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM\ProductVariantRepositoryTrait; use Sylius\Bundle\CoreBundle\Doctrine\ORM\ProductVariantRepository as BaseProductVariantRepository; final class ProductVariantRepository extends BaseProductVariantRepository implements ProductVariantRepositoryInterface diff --git a/assets/admin/controllers.json b/assets/admin/controllers.json new file mode 100644 index 000000000..e2ce9b5cb --- /dev/null +++ b/assets/admin/controllers.json @@ -0,0 +1,4 @@ +{ + "controllers": [], + "entrypoints": [] +} diff --git a/tests/Application/config/api_platform/.gitignore b/assets/admin/entrypoint.js similarity index 100% rename from tests/Application/config/api_platform/.gitignore rename to assets/admin/entrypoint.js diff --git a/assets/shop/controllers.json b/assets/shop/controllers.json new file mode 100644 index 000000000..e2ce9b5cb --- /dev/null +++ b/assets/shop/controllers.json @@ -0,0 +1,4 @@ +{ + "controllers": [], + "entrypoints": [] +} diff --git a/tests/Application/public/media/image/.gitignore b/assets/shop/entrypoint.js similarity index 100% rename from tests/Application/public/media/image/.gitignore rename to assets/shop/entrypoint.js diff --git a/behat.yml.dist b/behat.yml.dist index bab155d7e..531dee831 100644 --- a/behat.yml.dist +++ b/behat.yml.dist @@ -3,53 +3,51 @@ imports: - tests/Behat/Resources/suites.yml default: + calls: + error_reporting: 8191 # E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED + + formatters: + pretty: + verbose: true + paths: false + snippets: false + extensions: DMore\ChromeExtension\Behat\ServiceContainer\ChromeExtension: ~ - FriendsOfBehat\MinkDebugExtension\ServiceContainer\MinkDebugExtension: + FriendsOfBehat\MinkDebugExtension: directory: etc/build clean_start: false screenshot: true Behat\MinkExtension: files_path: "%paths.base%/vendor/sylius/sylius/src/Sylius/Behat/Resources/fixtures/" - base_url: "https://127.0.0.1:8080/" + base_url: "%env(BEHAT_BASE_URL)%" default_session: symfony - javascript_session: chrome_headless + javascript_session: chromedriver sessions: symfony: symfony: ~ - chrome_headless: + chromedriver: chrome: - api_url: http://127.0.0.1:9222 + api_url: "%env(BEHAT_CHROME_URL)%" + validate_certificate: false + chrome_headless_second_session: + chrome: + api_url: "%env(BEHAT_CHROME_URL)%" validate_certificate: false - chrome: - selenium2: - browser: chrome - capabilities: - browserName: chrome - browser: chrome - version: "" - marionette: null # https://github.com/Behat/MinkExtension/pull/311 - chrome: - switches: - - "start-fullscreen" - - "start-maximized" - - "no-sandbox" - extra_capabilities: - unexpectedAlertBehaviour: accept - firefox: - selenium2: - browser: firefox show_auto: false FriendsOfBehat\SymfonyExtension: - bootstrap: tests/Application/config/bootstrap.php + bootstrap: vendor/sylius/test-application/config/bootstrap.php kernel: - class: Tests\Sylius\AdminOrderCreationPlugin\Application\Kernel + class: Sylius\TestApplication\Kernel + environment: test FriendsOfBehat\VariadicExtension: ~ FriendsOfBehat\SuiteSettingsExtension: paths: - "features" + + SyliusLabs\SuiteTagsExtension: ~ diff --git a/bin/create_console_symlink.php b/bin/create_console_symlink.php new file mode 100644 index 000000000..9dbf03cb1 --- /dev/null +++ b/bin/create_console_symlink.php @@ -0,0 +1,53 @@ + {$targetRelativeFromBin}"); +} + +@chmod($linkPath, 0755); +info("Created symlink: bin/console -> {$targetRelativeFromBin}"); diff --git a/compose.override.dist.yml b/compose.override.dist.yml new file mode 100644 index 000000000..5b4e56e62 --- /dev/null +++ b/compose.override.dist.yml @@ -0,0 +1,66 @@ +services: + php: + image: ghcr.io/sylius/sylius-php:8.3-fixuid-xdebug-alpine + user: ${DOCKER_USER:-1000:1000} + depends_on: + mysql: + condition: service_healthy + environment: + # You can move these environment variables to your .env.local file + APP_ENV: ${ENV:-prod} + APP_SECRET: EDITME + DATABASE_URL: "mysql://root@mysql/sylius_%kernel.environment%" + MAILER_DSN: smtp://mailhog:1025 + MESSENGER_TRANSPORT_DSN: doctrine://default + SYLIUS_MESSENGER_TRANSPORT_MAIN_DSN: doctrine://default + SYLIUS_MESSENGER_TRANSPORT_MAIN_FAILED_DSN: doctrine://default?queue_name=main_failed + SYLIUS_MESSENGER_TRANSPORT_CATALOG_PROMOTION_REMOVAL_DSN: doctrine://default?queue_name=catalog_promotion_removal + SYLIUS_MESSENGER_TRANSPORT_CATALOG_PROMOTION_REMOVAL_FAILED_DSN: doctrine://default?queue_name=catalog_promotion_removal_failed + SYLIUS_MESSENGER_TRANSPORT_PAYMENT_REQUEST_DSN: sync:// + SYLIUS_MESSENGER_TRANSPORT_PAYMENT_REQUEST_FAILED_DSN: sync:// + PHP_DATE_TIMEZONE: ${PHP_DATE_TIMEZONE:-UTC} + XDEBUG_MODE: debug + XDEBUG_CONFIG: >- + client_host=host.docker.internal + client_port=9003 + # This should correspond to the server declared in PHPStorm `Preferences | Languages & Frameworks | PHP | Servers` + # Then PHPStorm will use the corresponding path mappings + PHP_IDE_CONFIG: serverName=sylius + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - .:/srv/sylius:rw,cached + # if you develop on Linux, you may use a bind-mounted host directory instead +# - ./var:/srv/sylius/var:rw + - ./public:/srv/sylius/public:rw,delegated + # if you develop on Linux, you may use a bind-mounted host directory instead +# - ./public/media:/srv/sylius/public/media:rw + - public-media:/srv/sylius/public/media:rw + nginx: + environment: + WORKING_DIR: /srv/sylius/vendor/sylius/test-application + volumes: + - .:/srv/sylius:ro + # if you develop on Linux, you may use a bind-mounted host directory instead +# - ./public/media:/srv/sylius/public/media:ro + ports: + - "80:80" + nodejs: + image: node:${NODE_VERSION:-20}-alpine + user: ${DOCKER_USER:-1000:1000} + working_dir: /srv/sylius + entrypoint: [ "/bin/sh","-c" ] + command: + - | + cd vendor/sylius/test-application + yarn install + yarn build + volumes: + - .:/srv/sylius:rw,cached + - ./public:/srv/sylius/public:rw,delegated + mailhog: + ports: + - "8025:8025" + +volumes: + public-media: diff --git a/compose.yml b/compose.yml new file mode 100644 index 000000000..3e76f267d --- /dev/null +++ b/compose.yml @@ -0,0 +1,40 @@ +services: + php: + image: ghcr.io/sylius/sylius-php:8.4-alpine + mysql: + image: mysql:8.4 + platform: linux/amd64 + healthcheck: + test: '/usr/bin/mysql --execute "SHOW databases;"' + timeout: 3s + interval: 1s + retries: 10 + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: 1 + cap_add: + - SYS_NICE # prevent "mbind: Operation not permitted" errors + ports: + - ${MYSQL_PORT:-3306}:3306 + volumes: + - sylius-admin-order-creation-plugin-mysql-data:/var/lib/mysql:rw + nginx: + image: ghcr.io/sylius/sylius-nginx:latest + depends_on: + - php + chrome: + image: chromedp/headless-shell:stable + command: --remote-allow-origins=* + + chrome-proxy: + image: nginx:alpine + volumes: + - ./docker/nginx/chrome-proxy.conf:/etc/nginx/conf.d/default.conf:ro + depends_on: + - chrome + + mailhog: + # do not use in production! + image: axllent/mailpit:latest + +volumes: + sylius-admin-order-creation-plugin-mysql-data: diff --git a/composer.json b/composer.json index 4047626fd..02bd81b62 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "sylius/admin-order-creation-plugin", + "name": "webgriffe/sylius-admin-order-creation-plugin", "type": "sylius-plugin", "keywords": ["sylius", "sylius-plugin", "symfony", "e-commerce", "admin order creation"], "description": "Sylius Plugin for order creation in Admin panel", @@ -19,59 +19,116 @@ } ], "require": { - "php": "^8.0", - - "sylius/sylius": "~1.10.11 || ~1.11.2", - "friendsofsymfony/jsrouting-bundle": "^2.2" + "php": "^8.2", + "sylius/sylius": "^2.0", + "symfony/type-info": ">=7.2 <7.4" }, "require-dev": { - "behat/behat": "^3.6", - "behat/mink-selenium2-driver": "^1.4", - "dmore/behat-chrome-extension": "^1.3", - "dmore/chrome-mink-driver": "^2.7", - "friends-of-behat/mink": "^1.8", - "friends-of-behat/mink-browserkit-driver": "^1.3", - "friends-of-behat/mink-extension": "^2.5", + "behat/behat": "^3.16", + "behat/mink": "^1.13", + "behat/mink-selenium2-driver": "^1.7", + "dbrekelmans/bdi": "^1.4", + "dmore/behat-chrome-extension": "^1.4", + "dmore/chrome-mink-driver": "^2.9", + "friends-of-behat/mink-browserkit-driver": "^1.6", + "friends-of-behat/mink-debug-extension": "^2.1", + "friends-of-behat/mink-extension": "^2.7", "friends-of-behat/page-object-extension": "^0.3", - "friends-of-behat/suite-settings-extension": "^1.0", - "friends-of-behat/symfony-extension": "^2.1", - "friends-of-behat/variadic-extension": "^1.3", - "friends-of-behat/mink-debug-extension": "^2.0", - "friendsofsymfony/oauth-server-bundle": "^1.6 || >2.0.0-alpha.0 ^2.0@dev", - "phpspec/phpspec": "^7.0", - "phpstan/phpstan": "0.12.96", - "phpstan/phpstan-webmozart-assert": "0.12.12", - "phpunit/phpunit": "^9.5", + "friends-of-behat/suite-settings-extension": "^1.1", + "friends-of-behat/symfony-extension": "^2.6", + "friends-of-behat/variadic-extension": "^1.6", + "phpspec/phpspec": "^8.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.12", + "phpstan/phpstan-doctrine": "^1.3", + "phpstan/phpstan-strict-rules": "^1.3.0", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^10.5", "polishsymfonycommunity/symfony-mocker-container": "^1.0", - "se/selenium-server-standalone": "^2.52", - "sylius-labs/coding-standard": "^3.0", - "symfony/debug-bundle": "^4.4 || ^5.4", - "symfony/dotenv": "^4.4 || ^5.4", - "symfony/web-profiler-bundle": "^4.4 || ^5.4" + "psalm/plugin-symfony": "^5.2", + "rector/rector": "^1.0", + "sylius-labs/coding-standard": "^4.4", + "sylius-labs/suite-tags-extension": "~0.2", + "sylius/sylius-rector": "^2.0", + "sylius/test-application": "^2.0.0@alpha", + "symfony/browser-kit": "^6.4 || ^7.4", + "symfony/debug-bundle": "^6.4 || ^7.4", + "symfony/dotenv": "^6.4 || ^7.4", + "symfony/intl": "^6.4 || ^7.4", + "symfony/runtime": "^6.4 || ^7.4", + "symfony/web-profiler-bundle": "^6.4 || ^7.4", + "symfony/webpack-encore-bundle": "^2.2", + "vimeo/psalm": "^6.13" }, - "conflict": { - "doctrine/dbal": "^3.0" + "config": { + "sort-packages": true, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": false, + "php-http/discovery": false, + "phpstan/extension-installer": true, + "symfony/flex": true, + "symfony/runtime": true + } + }, + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + }, + "symfony": { + "require": "^7.4" + }, + "public-dir": "vendor/sylius/test-application/public" }, - "prefer-stable": true, "autoload": { "psr-4": { - "Sylius\\AdminOrderCreationPlugin\\": "src/", - "Tests\\Sylius\\AdminOrderCreationPlugin\\": "tests/" + "Webgriffe\\SyliusAdminOrderCreationPlugin\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\Webgriffe\\SyliusAdminOrderCreationPlugin\\": ["tests/", "tests/TestApplication/src/"] } }, "scripts": { - "analyse": [ - "@composer validate --strict", - "vendor/bin/phpstan analyse -c phpstan.neon -l max src/", - "vendor/bin/ecs check src/ spec/" + "database-reset": [ + "vendor/bin/console doctrine:database:drop --force --if-exists", + "vendor/bin/console doctrine:database:create", + "vendor/bin/console doctrine:migration:migrate -n", + "vendor/bin/console sylius:fixtures:load -n" + ], + "frontend-clear": [ + "cd vendor/sylius/test-application && yarn install && yarn build", + "vendor/bin/console assets:install" ], - "fix": [ - "vendor/bin/ecs check src/ spec/ --fix" + "test-app-init": [ + "@database-reset", + "@frontend-clear" + ], + "ecs": "ecs", + "phpstan": "phpstan analyse", + "psalm": "psalm --no-cache", + "phpunit": "phpunit", + "phpspec": "phpspec run", + "behat": "behat --strict -vvv --no-interaction || behat --strict -vvv --no-interaction --rerun", + "suite": [ + "@ecs", + "@phpstan", + "@psalm", + "@phpunit", + "@phpspec", + "@behat" + ], + "auto-scripts": { + "security-checker security:check": "script" + }, + "post-install-cmd": [ + "@create-console-symlink" + ], + "post-update-cmd": [ + "@create-console-symlink" + ], + "create-console-symlink": [ + "@php bin/create_console_symlink.php" ] - }, - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } } } diff --git a/src/Resources/config/app/config.yml b/config/config.yaml similarity index 82% rename from src/Resources/config/app/config.yml rename to config/config.yaml index 8964c6e19..96ec9f49d 100644 --- a/src/Resources/config/app/config.yml +++ b/config/config.yaml @@ -1,5 +1,6 @@ imports: - - { resource: '@SyliusAdminOrderCreationPlugin/Resources/config/services.xml' } + - { resource: '@WebgriffeSyliusAdminOrderCreationPlugin/config/services.xml' } + - { resource: 'twig_hooks/**/*.yaml' } sylius_grid: grids: @@ -29,4 +30,4 @@ sylius_mailer: emails: order_created_in_admin_panel: subject: 'sylius_admin_order_creation.email.order_created.subject' - template: '@SyliusAdminOrderCreationPlugin/Emails/orderCreated.html.twig' + template: '@WebgriffeSyliusAdminOrderCreationPlugin/emails/order_created.html.twig' diff --git a/src/Resources/config/app/routing.yml b/config/routing.yaml similarity index 51% rename from src/Resources/config/app/routing.yml rename to config/routing.yaml index a235552ba..3372742a1 100644 --- a/src/Resources/config/app/routing.yml +++ b/config/routing.yaml @@ -2,7 +2,7 @@ sylius_admin_order_creation_select_order_customer: path: /admin/orders/new/select-customer methods: [GET] defaults: - _controller: Sylius\AdminOrderCreationPlugin\Controller\SelectNewOrderCustomerAction + _controller: Webgriffe\SyliusAdminOrderCreationPlugin\Controller\SelectNewOrderCustomerAction sylius_admin_order_creation_customer_create: path: /admin/orders/new/customer/{customerEmail}/{channelCode} @@ -10,7 +10,7 @@ sylius_admin_order_creation_customer_create: options: expose: true defaults: - _controller: Sylius\AdminOrderCreationPlugin\Controller\CustomerCreationAction + _controller: Webgriffe\SyliusAdminOrderCreationPlugin\Controller\CustomerCreationAction sylius_admin_order_creation_order_create: path: /admin/orders/new/{customerId}/{channelCode} @@ -18,16 +18,16 @@ sylius_admin_order_creation_order_create: options: expose: true defaults: - _controller: sylius.controller.order:createAction + _controller: sylius.controller.order::createAction _sylius: event: admin_create section: admin permission: true - template: '@SyliusAdminOrderCreationPlugin/Order/create.html.twig' + template: '@WebgriffeSyliusAdminOrderCreationPlugin/order/create.html.twig' form: - type: Sylius\AdminOrderCreationPlugin\Form\Type\NewOrderType + type: Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type\NewOrderType factory: - method: ['expr:service("Sylius\\AdminOrderCreationPlugin\\Factory\\OrderFactory")', 'createForCustomerAndChannel'] + method: ['expr:service("Webgriffe\\SyliusAdminOrderCreationPlugin\\Factory\\OrderFactoryInterface")', 'createForCustomerAndChannel'] arguments: [$customerId, $channelCode] requirements: customerId: \d+ @@ -36,12 +36,12 @@ sylius_admin_order_creation_order_preview: path: /admin/orders/new/{customerId}/{channelCode}/preview methods: [POST] defaults: - _controller: Sylius\AdminOrderCreationPlugin\Controller\OrderPreviewAction + _controller: Webgriffe\SyliusAdminOrderCreationPlugin\Controller\OrderPreviewAction _sylius: form: - type: Sylius\AdminOrderCreationPlugin\Form\Type\NewOrderType + type: Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type\NewOrderType factory: - method: ['expr:service("Sylius\\AdminOrderCreationPlugin\\Factory\\OrderFactory")', 'createForCustomerAndChannel'] + method: ['expr:service("Webgriffe\\SyliusAdminOrderCreationPlugin\\Factory\\OrderFactoryInterface")', 'createForCustomerAndChannel'] arguments: [$customerId, $channelCode] sylius_admin_order_creation_order_create_from_preview: @@ -50,16 +50,16 @@ sylius_admin_order_creation_order_create_from_preview: options: expose: true defaults: - _controller: sylius.controller.order:createAction + _controller: sylius.controller.order::createAction _sylius: event: admin_create section: admin permission: true - template: '@SyliusAdminOrderCreationPlugin/Order/preview.html.twig' + template: '@WebgriffeSyliusAdminOrderCreationPlugin/order/preview.html.twig' form: - type: Sylius\AdminOrderCreationPlugin\Form\Type\NewOrderType + type: Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type\NewOrderType factory: - method: ['expr:service("Sylius\\AdminOrderCreationPlugin\\Factory\\OrderFactory")', 'createForCustomerAndChannel'] + method: ['expr:service("Webgriffe\\SyliusAdminOrderCreationPlugin\\Factory\\OrderFactoryInterface")', 'createForCustomerAndChannel'] arguments: [$customerId, $channelCode] sylius_admin_order_creation_order_create_back: @@ -68,7 +68,7 @@ sylius_admin_order_creation_order_create_back: options: expose: true defaults: - _controller: Sylius\AdminOrderCreationPlugin\Controller\OrderCreateAction + _controller: Webgriffe\SyliusAdminOrderCreationPlugin\Controller\OrderCreateAction requirements: customerId: \d+ @@ -78,21 +78,14 @@ sylius_admin_order_creation_reorder: options: expose: true defaults: - _controller: sylius.controller.order:createAction + _controller: sylius.controller.order::createAction _sylius: event: admin_create section: admin permission: true - template: '@SyliusAdminOrderCreationPlugin/Order/create.html.twig' + template: '@WebgriffeSyliusAdminOrderCreationPlugin/order/create.html.twig' form: - type: Sylius\AdminOrderCreationPlugin\Form\Type\NewOrderType + type: Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type\NewOrderType factory: - method: ['expr:service("Sylius\\AdminOrderCreationPlugin\\Factory\\OrderFactory")', 'createFromExistingOrder'] + method: ['expr:service("Webgriffe\\SyliusAdminOrderCreationPlugin\\Factory\\OrderFactoryInterface")', 'createFromExistingOrder'] arguments: ["expr:service('sylius.repository.order').find($id)"] - -sylius_admin_order_creation_ajax: - prefix: admin/ajax - resource: "@SyliusAdminOrderCreationPlugin/Resources/config/app/ajax.yml" - -fos_js_routing: - resource: "@FOSJsRoutingBundle/Resources/config/routing/routing.xml" diff --git a/config/services.xml b/config/services.xml new file mode 100644 index 000000000..12a517cd2 --- /dev/null +++ b/config/services.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %sylius.model.customer.class% + + + + + %sylius.model.product_variant.class% + + + + + + + + %sylius.model.order.class% + %sylius.form.type.order.validation_groups% + + + + %sylius.model.order_item.class% + + %sylius.form.type.order_item.validation_groups% + + + + %sylius.model.shipment.class% + %sylius.form.type.shipment.validation_groups% + + + + %sylius.model.payment.class% + %sylius.form.type.payment.validation_groups% + + + + %sylius.model.adjustment.class% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %sylius_admin_order_creation_plugin.offline_gateway_names% + %sylius_admin_order_creation_plugin.payment_link_generation_enabled% + + + + + + + + + + sylius_shop_order_after_pay + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/services/reorder_processing.xml b/config/services/reorder_processing.xml similarity index 56% rename from src/Resources/config/services/reorder_processing.xml rename to config/services/reorder_processing.xml index 9eddea77d..e3879ee09 100644 --- a/src/Resources/config/services/reorder_processing.xml +++ b/config/services/reorder_processing.xml @@ -4,26 +4,26 @@ - + - + - + - + - + - - + + diff --git a/config/twig_hooks/order_create.yaml b/config/twig_hooks/order_create.yaml new file mode 100644 index 000000000..9a35face8 --- /dev/null +++ b/config/twig_hooks/order_create.yaml @@ -0,0 +1,6 @@ +sylius_twig_hooks: + hooks: + 'sylius_admin_order_creation.order.create.content': + form: + template: '@WebgriffeSyliusAdminOrderCreationPlugin/order/create/sections/form.html.twig' + priority: 0 diff --git a/config/twig_hooks/order_preview.yaml b/config/twig_hooks/order_preview.yaml new file mode 100644 index 000000000..d407aa532 --- /dev/null +++ b/config/twig_hooks/order_preview.yaml @@ -0,0 +1,6 @@ +sylius_twig_hooks: + hooks: + 'sylius_admin_order_creation.order.preview.content': + form: + template: '@WebgriffeSyliusAdminOrderCreationPlugin/order/preview/sections/form.html.twig' + priority: 0 diff --git a/config/twig_hooks/order_select_customer.yaml b/config/twig_hooks/order_select_customer.yaml new file mode 100644 index 000000000..73f8fee85 --- /dev/null +++ b/config/twig_hooks/order_select_customer.yaml @@ -0,0 +1,9 @@ +sylius_twig_hooks: + hooks: + 'sylius_admin_order_creation.order.select_customer.content': + existing_customer: + template: '@WebgriffeSyliusAdminOrderCreationPlugin/order/select_customer/sections/existing_customer.html.twig' + priority: 100 + new_customer: + template: '@WebgriffeSyliusAdminOrderCreationPlugin/order/select_customer/sections/new_customer.html.twig' + priority: 0 diff --git a/config/twig_hooks/order_show.yaml b/config/twig_hooks/order_show.yaml new file mode 100644 index 000000000..8769953ee --- /dev/null +++ b/config/twig_hooks/order_show.yaml @@ -0,0 +1,22 @@ +sylius_twig_hooks: + hooks: + 'sylius_admin.order.show.content.sections.payments.item.actions': + pay_via_payment_link: + template: '@WebgriffeSyliusAdminOrderCreationPlugin/order/show/sections/payments/item/actions/pay_via_payment_link.html.twig' + priority: 150 + + 'sylius_admin.order.show.content.sections.summary': + order_discount: + template: '@WebgriffeSyliusAdminOrderCreationPlugin/order/show/sections/summary/order_discount.html.twig' + priority: -100 + + 'sylius_admin.order.show.content.sections.items.body': + unit_discount: + template: '@WebgriffeSyliusAdminOrderCreationPlugin/order/show/sections/items/body/unit_discount.html.twig' + priority: 600 + discounted_unit_price: + template: '@WebgriffeSyliusAdminOrderCreationPlugin/order/show/sections/items/body/discounted_unit_price.html.twig' + priority: 400 + subtotal: + template: '@WebgriffeSyliusAdminOrderCreationPlugin/order/show/sections/items/body/subtotal.html.twig' + priority: 200 diff --git a/docker/nginx/chrome-proxy.conf b/docker/nginx/chrome-proxy.conf new file mode 100644 index 000000000..88e01e577 --- /dev/null +++ b/docker/nginx/chrome-proxy.conf @@ -0,0 +1,22 @@ +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +server { + listen 9222; + + sub_filter '"ws://localhost/' '"ws://chrome-proxy:9222/'; + sub_filter_once off; + sub_filter_types application/json; + + location / { + proxy_pass http://chrome:9222; + proxy_set_header Host localhost; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_buffering off; + proxy_read_timeout 86400; + } +} diff --git a/docs/legacy_installation.md b/docs/legacy_installation.md deleted file mode 100644 index 2b9443770..000000000 --- a/docs/legacy_installation.md +++ /dev/null @@ -1,61 +0,0 @@ -### Legacy installation (without Symfony Flex) - -1. Require plugin with composer: - - ```bash - composer require sylius/admin-order-creation-plugin - ``` - -2. Import configuration to `app/config/config.yml`: - - ```yaml - imports: - - { resource: "@SyliusAdminOrderCreationPlugin/Resources/config/app/config.yml" } - ``` - -3. Import routing to `app/config/routing.yml`: - - ```yaml - sylius_admin_order_creation: - resource: "@SyliusAdminOrderCreationPlugin/Resources/config/app/routing.yml" - ``` - -4. Add plugin class to your `AppKernel`: - - ```php - $bundles = [ - new \FOS\JsRoutingBundle\FOSJsRoutingBundle(), - new \Sylius\AdminOrderCreationPlugin\SyliusAdminOrderCreationPlugin(), - ]; - ``` - -5. Copy Sylius templates overridden in plugin to your templates directory (e.g `app/Resources/SyliusAdminBundle/views/`): - - ```bash - mkdir -p app/Resources/SyliusAdminBundle/views/ - cp -R vendor/sylius/admin-order-creation-plugin/src/Resources/views/SyliusAdminBundle/* app/Resources/SyliusAdminBundle/views/ - ``` - -6. Override repositories - - As shown [here](tests/Application/Doctrine/ORM) - and [here](tests/Application/config/packages/_sylius.yaml). - -7. Copy plugin migrations to your migrations directory (e.g. `src/Migrations`) and apply them to your database: - - ```bash - cp -R vendor/sylius/admin-order-creation-plugin/migrations/* src/Migrations - bin/console doctrine:migrations:migrate - ``` - -8. Install `FOSJsRoutingBundle` assets: - - ```bash - bin/console assets:install --symlink web - ``` - -8. Clear cache: - - ```bash - bin/console cache:clear - ``` diff --git a/easy-coding-standard.neon b/easy-coding-standard.neon deleted file mode 100644 index 004aef043..000000000 --- a/easy-coding-standard.neon +++ /dev/null @@ -1,2 +0,0 @@ -includes: - - vendor/sylius-labs/coding-standard/easy-coding-standard.neon diff --git a/easy-coding-standard.yml b/easy-coding-standard.yml deleted file mode 100644 index 240359952..000000000 --- a/easy-coding-standard.yml +++ /dev/null @@ -1,2 +0,0 @@ -imports: - - { resource: 'vendor/sylius-labs/coding-standard/easy-coding-standard.yml' } diff --git a/ecs.php b/ecs.php new file mode 100644 index 000000000..ebb975777 --- /dev/null +++ b/ecs.php @@ -0,0 +1,21 @@ +paths([ + __DIR__ . '/src', + __DIR__ . '/spec', + __DIR__ . '/tests/Behat', + __DIR__ . '/ecs.php', + ]); + + $ecsConfig->import('vendor/sylius-labs/coding-standard/ecs.php'); + + $ecsConfig->skip([ + VisibilityRequiredFixer::class => ['*Spec.php'], + ]); +}; diff --git a/features/creating_order/being_unable_to_create_order_when_item_prices_are_not_defined_for_channel.feature b/features/creating_order/being_unable_to_create_order_when_item_prices_are_not_defined_for_channel.feature index 9c49b34b9..ba62af564 100644 --- a/features/creating_order/being_unable_to_create_order_when_item_prices_are_not_defined_for_channel.feature +++ b/features/creating_order/being_unable_to_create_order_when_item_prices_are_not_defined_for_channel.feature @@ -5,10 +5,10 @@ Feature: Being unable to create order when item prices are not defined for chann I want to be unable to create an order when item prices are not defined for channel Background: - Given the store operates on a channel named "Web-US" in "USD" currency + Given the store operates on a channel named "Web-PW" in "USD" currency + And the store operates on a channel named "Web-US" in "USD" currency And the store operates on a channel named "Web-EC" in "USD" currency - And the store operates on a channel named "Web-PW" in "USD" currency - And the store has a product "Stark Coat" priced at "$100" available in channel "Web-US" and channel "Web-EC" + And the store has a product "Stark Coat" priced at "$100.00" available in channel "Web-US" and channel "Web-EC" And the store ships everywhere for free And the store allows paying with "Cash on Delivery" And there is a customer account "jon.snow@the-wall.com" diff --git a/features/creating_order/creating_order_with_different_billing_address.feature b/features/creating_order/creating_order_with_different_billing_address.feature index 3954232ee..bca696838 100644 --- a/features/creating_order/creating_order_with_different_billing_address.feature +++ b/features/creating_order/creating_order_with_different_billing_address.feature @@ -6,8 +6,8 @@ Feature: Creating order with different billing address Background: Given the store operates on a single channel in "United States" - And the store has a product "Stark Coat" priced at "$100" - And the store has a product "Lannister Banner" priced at "$40" + And the store has a product "Stark Coat" priced at "$100.00" + And the store has a product "Lannister Banner" priced at "$40.00" And the store ships everywhere for free And the store allows paying with "Cash on Delivery" And there is a customer account "jon.snow@the-wall.com" diff --git a/features/creating_order/creating_order_with_multiple_items.feature b/features/creating_order/creating_order_with_multiple_items.feature index 3b8ebf945..1374655df 100644 --- a/features/creating_order/creating_order_with_multiple_items.feature +++ b/features/creating_order/creating_order_with_multiple_items.feature @@ -6,10 +6,10 @@ Feature: Creating order with multiple items Background: Given the store operates on a single channel in "United States" - And the store has a product "Stark Coat" priced at "$100" - And the store has a product "Lannister Banner" priced at "$40" - And the store has a product "Greyjoy Boat" priced at "$1000" - And the store has a product "Targaryen Shield" priced at "$200" + And the store has a product "Stark Coat" priced at "$100.00" + And the store has a product "Lannister Banner" priced at "$40.00" + And the store has a product "Greyjoy Boat" priced at "$1,000.00" + And the store has a product "Targaryen Shield" priced at "$200.00" And the store ships everywhere for free And the store allows paying with "Cash on Delivery" And there is a customer account "jon.snow@the-wall.com" diff --git a/features/creating_order/creating_order_with_offline_payment.feature b/features/creating_order/creating_order_with_offline_payment.feature index 73d5e0f70..a12f33aad 100644 --- a/features/creating_order/creating_order_with_offline_payment.feature +++ b/features/creating_order/creating_order_with_offline_payment.feature @@ -6,8 +6,8 @@ Feature: Creating order with offline payment Background: Given the store operates on a single channel in "United States" - And the store has a product "Stark Coat" priced at "$100" - And the store has a product "Lannister Banner" priced at "$40" + And the store has a product "Stark Coat" priced at "$100.00" + And the store has a product "Lannister Banner" priced at "$40.00" And the store ships everywhere for free And the store allows paying with "Cash on Delivery" And there is a customer account "jon.snow@the-wall.com" diff --git a/features/creating_order/creating_order_with_online_payment.feature b/features/creating_order/creating_order_with_online_payment.feature index 8f8020048..b52a50c2f 100644 --- a/features/creating_order/creating_order_with_online_payment.feature +++ b/features/creating_order/creating_order_with_online_payment.feature @@ -6,8 +6,8 @@ Feature: Creating order with online payment Background: Given the store operates on a single channel in "United States" - And the store has a product "Stark Coat" priced at "$100" - And the store has a product "Lannister Banner" priced at "$40" + And the store has a product "Stark Coat" priced at "$100.00" + And the store has a product "Lannister Banner" priced at "$40.00" And the store ships everywhere for free And the store has a payment method "Paypal" with a code "PAYPAL" and Paypal Express Checkout gateway And there is a customer account "jon.snow@the-wall.com" @@ -20,8 +20,22 @@ Feature: Creating order with online payment And I specify this order shipping address as "Ankh Morpork", "Frost Alley", "90210", "United States" for "Jon Snow" And I select "Free" shipping method And I select "Paypal" payment method - And I place and confirm this order + And I place this order + And I want to send a payment link email to the customer + And I confirm this order Then I should be notified that order has been successfully created And there should be a payment link displayed next to order's payment And there should be a payment link sent to "jon.snow@the-wall.com" And there should be one not paid nor shipped order with channel "United States" for "jon.snow@the-wall.com" in the registry + + @ui @javascript @email + Scenario: Not sending a payment link email by default + When I create a new order for "jon.snow@the-wall.com" and channel "United States" + And I add "Stark Coat" to this order + And I specify this order shipping address as "Ankh Morpork", "Frost Alley", "90210", "United States" for "Jon Snow" + And I select "Free" shipping method + And I select "Paypal" payment method + And I place and confirm this order + Then I should be notified that order has been successfully created + And there should be a payment link displayed next to order's payment + And there should be no payment link sent to "jon.snow@the-wall.com" diff --git a/features/creating_order/creating_order_without_payment.feature b/features/creating_order/creating_order_without_payment.feature index ce1094319..52b185926 100644 --- a/features/creating_order/creating_order_without_payment.feature +++ b/features/creating_order/creating_order_without_payment.feature @@ -6,7 +6,7 @@ Feature: Creating order without payment if order is free Background: Given the store operates on a single channel in "United States" - And the store has a product "Stark Coat" priced at "$0" + And the store has a product "Stark Coat" priced at "$0.00" And the store ships everywhere for free And there is a customer account "jon.snow@the-wall.com" And I am logged in as an administrator diff --git a/features/creating_order/creating_simple_order.feature b/features/creating_order/creating_simple_order.feature index 02474458e..4ad5973e2 100644 --- a/features/creating_order/creating_simple_order.feature +++ b/features/creating_order/creating_simple_order.feature @@ -6,8 +6,8 @@ Feature: Creating simple order Background: Given the store operates on a single channel in "United States" - And the store has a product "Stark Coat" priced at "$100" - And the store has a product "Lannister Banner" priced at "$40" + And the store has a product "Stark Coat" priced at "$100.00" + And the store has a product "Lannister Banner" priced at "$40.00" And the store ships everywhere for free And the store allows paying with "Cash on Delivery" And there is a customer account "jon.snow@the-wall.com" diff --git a/features/creating_order/modifying_item_price.feature b/features/creating_order/modifying_item_price.feature index db3e72018..ce1144ff8 100644 --- a/features/creating_order/modifying_item_price.feature +++ b/features/creating_order/modifying_item_price.feature @@ -6,8 +6,8 @@ Feature: Modifying unit price Background: Given the store operates on a single channel in "United States" - And the store has a product "Stark Coat" priced at "$100" - And the store has a product "Lannister Banner" priced at "$10" + And the store has a product "Stark Coat" priced at "$100.00" + And the store has a product "Lannister Banner" priced at "$10.00" And the store ships everywhere for free And the store allows paying with "Cash on Delivery" And there is a customer account "jon.snow@the-wall.com" @@ -22,7 +22,7 @@ Feature: Modifying unit price And I select "Free" shipping method And I select "Cash on Delivery" payment method And I place this order - And I lower item with "Stark Coat" price by "$100" + And I lower item with "Stark Coat" price by "$100.00" And I confirm this order And I check "Stark Coat" data Then I should be notified that order has been successfully created @@ -40,6 +40,6 @@ Feature: Modifying unit price And I select "Free" shipping method And I select "Cash on Delivery" payment method And I place this order - And I lower item with "Stark Coat" price by "-$5" + And I lower item with "Stark Coat" price by "-$5.00" And I confirm this order Then I should be notified that item with "Stark Coat" discount cannot be below 0 diff --git a/features/creating_order/modifying_order_total.feature b/features/creating_order/modifying_order_total.feature index b4852cd9b..c8c915eb4 100644 --- a/features/creating_order/modifying_order_total.feature +++ b/features/creating_order/modifying_order_total.feature @@ -6,7 +6,7 @@ Feature: Modifying order total Background: Given the store operates on a single channel in "United States" - And the store has a product "Stark Coat" priced at "$100" + And the store has a product "Stark Coat" priced at "$100.00" And the store ships everywhere for free And the store allows paying with "Cash on Delivery" And there is a customer account "jon.snow@the-wall.com" diff --git a/features/creating_order/placing_order_after_going_back_from_order_preview.feature b/features/creating_order/placing_order_after_going_back_from_order_preview.feature index 68cdfe3a8..f4a9e08fa 100644 --- a/features/creating_order/placing_order_after_going_back_from_order_preview.feature +++ b/features/creating_order/placing_order_after_going_back_from_order_preview.feature @@ -6,7 +6,7 @@ Feature: Placing order after going back from the order preview Background: Given the store operates on a single channel in "United States" - And the store has a product "Stark Coat" priced at "$100" + And the store has a product "Stark Coat" priced at "$100.00" And the store ships everywhere for free And the store allows paying with "Cash on Delivery" And there is a customer account "jon.snow@the-wall.com" diff --git a/features/creating_order/previewing_order_before_creation.feature b/features/creating_order/previewing_order_before_creation.feature index ec8fc23ed..f6459d415 100644 --- a/features/creating_order/previewing_order_before_creation.feature +++ b/features/creating_order/previewing_order_before_creation.feature @@ -8,7 +8,7 @@ Feature: Previewing order before creation Given the store operates on a single channel in "United States" And that channel allows to shop using "EUR" and "PLN" currencies And the store has locale "en_US" - And the store has a product "Stark Coat" priced at "$100" + And the store has a product "Stark Coat" priced at "$100.00" And the store ships everywhere for free And the store allows paying with "Cash on Delivery" And there is a customer account "jon.snow@the-wall.com" diff --git a/features/creating_order/validating_order_before_preview.feature b/features/creating_order/validating_order_before_preview.feature new file mode 100644 index 000000000..02b3d0e4e --- /dev/null +++ b/features/creating_order/validating_order_before_preview.feature @@ -0,0 +1,20 @@ +@admin_order_creation_managing_orders @ui @javascript +Feature: Validating the order before previewing it + In order to avoid placing an incomplete order + As an Administrator + I want the order creation form to be validated before I see its preview + + Background: + Given the store operates on a single channel in "United States" + And the store has a product "Stark Coat" priced at "$100.00" + And the store ships everywhere for free + And the store allows paying with "Cash on Delivery" + And there is a customer account "jon.snow@the-wall.com" + And I am logged in as an administrator + + Scenario: Trying to preview an order without a shipping address + When I create a new order for "jon.snow@the-wall.com" and channel "United States" + And I add "Stark Coat" to this order + And I place this order + Then I should still be on the order creation form + And I should see a validation error diff --git a/features/reordering/reordering_placed_order_with_promotions.feature b/features/reordering/reordering_placed_order_with_promotions.feature index 049d0720a..813e37651 100644 --- a/features/reordering/reordering_placed_order_with_promotions.feature +++ b/features/reordering/reordering_placed_order_with_promotions.feature @@ -33,7 +33,7 @@ Feature: Applying promotions during reordering previously placed order And I place and confirm this order Then I should be notified that order has been successfully created And the order's total should be "$200.00" - And the order's promotion total should be "$0.00" + And the order's promotion total should be "No promotion applied" Scenario: Not applying expired promotion during reordering previously placed order Given this promotion has already expired @@ -41,4 +41,4 @@ Feature: Applying promotions during reordering previously placed order And I place and confirm this order Then I should be notified that order has been successfully created And the order's total should be "$300.00" - And the order's promotion total should be "$0.00" + And the order's promotion total should be "No promotion applied" diff --git a/phpspec.yml.dist b/phpspec.yml.dist index 1540dc27c..4eb8d1977 100644 --- a/phpspec.yml.dist +++ b/phpspec.yml.dist @@ -1,4 +1,4 @@ suites: main: - namespace: Sylius\AdminOrderCreationPlugin - psr4_prefix: Sylius\AdminOrderCreationPlugin + namespace: Webgriffe\SyliusAdminOrderCreationPlugin + psr4_prefix: Webgriffe\SyliusAdminOrderCreationPlugin diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 000000000..112a78f03 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,321 @@ +parameters: + ignoreErrors: + - + message: "#^Parameter \\#1 \\$email of method Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Provider\\\\CustomerProviderInterface\\:\\:provideNewCustomer\\(\\) expects string, mixed given\\.$#" + count: 1 + path: src/Controller/CustomerCreationAction.php + + - + message: "#^Parameter \\#1 \\$customerId of method Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Factory\\\\OrderFactoryInterface\\:\\:createForCustomerAndChannel\\(\\) expects string, mixed given\\.$#" + count: 1 + path: src/Controller/OrderCreateAction.php + + - + message: "#^Parameter \\#2 \\$channelCode of method Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Factory\\\\OrderFactoryInterface\\:\\:createForCustomerAndChannel\\(\\) expects string, mixed given\\.$#" + count: 1 + path: src/Controller/OrderCreateAction.php + + - + message: "#^Parameter \\#1 \\$customerId of method Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Factory\\\\OrderFactoryInterface\\:\\:createForCustomerAndChannel\\(\\) expects string, mixed given\\.$#" + count: 1 + path: src/Controller/OrderPreviewAction.php + + - + message: "#^Parameter \\#1 \\$order of method Sylius\\\\Component\\\\Order\\\\Processor\\\\OrderProcessorInterface\\:\\:process\\(\\) expects Sylius\\\\Component\\\\Order\\\\Model\\\\OrderInterface, mixed given\\.$#" + count: 1 + path: src/Controller/OrderPreviewAction.php + + - + message: "#^Parameter \\#2 \\$channelCode of method Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Factory\\\\OrderFactoryInterface\\:\\:createForCustomerAndChannel\\(\\) expects string, mixed given\\.$#" + count: 1 + path: src/Controller/OrderPreviewAction.php + + - + message: "#^Call to static method Webmozart\\\\Assert\\\\Assert\\:\\:isInstanceOf\\(\\) with Sylius\\\\Component\\\\Core\\\\Model\\\\OrderInterface and 'Sylius\\\\\\\\Component\\\\\\\\Core\\\\\\\\Model\\\\\\\\OrderInterface' will always evaluate to true\\.$#" + count: 1 + path: src/EventListener/PaymentLinkCreationListener.php + + - + message: "#^Call to static method Webmozart\\\\Assert\\\\Assert\\:\\:isInstanceOf\\(\\) with Sylius\\\\Component\\\\Core\\\\Model\\\\ChannelInterface and 'Sylius\\\\\\\\Component\\\\\\\\Core\\\\\\\\Model\\\\\\\\ChannelInterface' will always evaluate to true\\.$#" + count: 1 + path: src/Factory/OrderFactory.php + + - + message: "#^Call to static method Webmozart\\\\Assert\\\\Assert\\:\\:isInstanceOf\\(\\) with Sylius\\\\Component\\\\Core\\\\Model\\\\CustomerInterface and 'Sylius\\\\\\\\Component\\\\\\\\Core\\\\\\\\Model\\\\\\\\CustomerInterface' will always evaluate to true\\.$#" + count: 1 + path: src/Factory/OrderFactory.php + + - + message: "#^Call to static method Webmozart\\\\Assert\\\\Assert\\:\\:isInstanceOf\\(\\) with Sylius\\\\Component\\\\Core\\\\Model\\\\OrderInterface and 'Sylius\\\\\\\\Component\\\\\\\\Core\\\\\\\\Model\\\\\\\\OrderInterface' will always evaluate to true\\.$#" + count: 2 + path: src/Factory/OrderFactory.php + + - + message: "#^Call to static method Webmozart\\\\Assert\\\\Assert\\:\\:isInstanceOf\\(\\) with Sylius\\\\Component\\\\Currency\\\\Model\\\\CurrencyInterface and 'Sylius\\\\\\\\Component\\\\\\\\Currency\\\\\\\\Model\\\\\\\\CurrencyInterface' will always evaluate to true\\.$#" + count: 1 + path: src/Factory/OrderFactory.php + + - + message: "#^Call to static method Webmozart\\\\Assert\\\\Assert\\:\\:isInstanceOf\\(\\) with Sylius\\\\Component\\\\Locale\\\\Model\\\\LocaleInterface and 'Sylius\\\\\\\\Component\\\\\\\\Locale\\\\\\\\Model\\\\\\\\LocaleInterface' will always evaluate to true\\.$#" + count: 1 + path: src/Factory/OrderFactory.php + + - + message: "#^Cannot call method getAmount\\(\\) on mixed\\.$#" + count: 1 + path: src/Form/Type/AdjustmentType.php + + - + message: "#^Cannot call method setAmount\\(\\) on mixed\\.$#" + count: 1 + path: src/Form/Type/AdjustmentType.php + + - + message: "#^Cannot call method setLabel\\(\\) on mixed\\.$#" + count: 1 + path: src/Form/Type/AdjustmentType.php + + - + message: "#^Cannot call method setType\\(\\) on mixed\\.$#" + count: 1 + path: src/Form/Type/AdjustmentType.php + + - + message: "#^Cannot access offset 'billingAddress' on mixed\\.$#" + count: 1 + path: src/Form/Type/NewOrderType.php + + - + message: "#^Cannot access offset 'shippingAddress' on mixed\\.$#" + count: 1 + path: src/Form/Type/NewOrderType.php + + - + message: "#^Parameter \\#1 \\$orderData of method Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Form\\\\Type\\\\NewOrderType\\:\\:isBillingAddressEmpty\\(\\) expects array, mixed given\\.$#" + count: 1 + path: src/Form/Type/NewOrderType.php + + - + message: "#^Parameter \\#1 \\$orderData of method Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Form\\\\Type\\\\NewOrderType\\:\\:isShippingAddressComplete\\(\\) expects array, mixed given\\.$#" + count: 1 + path: src/Form/Type/NewOrderType.php + + - + message: "#^Cannot access offset 'quantity' on mixed\\.$#" + count: 2 + path: src/Form/Type/OrderItemType.php + + - + message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" + count: 1 + path: src/Form/Type/OrderItemType.php + + - + message: "#^Cannot access offset 'channel_code' on mixed\\.$#" + count: 1 + path: src/Form/Type/ProductVariantInChannelAutocompleteType.php + + - + message: "#^Call to static method Webmozart\\\\Assert\\\\Assert\\:\\:notNull\\(\\) with Sylius\\\\Component\\\\Core\\\\Model\\\\CustomerInterface will always evaluate to true\\.$#" + count: 1 + path: src/Provider/CustomerProvider.php + + - + message: "#^Parameter \\#1 \\$gatewayName of method Payum\\\\Core\\\\Security\\\\GenericTokenFactoryInterface\\:\\:createAuthorizeToken\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/Provider/PaymentTokenProvider.php + + - + message: "#^Parameter \\#1 \\$gatewayName of method Payum\\\\Core\\\\Security\\\\GenericTokenFactoryInterface\\:\\:createCaptureToken\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/Provider/PaymentTokenProvider.php + + - + message: "#^Call to method Tests\\\\Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Behat\\\\Element\\\\Admin\\\\OrderCreateFormElementInterface\\:\\:selectCurrency\\(\\) with incorrect case\\: selectcurrency$#" + count: 1 + path: tests/Behat/Context/Admin/ManagingOrdersContext.php + + - + message: "#^Cannot call method getCode\\(\\) on Sylius\\\\Component\\\\Product\\\\Model\\\\ProductVariantInterface\\|false\\.$#" + count: 2 + path: tests/Behat/Context/Admin/ManagingOrdersContext.php + + - + message: "#^Cannot call method getDescriptor\\(\\) on Sylius\\\\Component\\\\Product\\\\Model\\\\ProductVariantInterface\\|false\\.$#" + count: 4 + path: tests/Behat/Context/Admin/ManagingOrdersContext.php + + - + message: "#^Parameter \\#1 \\$customerEmail of method Tests\\\\Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Behat\\\\Page\\\\Admin\\\\NewOrderCustomerPageInterface\\:\\:selectCustomer\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: tests/Behat/Context/Admin/ManagingOrdersContext.php + + - + message: "#^Parameter \\#1 \\$productCode of method Tests\\\\Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Behat\\\\Page\\\\Admin\\\\OrderPreviewPageInterface\\:\\:hasItemDiscountValidationMessage\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: tests/Behat/Context/Admin/ManagingOrdersContext.php + + - + message: "#^Parameter \\#1 \\$productCode of method Tests\\\\Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Behat\\\\Page\\\\Admin\\\\OrderPreviewPageInterface\\:\\:lowerItemWithProductPriceBy\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: tests/Behat/Context/Admin/ManagingOrdersContext.php + + - + message: "#^Parameter \\#1 \\$productVariantDescriptor of method Tests\\\\Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Behat\\\\Element\\\\Admin\\\\OrderCreateFormElementInterface\\:\\:removeProduct\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: tests/Behat/Context/Admin/ManagingOrdersContext.php + + - + message: "#^Parameter \\#1 \\$productVariantDescriptor of method Tests\\\\Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Behat\\\\Element\\\\Admin\\\\OrderCreateFormElementInterface\\:\\:specifyQuantity\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: tests/Behat/Context/Admin/ManagingOrdersContext.php + + - + message: "#^Property Tests\\\\Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Behat\\\\Context\\\\Admin\\\\ManagingOrdersContext\\:\\:\\$addressComparator is never read, only written\\.$#" + count: 1 + path: tests/Behat/Context/Admin/ManagingOrdersContext.php + + - + message: "#^Cannot call method click\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 1 + path: tests/Behat/Element/Admin/OrderCreateFormElement.php + + - + message: "#^Cannot call method clickLink\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 1 + path: tests/Behat/Element/Admin/OrderCreateFormElement.php + + - + message: "#^Cannot call method findAll\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 1 + path: tests/Behat/Element/Admin/OrderCreateFormElement.php + + - + message: "#^Cannot call method getText\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 2 + path: tests/Behat/Element/Admin/OrderCreateFormElement.php + + - + message: "#^Cannot call method hasClass\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 3 + path: tests/Behat/Element/Admin/OrderCreateFormElement.php + + - + message: "#^Cannot call method isVisible\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 1 + path: tests/Behat/Element/Admin/OrderCreateFormElement.php + + - + message: "#^Method Tests\\\\Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Behat\\\\Element\\\\Admin\\\\OrderCreateFormElement\\:\\:__construct\\(\\) has parameter \\$parameters with no type specified\\.$#" + count: 1 + path: tests/Behat/Element/Admin/OrderCreateFormElement.php + + - + message: "#^Method Tests\\\\Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Behat\\\\Element\\\\Admin\\\\OrderCreateFormElement\\:\\:addItemAndWaitForIt\\(\\) should return Behat\\\\Mink\\\\Element\\\\NodeElement but returns Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 1 + path: tests/Behat/Element/Admin/OrderCreateFormElement.php + + - + message: "#^Parameter \\#1 \\$addressForm of method Tests\\\\Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Behat\\\\Element\\\\Admin\\\\OrderCreateFormElement\\:\\:fillAddressData\\(\\) expects Behat\\\\Mink\\\\Element\\\\NodeElement, Behat\\\\Mink\\\\Element\\\\NodeElement\\|null given\\.$#" + count: 2 + path: tests/Behat/Element/Admin/OrderCreateFormElement.php + + - + message: "#^Parameter \\#2 \\$value of method Behat\\\\Mink\\\\Element\\\\TraversableElement\\:\\:fillField\\(\\) expects array\\\\|bool\\|string, int given\\.$#" + count: 2 + path: tests/Behat/Element/Admin/OrderCreateFormElement.php + + - + message: "#^Parameter \\#2 \\$value of method Behat\\\\Mink\\\\Element\\\\TraversableElement\\:\\:fillField\\(\\) expects array\\\\|bool\\|string, string\\|null given\\.$#" + count: 6 + path: tests/Behat/Element/Admin/OrderCreateFormElement.php + + - + message: "#^Method Tests\\\\Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Behat\\\\Element\\\\Element\\:\\:__construct\\(\\) has parameter \\$parameters with no type specified\\.$#" + count: 1 + path: tests/Behat/Element/Element.php + + - + message: "#^Method Tests\\\\Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Behat\\\\Element\\\\Element\\:\\:getSelectorAsXpath\\(\\) has parameter \\$selector with no type specified\\.$#" + count: 1 + path: tests/Behat/Element/Element.php + + - + message: "#^Method Tests\\\\Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Behat\\\\Element\\\\Element\\:\\:resolveParameters\\(\\) should return string but returns array\\.$#" + count: 1 + path: tests/Behat/Element/Element.php + + - + message: "#^Parameter \\#1 \\$selector of method Behat\\\\Mink\\\\Selector\\\\SelectorsHandler\\:\\:selectorToXpath\\(\\) expects string, int\\|string\\|null given\\.$#" + count: 1 + path: tests/Behat/Element/Element.php + + - + message: "#^Property Tests\\\\Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Behat\\\\Element\\\\Element\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: tests/Behat/Element/Element.php + + - + message: "#^Method Tests\\\\Webgriffe\\\\SyliusAdminOrderCreationPlugin\\\\Behat\\\\Page\\\\Admin\\\\NewOrderCustomerPage\\:\\:__construct\\(\\) has parameter \\$parameters with no type specified\\.$#" + count: 1 + path: tests/Behat/Page/Admin/NewOrderCustomerPage.php + + - + message: "#^Cannot call method clickLink\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 2 + path: tests/Behat/Page/Admin/OrderPreviewPage.php + + - + message: "#^Cannot call method fillField\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 2 + path: tests/Behat/Page/Admin/OrderPreviewPage.php + + - + message: "#^Cannot call method find\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 2 + path: tests/Behat/Page/Admin/OrderPreviewPage.php + + - + message: "#^Cannot call method focus\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 2 + path: tests/Behat/Page/Admin/OrderPreviewPage.php + + - + message: "#^Cannot call method getText\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 2 + path: tests/Behat/Page/Admin/OrderPreviewPage.php + + - + message: "#^Cannot call method has\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 2 + path: tests/Behat/Page/Admin/OrderPreviewPage.php + + - + message: "#^Cannot call method press\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 2 + path: tests/Behat/Page/Admin/OrderPreviewPage.php + + - + message: "#^Cannot call method find\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 1 + path: tests/Behat/Page/Admin/OrderShowPage.php + + - + message: "#^Strict comparison using \\!\\=\\= between null and Behat\\\\Mink\\\\Element\\\\NodeElement will always evaluate to true\\.$#" + count: 1 + path: tests/Behat/Page/Admin/OrderShowPage.php + + - + message: "#^Cannot call method click\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 2 + path: tests/Behat/Service/AutoCompleteSelector.php + + - + message: "#^Cannot call method getText\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 1 + path: tests/Behat/Service/AutoCompleteSelector.php + + - + message: "#^Cannot call method hasClass\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 1 + path: tests/Behat/Service/AutoCompleteSelector.php diff --git a/phpstan.neon b/phpstan.neon index 642a2abba..7c4addf1f 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,14 +1,23 @@ +includes: + - phpstan-baseline.neon + parameters: - checkMissingIterableValueType: false - checkGenericClassInNonGenericObjectType: false + level: max + reportUnmatchedIgnoredErrors: false + paths: + - src + - tests/Behat - excludes_analyse: + excludePaths: # Makes PHPStan crash - 'src/DependencyInjection/Configuration.php' # Test dependencies - - 'tests/Application/app/**.php' - - 'tests/Application/src/**.php' + - 'tests/TestApplication/src/**.php' ignoreErrors: + - + identifier: missingType.generics + - + identifier: missingType.iterableValue - '/Parameter #1 \$configuration of method Symfony\\Component\\DependencyInjection\\Extension\\Extension::processConfiguration\(\) expects Symfony\\Component\\Config\\Definition\\ConfigurationInterface, Symfony\\Component\\Config\\Definition\\ConfigurationInterface\|null given\./' diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 811338620..bce1cd440 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,18 +1,43 @@ - + bootstrap="vendor/sylius/test-application/config/bootstrap.php" +> + + + + + + + + + - + tests + tests/Application + tests/TestApplication + tests/Behat - - - - - + + tests/Unit + + + + tests/Functional + + + + tests/Integration + + + + tests/Functional + tests/Integration + + diff --git a/psalm-baseline.xml b/psalm-baseline.xml new file mode 100644 index 000000000..1e5de1b10 --- /dev/null +++ b/psalm-baseline.xml @@ -0,0 +1,296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 000000000..cb0e59176 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/spec/EventListener/OrderCreationListenerSpec.php b/spec/EventListener/OrderCreationListenerSpec.php index c3c000b47..25c16e7a3 100644 --- a/spec/EventListener/OrderCreationListenerSpec.php +++ b/spec/EventListener/OrderCreationListenerSpec.php @@ -2,29 +2,32 @@ declare(strict_types=1); -namespace spec\Sylius\AdminOrderCreationPlugin\EventListener; +namespace spec\Webgriffe\SyliusAdminOrderCreationPlugin\EventListener; use PhpSpec\ObjectBehavior; -use SM\Factory\FactoryInterface; +use Prophecy\Argument; +use Sylius\Abstraction\StateMachine\StateMachineInterface; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\OrderCheckoutTransitions; use Sylius\Component\Order\Processor\OrderProcessorInterface; -use Sylius\Component\Resource\StateMachine\StateMachineInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; +use Webgriffe\SyliusAdminOrderCreationPlugin\Event\OrderCreatedByAdminEvent; final class OrderCreationListenerSpec extends ObjectBehavior { function let( OrderProcessorInterface $orderProcessor, - FactoryInterface $stateMachineFactory + StateMachineInterface $stateMachine, + EventDispatcherInterface $eventDispatcher, ) { - $this->beConstructedWith($orderProcessor, $stateMachineFactory); + $this->beConstructedWith($orderProcessor, $stateMachine, $eventDispatcher); } function it_processes_order_before_creation( OrderProcessorInterface $orderProcessor, GenericEvent $event, - OrderInterface $order + OrderInterface $order, ) { $event->getSubject()->willReturn($order); @@ -35,65 +38,59 @@ function it_processes_order_before_creation( } function it_completes_order_before_creation( - FactoryInterface $stateMachineFactory, - GenericEvent $event, StateMachineInterface $stateMachine, - OrderInterface $order + GenericEvent $event, + OrderInterface $order, ) { $event->getSubject()->willReturn($order); - $stateMachineFactory->get($order, 'sylius_order_checkout')->willReturn($stateMachine); - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_ADDRESS)->shouldBeCalled(); - $stateMachine->can(OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING)->willReturn(true); - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING)->shouldBeCalled(); - $stateMachine->can(OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT)->willReturn(true); - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT)->shouldBeCalled(); - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_COMPLETE)->shouldBeCalled(); + $stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_ADDRESS)->shouldBeCalled(); + $stateMachine->can($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING)->willReturn(true); + $stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING)->shouldBeCalled(); + $stateMachine->can($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT)->willReturn(true); + $stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT)->shouldBeCalled(); + $stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_COMPLETE)->shouldBeCalled(); $this->completeOrderBeforeCreation($event); } function it_completes_order_without_payment_before_creation( - FactoryInterface $stateMachineFactory, - GenericEvent $event, StateMachineInterface $stateMachine, - OrderInterface $order + GenericEvent $event, + OrderInterface $order, ) { $event->getSubject()->willReturn($order); - $stateMachineFactory->get($order, 'sylius_order_checkout')->willReturn($stateMachine); - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_ADDRESS)->shouldBeCalled(); - $stateMachine->can(OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING)->willReturn(true); - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING)->shouldBeCalled(); - $stateMachine->can(OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT)->willReturn(false); - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT)->shouldNotBeCalled(); - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_COMPLETE)->shouldBeCalled(); + $stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_ADDRESS)->shouldBeCalled(); + $stateMachine->can($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING)->willReturn(true); + $stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING)->shouldBeCalled(); + $stateMachine->can($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT)->willReturn(false); + $stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT)->shouldNotBeCalled(); + $stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_COMPLETE)->shouldBeCalled(); $this->completeOrderBeforeCreation($event); } function it_completes_order_without_shipping_before_creation( - FactoryInterface $stateMachineFactory, - GenericEvent $event, StateMachineInterface $stateMachine, - OrderInterface $order + GenericEvent $event, + OrderInterface $order, ) { $event->getSubject()->willReturn($order); - $stateMachineFactory->get($order, 'sylius_order_checkout')->willReturn($stateMachine); - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_ADDRESS)->shouldBeCalled(); - $stateMachine->can(OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING)->willReturn(false); - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING)->shouldNotBeCalled(); - $stateMachine->can(OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT)->willReturn(true); - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT)->shouldBeCalled(); - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_COMPLETE)->shouldBeCalled(); + $stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_ADDRESS)->shouldBeCalled(); + $stateMachine->can($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING)->willReturn(false); + $stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING)->shouldNotBeCalled(); + $stateMachine->can($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT)->willReturn(true); + $stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT)->shouldBeCalled(); + $stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_COMPLETE)->shouldBeCalled(); $this->completeOrderBeforeCreation($event); } function it_throws_exception_if_event_subject_is_not_order(GenericEvent $event) { - $event->getSubject()->willReturn('badObject', 'badObject'); + $event->getSubject()->willReturn('badObject', 'badObject', 'badObject'); $this ->shouldThrow(\InvalidArgumentException::class) @@ -102,5 +99,23 @@ function it_throws_exception_if_event_subject_is_not_order(GenericEvent $event) $this ->shouldThrow(\InvalidArgumentException::class) ->during('completeOrderBeforeCreation', [$event]); + + $this + ->shouldThrow(\InvalidArgumentException::class) + ->during('dispatchOrderCreatedEvent', [$event]); + } + + function it_dispatches_an_order_created_by_admin_event_after_order_creation( + EventDispatcherInterface $eventDispatcher, + GenericEvent $event, + OrderInterface $order, + ) { + $event->getSubject()->willReturn($order); + + $eventDispatcher->dispatch(Argument::that(function (OrderCreatedByAdminEvent $dispatchedEvent) use ($order) { + return $dispatchedEvent->getOrder() === $order->getWrappedObject(); + }))->shouldBeCalled(); + + $this->dispatchOrderCreatedEvent($event); } } diff --git a/spec/EventListener/PaymentLinkCreationListenerSpec.php b/spec/EventListener/PaymentLinkCreationListenerSpec.php index a37379312..85fe2eb9e 100644 --- a/spec/EventListener/PaymentLinkCreationListenerSpec.php +++ b/spec/EventListener/PaymentLinkCreationListenerSpec.php @@ -2,40 +2,50 @@ declare(strict_types=1); -namespace spec\Sylius\AdminOrderCreationPlugin\EventListener; +namespace spec\Webgriffe\SyliusAdminOrderCreationPlugin\EventListener; use Doctrine\Persistence\ObjectManager; -use Payum\Core\Model\GatewayConfigInterface; use Payum\Core\Payum; use Payum\Core\Security\TokenInterface; use PhpSpec\ObjectBehavior; -use Sylius\AdminOrderCreationPlugin\Provider\PaymentTokenProviderInterface; -use Sylius\AdminOrderCreationPlugin\Sender\OrderPaymentLinkSenderInterface; +use Prophecy\Argument; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; +use Sylius\Component\Payment\Model\GatewayConfigInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Webgriffe\SyliusAdminOrderCreationPlugin\Event\PaymentLinkGeneratedEvent; +use Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type\NewOrderType; +use Webgriffe\SyliusAdminOrderCreationPlugin\Provider\PaymentTokenProviderInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\Sender\OrderPaymentLinkSenderInterface; final class PaymentLinkCreationListenerSpec extends ObjectBehavior { function let( PaymentTokenProviderInterface $paymentTokenProvider, ObjectManager $orderManager, - OrderPaymentLinkSenderInterface $orderPaymentLinkSender + OrderPaymentLinkSenderInterface $orderPaymentLinkSender, + RequestStack $requestStack, + EventDispatcherInterface $eventDispatcher, ) { - $this->beConstructedWith($paymentTokenProvider, $orderManager, $orderPaymentLinkSender); + $this->beConstructedWith($paymentTokenProvider, $orderManager, $orderPaymentLinkSender, $requestStack, $eventDispatcher, ['offline'], true); } - function it_sets_after_url_from_token_of_last_order_new_payment_and_sends_it( + function it_sets_after_url_from_token_of_last_order_new_payment_and_sends_it_when_checkbox_is_checked( PaymentTokenProviderInterface $paymentTokenProvider, ObjectManager $orderManager, OrderPaymentLinkSenderInterface $orderPaymentLinkSender, + RequestStack $requestStack, + EventDispatcherInterface $eventDispatcher, TokenInterface $token, GenericEvent $event, OrderInterface $order, PaymentInterface $payment, PaymentMethodInterface $paymentMethod, - GatewayConfigInterface $gatewayConfig + GatewayConfigInterface $gatewayConfig, ) { $event->getSubject()->willReturn($order); $order->getLastPayment(PaymentInterface::STATE_NEW)->willReturn($payment); @@ -47,7 +57,14 @@ function it_sets_after_url_from_token_of_last_order_new_payment_and_sends_it( $paymentTokenProvider->getPaymentToken($payment)->willReturn($token); $token->getAfterUrl()->willReturn('http://url-to-pay.com'); + $requestStack->getCurrentRequest()->willReturn(new Request([], [ + NewOrderType::BLOCK_PREFIX => ['sendPaymentLinkEmail' => '1'], + ])); + $payment->setDetails(['payment-link' => 'http://url-to-pay.com'])->shouldBeCalled(); + $eventDispatcher->dispatch(Argument::that(function (PaymentLinkGeneratedEvent $dispatchedEvent) use ($payment) { + return $dispatchedEvent->getPayment() === $payment->getWrappedObject(); + }))->shouldBeCalled(); $orderPaymentLinkSender->sendPaymentLink($order)->shouldBeCalled(); $orderManager->flush()->shouldBeCalled(); @@ -55,6 +72,40 @@ function it_sets_after_url_from_token_of_last_order_new_payment_and_sends_it( $this->setPaymentLink($event); } + function it_does_not_send_the_email_when_the_checkbox_is_not_checked( + PaymentTokenProviderInterface $paymentTokenProvider, + ObjectManager $orderManager, + OrderPaymentLinkSenderInterface $orderPaymentLinkSender, + RequestStack $requestStack, + EventDispatcherInterface $eventDispatcher, + TokenInterface $token, + GenericEvent $event, + OrderInterface $order, + PaymentInterface $payment, + PaymentMethodInterface $paymentMethod, + GatewayConfigInterface $gatewayConfig, + ) { + $event->getSubject()->willReturn($order); + $order->getLastPayment(PaymentInterface::STATE_NEW)->willReturn($payment); + + $payment->getMethod()->willReturn($paymentMethod); + $paymentMethod->getGatewayConfig()->willReturn($gatewayConfig); + $gatewayConfig->getGatewayName()->willReturn('paypal_express_checkout'); + + $paymentTokenProvider->getPaymentToken($payment)->willReturn($token); + $token->getAfterUrl()->willReturn('http://url-to-pay.com'); + + $requestStack->getCurrentRequest()->willReturn(new Request([], [])); + + $payment->setDetails(['payment-link' => 'http://url-to-pay.com'])->shouldBeCalled(); + $eventDispatcher->dispatch(Argument::type(PaymentLinkGeneratedEvent::class))->willReturn(new PaymentLinkGeneratedEvent($payment->getWrappedObject())); + $orderPaymentLinkSender->sendPaymentLink($order)->shouldNotBeCalled(); + + $orderManager->flush()->shouldBeCalled(); + + $this->setPaymentLink($event); + } + function it_throws_exception_if_event_subject_is_not_payment(GenericEvent $event) { $event->getSubject()->willReturn('badObject'); @@ -81,7 +132,7 @@ function it_does_nothing_if_order_payment_gateway_is_offline( OrderInterface $order, PaymentInterface $payment, PaymentMethodInterface $paymentMethod, - GatewayConfigInterface $gatewayConfig + GatewayConfigInterface $gatewayConfig, ) { $event->getSubject()->willReturn($order); $order->getLastPayment(PaymentInterface::STATE_NEW)->willReturn($payment); @@ -94,4 +145,67 @@ function it_does_nothing_if_order_payment_gateway_is_offline( $this->setPaymentLink($event); } + + function it_does_nothing_if_order_payment_gateway_is_one_of_the_configured_offline_gateway_names( + PaymentTokenProviderInterface $paymentTokenProvider, + ObjectManager $orderManager, + OrderPaymentLinkSenderInterface $orderPaymentLinkSender, + RequestStack $requestStack, + EventDispatcherInterface $eventDispatcher, + Payum $payum, + GenericEvent $event, + OrderInterface $order, + PaymentInterface $payment, + PaymentMethodInterface $paymentMethod, + GatewayConfigInterface $gatewayConfig, + ) { + $this->beConstructedWith( + $paymentTokenProvider, + $orderManager, + $orderPaymentLinkSender, + $requestStack, + $eventDispatcher, + ['offline', 'bank_transfer'], + true, + ); + + $event->getSubject()->willReturn($order); + $order->getLastPayment(PaymentInterface::STATE_NEW)->willReturn($payment); + + $payment->getMethod()->willReturn($paymentMethod); + $paymentMethod->getGatewayConfig()->willReturn($gatewayConfig); + $gatewayConfig->getGatewayName()->willReturn('bank_transfer'); + + $payum->getTokenFactory()->shouldNotBeCalled(); + + $this->setPaymentLink($event); + } + + function it_does_nothing_when_payment_link_generation_is_disabled( + PaymentTokenProviderInterface $paymentTokenProvider, + ObjectManager $orderManager, + OrderPaymentLinkSenderInterface $orderPaymentLinkSender, + RequestStack $requestStack, + EventDispatcherInterface $eventDispatcher, + Payum $payum, + GenericEvent $event, + OrderInterface $order, + ) { + $this->beConstructedWith( + $paymentTokenProvider, + $orderManager, + $orderPaymentLinkSender, + $requestStack, + $eventDispatcher, + ['offline'], + false, + ); + + $event->getSubject()->willReturn($order); + + $order->getLastPayment(PaymentInterface::STATE_NEW)->shouldNotBeCalled(); + $payum->getTokenFactory()->shouldNotBeCalled(); + + $this->setPaymentLink($event); + } } diff --git a/spec/Factory/OrderFactorySpec.php b/spec/Factory/OrderFactorySpec.php index 3a4bd7254..2ef253f5a 100644 --- a/spec/Factory/OrderFactorySpec.php +++ b/spec/Factory/OrderFactorySpec.php @@ -2,12 +2,10 @@ declare(strict_types=1); -namespace spec\Sylius\AdminOrderCreationPlugin\Factory; +namespace spec\Webgriffe\SyliusAdminOrderCreationPlugin\Factory; use PhpSpec\ObjectBehavior; use Prophecy\Argument; -use Sylius\AdminOrderCreationPlugin\Factory\OrderFactoryInterface; -use Sylius\AdminOrderCreationPlugin\ReorderProcessing\ReorderProcessor; use Sylius\Component\Channel\Repository\ChannelRepositoryInterface; use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\CustomerInterface; @@ -16,6 +14,10 @@ use Sylius\Component\Currency\Model\CurrencyInterface; use Sylius\Component\Locale\Model\LocaleInterface; use Sylius\Component\Resource\Factory\FactoryInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\Event\OrderCreationInitializedEvent; +use Webgriffe\SyliusAdminOrderCreationPlugin\Factory\OrderFactoryInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing\ReorderProcessor; final class OrderFactorySpec extends ObjectBehavior { @@ -23,13 +25,15 @@ function let( FactoryInterface $baseOrderFactory, CustomerRepositoryInterface $customerRepository, ChannelRepositoryInterface $channelRepository, - ReorderProcessor $reorderProcessor + ReorderProcessor $reorderProcessor, + EventDispatcherInterface $eventDispatcher, ) { $this->beConstructedWith( $baseOrderFactory, $customerRepository, $channelRepository, - $reorderProcessor + $reorderProcessor, + $eventDispatcher, ); } @@ -49,11 +53,12 @@ function it_creates_order_for_customer_with_default_channel_locale_and_currency( FactoryInterface $baseOrderFactory, CustomerRepositoryInterface $customerRepository, ChannelRepositoryInterface $channelRepository, + EventDispatcherInterface $eventDispatcher, OrderInterface $order, CustomerInterface $customer, ChannelInterface $channel, CurrencyInterface $currency, - LocaleInterface $locale + LocaleInterface $locale, ): void { $baseOrderFactory->createNew()->willReturn($order); @@ -71,6 +76,10 @@ function it_creates_order_for_customer_with_default_channel_locale_and_currency( $order->setCurrencyCode('USD')->shouldBeCalled(); $order->setLocaleCode('en_US')->shouldBeCalled(); + $eventDispatcher->dispatch(Argument::that(function (OrderCreationInitializedEvent $event) use ($order) { + return $event->getOrder() === $order->getWrappedObject(); + }))->shouldBeCalled(); + $this ->createForCustomerAndChannel('1', 'WEB-US') ->shouldReturn($order) @@ -79,7 +88,7 @@ function it_creates_order_for_customer_with_default_channel_locale_and_currency( function it_throws_an_exception_if_the_customer_does_not_exist( FactoryInterface $baseOrderFactory, - CustomerRepositoryInterface $customerRepository + CustomerRepositoryInterface $customerRepository, ): void { $customerRepository->find('1')->willReturn(null); @@ -97,7 +106,7 @@ function it_throws_an_exception_if_there_is_no_default_currency( ChannelRepositoryInterface $channelRepository, OrderInterface $order, CustomerInterface $customer, - ChannelInterface $channel + ChannelInterface $channel, ): void { $baseOrderFactory->createNew()->willReturn($order); @@ -123,7 +132,7 @@ function it_throws_an_exception_if_there_is_no_default_locale( OrderInterface $order, CustomerInterface $customer, ChannelInterface $channel, - CurrencyInterface $currency + CurrencyInterface $currency, ): void { $baseOrderFactory->createNew()->willReturn($order); @@ -149,13 +158,18 @@ function it_throws_an_exception_if_there_is_no_default_locale( function it_creates_reorder_from_an_existing_order( FactoryInterface $baseOrderFactory, ReorderProcessor $reorderProcessor, + EventDispatcherInterface $eventDispatcher, OrderInterface $order, - OrderInterface $reorder + OrderInterface $reorder, ): void { $baseOrderFactory->createNew()->willReturn($reorder); $reorderProcessor->process($order, $reorder)->shouldBeCalled(); + $eventDispatcher->dispatch(Argument::that(function (OrderCreationInitializedEvent $event) use ($reorder) { + return $event->getOrder() === $reorder->getWrappedObject(); + }))->shouldBeCalled(); + $this->createFromExistingOrder($order); } } diff --git a/spec/Preparator/NewOrderPreparatorSpec.php b/spec/Preparator/NewOrderPreparatorSpec.php deleted file mode 100644 index 570214841..000000000 --- a/spec/Preparator/NewOrderPreparatorSpec.php +++ /dev/null @@ -1,67 +0,0 @@ -beConstructedWith($orderFactory, $formFactory, $orderProcessor); - } - - function it_is_order_preparator(): void - { - $this->shouldImplement(OrderPreparatorInterface::class); - } - - function it_prepares_new_order_based_on_request_data( - OrderFactoryInterface $orderFactory, - FormFactoryInterface $formFactory, - OrderProcessorInterface $orderProcessor, - Request $request, - OrderInterface $order, - OrderInterface $orderWithData, - FormInterface $form - ): void { - $request->attributes = new ParameterBag([ - 'customerId' => '1', - 'channelCode' => 'WEB-US', - ]); - - $orderFactory->createForCustomerAndChannel('1', 'WEB-US')->willReturn($order); - $formFactory->create(NewOrderType::class, $order)->willReturn($form); - - $form->handleRequest($request)->willReturn($form); - $form->getData()->willReturn($orderWithData); - - $orderProcessor->process($orderWithData)->shouldBeCalled(); - - $this->prepareFromRequest($request)->shouldReturn($orderWithData); - } - - function it_throws_exception_if_there_is_no_customer_email_specified_in_request(Request $request): void - { - $request->attributes = new ParameterBag([]); - - $this - ->shouldThrow(\InvalidArgumentException::class) - ->during('prepareFromRequest', [$request]) - ; - } -} diff --git a/spec/Provider/AvailableShippingMethodsListProviderSpec.php b/spec/Provider/AvailableShippingMethodsListProviderSpec.php deleted file mode 100644 index 0919a7975..000000000 --- a/spec/Provider/AvailableShippingMethodsListProviderSpec.php +++ /dev/null @@ -1,38 +0,0 @@ -beConstructedWith($shippingMethodsResolver); - } - - function it_provides_supported_shipping_methods_list_for_order_shipment( - ShippingMethodsResolverInterface $shippingMethodsResolver, - ShipmentInterface $shipment, - ShippingMethodInterface $freeShippingMethod, - ShippingMethodInterface $dhlShippingMethod - ) { - $shippingMethodsResolver->getSupportedMethods($shipment)->willReturn([ - $freeShippingMethod, - $dhlShippingMethod, - ]); - - $freeShippingMethod->getCode()->willReturn('FREE'); - $freeShippingMethod->getName()->willReturn('Free'); - - $dhlShippingMethod->getCode()->willReturn('DHL'); - $dhlShippingMethod->getName()->willReturn('DHL'); - - $this($shipment)->shouldReturn(['FREE' => 'Free', 'DHL' => 'DHL']); - } -} diff --git a/spec/Provider/CustomerProviderSpec.php b/spec/Provider/CustomerProviderSpec.php index febc7655d..041a944d3 100644 --- a/spec/Provider/CustomerProviderSpec.php +++ b/spec/Provider/CustomerProviderSpec.php @@ -2,15 +2,15 @@ declare(strict_types=1); -namespace spec\Sylius\AdminOrderCreationPlugin\Provider; +namespace spec\Webgriffe\SyliusAdminOrderCreationPlugin\Provider; use InvalidArgumentException; use PhpSpec\ObjectBehavior; -use Sylius\AdminOrderCreationPlugin\Provider\CustomerProvider; -use Sylius\AdminOrderCreationPlugin\Provider\CustomerProviderInterface; use Sylius\Component\Core\Model\CustomerInterface; use Sylius\Component\Core\Repository\CustomerRepositoryInterface; use Sylius\Component\Resource\Factory\FactoryInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\Provider\CustomerProvider; +use Webgriffe\SyliusAdminOrderCreationPlugin\Provider\CustomerProviderInterface; class CustomerProviderSpec extends ObjectBehavior { @@ -31,7 +31,7 @@ function let(CustomerRepositoryInterface $customerRepository, FactoryInterface $ function it_finds_a_customer_by_id( CustomerRepositoryInterface $customerRepository, - CustomerInterface $customer + CustomerInterface $customer, ) { $customerRepository->find('1')->willReturn($customer); @@ -39,7 +39,7 @@ function it_finds_a_customer_by_id( } function it_throws_an_exception_if_no_customer_was_found( - CustomerRepositoryInterface $customerRepository + CustomerRepositoryInterface $customerRepository, ) { $customerRepository->find('2')->willReturn(null); @@ -50,7 +50,7 @@ function it_throws_an_exception_if_no_customer_was_found( function it_creates_a_customer_with_a_given_email( FactoryInterface $customerFactory, CustomerInterface $customer, - CustomerRepositoryInterface $customerRepository + CustomerRepositoryInterface $customerRepository, ) { $customerFactory->createNew()->willReturn($customer); diff --git a/spec/Provider/PaymentTokenProviderSpec.php b/spec/Provider/PaymentTokenProviderSpec.php index 82752c02d..28b21fc5b 100644 --- a/spec/Provider/PaymentTokenProviderSpec.php +++ b/spec/Provider/PaymentTokenProviderSpec.php @@ -2,16 +2,16 @@ declare(strict_types=1); -namespace spec\Sylius\AdminOrderCreationPlugin\Provider; +namespace spec\Webgriffe\SyliusAdminOrderCreationPlugin\Provider; -use Payum\Core\Model\GatewayConfigInterface; use Payum\Core\Payum; use Payum\Core\Security\GenericTokenFactoryInterface; use Payum\Core\Security\TokenInterface; use PhpSpec\ObjectBehavior; -use Sylius\AdminOrderCreationPlugin\Provider\PaymentTokenProviderInterface; use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; +use Sylius\Component\Payment\Model\GatewayConfigInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\Provider\PaymentTokenProviderInterface; final class PaymentTokenProviderSpec extends ObjectBehavior { @@ -31,7 +31,7 @@ function it_provides_authorize_token_for_payment_if_it_requires_authorization( PaymentInterface $payment, PaymentMethodInterface $paymentMethod, GatewayConfigInterface $gatewayConfig, - TokenInterface $token + TokenInterface $token, ) { $payum->getTokenFactory()->willReturn($tokenFactory); @@ -55,7 +55,7 @@ function it_provides_capture_token_for_payment( PaymentInterface $payment, PaymentMethodInterface $paymentMethod, GatewayConfigInterface $gatewayConfig, - TokenInterface $token + TokenInterface $token, ) { $payum->getTokenFactory()->willReturn($tokenFactory); diff --git a/spec/ReorderProcessing/ReorderDataProcessorSpec.php b/spec/ReorderProcessing/ReorderDataProcessorSpec.php index 17c2ced7a..ef0ba4de9 100644 --- a/spec/ReorderProcessing/ReorderDataProcessorSpec.php +++ b/spec/ReorderProcessing/ReorderDataProcessorSpec.php @@ -2,14 +2,14 @@ declare(strict_types=1); -namespace spec\Sylius\AdminOrderCreationPlugin\ReorderProcessing; +namespace spec\Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing; use PhpSpec\ObjectBehavior; -use Sylius\AdminOrderCreationPlugin\ReorderProcessing\ReorderProcessor; use Sylius\Component\Core\Model\AddressInterface; use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\CustomerInterface; use Sylius\Component\Core\Model\OrderInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing\ReorderProcessor; final class ReorderDataProcessorSpec extends ObjectBehavior { @@ -24,7 +24,7 @@ function it_copies_basic_order_data_to_reorder_instance( ChannelInterface $channel, CustomerInterface $customer, AddressInterface $shippingAddress, - AddressInterface $billingAddress + AddressInterface $billingAddress, ): void { $order->getChannel()->willReturn($channel); $order->getCustomer()->willReturn($customer); diff --git a/spec/ReorderProcessing/ReorderItemsProcessorSpec.php b/spec/ReorderProcessing/ReorderItemsProcessorSpec.php index 31198e142..f0e5620d1 100644 --- a/spec/ReorderProcessing/ReorderItemsProcessorSpec.php +++ b/spec/ReorderProcessing/ReorderItemsProcessorSpec.php @@ -2,24 +2,24 @@ declare(strict_types=1); -namespace spec\Sylius\AdminOrderCreationPlugin\ReorderProcessing; +namespace spec\Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing; use Doctrine\Common\Collections\ArrayCollection; use PhpSpec\ObjectBehavior; -use Sylius\AdminOrderCreationPlugin\ReorderProcessing\ReorderProcessor; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderItemInterface; use Sylius\Component\Core\Model\ProductVariantInterface; use Sylius\Component\Order\Modifier\OrderItemQuantityModifierInterface; use Sylius\Component\Order\Modifier\OrderModifierInterface; use Sylius\Component\Resource\Factory\FactoryInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing\ReorderProcessor; final class ReorderItemsProcessorSpec extends ObjectBehavior { function let( FactoryInterface $orderItemFactory, OrderItemQuantityModifierInterface $orderItemQuantityModifier, - OrderModifierInterface $orderModifier + OrderModifierInterface $orderModifier, ): void { $this->beConstructedWith($orderItemFactory, $orderItemQuantityModifier, $orderModifier); } @@ -40,7 +40,7 @@ function it_copies_items_from_existing_order_to_reorder_instance( OrderItemInterface $firstNewOrderItem, OrderItemInterface $secondNewOrderItem, ProductVariantInterface $firstProductVariant, - ProductVariantInterface $secondProductVariant + ProductVariantInterface $secondProductVariant, ): void { $order->getItems()->willReturn(new ArrayCollection([ $firstOrderItem->getWrappedObject(), diff --git a/spec/ReorderProcessing/ReorderPaymentProcessorSpec.php b/spec/ReorderProcessing/ReorderPaymentProcessorSpec.php index 563ce3925..4fc086c03 100644 --- a/spec/ReorderProcessing/ReorderPaymentProcessorSpec.php +++ b/spec/ReorderProcessing/ReorderPaymentProcessorSpec.php @@ -2,15 +2,15 @@ declare(strict_types=1); -namespace spec\Sylius\AdminOrderCreationPlugin\ReorderProcessing; +namespace spec\Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing; use Doctrine\Common\Collections\ArrayCollection; use PhpSpec\ObjectBehavior; -use Sylius\AdminOrderCreationPlugin\ReorderProcessing\ReorderProcessor; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; use Sylius\Component\Payment\Factory\PaymentFactoryInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing\ReorderProcessor; final class ReorderPaymentProcessorSpec extends ObjectBehavior { @@ -30,7 +30,7 @@ function it_copies_payment_from_existing_order_to_reorder_instance( OrderInterface $reorder, PaymentInterface $payment, PaymentInterface $newPayment, - PaymentMethodInterface $paymentMethod + PaymentMethodInterface $paymentMethod, ): void { $order->hasPayments()->willReturn(true); $order->getPayments()->willReturn(new ArrayCollection([$payment->getWrappedObject()])); diff --git a/spec/ReorderProcessing/ReorderShipmentProcessorSpec.php b/spec/ReorderProcessing/ReorderShipmentProcessorSpec.php index d40da71c3..74b342471 100644 --- a/spec/ReorderProcessing/ReorderShipmentProcessorSpec.php +++ b/spec/ReorderProcessing/ReorderShipmentProcessorSpec.php @@ -2,15 +2,15 @@ declare(strict_types=1); -namespace spec\Sylius\AdminOrderCreationPlugin\ReorderProcessing; +namespace spec\Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing; use Doctrine\Common\Collections\ArrayCollection; use PhpSpec\ObjectBehavior; -use Sylius\AdminOrderCreationPlugin\ReorderProcessing\ReorderProcessor; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\ShipmentInterface; use Sylius\Component\Core\Model\ShippingMethodInterface; use Sylius\Component\Resource\Factory\FactoryInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing\ReorderProcessor; final class ReorderShipmentProcessorSpec extends ObjectBehavior { @@ -30,7 +30,7 @@ function it_copies_shipment_from_existing_order_to_reorder_instance( OrderInterface $reorder, ShipmentInterface $shipment, ShipmentInterface $newShipment, - ShippingMethodInterface $shippingMethod + ShippingMethodInterface $shippingMethod, ): void { $order->hasShipments()->willReturn(true); $order->getShipments()->willReturn(new ArrayCollection([$shipment->getWrappedObject()])); diff --git a/spec/Sender/OrderPaymentLinkSenderSpec.php b/spec/Sender/OrderPaymentLinkSenderSpec.php index 0c397c4f0..ed8144b8c 100644 --- a/spec/Sender/OrderPaymentLinkSenderSpec.php +++ b/spec/Sender/OrderPaymentLinkSenderSpec.php @@ -2,14 +2,14 @@ declare(strict_types=1); -namespace spec\Sylius\AdminOrderCreationPlugin\Sender; +namespace spec\Webgriffe\SyliusAdminOrderCreationPlugin\Sender; use PhpSpec\ObjectBehavior; -use Sylius\AdminOrderCreationPlugin\Sender\OrderPaymentLinkSenderInterface; use Sylius\Component\Core\Model\CustomerInterface; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Mailer\Sender\SenderInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\Sender\OrderPaymentLinkSenderInterface; final class OrderPaymentLinkSenderSpec extends ObjectBehavior { @@ -27,7 +27,7 @@ function it_sends_payment_link_for_order_to_the_customer( SenderInterface $sender, OrderInterface $order, CustomerInterface $customer, - PaymentInterface $payment + PaymentInterface $payment, ) { $order->getLastPayment(PaymentInterface::STATE_NEW)->willReturn($payment); $order->getCustomer()->willReturn($customer); @@ -42,7 +42,7 @@ function it_sends_payment_link_for_order_to_the_customer( [ 'order' => $order, 'paymentLink' => 'http://payment-link.com', - ] + ], ) ->shouldBeCalled() ; diff --git a/src/Controller/CustomerCreationAction.php b/src/Controller/CustomerCreationAction.php index e3ae4ea08..4ebb02102 100644 --- a/src/Controller/CustomerCreationAction.php +++ b/src/Controller/CustomerCreationAction.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Controller; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Controller; -use Sylius\AdminOrderCreationPlugin\Provider\CustomerProviderInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\RouterInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\Provider\CustomerProviderInterface; final class CustomerCreationAction { @@ -20,7 +20,7 @@ final class CustomerCreationAction public function __construct( RouterInterface $router, - CustomerProviderInterface $customerProvider + CustomerProviderInterface $customerProvider, ) { $this->router = $router; $this->customerProvider = $customerProvider; @@ -36,8 +36,8 @@ public function __invoke(Request $request): Response [ 'customerId' => $customer->getId(), 'channelCode' => $request->attributes->get('channelCode'), - ] - ) + ], + ), ); } } diff --git a/src/Controller/OrderCreateAction.php b/src/Controller/OrderCreateAction.php index bd43332ce..f210a69b8 100644 --- a/src/Controller/OrderCreateAction.php +++ b/src/Controller/OrderCreateAction.php @@ -2,14 +2,14 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Controller; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Controller; -use Sylius\AdminOrderCreationPlugin\Factory\OrderFactoryInterface; -use Sylius\AdminOrderCreationPlugin\Form\Type\NewOrderType; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Twig\Environment; +use Webgriffe\SyliusAdminOrderCreationPlugin\Factory\OrderFactoryInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type\NewOrderType; final class OrderCreateAction { @@ -25,7 +25,7 @@ final class OrderCreateAction public function __construct( OrderFactoryInterface $orderFactory, FormFactoryInterface $formFactory, - Environment $twig + Environment $twig, ) { $this->orderFactory = $orderFactory; $this->formFactory = $formFactory; @@ -42,7 +42,7 @@ public function __invoke(Request $request): Response $form = $this->formFactory->create(NewOrderType::class, $order); $form->handleRequest($request); - return new Response($this->twig->render('@SyliusAdminOrderCreationPlugin/Order/create.html.twig', [ + return new Response($this->twig->render('@WebgriffeSyliusAdminOrderCreationPlugin/order/create.html.twig', [ 'form' => $form->createView(), ])); } diff --git a/src/Controller/OrderPreviewAction.php b/src/Controller/OrderPreviewAction.php index 5f60575f8..325b4f3f7 100644 --- a/src/Controller/OrderPreviewAction.php +++ b/src/Controller/OrderPreviewAction.php @@ -2,15 +2,15 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Controller; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Controller; -use Sylius\AdminOrderCreationPlugin\Factory\OrderFactoryInterface; -use Sylius\AdminOrderCreationPlugin\Form\Type\NewOrderType; use Sylius\Component\Order\Processor\OrderProcessorInterface; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Twig\Environment; +use Webgriffe\SyliusAdminOrderCreationPlugin\Factory\OrderFactoryInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type\NewOrderType; final class OrderPreviewAction { @@ -30,7 +30,7 @@ public function __construct( OrderFactoryInterface $orderFactory, FormFactoryInterface $formFactory, OrderProcessorInterface $orderProcessor, - Environment $twig + Environment $twig, ) { $this->orderFactory = $orderFactory; $this->formFactory = $formFactory; @@ -46,10 +46,21 @@ public function __invoke(Request $request): Response $order = $this->orderFactory->createForCustomerAndChannel($customerId, $channelCode); $form = $this->formFactory->create(NewOrderType::class, $order); - $order = $form->handleRequest($request)->getData(); + $form->handleRequest($request); + + if (!$form->isSubmitted() || !$form->isValid()) { + return new Response( + $this->twig->render('@WebgriffeSyliusAdminOrderCreationPlugin/order/create.html.twig', [ + 'form' => $form->createView(), + ]), + Response::HTTP_UNPROCESSABLE_ENTITY, + ); + } + + $order = $form->getData(); $this->orderProcessor->process($order); - return new Response($this->twig->render('@SyliusAdminOrderCreationPlugin/Order/preview.html.twig', [ + return new Response($this->twig->render('@WebgriffeSyliusAdminOrderCreationPlugin/order/preview.html.twig', [ 'form' => $form->createView(), ])); } diff --git a/src/Controller/ProvideAvailableShippingMethodsAction.php b/src/Controller/ProvideAvailableShippingMethodsAction.php deleted file mode 100644 index d9e901c02..000000000 --- a/src/Controller/ProvideAvailableShippingMethodsAction.php +++ /dev/null @@ -1,40 +0,0 @@ -orderPreparator = $orderPreparator; - $this->availableShippingMethodsListProvider = $availableShippingMethodsListProvider; - } - - public function __invoke(Request $request): Response - { - $order = $this->orderPreparator->prepareFromRequest($request); - $shipment = $order->getShipments()->get((int) $request->attributes->get('shipmentNumber')); - - if ($shipment === null) { - return new JsonResponse([]); - } - - return new JsonResponse($this->availableShippingMethodsListProvider->__invoke($shipment)); - } -} diff --git a/src/Controller/SelectNewOrderCustomerAction.php b/src/Controller/SelectNewOrderCustomerAction.php index 188f18bbe..8fe90ce63 100644 --- a/src/Controller/SelectNewOrderCustomerAction.php +++ b/src/Controller/SelectNewOrderCustomerAction.php @@ -2,27 +2,26 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Controller; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Controller; -use Sylius\AdminOrderCreationPlugin\Form\Type\NewOrderCustomerCreateType; -use Sylius\AdminOrderCreationPlugin\Form\Type\NewOrderCustomerSelectType; +use Sylius\Component\Core\Model\ChannelInterface; +use Sylius\Component\Core\Model\CustomerInterface; use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Twig\Environment; +use Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type\NewOrderCustomerCreateType; +use Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type\NewOrderCustomerSelectType; final class SelectNewOrderCustomerAction { - /** @var FormFactoryInterface */ - private $formFactory; - - /** @var Environment */ - private $twig; - - public function __construct(FormFactoryInterface $formFactory, Environment $twig) - { - $this->formFactory = $formFactory; - $this->twig = $twig; + public function __construct( + private readonly FormFactoryInterface $formFactory, + private readonly Environment $twig, + private readonly UrlGeneratorInterface $router, + ) { } public function __invoke(Request $request): Response @@ -30,7 +29,37 @@ public function __invoke(Request $request): Response $selectCustomerForm = $this->formFactory->create(NewOrderCustomerSelectType::class); $createCustomerForm = $this->formFactory->create(NewOrderCustomerCreateType::class); - return new Response($this->twig->render('@SyliusAdminOrderCreationPlugin/Order/selectCustomer.html.twig', [ + if ($request->query->has($selectCustomerForm->getName())) { + $selectCustomerForm->handleRequest($request); + + if ($selectCustomerForm->isSubmitted() && $selectCustomerForm->isValid()) { + /** @var CustomerInterface $customer */ + $customer = $selectCustomerForm->get('customer')->getData(); + /** @var ChannelInterface $channel */ + $channel = $selectCustomerForm->get('channel')->getData(); + + return new RedirectResponse($this->router->generate('sylius_admin_order_creation_order_create', [ + 'customerId' => $customer->getId(), + 'channelCode' => $channel->getCode(), + ])); + } + } + + if ($request->query->has($createCustomerForm->getName())) { + $createCustomerForm->handleRequest($request); + + if ($createCustomerForm->isSubmitted() && $createCustomerForm->isValid()) { + /** @var ChannelInterface $channel */ + $channel = $createCustomerForm->get('channel')->getData(); + + return new RedirectResponse($this->router->generate('sylius_admin_order_creation_customer_create', [ + 'customerEmail' => $createCustomerForm->get('customerEmail')->getData(), + 'channelCode' => $channel->getCode(), + ])); + } + } + + return new Response($this->twig->render('@WebgriffeSyliusAdminOrderCreationPlugin/order/select_customer.html.twig', [ 'selectCustomerForm' => $selectCustomerForm->createView(), 'createCustomerForm' => $createCustomerForm->createView(), ])); diff --git a/src/DependencyInjection/Compiler/RegisterReorderProcessorsPass.php b/src/DependencyInjection/Compiler/RegisterReorderProcessorsPass.php index e506fc115..ce8d49777 100644 --- a/src/DependencyInjection/Compiler/RegisterReorderProcessorsPass.php +++ b/src/DependencyInjection/Compiler/RegisterReorderProcessorsPass.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\DependencyInjection\Compiler; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\DependencyInjection\Compiler; use Sylius\Bundle\ResourceBundle\DependencyInjection\Compiler\PrioritizedCompositeServicePass; @@ -11,10 +11,10 @@ final class RegisterReorderProcessorsPass extends PrioritizedCompositeServicePas public function __construct() { parent::__construct( - 'Sylius\AdminOrderCreationPlugin\ReorderProcessing\CompositeReorderProcessor', - 'Sylius\AdminOrderCreationPlugin\ReorderProcessing\CompositeReorderProcessor', + 'Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing\CompositeReorderProcessor', + 'Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing\CompositeReorderProcessor', 'sylius_admin_order_creation.reorder_processor', - 'addProcessor' + 'addProcessor', ); } } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 1997d0487..e031ad226 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\DependencyInjection; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\DependencyInjection; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -11,6 +11,21 @@ final class Configuration implements ConfigurationInterface { public function getConfigTreeBuilder(): TreeBuilder { - return new TreeBuilder('sylius_admin_order_creation_plugin'); + $treeBuilder = new TreeBuilder('sylius_admin_order_creation_plugin'); + $rootNode = $treeBuilder->getRootNode(); + + $rootNode + ->children() + ->arrayNode('offline_gateway_names') + ->info('Payment gateway names for which no payment link is generated after an order is created from the admin panel.') + ->scalarPrototype()->end() + ->defaultValue(['offline']) + ->end() + ->booleanNode('payment_link_generation_enabled') + ->info('Whether to generate (and optionally send) a payment link after an order is created from the admin panel.') + ->defaultTrue() + ; + + return $treeBuilder; } } diff --git a/src/DependencyInjection/SyliusAdminOrderCreationExtension.php b/src/DependencyInjection/SyliusAdminOrderCreationExtension.php index 4f77fad5a..1cc3a1771 100644 --- a/src/DependencyInjection/SyliusAdminOrderCreationExtension.php +++ b/src/DependencyInjection/SyliusAdminOrderCreationExtension.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\DependencyInjection; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\DependencyInjection; use Sylius\Bundle\CoreBundle\DependencyInjection\PrependDoctrineMigrationsTrait; use Symfony\Component\Config\FileLocator; @@ -17,8 +17,11 @@ final class SyliusAdminOrderCreationExtension extends Extension implements Prepe public function load(array $config, ContainerBuilder $container): void { - $this->processConfiguration($this->getConfiguration([], $container), $config); - $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); + $config = $this->processConfiguration($this->getConfiguration([], $container), $config); + $container->setParameter('sylius_admin_order_creation_plugin.offline_gateway_names', $config['offline_gateway_names']); + $container->setParameter('sylius_admin_order_creation_plugin.payment_link_generation_enabled', $config['payment_link_generation_enabled']); + + $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../../config')); } public function prepend(ContainerBuilder $container): void @@ -28,12 +31,12 @@ public function prepend(ContainerBuilder $container): void protected function getMigrationsNamespace(): string { - return 'Sylius\AdminOrderCreationPlugin\Migrations'; + return 'Webgriffe\SyliusAdminOrderCreationPlugin\Migrations'; } protected function getMigrationsDirectory(): string { - return '@SyliusAdminOrderCreationPlugin/Migrations'; + return '@WebgriffeSyliusAdminOrderCreationPlugin/src/Migrations'; } protected function getNamespacesOfMigrationsExecutedBefore(): array diff --git a/src/Doctrine/ORM/CustomerRepositoryInterface.php b/src/Doctrine/ORM/CustomerRepositoryInterface.php index c6cb7d7b8..00c5c2842 100644 --- a/src/Doctrine/ORM/CustomerRepositoryInterface.php +++ b/src/Doctrine/ORM/CustomerRepositoryInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Doctrine\ORM; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM; use Sylius\Component\Core\Repository\CustomerRepositoryInterface as BaseCustomerRepositoryInterface; diff --git a/src/Doctrine/ORM/CustomerRepositoryTrait.php b/src/Doctrine/ORM/CustomerRepositoryTrait.php index 60a8f86ea..bd03b9bb0 100644 --- a/src/Doctrine/ORM/CustomerRepositoryTrait.php +++ b/src/Doctrine/ORM/CustomerRepositoryTrait.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Doctrine\ORM; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM; use Doctrine\ORM\EntityManager; diff --git a/src/Doctrine/ORM/ProductVariantRepositoryInterface.php b/src/Doctrine/ORM/ProductVariantRepositoryInterface.php index 4a21c174f..f7f88ecda 100644 --- a/src/Doctrine/ORM/ProductVariantRepositoryInterface.php +++ b/src/Doctrine/ORM/ProductVariantRepositoryInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Doctrine\ORM; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM; use Sylius\Component\Core\Repository\ProductVariantRepositoryInterface as BaseProductVariantRepositoryInterface; diff --git a/src/Doctrine/ORM/ProductVariantRepositoryTrait.php b/src/Doctrine/ORM/ProductVariantRepositoryTrait.php index ba3dc7f16..8afcd3f79 100644 --- a/src/Doctrine/ORM/ProductVariantRepositoryTrait.php +++ b/src/Doctrine/ORM/ProductVariantRepositoryTrait.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Doctrine\ORM; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM; use Doctrine\ORM\EntityManager; diff --git a/src/Event/OrderCreatedByAdminEvent.php b/src/Event/OrderCreatedByAdminEvent.php new file mode 100644 index 000000000..2ae319d4d --- /dev/null +++ b/src/Event/OrderCreatedByAdminEvent.php @@ -0,0 +1,24 @@ +order = $order; + } + + public function getOrder(): OrderInterface + { + return $this->order; + } +} diff --git a/src/Event/OrderCreationInitializedEvent.php b/src/Event/OrderCreationInitializedEvent.php new file mode 100644 index 000000000..29b26ed8f --- /dev/null +++ b/src/Event/OrderCreationInitializedEvent.php @@ -0,0 +1,24 @@ +order = $order; + } + + public function getOrder(): OrderInterface + { + return $this->order; + } +} diff --git a/src/Event/PaymentLinkGeneratedEvent.php b/src/Event/PaymentLinkGeneratedEvent.php new file mode 100644 index 000000000..4d30dfb26 --- /dev/null +++ b/src/Event/PaymentLinkGeneratedEvent.php @@ -0,0 +1,24 @@ +payment = $payment; + } + + public function getPayment(): PaymentInterface + { + return $this->payment; + } +} diff --git a/src/EventListener/OrderCreationListener.php b/src/EventListener/OrderCreationListener.php index 83752ef2a..d0acf6fe5 100644 --- a/src/EventListener/OrderCreationListener.php +++ b/src/EventListener/OrderCreationListener.php @@ -2,13 +2,15 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\EventListener; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\EventListener; -use SM\Factory\FactoryInterface; +use Sylius\Abstraction\StateMachine\StateMachineInterface; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\OrderCheckoutTransitions; use Sylius\Component\Order\Processor\OrderProcessorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; +use Webgriffe\SyliusAdminOrderCreationPlugin\Event\OrderCreatedByAdminEvent; use Webmozart\Assert\Assert; final class OrderCreationListener @@ -16,13 +18,20 @@ final class OrderCreationListener /** @var OrderProcessorInterface */ private $orderProcessor; - /** @var FactoryInterface */ - private $stateMachineFactory; + /** @var StateMachineInterface */ + private $stateMachine; - public function __construct(OrderProcessorInterface $orderProcessor, FactoryInterface $stateMachineFactory) - { + /** @var EventDispatcherInterface */ + private $eventDispatcher; + + public function __construct( + OrderProcessorInterface $orderProcessor, + StateMachineInterface $stateMachine, + EventDispatcherInterface $eventDispatcher, + ) { $this->orderProcessor = $orderProcessor; - $this->stateMachineFactory = $stateMachineFactory; + $this->stateMachine = $stateMachine; + $this->eventDispatcher = $eventDispatcher; } public function processOrderBeforeCreation(GenericEvent $event): void @@ -39,14 +48,21 @@ public function completeOrderBeforeCreation(GenericEvent $event): void $order = $event->getSubject(); Assert::isInstanceOf($order, OrderInterface::class); - $stateMachine = $this->stateMachineFactory->get($order, 'sylius_order_checkout'); - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_ADDRESS); - if ($stateMachine->can(OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING)) { - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING); + $this->stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_ADDRESS); + if ($this->stateMachine->can($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING)) { + $this->stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING); } - if ($stateMachine->can(OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT)) { - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT); + if ($this->stateMachine->can($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT)) { + $this->stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT); } - $stateMachine->apply(OrderCheckoutTransitions::TRANSITION_COMPLETE); + $this->stateMachine->apply($order, OrderCheckoutTransitions::GRAPH, OrderCheckoutTransitions::TRANSITION_COMPLETE); + } + + public function dispatchOrderCreatedEvent(GenericEvent $event): void + { + $order = $event->getSubject(); + Assert::isInstanceOf($order, OrderInterface::class); + + $this->eventDispatcher->dispatch(new OrderCreatedByAdminEvent($order)); } } diff --git a/src/EventListener/PaymentLinkCreationListener.php b/src/EventListener/PaymentLinkCreationListener.php index 91fc103c3..b3bf379fc 100644 --- a/src/EventListener/PaymentLinkCreationListener.php +++ b/src/EventListener/PaymentLinkCreationListener.php @@ -2,16 +2,20 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\EventListener; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\EventListener; use Doctrine\Persistence\ObjectManager; -use Payum\Core\Model\GatewayConfigInterface; -use Sylius\AdminOrderCreationPlugin\Provider\PaymentTokenProviderInterface; -use Sylius\AdminOrderCreationPlugin\Sender\OrderPaymentLinkSenderInterface; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; +use Sylius\Component\Payment\Model\GatewayConfigInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; +use Symfony\Component\HttpFoundation\RequestStack; +use Webgriffe\SyliusAdminOrderCreationPlugin\Event\PaymentLinkGeneratedEvent; +use Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type\NewOrderType; +use Webgriffe\SyliusAdminOrderCreationPlugin\Provider\PaymentTokenProviderInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\Sender\OrderPaymentLinkSenderInterface; use Webmozart\Assert\Assert; final class PaymentLinkCreationListener @@ -25,18 +29,45 @@ final class PaymentLinkCreationListener /** @var OrderPaymentLinkSenderInterface */ private $orderPaymentLinkSender; + /** @var RequestStack */ + private $requestStack; + + /** @var EventDispatcherInterface */ + private $eventDispatcher; + + /** @var list */ + private $offlineGatewayNames; + + /** @var bool */ + private $enabled; + + /** + * @param list $offlineGatewayNames + */ public function __construct( PaymentTokenProviderInterface $paymentTokenProvider, ObjectManager $orderManager, - OrderPaymentLinkSenderInterface $orderPaymentLinkSender + OrderPaymentLinkSenderInterface $orderPaymentLinkSender, + RequestStack $requestStack, + EventDispatcherInterface $eventDispatcher, + array $offlineGatewayNames, + bool $enabled, ) { $this->paymentTokenProvider = $paymentTokenProvider; $this->orderManager = $orderManager; $this->orderPaymentLinkSender = $orderPaymentLinkSender; + $this->requestStack = $requestStack; + $this->eventDispatcher = $eventDispatcher; + $this->offlineGatewayNames = $offlineGatewayNames; + $this->enabled = $enabled; } public function setPaymentLink(GenericEvent $event): void { + if (!$this->enabled) { + return; + } + /** @var OrderInterface $order */ $order = $event->getSubject(); Assert::isInstanceOf($order, OrderInterface::class); @@ -51,14 +82,31 @@ public function setPaymentLink(GenericEvent $event): void /** @var GatewayConfigInterface $gatewayConfig */ $gatewayConfig = $paymentMethod->getGatewayConfig(); - if ('offline' === $gatewayConfig->getGatewayName()) { + if (\in_array($gatewayConfig->getGatewayName(), $this->offlineGatewayNames, true)) { return; } $token = $this->paymentTokenProvider->getPaymentToken($payment); $payment->setDetails(['payment-link' => $token->getAfterUrl()]); - $this->orderPaymentLinkSender->sendPaymentLink($order); + $this->eventDispatcher->dispatch(new PaymentLinkGeneratedEvent($payment)); + + if ($this->shouldSendPaymentLinkEmail()) { + $this->orderPaymentLinkSender->sendPaymentLink($order); + } + $this->orderManager->flush(); } + + private function shouldSendPaymentLinkEmail(): bool + { + $request = $this->requestStack->getCurrentRequest(); + if (null === $request) { + return false; + } + + $formData = $request->request->all(NewOrderType::BLOCK_PREFIX); + + return (bool) ($formData['sendPaymentLinkEmail'] ?? false); + } } diff --git a/src/Factory/OrderFactory.php b/src/Factory/OrderFactory.php index db322eb31..21a6bac19 100644 --- a/src/Factory/OrderFactory.php +++ b/src/Factory/OrderFactory.php @@ -2,9 +2,8 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Factory; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Factory; -use Sylius\AdminOrderCreationPlugin\ReorderProcessing\ReorderProcessor; use Sylius\Component\Channel\Repository\ChannelRepositoryInterface; use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\CustomerInterface; @@ -13,6 +12,9 @@ use Sylius\Component\Currency\Model\CurrencyInterface; use Sylius\Component\Locale\Model\LocaleInterface; use Sylius\Component\Resource\Factory\FactoryInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\Event\OrderCreationInitializedEvent; +use Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing\ReorderProcessor; use Webmozart\Assert\Assert; final class OrderFactory implements OrderFactoryInterface @@ -29,17 +31,22 @@ final class OrderFactory implements OrderFactoryInterface /** @var ReorderProcessor */ private $reorderProcessor; + /** @var EventDispatcherInterface */ + private $eventDispatcher; + public function __construct( FactoryInterface $baseOrderFactory, CustomerRepositoryInterface $customerRepository, ChannelRepositoryInterface $channelRepository, - ReorderProcessor $reorderProcessor + ReorderProcessor $reorderProcessor, + EventDispatcherInterface $eventDispatcher, ) { $this->baseOrderFactory = $baseOrderFactory; $this->customerRepository = $customerRepository; $this->channelRepository = $channelRepository; $this->reorderProcessor = $reorderProcessor; + $this->eventDispatcher = $eventDispatcher; } public function createNew(): OrderInterface @@ -77,6 +84,8 @@ public function createForCustomerAndChannel(string $customerId, string $channelC Assert::isInstanceOf($defaultLocale, LocaleInterface::class); $order->setLocaleCode($defaultLocale->getCode()); + $this->eventDispatcher->dispatch(new OrderCreationInitializedEvent($order)); + return $order; } @@ -87,6 +96,8 @@ public function createFromExistingOrder(OrderInterface $order): OrderInterface $this->reorderProcessor->process($order, $reorder); + $this->eventDispatcher->dispatch(new OrderCreationInitializedEvent($reorder)); + return $reorder; } } diff --git a/src/Factory/OrderFactoryInterface.php b/src/Factory/OrderFactoryInterface.php index 941553aec..09a95e655 100644 --- a/src/Factory/OrderFactoryInterface.php +++ b/src/Factory/OrderFactoryInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Factory; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Factory; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Resource\Factory\FactoryInterface; diff --git a/src/Form/Type/AdjustmentType.php b/src/Form/Type/AdjustmentType.php index cfd89dcc1..1025ee20d 100644 --- a/src/Form/Type/AdjustmentType.php +++ b/src/Form/Type/AdjustmentType.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Form\Type; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type; use Sylius\Bundle\MoneyBundle\Form\Type\MoneyType; use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType; @@ -23,6 +23,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void $builder->add('amount', MoneyType::class, [ 'label' => $options['label'], 'currency' => $options['currency'], + 'empty_data' => 0, 'constraints' => [ new Range(['min' => 0, 'minMessage' => 'sylius_admin_order_creation.order_discount', 'groups' => ['sylius']]), ], diff --git a/src/Form/Type/CurrencyCodeChoiceType.php b/src/Form/Type/CurrencyCodeChoiceType.php index 5a747dc53..d8539f7d3 100644 --- a/src/Form/Type/CurrencyCodeChoiceType.php +++ b/src/Form/Type/CurrencyCodeChoiceType.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Form\Type; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type; use Sylius\Bundle\CurrencyBundle\Form\Type\CurrencyChoiceType; use Sylius\Bundle\ResourceBundle\Form\DataTransformer\ResourceToIdentifierTransformer; @@ -24,7 +24,7 @@ public function __construct(RepositoryInterface $currencyRepository) public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->addModelTransformer( - new ReversedTransformer(new ResourceToIdentifierTransformer($this->currencyRepository, 'code')) + new ReversedTransformer(new ResourceToIdentifierTransformer($this->currencyRepository, 'code')), ); } diff --git a/src/Form/Type/CustomerAutocompleteChoiceType.php b/src/Form/Type/CustomerAutocompleteChoiceType.php deleted file mode 100644 index 7069f9542..000000000 --- a/src/Form/Type/CustomerAutocompleteChoiceType.php +++ /dev/null @@ -1,49 +0,0 @@ -setDefaults([ - 'resource' => 'sylius.customer', - 'choice_name' => 'email', - 'choice_value' => 'id', - 'label' => false, - ]); - } - - /** - * {@inheritdoc} - */ - public function buildView(FormView $view, FormInterface $form, array $options): void - { - $view->vars['remote_criteria_type'] = 'contains'; - $view->vars['remote_criteria_name'] = 'email'; - } - - /** - * {@inheritdoc} - */ - public function getBlockPrefix(): string - { - return 'sylius_customer_autocomplete_choice'; - } - - /** - * {@inheritdoc} - */ - public function getParent(): string - { - return ResourceAutocompleteChoiceType::class; - } -} diff --git a/src/Form/Type/CustomerAutocompleteType.php b/src/Form/Type/CustomerAutocompleteType.php new file mode 100644 index 000000000..51cc5f98e --- /dev/null +++ b/src/Form/Type/CustomerAutocompleteType.php @@ -0,0 +1,40 @@ +setDefaults([ + 'class' => $this->customerClass, + 'choice_label' => 'email', + 'searchable_fields' => ['email'], + ]); + } + + public function getBlockPrefix(): string + { + return 'webgriffe_sylius_admin_order_creation_customer_autocomplete'; + } + + public function getParent(): string + { + return BaseEntityAutocompleteType::class; + } +} diff --git a/src/Form/Type/LocaleCodeChoiceType.php b/src/Form/Type/LocaleCodeChoiceType.php index 92508246b..2f639c52a 100644 --- a/src/Form/Type/LocaleCodeChoiceType.php +++ b/src/Form/Type/LocaleCodeChoiceType.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Form\Type; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type; use Sylius\Bundle\LocaleBundle\Form\Type\LocaleChoiceType; use Sylius\Bundle\ResourceBundle\Form\DataTransformer\ResourceToIdentifierTransformer; @@ -22,7 +22,7 @@ public function __construct(RepositoryInterface $localeRepository) } /** - * {@inheritdoc} + * @inheritdoc */ public function buildForm(FormBuilderInterface $builder, array $options): void { @@ -30,7 +30,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void } /** - * {@inheritdoc} + * @inheritdoc */ public function getParent(): string { @@ -38,7 +38,7 @@ public function getParent(): string } /** - * {@inheritdoc} + * @inheritdoc */ public function getBlockPrefix(): string { diff --git a/src/Form/Type/NewOrderCustomerCreateType.php b/src/Form/Type/NewOrderCustomerCreateType.php index 2dad4d11b..8314d83ca 100644 --- a/src/Form/Type/NewOrderCustomerCreateType.php +++ b/src/Form/Type/NewOrderCustomerCreateType.php @@ -2,12 +2,13 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Form\Type; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type; use Sylius\Bundle\ChannelBundle\Form\Type\ChannelChoiceType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Validator\Constraints\NotBlank; final class NewOrderCustomerCreateType extends AbstractType { @@ -15,10 +16,14 @@ public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add('customerEmail', TextType::class, [ - 'label' => false, + 'label' => 'sylius_admin_order_creation.ui.new_customer_email', + 'required' => false, + 'constraints' => [ + new NotBlank(message: 'sylius_admin_order_creation.customer_email'), + ], ]) ->add('channel', ChannelChoiceType::class, [ - 'label' => false, + 'label' => 'sylius.ui.channel', ]) ; } diff --git a/src/Form/Type/NewOrderCustomerSelectType.php b/src/Form/Type/NewOrderCustomerSelectType.php index bd5e2f284..51833b03e 100644 --- a/src/Form/Type/NewOrderCustomerSelectType.php +++ b/src/Form/Type/NewOrderCustomerSelectType.php @@ -2,23 +2,27 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Form\Type; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type; use Sylius\Bundle\ChannelBundle\Form\Type\ChannelChoiceType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Validator\Constraints\NotBlank; final class NewOrderCustomerSelectType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void { $builder - ->add('customer', CustomerAutocompleteChoiceType::class, [ - 'multiple' => false, - 'required' => true, + ->add('customer', CustomerAutocompleteType::class, [ + 'label' => 'sylius.ui.customer', + 'required' => false, + 'constraints' => [ + new NotBlank(message: 'sylius_admin_order_creation.no_customer_selected'), + ], ]) ->add('channel', ChannelChoiceType::class, [ - 'label' => false, + 'label' => 'sylius.ui.channel', ]) ; } diff --git a/src/Form/Type/NewOrderType.php b/src/Form/Type/NewOrderType.php index 0ba3d954d..056a35f6c 100644 --- a/src/Form/Type/NewOrderType.php +++ b/src/Form/Type/NewOrderType.php @@ -2,20 +2,25 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Form\Type; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type; use Sylius\Bundle\AddressingBundle\Form\Type\AddressType; use Sylius\Bundle\PromotionBundle\Form\Type\PromotionCouponToCodeType; use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType; use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\OrderInterface; -use Symfony\Component\Form\Extension\Core\Type\CollectionType; +use Sylius\Component\Shipping\Model\ShippingSubjectInterface; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\UX\LiveComponent\Form\Type\LiveCollectionType; final class NewOrderType extends AbstractResourceType { + public const BLOCK_PREFIX = 'sylius_admin_order_creation_new_order'; + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder @@ -31,15 +36,23 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'label' => 'sylius.ui.billing_address', 'required' => false, ]) - ->add('payments', CollectionType::class, [ + ->add('payments', LiveCollectionType::class, [ 'entry_type' => PaymentType::class, 'label' => 'sylius.ui.payments', 'allow_add' => true, 'allow_delete' => true, 'by_reference' => false, ]) - ->add('shipments', CollectionType::class, [ + ->add('sendPaymentLinkEmail', CheckboxType::class, [ + 'mapped' => false, + 'required' => false, + 'label' => 'sylius_admin_order_creation.ui.send_payment_link_email', + ]) + ->add('shipments', LiveCollectionType::class, [ 'entry_type' => ShipmentType::class, + 'entry_options' => [ + 'subject' => $options['shipmentChoicesSubject'], + ], 'label' => 'sylius.ui.shipments', 'allow_add' => true, 'allow_delete' => true, @@ -55,17 +68,18 @@ public function buildForm(FormBuilderInterface $builder, array $options): void $event ->getForm() - ->add('items', CollectionType::class, [ + ->add('items', LiveCollectionType::class, [ 'label' => false, 'entry_type' => OrderItemType::class, 'entry_options' => [ 'currency' => $order->getCurrencyCode(), + 'channelCode' => $channel->getCode(), ], 'allow_add' => true, 'allow_delete' => true, 'by_reference' => false, ]) - ->add('adjustments', CollectionType::class, [ + ->add('adjustments', LiveCollectionType::class, [ 'label' => false, 'entry_type' => AdjustmentType::class, 'entry_options' => [ @@ -76,15 +90,17 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'allow_add' => true, 'allow_delete' => true, 'by_reference' => false, - 'button_add_label' => 'sylius_admin_order_creation.ui.add_discount', + 'button_add_options' => [ + 'label' => 'sylius_admin_order_creation.ui.add_discount', + ], ]) ->add('localeCode', LocaleCodeChoiceType::class, [ - 'label' => false, + 'label' => 'sylius.ui.locale', 'choices' => $channel->getLocales(), 'empty_data' => $order->getLocaleCode(), ]) ->add('currencyCode', CurrencyCodeChoiceType::class, [ - 'label' => false, + 'label' => 'sylius.ui.currency', 'choices' => $channel->getCurrencies(), 'empty_data' => $order->getCurrencyCode(), ]) @@ -93,7 +109,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event): void { $orderData = $event->getData(); - if (isset($orderData['shippingAddress']) && $this->isBillingAddressEmpty($orderData)) { + if ($this->isShippingAddressComplete($orderData) && $this->isBillingAddressEmpty($orderData)) { $orderData['billingAddress'] = $orderData['shippingAddress']; $event->setData($orderData); @@ -102,9 +118,17 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ; } + public function configureOptions(OptionsResolver $resolver): void + { + parent::configureOptions($resolver); + + $resolver->setDefault('shipmentChoicesSubject', null); + $resolver->setAllowedTypes('shipmentChoicesSubject', ['null', ShippingSubjectInterface::class]); + } + public function getBlockPrefix(): string { - return 'sylius_admin_order_creation_new_order'; + return self::BLOCK_PREFIX; } private function isBillingAddressEmpty(array $orderData): bool @@ -122,4 +146,26 @@ private function isBillingAddressEmpty(array $orderData): bool $orderData['billingAddress']['postcode'] === '' ; } + + /** + * The order creation page re-renders live as the admin types (via the order-form Live Component), so + * this form's PRE_SUBMIT listener runs on every keystroke-triggered re-render, not just on the final + * submit. Only copying the shipping address into an empty billing address once shipping is itself + * fully filled in prevents a half-typed shipping address from being copied over field-by-field, which + * would otherwise permanently block the rest of the copy (billing would no longer read as "empty"). + */ + private function isShippingAddressComplete(array $orderData): bool + { + if (!isset($orderData['shippingAddress'])) { + return false; + } + + foreach (['firstName', 'lastName', 'street', 'countryCode', 'city', 'postcode'] as $field) { + if (($orderData['shippingAddress'][$field] ?? '') === '') { + return false; + } + } + + return true; + } } diff --git a/src/Form/Type/OrderItemType.php b/src/Form/Type/OrderItemType.php index 74e1125e9..d320df377 100644 --- a/src/Form/Type/OrderItemType.php +++ b/src/Form/Type/OrderItemType.php @@ -2,17 +2,16 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Form\Type; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type; use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType; -use Sylius\Bundle\ResourceBundle\Form\Type\ResourceAutocompleteChoiceType; use Symfony\Component\Form\DataMapperInterface; -use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\UX\LiveComponent\Form\Type\LiveCollectionType; final class OrderItemType extends AbstractResourceType { @@ -22,7 +21,7 @@ final class OrderItemType extends AbstractResourceType public function __construct( string $dataClass, DataMapperInterface $dataMapper, - array $validationGroups = [] + array $validationGroups = [], ) { parent::__construct($dataClass, $validationGroups); @@ -37,16 +36,17 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'label' => 'sylius.ui.quantity', 'empty_data' => 1, ]) - ->add('variant', ResourceAutocompleteChoiceType::class, [ + ->add('variant', ProductVariantInChannelAutocompleteType::class, [ 'label' => 'sylius.ui.variant', - 'choice_name' => 'descriptor', - 'choice_value' => 'code', - 'resource' => 'sylius.product_variant', + 'extra_options' => [ + 'channel_code' => $options['channelCode'], + 'choice_label' => 'descriptor', + ], ]) ->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($options): void { $event ->getForm() - ->add('adjustments', CollectionType::class, [ + ->add('adjustments', LiveCollectionType::class, [ 'label' => false, 'entry_type' => AdjustmentType::class, 'entry_options' => [ @@ -57,7 +57,9 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'allow_add' => true, 'allow_delete' => true, 'by_reference' => false, - 'button_add_label' => 'sylius_admin_order_creation.ui.add_discount', + 'button_add_options' => [ + 'label' => 'sylius_admin_order_creation.ui.add_discount', + ], ]) ; }) @@ -78,10 +80,11 @@ public function configureOptions(OptionsResolver $resolver): void parent::configureOptions($resolver); $resolver->setRequired('currency'); + $resolver->setRequired('channelCode'); } /** - * {@inheritdoc} + * @inheritdoc */ public function getBlockPrefix(): string { diff --git a/src/Form/Type/PaymentType.php b/src/Form/Type/PaymentType.php index 8402a63ab..ef174db4e 100644 --- a/src/Form/Type/PaymentType.php +++ b/src/Form/Type/PaymentType.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Form\Type; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type; use Sylius\Bundle\PaymentBundle\Form\Type\PaymentMethodChoiceType; use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType; @@ -19,7 +19,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void } /** - * {@inheritdoc} + * @inheritdoc */ public function getBlockPrefix(): string { diff --git a/src/Form/Type/ProductVariantInChannelAutocompleteType.php b/src/Form/Type/ProductVariantInChannelAutocompleteType.php new file mode 100644 index 000000000..73e2380df --- /dev/null +++ b/src/Form/Type/ProductVariantInChannelAutocompleteType.php @@ -0,0 +1,56 @@ +setDefaults([ + 'class' => $this->productVariantClass, + 'filter_query' => function (Options $options): ?callable { + $channelCode = $options['extra_options']['channel_code'] ?? null; + + if ($channelCode === null) { + return null; + } + + return function (QueryBuilder $queryBuilder, string $query, EntityRepository $repository) use ($channelCode): void { + $queryBuilder + ->innerJoin('entity.channelPricings', 'channelPricing') + ->andWhere('channelPricing.channelCode = :channelPricingChannelCode') + ->setParameter('channelPricingChannelCode', $channelCode) + ; + }; + }, + ]); + } + + public function getBlockPrefix(): string + { + return 'webgriffe_sylius_admin_order_creation_product_variant_in_channel_autocomplete'; + } + + public function getParent(): string + { + return TranslatableAutocompleteType::class; + } +} diff --git a/src/Form/Type/ShipmentType.php b/src/Form/Type/ShipmentType.php index 60ffecc9f..a141270d3 100644 --- a/src/Form/Type/ShipmentType.php +++ b/src/Form/Type/ShipmentType.php @@ -2,24 +2,40 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Form\Type; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Form\Type; use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType; use Sylius\Bundle\ShippingBundle\Form\Type\ShippingMethodChoiceType; +use Sylius\Component\Shipping\Model\ShippingSubjectInterface; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; final class ShipmentType extends AbstractResourceType { public function buildForm(FormBuilderInterface $builder, array $options): void { - $builder->add('method', ShippingMethodChoiceType::class, [ + $methodOptions = [ 'required' => true, 'label' => 'sylius.form.checkout.shipping_method', - ]); + ]; + + if ($options['subject'] !== null) { + $methodOptions['subject'] = $options['subject']; + } + + $builder->add('method', ShippingMethodChoiceType::class, $methodOptions); + } + + public function configureOptions(OptionsResolver $resolver): void + { + parent::configureOptions($resolver); + + $resolver->setDefault('subject', null); + $resolver->setAllowedTypes('subject', ['null', ShippingSubjectInterface::class]); } /** - * {@inheritdoc} + * @inheritdoc */ public function getBlockPrefix(): string { diff --git a/src/Preparator/NewOrderPreparator.php b/src/Preparator/NewOrderPreparator.php deleted file mode 100644 index a5dc7f759..000000000 --- a/src/Preparator/NewOrderPreparator.php +++ /dev/null @@ -1,53 +0,0 @@ -orderFactory = $orderFactory; - $this->formFactory = $formFactory; - $this->orderProcessor = $orderProcessor; - } - - public function prepareFromRequest(Request $request): OrderInterface - { - Assert::true($request->attributes->has('customerId')); - $customerEmail = $request->attributes->get('customerId'); - - Assert::true($request->attributes->has('channelCode')); - $channelCode = $request->attributes->get('channelCode'); - - $order = $this->orderFactory->createForCustomerAndChannel($customerEmail, $channelCode); - $form = $this->formFactory->create(NewOrderType::class, $order); - - /** @var OrderInterface $order */ - $order = $form->handleRequest($request)->getData(); - $this->orderProcessor->process($order); - - return $order; - } -} diff --git a/src/Preparator/OrderPreparatorInterface.php b/src/Preparator/OrderPreparatorInterface.php deleted file mode 100644 index c17884746..000000000 --- a/src/Preparator/OrderPreparatorInterface.php +++ /dev/null @@ -1,13 +0,0 @@ -shippingMethodsResolver = $shippingMethodsResolver; - } - - public function __invoke(ShipmentInterface $shipment): array - { - $shippingMethods = $this->shippingMethodsResolver->getSupportedMethods($shipment); - $shippingMethodsList = []; - - foreach ($shippingMethods as $shippingMethod) { - $shippingMethodsList[$shippingMethod->getCode()] = $shippingMethod->getName(); - } - - return $shippingMethodsList; - } -} diff --git a/src/Provider/CustomerProvider.php b/src/Provider/CustomerProvider.php index 3f7fc93b3..8e288e674 100644 --- a/src/Provider/CustomerProvider.php +++ b/src/Provider/CustomerProvider.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Provider; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Provider; use Sylius\Component\Core\Model\CustomerInterface; use Sylius\Component\Core\Repository\CustomerRepositoryInterface; @@ -19,7 +19,7 @@ final class CustomerProvider implements CustomerProviderInterface public function __construct( CustomerRepositoryInterface $customerRepository, - FactoryInterface $customerFactory + FactoryInterface $customerFactory, ) { $this->customerRepository = $customerRepository; $this->customerFactory = $customerFactory; diff --git a/src/Provider/CustomerProviderInterface.php b/src/Provider/CustomerProviderInterface.php index 998a5f262..0d51c4239 100644 --- a/src/Provider/CustomerProviderInterface.php +++ b/src/Provider/CustomerProviderInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Provider; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Provider; use Sylius\Component\Core\Model\CustomerInterface; diff --git a/src/Provider/PaymentTokenProvider.php b/src/Provider/PaymentTokenProvider.php index 21d0fd4a0..20cc7b8c4 100644 --- a/src/Provider/PaymentTokenProvider.php +++ b/src/Provider/PaymentTokenProvider.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Provider; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Provider; -use Payum\Core\Model\GatewayConfigInterface; use Payum\Core\Payum; use Payum\Core\Security\TokenInterface; use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; +use Sylius\Component\Payment\Model\GatewayConfigInterface; final class PaymentTokenProvider implements PaymentTokenProviderInterface { @@ -40,14 +40,14 @@ public function getPaymentToken(PaymentInterface $payment): TokenInterface return $tokenFactory->createAuthorizeToken( $gatewayConfig->getGatewayName(), $payment, - $this->afterPayRoute + $this->afterPayRoute, ); } return $tokenFactory->createCaptureToken( $gatewayConfig->getGatewayName(), $payment, - $this->afterPayRoute + $this->afterPayRoute, ); } } diff --git a/src/Provider/PaymentTokenProviderInterface.php b/src/Provider/PaymentTokenProviderInterface.php index 342051a28..28789cd69 100644 --- a/src/Provider/PaymentTokenProviderInterface.php +++ b/src/Provider/PaymentTokenProviderInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Provider; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Provider; use Payum\Core\Security\TokenInterface; use Sylius\Component\Core\Model\PaymentInterface; diff --git a/src/ReorderProcessing/CompositeReorderProcessor.php b/src/ReorderProcessing/CompositeReorderProcessor.php index 2ebf7f8ff..7906cf6ab 100644 --- a/src/ReorderProcessing/CompositeReorderProcessor.php +++ b/src/ReorderProcessing/CompositeReorderProcessor.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\ReorderProcessing; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing; -use Sylius\Component\Core\Model\OrderInterface; use Laminas\Stdlib\PriorityQueue; +use Sylius\Component\Core\Model\OrderInterface; final class CompositeReorderProcessor implements ReorderProcessor { diff --git a/src/ReorderProcessing/ReorderDataProcessor.php b/src/ReorderProcessing/ReorderDataProcessor.php index ce39f7d17..177873904 100644 --- a/src/ReorderProcessing/ReorderDataProcessor.php +++ b/src/ReorderProcessing/ReorderDataProcessor.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\ReorderProcessing; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing; use Sylius\Component\Core\Model\AddressInterface; use Sylius\Component\Core\Model\OrderInterface; diff --git a/src/ReorderProcessing/ReorderItemsProcessor.php b/src/ReorderProcessing/ReorderItemsProcessor.php index 3304745ec..efa67fbd2 100644 --- a/src/ReorderProcessing/ReorderItemsProcessor.php +++ b/src/ReorderProcessing/ReorderItemsProcessor.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\ReorderProcessing; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderItemInterface; @@ -25,7 +25,7 @@ final class ReorderItemsProcessor implements ReorderProcessor public function __construct( FactoryInterface $orderItemFactory, OrderItemQuantityModifierInterface $orderItemQuantityModifier, - OrderModifierInterface $orderModifier + OrderModifierInterface $orderModifier, ) { $this->orderItemFactory = $orderItemFactory; $this->orderItemQuantityModifier = $orderItemQuantityModifier; diff --git a/src/ReorderProcessing/ReorderPaymentProcessor.php b/src/ReorderProcessing/ReorderPaymentProcessor.php index 3db804fc0..aca978425 100644 --- a/src/ReorderProcessing/ReorderPaymentProcessor.php +++ b/src/ReorderProcessing/ReorderPaymentProcessor.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\ReorderProcessing; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\PaymentInterface; diff --git a/src/ReorderProcessing/ReorderProcessor.php b/src/ReorderProcessing/ReorderProcessor.php index 172f5685c..229064e39 100644 --- a/src/ReorderProcessing/ReorderProcessor.php +++ b/src/ReorderProcessing/ReorderProcessor.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\ReorderProcessing; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing; use Sylius\Component\Core\Model\OrderInterface; diff --git a/src/ReorderProcessing/ReorderShipmentProcessor.php b/src/ReorderProcessing/ReorderShipmentProcessor.php index 42935195c..906a7d93f 100644 --- a/src/ReorderProcessing/ReorderShipmentProcessor.php +++ b/src/ReorderProcessing/ReorderShipmentProcessor.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\ReorderProcessing; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\ReorderProcessing; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\ShipmentInterface; diff --git a/src/Resources/config/app/ajax.yml b/src/Resources/config/app/ajax.yml deleted file mode 100644 index 22c3e21cb..000000000 --- a/src/Resources/config/app/ajax.yml +++ /dev/null @@ -1,60 +0,0 @@ -sylius_admin_order_creation_ajax_customer: - path: /customer-search - methods: [GET] - defaults: - _controller: sylius.controller.customer:indexAction - _format: json - _sylius: - permission: true - repository: - method: findByEmailPart - arguments: $email - -sylius_admin_order_creation_ajax_customer_by_email: - path: /customer-search-by-email - methods: [GET] - defaults: - _controller: sylius.controller.customer:indexAction - _format: json - _sylius: - permission: true - repository: - method: findBy - arguments: [email: $email] - -sylius_admin_order_creation_ajax_product_variants_by_phrase_and_channel: - path: /product-variant-search/{channelCode} - methods: [GET] - defaults: - _controller: sylius.controller.product_variant:indexAction - _format: json - _sylius: - permission: true - serialization_groups: [Autocomplete] - repository: - method: findByPhraseAndChannelCode - arguments: - phrase: $phrase - channelCode: $channelCode - locale: expr:service('sylius.context.locale').getLocaleCode() - -sylius_admin_order_creation_ajax_product_variant_by_codes: - path: /product-variant-search-by-code - methods: [GET] - defaults: - _controller: sylius.controller.product_variant:indexAction - _format: json - _sylius: - permission: true - serialization_groups: [Autocomplete] - repository: - method: findOneByCode - arguments: $code - -sylius_admin_order_creation_ajax_provide_available_shipping_methods: - path: /admin/orders/available-shipping-methods/{customerId}/{channelCode}/{shipmentNumber} - methods: [GET] - defaults: - _controller: Sylius\AdminOrderCreationPlugin\Controller\ProvideAvailableShippingMethodsAction - options: - expose: true diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml deleted file mode 100644 index 5e3f1bce4..000000000 --- a/src/Resources/config/services.xml +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %sylius.model.order.class% - %sylius.form.type.order.validation_groups% - - - - %sylius.model.order_item.class% - - %sylius.form.type.order_item.validation_groups% - - - - %sylius.model.shipment.class% - %sylius.form.type.shipment.validation_groups% - - - - %sylius.model.payment.class% - %sylius.form.type.payment.validation_groups% - - - - %sylius.model.adjustment.class% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sylius_shop_order_after_pay - - - - - - - - - - - - - - diff --git a/src/Resources/views/Order/Create/_breadcrumb.html.twig b/src/Resources/views/Order/Create/_breadcrumb.html.twig deleted file mode 100644 index c2e087795..000000000 --- a/src/Resources/views/Order/Create/_breadcrumb.html.twig +++ /dev/null @@ -1,10 +0,0 @@ -{% import '@SyliusAdmin/Macro/breadcrumb.html.twig' as breadcrumb %} - -{% set breadcrumbs = [ - { label: 'sylius.ui.administration'|trans, url: path('sylius_admin_dashboard') }, - { label: 'sylius.ui.orders'|trans, url: path('sylius_admin_order_index') }, - { label: 'sylius.ui.new'|trans } -] -%} - -{{ breadcrumb.crumble(breadcrumbs) }} diff --git a/src/Resources/views/Order/Create/_form.html.twig b/src/Resources/views/Order/Create/_form.html.twig deleted file mode 100644 index 82515cbb9..000000000 --- a/src/Resources/views/Order/Create/_form.html.twig +++ /dev/null @@ -1,52 +0,0 @@ -
- {{ form_start(form, {'action': action, 'attr': {'class': 'ui loadable form', 'novalidate': 'novalidate'}}) }} - -
-
- {{ 'sylius.ui.items'|trans }} -
-
- {{ form_row(form.items) }} -
- -
- {{ 'sylius.ui.shipping_address'|trans }} & {{ 'sylius.ui.billing_address'|trans }} -
-
-
-
{{ form_row(form.shippingAddress) }}
-
{{ form_row(form.billingAddress) }}
-
-
- -
- {{ 'sylius.ui.locale'|trans }} & {{ 'sylius.ui.currency'|trans }} -
-
-
-
{{ form_row(form.localeCode) }}
-
{{ form_row(form.currencyCode) }}
-
-
- -
- {{ 'sylius.ui.shipments'|trans }} & {{ 'sylius.ui.payments'|trans }} -
-
-
-
-
{{ 'sylius_admin_order_creation.ui.shipping_methods_selection_requirement'|trans }}
- {{ form_row(form.shipments) }} -
-
- {{ form_row(form.payments) }} -
-
-
-
- - {% include '@SyliusUi/Form/Buttons/_create.html.twig' with {'paths': {'cancel': path('sylius_admin_order_index')}} %} - - {{ form_row(form._token) }} - {{ form_end(form, {'render_rest': false}) }} -
diff --git a/src/Resources/views/Order/Show/_item.html.twig b/src/Resources/views/Order/Show/_item.html.twig deleted file mode 100644 index b1e5cd819..000000000 --- a/src/Resources/views/Order/Show/_item.html.twig +++ /dev/null @@ -1,43 +0,0 @@ -{% import "@SyliusAdmin/Common/Macro/money.html.twig" as money %} - -{% set orderPromotionAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_PROMOTION_ADJUSTMENT') %} -{% set itemPromotionAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_ITEM_PROMOTION_ADJUSTMENT') %} -{% set orderItemDiscountAdjustment = constant('Sylius\\AdminOrderCreationPlugin\\Form\\Type\\AdjustmentType::ORDER_ITEM_DISCOUNT_ADJUSTMENT') %} -{% set shippingAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::SHIPPING_ADJUSTMENT') %} -{% set taxAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::TAX_ADJUSTMENT') %} - -{% set variant = item.variant %} -{% set product = variant.product %} - - - - {% include '@SyliusAdmin/Product/_info.html.twig' %} - - - {{ money.format(item.unitPrice, order.currencyCode) }} - - - {{ money.format(item.discountedUnitPrice, order.currencyCode) }} - - - {{ item.quantity }} - - - {{ money.format(item.subtotal, order.currencyCode) }} - - - {% set itemDiscountTotal = item.getAdjustmentsTotalRecursively(orderPromotionAdjustment) + item.getAdjustmentsTotalRecursively(orderItemDiscountAdjustment) %} - {{ money.format(itemDiscountTotal, order.currencyCode) }} - - - {{ money.format(item.taxTotal, order.currencyCode) }} - - - {{ money.format(item.total, order.currencyCode) }} - - - - - {{ form_row(form.adjustments) }} - - diff --git a/src/Resources/views/Order/Show/_summary.html.twig b/src/Resources/views/Order/Show/_summary.html.twig deleted file mode 100644 index cbcfcea4f..000000000 --- a/src/Resources/views/Order/Show/_summary.html.twig +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - {% for item_form in form.children %} - {% include '@SyliusAdmin/Order/Show/Summary/_item.html.twig' with {'item': order.items.get(loop.index0)} %} - - - - - {% endfor %} - - - {% include '@SyliusAdmin/Order/Show/Summary/_totals.html.twig' %} - -
{{ 'sylius.ui.order_item_product'|trans }}{{ 'sylius.ui.unit_price'|trans }}{{ 'sylius.ui.item_discount'|trans }}{{ 'sylius.ui.distributed_order_discount'|trans }}{{ 'sylius.ui.discounted_unit_price'|trans }}{{ 'sylius.ui.quantity'|trans }}{{ 'sylius.ui.subtotal'|trans }}{{ 'sylius.ui.tax'|trans }}{{ 'sylius.ui.total'|trans }}
- {{ form_row(item_form.adjustments) }} -
diff --git a/src/Resources/views/Order/create.html.twig b/src/Resources/views/Order/create.html.twig deleted file mode 100644 index 02275723c..000000000 --- a/src/Resources/views/Order/create.html.twig +++ /dev/null @@ -1,109 +0,0 @@ -{% extends '@SyliusAdmin/layout.html.twig' %} - -{% block title %}{{ 'sylius.ui.new_order'|trans }} {{ parent() }}{% endblock %} - -{% form_theme form '@SyliusAdminOrderCreationPlugin/Order/itemCollectionTheme.html.twig' %} - -{% if order is defined %} - {% set customer_id = order.customer.id %} - {% set channel_code = order.channel.code %} -{% else %} - {% set customer_id = app.request.attributes.get('customerId') %} - {% set channel_code = app.request.attributes.get('channelCode') %} -{% endif %} - -{% block content %} -
-
-

- -
{{ 'sylius.ui.new_order'|trans }}
-

- {% include '@SyliusAdminOrderCreationPlugin/Order/Create/_breadcrumb.html.twig' %} -
-
- {% include '@SyliusAdminOrderCreationPlugin/Order/Create/_form.html.twig' with { - 'action': path('sylius_admin_order_creation_order_preview', {'customerId': customer_id, 'channelCode': channel_code}) - } %} -{% endblock %} - -{% block javascripts %} - {{ parent() }} - - - -{% endblock %} diff --git a/src/Resources/views/Order/itemCollectionTheme.html.twig b/src/Resources/views/Order/itemCollectionTheme.html.twig deleted file mode 100644 index f2dc5e303..000000000 --- a/src/Resources/views/Order/itemCollectionTheme.html.twig +++ /dev/null @@ -1,15 +0,0 @@ -{% extends '@SyliusAdmin/Form/theme.html.twig' %} - -{% block _sylius_admin_order_creation_new_order_items_entry_widget %} - {% spaceless %} -
- {{ form_row(form.quantity, {'attr': {'class' : 'item-quantity'}}) }} - {{ form_row(form.variant, { - 'remote_url': path('sylius_admin_order_creation_ajax_product_variants_by_phrase_and_channel', {'channelCode': form.parent.parent.vars.value.channel.code}), - 'remote_criteria_type': 'contains', - 'remote_criteria_name': 'phrase', - 'load_edit_url': path('sylius_admin_order_creation_ajax_product_variant_by_codes') - }) }} -
- {% endspaceless %} -{% endblock %} diff --git a/src/Resources/views/Order/preview.html.twig b/src/Resources/views/Order/preview.html.twig deleted file mode 100644 index dc76941a3..000000000 --- a/src/Resources/views/Order/preview.html.twig +++ /dev/null @@ -1,175 +0,0 @@ -{% extends '@SyliusAdmin/layout.html.twig' %} - -{% import '@SyliusUi/Macro/flags.html.twig' as flags %} - -{% block title %}{{ 'sylius_admin_order_creation.ui.order_preview'|trans }}{% endblock %} - -{% form_theme form '@SyliusAdminOrderCreationPlugin/Order/itemCollectionTheme.html.twig' %} - -{% block content %} - {% set order = form.vars.data %} - - {{ form_start(form, { - 'attr': {'novalidate': 'novalidate', 'id': form.vars.id }, - 'method': 'POST' - }) }} -
-
-

- -
- {{ 'sylius_admin_order_creation.ui.order_preview'|trans }} -
-
-
- {{ order.currencyCode }} -
-
- {% include [('@SyliusAdmin/Order/Label/State' ~ '/' ~ order.state ~ '.html.twig'), '@SyliusUi/Label/_default.html.twig'] with {'value': ('sylius.ui.' ~ order.state)|trans} %} -
-
- {{ flags.fromLocaleCode(order.localeCode) }}{{ order.localeCode|locale_name }} -
-
- {{ 'sylius.ui.purchased_from'|trans }} - {{ order.channel }} -
-
-
-
-

-
-
-
-
- {{ form_errors(form) }} -
-
-
-
-
- {% include '@SyliusAdminOrderCreationPlugin/Order/Show/_summary.html.twig' with {'form': form.items} %} -
-
-
-
-
- {{ order.customer.fullName }} -
- {{ 'sylius.ui.customer_since'|trans }} {{ order.customer.createdAt|format_date }}. -
-
- - {% if order.customer.phoneNumber is not empty %} -
- - - {{ order.customer.phoneNumber }} - -
- {% endif %} - {% if order.customerIp is defined and order.customerIp is not empty %} -
- - - {{ order.customerIp }} - -
- {% endif %} -
-

- {{ 'sylius.ui.shipping_address'|trans }} -

-
- {% include '@SyliusAdmin/Common/_address.html.twig' with {'address': order.shippingAddress} %} -
-

- {{ 'sylius.ui.billing_address'|trans }} -

-
- {% include '@SyliusAdmin/Common/_address.html.twig' with {'address': order.billingAddress} %} -
-
-
-
-
-
-
- {{ form_row(form.promotionCoupon) }} - {{ form_row(form.shippingAddress) }} - {{ form_row(form.billingAddress) }} - {{ form_row(form.payments) }} - {{ form_row(form.shipments) }} -
- {{ form_row(form.adjustments) }} -
-
-
-
-
-
- {% if order.hasPayments %} -

{{ 'sylius.ui.payments'|trans }}

-
- {% for payment in order.payments %} - {% include '@SyliusAdmin/Order/Show/_payment.html.twig' %} - {% endfor %} -
- {% endif %} -
-
-
-
- {% if order.hasShipments %} -

{{ 'sylius.ui.shipments'|trans }}

-
- {% for shipment in order.shipments %} - {% include '@SyliusAdmin/Order/Show/_shipment.html.twig' %} - {% endfor %} -
- {% endif %} -
-
-
-
- {{ form_rest(form) }} -
- {{ form_end(form) }} - -
- -
- - -
-{% endblock %} - -{% block javascripts %} - {{ parent() }} - -{% endblock %} diff --git a/src/Resources/views/Order/selectCustomer.html.twig b/src/Resources/views/Order/selectCustomer.html.twig deleted file mode 100644 index 3cda40f3a..000000000 --- a/src/Resources/views/Order/selectCustomer.html.twig +++ /dev/null @@ -1,107 +0,0 @@ -{% extends '@SyliusAdmin/layout.html.twig' %} - -{% form_theme selectCustomerForm '@SyliusAdminOrderCreationPlugin/Order/selectCustomerTheme.html.twig' %} -{% form_theme createCustomerForm '@SyliusAdmin/Form/theme.html.twig' %} - -{% block pre_content %} -

- -
- {{ 'sylius.ui.new_order'|trans }} -
{{ 'sylius_admin_order_creation.ui.customer_selection'|trans }}
-
-

-{% endblock %} - -{% block content %} -
- {{ form_start(selectCustomerForm, {'method': 'GET'})}} - {{ form_row(selectCustomerForm.customer) }}

- {{ form_row(selectCustomerForm.channel, {'attr': {'class': 'ui fluid selection dropdown'}}) }}

- - {{ form_end(selectCustomerForm) }} -
-
- {{ 'sylius.ui.or'|trans }} -
- -
- {{ form_start(createCustomerForm, {'method': 'GET'})}} -
- {{ form_widget(createCustomerForm.customerEmail, {'attr': {'placeholder': 'sylius_admin_order_creation.ui.new_customer_email'|trans}}) }} -


- {{ form_row(createCustomerForm.channel, {'attr': {'class': 'ui fluid selection dropdown'}}) }}

-

- {{ form_end(createCustomerForm) }} -
-{% endblock %} - -{% block javascripts %} - {{ parent() }} - - - - -{% endblock %} diff --git a/src/Resources/views/Order/selectCustomerTheme.html.twig b/src/Resources/views/Order/selectCustomerTheme.html.twig deleted file mode 100644 index 44b7b6d30..000000000 --- a/src/Resources/views/Order/selectCustomerTheme.html.twig +++ /dev/null @@ -1,8 +0,0 @@ -{% extends '@SyliusAdmin/Form/theme.html.twig' %} - -{% block sylius_customer_autocomplete_choice_row %} - {{ form_row(form, { - 'remote_url': path('sylius_admin_order_creation_ajax_customer'), - 'load_edit_url': path('sylius_admin_order_creation_ajax_customer_by_email') - }) }} -{% endblock %} diff --git a/src/Resources/views/SyliusAdminBundle/Order/Show/Summary/_item.html.twig b/src/Resources/views/SyliusAdminBundle/Order/Show/Summary/_item.html.twig deleted file mode 100644 index fe177a554..000000000 --- a/src/Resources/views/SyliusAdminBundle/Order/Show/Summary/_item.html.twig +++ /dev/null @@ -1,52 +0,0 @@ -{% import "@SyliusAdmin/Common/Macro/money.html.twig" as money %} - -{% set orderPromotionAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_PROMOTION_ADJUSTMENT') %} -{% set unitPromotionAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_UNIT_PROMOTION_ADJUSTMENT') %} -{% set shippingAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::SHIPPING_ADJUSTMENT') %} -{% set taxAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::TAX_ADJUSTMENT') %} -{% set orderItemDiscountAdjustment = constant('Sylius\\AdminOrderCreationPlugin\\Form\\Type\\AdjustmentType::ORDER_ITEM_DISCOUNT_ADJUSTMENT') %} - -{% set variant = item.variant %} -{% set product = variant.product %} - -{% set unitDiscount = item.units.first.adjustmentsTotal(unitPromotionAdjustment) + item.getAdjustmentsTotalRecursively(orderItemDiscountAdjustment) / item.quantity %} -{% set discountedUnitPrice = item.fullDiscountedUnitPrice + item.getAdjustmentsTotalRecursively(orderItemDiscountAdjustment) / item.quantity %} -{% set subtotal = item.quantity * (item.unitPrice + item.units.first.adjustmentsTotal(unitPromotionAdjustment) + item.units.first.adjustmentsTotal(orderPromotionAdjustment)) + item.getAdjustmentsTotalRecursively(orderItemDiscountAdjustment) %} - -{% set taxIncluded = sylius_admin_order_unit_tax_included(item) %} -{% set taxExcluded = sylius_admin_order_unit_tax_excluded(item) %} - - - - {% include '@SyliusAdmin/Product/_info.html.twig' %} - - - {{ money.format(item.unitPrice, order.currencyCode) }} - - - {{ money.format(unitDiscount, order.currencyCode) }} - - - {{ money.format(item.units.first.adjustmentsTotal(orderPromotionAdjustment), order.currencyCode) }} - - - {{ money.format(discountedUnitPrice, order.currencyCode) }} - - - {{ item.quantity }} - - - {{ money.format(subtotal, order.currencyCode) }} - - -
{{ money.format(taxExcluded, order.currencyCode) }}
-
-
{{ money.format(taxIncluded, order.currencyCode) }} -
- ({{ 'sylius.ui.included_in_price'|trans }}) -
- - - {{ money.format(item.total, order.currencyCode) }} - - diff --git a/src/Resources/views/SyliusAdminBundle/Order/Show/Summary/_totals.html.twig b/src/Resources/views/SyliusAdminBundle/Order/Show/Summary/_totals.html.twig deleted file mode 100644 index ab2a43d97..000000000 --- a/src/Resources/views/SyliusAdminBundle/Order/Show/Summary/_totals.html.twig +++ /dev/null @@ -1,73 +0,0 @@ -{% import "@SyliusAdmin/Common/Macro/money.html.twig" as money %} - -{% set orderPromotionAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_PROMOTION_ADJUSTMENT') %} -{% set orderShippingPromotionAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_SHIPPING_PROMOTION_ADJUSTMENT') %} -{% set itemPromotionAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_ITEM_PROMOTION_ADJUSTMENT') %} -{% set shippingAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::SHIPPING_ADJUSTMENT') %} -{% set taxAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::TAX_ADJUSTMENT') %} -{% set orderDiscountAdjustment = constant('Sylius\\AdminOrderCreationPlugin\\Form\\Type\\AdjustmentType::ORDER_DISCOUNT_ADJUSTMENT') %} - -{% set orderShippingPromotions = sylius_aggregate_adjustments(order.getAdjustmentsRecursively(orderShippingPromotionAdjustment)) %} - - - - - {{ 'sylius.ui.tax_total'|trans }}: - {{ money.format(order.taxTotal, order.currencyCode) }} - - - {{ 'sylius.ui.items_total'|trans }}: - {{ money.format(order.itemsTotal, order.currencyCode) }} - - - - - {% if not order.adjustments(shippingAdjustment).isEmpty() %} -
-
{{ 'sylius.ui.shipping'|trans }}:
- {% for adjustment in order.adjustments(shippingAdjustment) %} -
-
{{ money.format(adjustment.amount, order.currencyCode) }}
-
-
- {{ adjustment.label }}: -
-
-
- {% endfor %} -
- {% else %} -

{{ 'sylius.ui.no_shipping_charges'|trans }}

- {% endif %} - - {% if not orderShippingPromotions is empty %} - -
-
{{ 'sylius.ui.shipping_discount'|trans }}:
- {% for label, amount in orderShippingPromotions %} -
-
- {{ money.format(amount, order.currencyCode) }} -
-
- {% endfor %} -
- - {% endif %} - - {{ 'sylius.ui.shipping_total'|trans }}: - {{ money.format(order.shippingTotal, order.currencyCode) }} - - - - - {{ 'sylius_admin_order_creation.ui.order_discount'|trans }}: - {{ money.format(order.getAdjustmentsTotalRecursively(orderDiscountAdjustment), order.currencyCode) }} - - - - - {{ 'sylius.ui.order_total'|trans }}: - {{ money.format(order.total, order.currencyCode) }} - - diff --git a/src/Resources/views/SyliusAdminBundle/Order/Show/_payment.html.twig b/src/Resources/views/SyliusAdminBundle/Order/Show/_payment.html.twig deleted file mode 100644 index 72e0eaa99..000000000 --- a/src/Resources/views/SyliusAdminBundle/Order/Show/_payment.html.twig +++ /dev/null @@ -1,44 +0,0 @@ -{% import "@SyliusAdmin/Common/Macro/money.html.twig" as money %} -{% import '@SyliusUi/Macro/labels.html.twig' as label %} - -
-
- {% include '@SyliusAdmin/Common/Label/paymentState.html.twig' with {'data': payment.state} %} -
- -
-
- {{ payment.method }} -
-
- {{ money.format(payment.amount, payment.order.currencyCode) }} -
-
- {% if sm_can(payment, 'complete', 'sylius_payment') %} -
-
- - - -
-
- {% if payment.details['payment-link'] is defined %} - - {% endif %} - {% endif %} - {% if sm_can(payment, 'refund', 'sylius_payment') %} -
-
- - - -
-
- {% endif %} -
diff --git a/src/Sender/OrderPaymentLinkSender.php b/src/Sender/OrderPaymentLinkSender.php index 51d1c63b0..f7459c022 100644 --- a/src/Sender/OrderPaymentLinkSender.php +++ b/src/Sender/OrderPaymentLinkSender.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Sender; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Sender; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\PaymentInterface; @@ -39,7 +39,7 @@ public function sendPaymentLink(OrderInterface $order): void [ 'order' => $order, 'paymentLink' => $paymentDetails['payment-link'], - ] + ], ) ; } diff --git a/src/Sender/OrderPaymentLinkSenderInterface.php b/src/Sender/OrderPaymentLinkSenderInterface.php index 359e9c109..680b7002f 100644 --- a/src/Sender/OrderPaymentLinkSenderInterface.php +++ b/src/Sender/OrderPaymentLinkSenderInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\AdminOrderCreationPlugin\Sender; +namespace Webgriffe\SyliusAdminOrderCreationPlugin\Sender; use Sylius\Component\Core\Model\OrderInterface; diff --git a/src/SyliusAdminOrderCreationPlugin.php b/src/SyliusAdminOrderCreationPlugin.php deleted file mode 100644 index 48973d09f..000000000 --- a/src/SyliusAdminOrderCreationPlugin.php +++ /dev/null @@ -1,20 +0,0 @@ -addCompilerPass(new RegisterReorderProcessorsPass()); - } -} diff --git a/src/Twig/Component/OrderFormComponent.php b/src/Twig/Component/OrderFormComponent.php new file mode 100644 index 000000000..163799f1c --- /dev/null +++ b/src/Twig/Component/OrderFormComponent.php @@ -0,0 +1,124 @@ +formFactory->create(NewOrderType::class, $this->createOrder(), [ + 'shipmentChoicesSubject' => $this->computeShipmentChoicesSubject(), + ]); + } + + /** + * Live Component actions (add/remove an item, a discount, a shipment...) only re-submit the + * raw form values onto a freshly instantiated order - nothing in Live Component itself + * recalculates derived data (unit prices, shipping cost, shipment/unit associations). Without + * this, every such action would render the order as if it had just been created empty: all + * prices at $0, shipments dropped, etc. This runs after Live Component's own form submission + * (which happens at the default #[PreReRender] priority of 0), never on the initial render. + */ + #[PreReRender(priority: -10)] + public function reprocessOrder(): void + { + $order = $this->getForm()->getData(); + + if (!$order instanceof OrderInterface) { + return; + } + + try { + $this->orderProcessor->process($order); + } catch (\Throwable) { + // Items freshly added on the create page have no variant selected yet, which the + // pricing processor can't handle - leave the order as submitted and let it settle + // once the user picks a variant and this hook runs again. + } + } + + private function createOrder(): OrderInterface + { + return $this->orderFactory->createForCustomerAndChannel($this->customerId, $this->channelCode); + } + + /** + * Builds a throwaway order from the current (live, not-yet-final) form values so that the + * shipment's "method" field can be restricted to the shipping methods actually eligible for + * the items/address entered so far, mirroring Sylius' own checkout behaviour. + * + * The eligibility restriction is only ever meant to guide a *new* selection. Never let it + * invalidate a method the admin already picked: zone matching on this throwaway, not-yet-fully + * submitted order can be momentarily narrower than on the final order (e.g. while the address + * form is only partially filled in across requests), and passing a subject whose eligible-method + * list excludes the already-chosen method makes Symfony's ChoiceType treat that submitted value + * as invalid - which silently drops the entire shipment from the collection instead of just + * rejecting the method field. + */ + private function computeShipmentChoicesSubject(): ?ShipmentInterface + { + if ($this->formValues === []) { + return null; + } + + try { + $order = $this->createOrder(); + $this->formFactory->create(NewOrderType::class, $order)->submit($this->formValues); + $this->orderProcessor->process($order); + } catch (\Throwable) { + return null; + } + + $shipment = $order->getShipments()->first(); + + if (!$shipment instanceof ShipmentInterface) { + return null; + } + + if ( + !$this->shippingMethodsResolver->supports($shipment) || + !in_array($shipment->getMethod(), $this->shippingMethodsResolver->getSupportedMethods($shipment), true) + ) { + return null; + } + + return $shipment; + } +} diff --git a/src/Twig/Component/OrderPreviewFormComponent.php b/src/Twig/Component/OrderPreviewFormComponent.php new file mode 100644 index 000000000..7839d8806 --- /dev/null +++ b/src/Twig/Component/OrderPreviewFormComponent.php @@ -0,0 +1,15 @@ +addCompilerPass(new RegisterReorderProcessorsPass()); + } + + #[\Override] + public function getContainerExtension(): ?ExtensionInterface + { + return new SyliusAdminOrderCreationExtension(); + } +} diff --git a/src/Resources/views/Emails/orderCreated.html.twig b/templates/emails/order_created.html.twig similarity index 100% rename from src/Resources/views/Emails/orderCreated.html.twig rename to templates/emails/order_created.html.twig diff --git a/templates/order/create.html.twig b/templates/order/create.html.twig new file mode 100644 index 000000000..396a684fc --- /dev/null +++ b/templates/order/create.html.twig @@ -0,0 +1,49 @@ +{% extends '@SyliusAdmin/shared/layout/base.html.twig' %} + +{% from '@SyliusAdmin/shared/helper/breadcrumbs.html.twig' import breadcrumbs %} + +{% if order is defined %} + {% set customer_id = order.customer.id %} + {% set channel_code = order.channel.code %} +{% else %} + {% set customer_id = app.request.attributes.get('customerId') %} + {% set channel_code = app.request.attributes.get('channelCode') %} +{% endif %} + +{% block title %}{{ 'sylius.ui.new_order'|trans }} {{ parent() }}{% endblock %} + +{% block body %} + {% include '@SyliusAdmin/shared/crud/common/sidebar.html.twig' %} + {% include '@SyliusAdmin/shared/crud/common/navbar.html.twig' %} + +
+ {% include '@SyliusAdmin/shared/crud/common/content/flashes.html.twig' %} + + + +
+
+ {% hook 'sylius_admin_order_creation.order.create.content' with { + customer_id: customer_id, + channel_code: channel_code, + form: form, + } %} +
+
+ + {% include '@SyliusAdmin/shared/crud/common/content/footer.html.twig' %} +
+{% endblock %} diff --git a/templates/order/create/_order_form_component.html.twig b/templates/order/create/_order_form_component.html.twig new file mode 100644 index 000000000..1e963194d --- /dev/null +++ b/templates/order/create/_order_form_component.html.twig @@ -0,0 +1,129 @@ +{% form_theme form '@WebgriffeSyliusAdminOrderCreationPlugin/order/item_collection_theme.html.twig' %} + +{% macro address_fields(address) %} +
{{ form_row(address.firstName) }}
+
{{ form_row(address.lastName) }}
+
{{ form_row(address.phoneNumber) }}
+
{{ form_row(address.company) }}
+
{{ form_row(address.countryCode) }}
+
+ {% if address.provinceCode is defined %}{{ form_row(address.provinceCode) }}{% endif %} + {% if address.provinceName is defined %}{{ form_row(address.provinceName) }}{% endif %} +
+
{{ form_row(address.street) }}
+
{{ form_row(address.city) }}
+
{{ form_row(address.postcode) }}
+{% endmacro %} + +
+ {{ form_start(form, { + 'action': path('sylius_admin_order_creation_order_preview', {'customerId': customerId, 'channelCode': channelCode}), + 'attr': {'novalidate': 'novalidate'}, + }) }} + {{ form_errors(form) }} + + + +
+
+
+
+
+ {{ form_row(form.items, {'label': false, 'skip_add_button': true}) }} +
+ {{ form_row(form.items.vars.button_add, {'label': 'sylius_admin_order_creation.ui.add_item'|trans, 'attr': {'class': 'btn btn-outline-primary'}}) }} +
+
+
+ +
+
+
+
+
{{ 'sylius.ui.shipping_address'|trans }}
+
+ {{ _self.address_fields(form.shippingAddress) }} +
+
+
+
+
+
{{ 'sylius.ui.billing_address'|trans }}
+
+ {{ _self.address_fields(form.billingAddress) }} +
+
+
+
+
+ +
+
+
+
{{ form_row(form.localeCode) }}
+
{{ form_row(form.currencyCode) }}
+
+
+
+ +
+
+
+
+
{{ 'sylius.ui.shipments'|trans }}
+
+
{{ 'sylius_admin_order_creation.ui.shipping_methods_selection_requirement'|trans }}
+ {{ form_row(form.shipments, {'label': false, 'skip_add_button': true}) }} + {{ form_row(form.shipments.vars.button_add, {'label': 'sylius_admin_order_creation.ui.add_shipment'|trans, 'attr': {'class': 'btn btn-outline-primary'}}) }} +
+
+
+
+
+
{{ 'sylius.ui.payments'|trans }}
+
+ {{ form_row(form.payments, {'label': false, 'skip_add_button': true}) }} + {% if form.payments|length == 0 %} + {{ form_row(form.payments.vars.button_add, {'label': 'sylius_admin_order_creation.ui.add_payment'|trans, 'attr': {'class': 'btn btn-outline-primary'}}) }} + {% endif %} +
+
+
+
+
+ +
+
+
+ {{ form_row(form.adjustments, {'label': false, 'skip_add_button': true}) }} + {{ form_row(form.adjustments.vars.button_add, {'attr': {'class': 'btn btn-outline-primary'}}) }} +
+
+
+
+ + {{ form_row(form.promotionCoupon) }} + +
+ {{ 'sylius.ui.cancel'|trans }} + +
+ + {{ form_end(form, {'render_rest': false}) }} +
diff --git a/templates/order/create/sections/form.html.twig b/templates/order/create/sections/form.html.twig new file mode 100644 index 000000000..e30eb097d --- /dev/null +++ b/templates/order/create/sections/form.html.twig @@ -0,0 +1,5 @@ +{{ component('webgriffe_sylius_admin_order_creation:order_form', { + customerId: hookable_metadata.context.customer_id, + channelCode: hookable_metadata.context.channel_code, + form: hookable_metadata.context.form, +}) }} diff --git a/templates/order/item_collection_theme.html.twig b/templates/order/item_collection_theme.html.twig new file mode 100644 index 000000000..b01347473 --- /dev/null +++ b/templates/order/item_collection_theme.html.twig @@ -0,0 +1,45 @@ +{% extends '@SyliusAdmin/shared/form_theme.html.twig' %} + +{% block _sylius_admin_order_creation_new_order_items_entry_row %} +
+
+
{{ form_row(form.variant) }}
+
{{ form_row(form.quantity) }}
+
{{ form_widget(button_delete, {'label': 'sylius.ui.delete'|trans, 'attr': {'class': 'btn btn-outline-danger'}}) }}
+
+
+
+ {{ form_row(form.adjustments, {'label': false, 'skip_add_button': true}) }} + {{ form_row(form.adjustments.vars.button_add, {'attr': {'class': 'btn btn-outline-primary'}}) }} +
+
+
+{% endblock %} + +{% block _sylius_admin_order_creation_new_order_order_item_adjustments_entry_row %} +
+
{{ form_row(form.amount) }}
+ {{ form_widget(button_delete, {'label': 'sylius.ui.delete'|trans, 'attr': {'class': 'btn btn-outline-danger'}}) }} +
+{% endblock %} + +{% block _sylius_admin_order_creation_new_order_adjustments_entry_row %} +
+
{{ form_row(form.amount) }}
+ {{ form_widget(button_delete, {'label': 'sylius.ui.delete'|trans, 'attr': {'class': 'btn btn-outline-danger'}}) }} +
+{% endblock %} + +{% block _sylius_admin_order_creation_new_order_shipments_entry_row %} +
+
{{ form_row(form.method) }}
+ {{ form_widget(button_delete, {'label': 'sylius.ui.delete'|trans, 'attr': {'class': 'btn btn-outline-danger'}}) }} +
+{% endblock %} + +{% block _sylius_admin_order_creation_new_order_payments_entry_row %} +
+
{{ form_row(form.method) }}
+ {{ form_widget(button_delete, {'label': 'sylius.ui.delete'|trans, 'attr': {'class': 'btn btn-outline-danger'}}) }} +
+{% endblock %} diff --git a/templates/order/preview.html.twig b/templates/order/preview.html.twig new file mode 100644 index 000000000..c41d8e549 --- /dev/null +++ b/templates/order/preview.html.twig @@ -0,0 +1,51 @@ +{% extends '@SyliusAdmin/shared/layout/base.html.twig' %} + +{% from '@SyliusAdmin/shared/helper/breadcrumbs.html.twig' import breadcrumbs %} +{% from '@SyliusAdmin/order/macro/order_state_label.html.twig' import label as orderStateLabel %} + +{% set order = form.vars.data %} +{% set customerId = app.request.attributes.get('customerId') %} +{% set channelCode = app.request.attributes.get('channelCode') %} + +{% block title %}{{ 'sylius_admin_order_creation.ui.order_preview'|trans }} {{ parent() }}{% endblock %} + +{% block body %} + {% include '@SyliusAdmin/shared/crud/common/sidebar.html.twig' %} + {% include '@SyliusAdmin/shared/crud/common/navbar.html.twig' %} + +
+ {% include '@SyliusAdmin/shared/crud/common/content/flashes.html.twig' %} + + + +
+
+ {% hook 'sylius_admin_order_creation.order.preview.content' with { + customer_id: customerId, + channel_code: channelCode, + form: form, + } %} +
+
+ + {% include '@SyliusAdmin/shared/crud/common/content/footer.html.twig' %} +
+{% endblock %} diff --git a/templates/order/preview/_order_preview_form_component.html.twig b/templates/order/preview/_order_preview_form_component.html.twig new file mode 100644 index 000000000..506c9f7dc --- /dev/null +++ b/templates/order/preview/_order_preview_form_component.html.twig @@ -0,0 +1,190 @@ +{% from '@SyliusAdmin/shared/helper/address.html.twig' import address %} +{% from '@SyliusAdmin/order/macro/order_payment_state_label.html.twig' import label as paymentStateLabel %} +{% from '@SyliusAdmin/order/macro/order_shipping_state_label.html.twig' import label as shippingStateLabel %} + +{% form_theme form + '@SyliusAdmin/shared/form_theme.html.twig' + '@WebgriffeSyliusAdminOrderCreationPlugin/order/item_collection_theme.html.twig' + '@WebgriffeSyliusAdminOrderCreationPlugin/order/preview/item_collection_theme.html.twig' +%} + +{% set order = form.vars.data %} + +{% macro nested_errors(field) %} + {{ form_errors(field) }} + {% for child in field %} + {{ _self.nested_errors(child) }} + {% endfor %} +{% endmacro %} + +
+ {{ form_start(form, {'attr': {'novalidate': 'novalidate', 'id': form.vars.id}}) }} + + {{ form_errors(form) }} + {{ _self.nested_errors(form.promotionCoupon) }} + {{ _self.nested_errors(form.shippingAddress) }} + {{ _self.nested_errors(form.billingAddress) }} + {{ _self.nested_errors(form.payments) }} + {{ _self.nested_errors(form.shipments) }} + {{ _self.nested_errors(form.localeCode) }} + {{ _self.nested_errors(form.currencyCode) }} + +
+
+
+
{{ 'sylius.ui.items'|trans }}
+
+ + + + + + + + + + + + {{ form_row(form.items, {'label': false, 'skip_add_button': true}) }} + + + + + + + + + + + +
{{ 'sylius.ui.order_item_product'|trans }}{{ 'sylius.ui.unit_price'|trans }}{{ 'sylius.ui.item_discount'|trans }}{{ 'sylius.ui.quantity'|trans }}{{ 'sylius.ui.total'|trans }}
{{ 'sylius.ui.shipping_total'|trans }}{{ order.shippingTotal|sylius_format_money(order.currencyCode) }}
{{ 'sylius.ui.total'|trans }}{{ order.total|sylius_format_money(order.currencyCode) }}
+
+
+ +
+
{{ 'sylius_admin_order_creation.ui.order_discount'|trans }}
+
+ {{ form_row(form.adjustments, {'label': false, 'skip_add_button': true}) }} + {{ form_row(form.adjustments.vars.button_add, { + 'label': 'sylius_admin_order_creation.ui.add_discount'|trans, + 'attr': {'class': 'btn btn-outline-primary'}, + }) }} +
+
+ +
+
+
+
{{ 'sylius.ui.payments'|trans }}
+
+ {% if order.hasPayments %} +
+ + + {% for payment in order.payments %} + + + + + + {% endfor %} + +
{{ payment.method.name }}{{ paymentStateLabel(payment.state) }}{{ payment.amount|sylius_format_money(order.currencyCode) }}
+
+ {% else %} + {{ 'sylius.ui.no_payments'|trans }} + {% endif %} +
+ {{ form_widget(form.sendPaymentLinkEmail, { + 'attr': {'class': 'form-check-input'}, + }) }} + {{ form_label(form.sendPaymentLinkEmail, null, {'label_attr': {'class': 'form-check-label'}}) }} +
+
+
+
+
+
+
{{ 'sylius.ui.shipments'|trans }}
+
+ {% if order.hasShipments %} +
+ + + {% for shipment in order.shipments %} + + + + + {% endfor %} + +
+ {{ shipment.method.name }} +
{{ ux_icon('tabler:world') }} {{ shipment.method.zone }}
+
{{ shippingStateLabel(shipment.state) }}
+
+ {% else %} + {{ 'sylius.ui.there_are_no_shipments_to_display'|trans }} + {% endif %} +
+
+
+
+
+ +
+
+
{{ 'sylius.ui.customer'|trans }}
+
+
{{ 'sylius.ui.name'|trans }}:
+
{{ order.customer.fullName }}
+
{{ 'sylius.ui.email'|trans }}:
+ + {% if order.customer.phoneNumber is not empty %} +
{{ 'sylius.form.address.phone_number'|trans }}:
+
{{ order.customer.phoneNumber }}
+ {% endif %} +
+
+ +
+
{{ 'sylius.ui.shipping_address'|trans }}
+
+ {% if order.shippingAddress is not null %}{{ address(order.shippingAddress) }}{% endif %} +
+
+ +
+
{{ 'sylius.ui.billing_address'|trans }}
+
+ {% if order.billingAddress is not null %}{{ address(order.billingAddress) }}{% endif %} +
+
+
+
+ +
+ {{ form_rest(form) }} +
+ + {{ form_end(form, {'render_rest': false}) }} + +
+ + +
+
diff --git a/templates/order/preview/item_collection_theme.html.twig b/templates/order/preview/item_collection_theme.html.twig new file mode 100644 index 000000000..222d2fdbe --- /dev/null +++ b/templates/order/preview/item_collection_theme.html.twig @@ -0,0 +1,42 @@ +{% block _sylius_admin_order_creation_new_order_items_entry_row %} + {% set item = form.vars.data %} + {% set variant = item.variant %} + {% set product = variant.product %} + + + +
+
+ {% if product.imagesByType('thumbnail') is not empty %} + {{ product.name }} + {% elseif product.images.first %} + {{ product.name }} + {% else %} + {{ product.name }} + {% endif %} +
+
+
{{ item.productName }}
+
{{ variant.code }}
+
{{ item.variantName }}
+
+
+ + {{ item.unitPrice|sylius_format_money(item.order.currencyCode) }} + + {{ form_row(form.adjustments, {'label': false, 'skip_add_button': true}) }} + {{ form_row(form.adjustments.vars.button_add, { + 'label': 'sylius_admin_order_creation.ui.add_discount'|trans, + 'attr': {'class': 'btn btn-sm btn-outline-primary'}, + }) }} + + {{ item.quantity }} + {{ item.total|sylius_format_money(item.order.currencyCode) }} + + + + {{ form_widget(form.variant) }} + {{ form_widget(form.quantity) }} + + +{% endblock %} diff --git a/templates/order/preview/sections/form.html.twig b/templates/order/preview/sections/form.html.twig new file mode 100644 index 000000000..4ae209564 --- /dev/null +++ b/templates/order/preview/sections/form.html.twig @@ -0,0 +1,5 @@ +{{ component('webgriffe_sylius_admin_order_creation:order_preview_form', { + customerId: hookable_metadata.context.customer_id, + channelCode: hookable_metadata.context.channel_code, + form: hookable_metadata.context.form, +}) }} diff --git a/templates/order/select_customer.html.twig b/templates/order/select_customer.html.twig new file mode 100644 index 000000000..80def4b74 --- /dev/null +++ b/templates/order/select_customer.html.twig @@ -0,0 +1,43 @@ +{% extends '@SyliusAdmin/shared/layout/base.html.twig' %} + +{% from '@SyliusAdmin/shared/helper/breadcrumbs.html.twig' import breadcrumbs %} + +{% block title %}{{ 'sylius.ui.new_order'|trans }} {{ parent() }}{% endblock %} + +{% block body %} + {% include '@SyliusAdmin/shared/crud/common/sidebar.html.twig' %} + {% include '@SyliusAdmin/shared/crud/common/navbar.html.twig' %} + +
+ {% include '@SyliusAdmin/shared/crud/common/content/flashes.html.twig' %} + + + +
+
+
+ {% hook 'sylius_admin_order_creation.order.select_customer.content' with { + select_customer_form: selectCustomerForm, + create_customer_form: createCustomerForm, + } %} +
+
+
+ + {% include '@SyliusAdmin/shared/crud/common/content/footer.html.twig' %} +
+{% endblock %} diff --git a/templates/order/select_customer/sections/existing_customer.html.twig b/templates/order/select_customer/sections/existing_customer.html.twig new file mode 100644 index 000000000..8e7727858 --- /dev/null +++ b/templates/order/select_customer/sections/existing_customer.html.twig @@ -0,0 +1,19 @@ +{% set selectCustomerForm = hookable_metadata.context.select_customer_form %} +{% form_theme selectCustomerForm '@SyliusAdmin/shared/form_theme.html.twig' %} + +
+
+
+
{{ 'sylius_admin_order_creation.ui.existing_customer'|trans }}
+
+
+ {{ form_start(selectCustomerForm, {'method': 'GET'}) }} + {{ form_row(selectCustomerForm.customer) }} + {{ form_row(selectCustomerForm.channel) }} + + {{ form_end(selectCustomerForm) }} +
+
+
diff --git a/templates/order/select_customer/sections/new_customer.html.twig b/templates/order/select_customer/sections/new_customer.html.twig new file mode 100644 index 000000000..7cae7a0e9 --- /dev/null +++ b/templates/order/select_customer/sections/new_customer.html.twig @@ -0,0 +1,19 @@ +{% set createCustomerForm = hookable_metadata.context.create_customer_form %} +{% form_theme createCustomerForm '@SyliusAdmin/shared/form_theme.html.twig' %} + +
+
+
+
{{ 'sylius_admin_order_creation.ui.create_new'|trans }}
+
+
+ {{ form_start(createCustomerForm, {'method': 'GET'}) }} + {{ form_row(createCustomerForm.customerEmail) }} + {{ form_row(createCustomerForm.channel) }} + + {{ form_end(createCustomerForm) }} +
+
+
diff --git a/templates/order/show/sections/items/body/discounted_unit_price.html.twig b/templates/order/show/sections/items/body/discounted_unit_price.html.twig new file mode 100644 index 000000000..cf2ae8929 --- /dev/null +++ b/templates/order/show/sections/items/body/discounted_unit_price.html.twig @@ -0,0 +1,17 @@ +{% import '@SyliusAdmin/shared/helper/money.html.twig' as money %} + +{% set unit_promotion_adjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_UNIT_PROMOTION_ADJUSTMENT') %} +{% set order_promotion_adjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_PROMOTION_ADJUSTMENT') %} +{% set item_discount_adjustment = constant('Webgriffe\\SyliusAdminOrderCreationPlugin\\Form\\Type\\AdjustmentType::ORDER_ITEM_DISCOUNT_ADJUSTMENT') %} + +{% set order = hookable_metadata.context.resource %} +{% set item = hookable_metadata.context.item %} +{% set discountedUnitPrice = item.unitPrice + + item.units.first.getAdjustmentsTotal(unit_promotion_adjustment) + + item.units.first.getAdjustmentsTotal(order_promotion_adjustment) + + (item.getAdjustmentsTotal(item_discount_adjustment) // item.quantity) +%} + + + {{ money.format(discountedUnitPrice, order.currencyCode) }} + diff --git a/templates/order/show/sections/items/body/subtotal.html.twig b/templates/order/show/sections/items/body/subtotal.html.twig new file mode 100644 index 000000000..c4c70ea44 --- /dev/null +++ b/templates/order/show/sections/items/body/subtotal.html.twig @@ -0,0 +1,17 @@ +{% import '@SyliusAdmin/shared/helper/money.html.twig' as money %} + +{% set order_promotion_adjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_PROMOTION_ADJUSTMENT') %} +{% set unit_promotion_adjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_UNIT_PROMOTION_ADJUSTMENT') %} +{% set item_discount_adjustment = constant('Webgriffe\\SyliusAdminOrderCreationPlugin\\Form\\Type\\AdjustmentType::ORDER_ITEM_DISCOUNT_ADJUSTMENT') %} + +{% set order = hookable_metadata.context.resource %} +{% set item = hookable_metadata.context.item %} +{% set aggregated_unit_promotion_adjustments = item.getAdjustmentsTotalRecursively(unit_promotion_adjustment) + + item.getAdjustmentsTotalRecursively(order_promotion_adjustment) + + item.getAdjustmentsTotal(item_discount_adjustment) +%} +{% set subtotal = (item.unitPrice * item.quantity) + aggregated_unit_promotion_adjustments %} + + + {{ money.format(subtotal, order.currencyCode) }} + diff --git a/templates/order/show/sections/items/body/unit_discount.html.twig b/templates/order/show/sections/items/body/unit_discount.html.twig new file mode 100644 index 000000000..8e2f55cbb --- /dev/null +++ b/templates/order/show/sections/items/body/unit_discount.html.twig @@ -0,0 +1,12 @@ +{% import '@SyliusAdmin/shared/helper/money.html.twig' as money %} + +{% set unit_promotion_adjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_UNIT_PROMOTION_ADJUSTMENT') %} +{% set item_discount_adjustment = constant('Webgriffe\\SyliusAdminOrderCreationPlugin\\Form\\Type\\AdjustmentType::ORDER_ITEM_DISCOUNT_ADJUSTMENT') %} + +{% set order = hookable_metadata.context.resource %} +{% set item = hookable_metadata.context.item %} +{% set unitDiscount = item.units.first.getAdjustmentsTotal(unit_promotion_adjustment) + (item.getAdjustmentsTotal(item_discount_adjustment) // item.quantity) %} + + + {{ money.format(unitDiscount, order.currencyCode) }} + diff --git a/templates/order/show/sections/payments/item/actions/pay_via_payment_link.html.twig b/templates/order/show/sections/payments/item/actions/pay_via_payment_link.html.twig new file mode 100644 index 000000000..a33f95888 --- /dev/null +++ b/templates/order/show/sections/payments/item/actions/pay_via_payment_link.html.twig @@ -0,0 +1,7 @@ +{% set payment = hookable_metadata.context.payment %} + +{% if payment.details['payment-link'] is defined %} + + {{ ux_icon('tabler:credit-card') }} {{ 'sylius_admin_order_creation.ui.pay'|trans }} + +{% endif %} diff --git a/templates/order/show/sections/summary/order_discount.html.twig b/templates/order/show/sections/summary/order_discount.html.twig new file mode 100644 index 000000000..fedee44bb --- /dev/null +++ b/templates/order/show/sections/summary/order_discount.html.twig @@ -0,0 +1,11 @@ +{% import '@SyliusAdmin/shared/helper/money.html.twig' as money %} + +{% set order = hookable_metadata.context.resource %} +{% set orderDiscountTotal = order.getAdjustmentsTotalRecursively(constant('Webgriffe\\SyliusAdminOrderCreationPlugin\\Form\\Type\\AdjustmentType::ORDER_DISCOUNT_ADJUSTMENT')) %} + +{% if orderDiscountTotal != 0 %} + + {{ 'sylius_admin_order_creation.ui.order_discount'|trans }}: + {{ money.format(orderDiscountTotal, order.currencyCode) }} + +{% endif %} diff --git a/tests/Application/.babelrc b/tests/Application/.babelrc deleted file mode 100644 index e563a62ea..000000000 --- a/tests/Application/.babelrc +++ /dev/null @@ -1,15 +0,0 @@ -{ - "presets": [ - ["env", { - "targets": { - "node": "6" - }, - "useBuiltIns": true - }] - ], - "plugins": [ - ["transform-object-rest-spread", { - "useBuiltIns": true - }] - ] -} diff --git a/tests/Application/.env b/tests/Application/.env deleted file mode 100644 index f6712f172..000000000 --- a/tests/Application/.env +++ /dev/null @@ -1,36 +0,0 @@ -# This file is a "template" of which env vars needs to be defined in your configuration or in an .env file -# Set variables here that may be different on each deployment target of the app, e.g. development, staging, production. -# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration - -###> symfony/framework-bundle ### -APP_ENV=dev -APP_DEBUG=1 -APP_SECRET=EDITME -###< symfony/framework-bundle ### - -###> doctrine/doctrine-bundle ### -# Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url -# For a sqlite database, use: "sqlite:///%kernel.project_dir%/var/data.db" -# Set "serverVersion" to your server version to avoid edge-case exceptions and extra database calls -DATABASE_URL=mysql://root@127.0.0.1/sylius_admin_order_creation_plugin_%kernel.environment%?serverVersion=5.5 -###< doctrine/doctrine-bundle ### - -###> symfony/swiftmailer-bundle ### -# For Gmail as a transport, use: "gmail://username:password@localhost" -# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode=" -# Delivery is disabled by default via "null://localhost" -MAILER_URL=smtp://localhost -###< symfony/swiftmailer-bundle ### - -###> lexik/jwt-authentication-bundle ### -JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem -JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem -JWT_PASSPHRASE=YOUR_SECRET_PASSPHRASE -###< lexik/jwt-authentication-bundle ### - -###> symfony/messenger ### -# Choose one of the transports below -# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages -MESSENGER_TRANSPORT_DSN=doctrine://default -# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages -###< symfony/messenger ### diff --git a/tests/Application/.env.test b/tests/Application/.env.test deleted file mode 100644 index 084908ecd..000000000 --- a/tests/Application/.env.test +++ /dev/null @@ -1,14 +0,0 @@ -APP_SECRET='ch4mb3r0f5ecr3ts' - -KERNEL_CLASS='Tests\Sylius\AdminOrderCreationPlugin\Application\Kernel' - -###> lexik/jwt-authentication-bundle ### -JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private-test.pem -JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public-test.pem -JWT_PASSPHRASE=ALL_THAT_IS_GOLD_DOES_NOT_GLITTER_NOT_ALL_THOSE_WHO_WANDER_ARE_LOST -###< lexik/jwt-authentication-bundle ### - -###> symfony/messenger ### -# Sync transport turned for testing env for the ease of testing -MESSENGER_TRANSPORT_DSN=sync:// -###< symfony/messenger ### diff --git a/tests/Application/.eslintrc.js b/tests/Application/.eslintrc.js deleted file mode 100644 index 92c4cee37..000000000 --- a/tests/Application/.eslintrc.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - extends: 'airbnb-base', - env: { - node: true, - }, - rules: { - 'object-shorthand': ['error', 'always', { - avoidQuotes: true, - avoidExplicitReturnArrows: true, - }], - 'function-paren-newline': ['error', 'consistent'], - 'max-len': ['warn', 120, 2, { - ignoreUrls: true, - ignoreComments: false, - ignoreRegExpLiterals: true, - ignoreStrings: true, - ignoreTemplateLiterals: true, - }], - }, -}; diff --git a/tests/Application/.gitignore b/tests/Application/.gitignore deleted file mode 100644 index 8ad1225e1..000000000 --- a/tests/Application/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -/public/assets -/public/css -/public/js -/public/media/* -!/public/media/image/ -/public/media/image/* -!/public/media/image/.gitignore - -/node_modules - -###> symfony/framework-bundle ### -/.env.*.local -/.env.local -/.env.local.php -/public/bundles -/var/ -/vendor/ -###< symfony/framework-bundle ### - -###> symfony/web-server-bundle ### -/.web-server-pid -###< symfony/web-server-bundle ### diff --git a/tests/Application/Kernel.php b/tests/Application/Kernel.php deleted file mode 100644 index 0a45032b9..000000000 --- a/tests/Application/Kernel.php +++ /dev/null @@ -1,145 +0,0 @@ -getProjectDir() . '/var/cache/' . $this->environment; - } - - public function getLogDir(): string - { - return $this->getProjectDir() . '/var/log'; - } - - public function registerBundles(): iterable - { - foreach ($this->getBundleListFiles() as $file) { - yield from $this->registerBundlesFromFile($file); - } - } - - protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void - { - foreach ($this->getBundleListFiles() as $file) { - $container->addResource(new FileResource($file)); - } - - $container->setParameter('container.dumper.inline_class_loader', true); - - foreach ($this->getConfigurationDirectories() as $confDir) { - $this->loadContainerConfiguration($loader, $confDir); - } - } - - protected function configureRoutes(RouteCollectionBuilder $routes): void - { - foreach ($this->getConfigurationDirectories() as $confDir) { - $this->loadRoutesConfiguration($routes, $confDir); - } - } - - protected function getContainerBaseClass(): string - { - if ($this->isTestEnvironment() && class_exists(MockerContainer::class)) { - return MockerContainer::class; - } - - return parent::getContainerBaseClass(); - } - - private function isTestEnvironment(): bool - { - return 0 === strpos($this->getEnvironment(), 'test'); - } - - private function loadContainerConfiguration(LoaderInterface $loader, string $confDir): void - { - $loader->load($confDir . '/{packages}/*' . self::CONFIG_EXTS, 'glob'); - $loader->load($confDir . '/{packages}/' . $this->environment . '/**/*' . self::CONFIG_EXTS, 'glob'); - $loader->load($confDir . '/{services}' . self::CONFIG_EXTS, 'glob'); - $loader->load($confDir . '/{services}_' . $this->environment . self::CONFIG_EXTS, 'glob'); - } - - private function loadRoutesConfiguration(RouteCollectionBuilder $routes, string $confDir): void - { - $routes->import($confDir . '/{routes}/*' . self::CONFIG_EXTS, '/', 'glob'); - $routes->import($confDir . '/{routes}/' . $this->environment . '/**/*' . self::CONFIG_EXTS, '/', 'glob'); - $routes->import($confDir . '/{routes}' . self::CONFIG_EXTS, '/', 'glob'); - } - - /** - * @return BundleInterface[] - */ - private function registerBundlesFromFile(string $bundlesFile): iterable - { - $contents = require $bundlesFile; - - if (SyliusKernel::MINOR_VERSION > 10) { - $contents = array_merge( - ['Sylius\Calendar\SyliusCalendarBundle' => ['all' => true]], - $contents - ); - } - - foreach ($contents as $class => $envs) { - if (isset($envs['all']) || isset($envs[$this->environment])) { - yield new $class(); - } - } - } - - /** - * @return string[] - */ - private function getBundleListFiles(): array - { - return array_filter( - array_map( - static function (string $directory): string { - return $directory . '/bundles.php'; - }, - $this->getConfigurationDirectories() - ), - 'file_exists' - ); - } - - /** - * @return string[] - */ - private function getConfigurationDirectories(): array - { - $directories = [ - $this->getProjectDir() . '/config', - $this->getProjectDir() . '/config/sylius/' . SyliusKernel::MAJOR_VERSION . '.' . SyliusKernel::MINOR_VERSION, - ]; - - return array_filter($directories, 'file_exists'); - } -} diff --git a/tests/Application/bin/console b/tests/Application/bin/console deleted file mode 100755 index 0554a1962..000000000 --- a/tests/Application/bin/console +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env php -getParameterOption(['--env', '-e'], null, true)) { - putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); -} - -if ($input->hasParameterOption('--no-debug', true)) { - putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); -} - -require dirname(__DIR__).'/config/bootstrap.php'; - -if ($_SERVER['APP_DEBUG']) { - umask(0000); - - if (class_exists(Debug::class)) { - Debug::enable(); - } -} - -$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); -$application = new Application($kernel); -$application->run($input); diff --git a/tests/Application/composer.json b/tests/Application/composer.json deleted file mode 100644 index 04efc4311..000000000 --- a/tests/Application/composer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "sylius/admin-order-creation-plugin-test-application", - "description": "Sylius application for plugin testing purposes (composer.json needed for project dir resolving)", - "license": "MIT" -} diff --git a/tests/Application/config/bootstrap.php b/tests/Application/config/bootstrap.php deleted file mode 100644 index 6bb0207a3..000000000 --- a/tests/Application/config/bootstrap.php +++ /dev/null @@ -1,21 +0,0 @@ -=1.2) -if (is_array($env = @include dirname(__DIR__).'/.env.local.php')) { - $_SERVER += $env; - $_ENV += $env; -} elseif (!class_exists(Dotenv::class)) { - throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.'); -} else { - // load all the .env files - (new Dotenv())->loadEnv(dirname(__DIR__).'/.env'); -} - -$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev'; -$_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; -$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; diff --git a/tests/Application/config/bundles.php b/tests/Application/config/bundles.php deleted file mode 100644 index 9669540b9..000000000 --- a/tests/Application/config/bundles.php +++ /dev/null @@ -1,59 +0,0 @@ - ['all' => true], - Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], - Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], - Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true], - Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], - Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], - Sylius\Bundle\OrderBundle\SyliusOrderBundle::class => ['all' => true], - Sylius\Bundle\MoneyBundle\SyliusMoneyBundle::class => ['all' => true], - Sylius\Bundle\CurrencyBundle\SyliusCurrencyBundle::class => ['all' => true], - Sylius\Bundle\LocaleBundle\SyliusLocaleBundle::class => ['all' => true], - Sylius\Bundle\ProductBundle\SyliusProductBundle::class => ['all' => true], - Sylius\Bundle\ChannelBundle\SyliusChannelBundle::class => ['all' => true], - Sylius\Bundle\AttributeBundle\SyliusAttributeBundle::class => ['all' => true], - Sylius\Bundle\TaxationBundle\SyliusTaxationBundle::class => ['all' => true], - Sylius\Bundle\ShippingBundle\SyliusShippingBundle::class => ['all' => true], - Sylius\Bundle\PaymentBundle\SyliusPaymentBundle::class => ['all' => true], - Sylius\Bundle\MailerBundle\SyliusMailerBundle::class => ['all' => true], - Sylius\Bundle\PromotionBundle\SyliusPromotionBundle::class => ['all' => true], - Sylius\Bundle\AddressingBundle\SyliusAddressingBundle::class => ['all' => true], - Sylius\Bundle\InventoryBundle\SyliusInventoryBundle::class => ['all' => true], - Sylius\Bundle\TaxonomyBundle\SyliusTaxonomyBundle::class => ['all' => true], - Sylius\Bundle\UserBundle\SyliusUserBundle::class => ['all' => true], - Sylius\Bundle\CustomerBundle\SyliusCustomerBundle::class => ['all' => true], - Sylius\Bundle\UiBundle\SyliusUiBundle::class => ['all' => true], - Sylius\Bundle\ReviewBundle\SyliusReviewBundle::class => ['all' => true], - Sylius\Bundle\CoreBundle\SyliusCoreBundle::class => ['all' => true], - Sylius\Bundle\ResourceBundle\SyliusResourceBundle::class => ['all' => true], - Sylius\Bundle\GridBundle\SyliusGridBundle::class => ['all' => true], - winzou\Bundle\StateMachineBundle\winzouStateMachineBundle::class => ['all' => true], - Sonata\BlockBundle\SonataBlockBundle::class => ['all' => true], - Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle::class => ['all' => true], - JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true], - FOS\RestBundle\FOSRestBundle::class => ['all' => true], - Knp\Bundle\GaufretteBundle\KnpGaufretteBundle::class => ['all' => true], - Knp\Bundle\MenuBundle\KnpMenuBundle::class => ['all' => true], - Liip\ImagineBundle\LiipImagineBundle::class => ['all' => true], - Payum\Bundle\PayumBundle\PayumBundle::class => ['all' => true], - Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true], - Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], - Sylius\Bundle\FixturesBundle\SyliusFixturesBundle::class => ['all' => true], - Sylius\Bundle\PayumBundle\SyliusPayumBundle::class => ['all' => true], - Sylius\Bundle\ThemeBundle\SyliusThemeBundle::class => ['all' => true], - Sylius\Bundle\AdminBundle\SyliusAdminBundle::class => ['all' => true], - Sylius\Bundle\ShopBundle\SyliusShopBundle::class => ['all' => true], - Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], - Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], - Sylius\AdminOrderCreationPlugin\SyliusAdminOrderCreationPlugin::class => ['all' => true], - FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true, 'test_cached' => true], - FOS\JsRoutingBundle\FOSJsRoutingBundle::class => ['all' => true], - ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], - Sylius\Bundle\ApiBundle\SyliusApiBundle::class => ['all' => true], - Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], - SyliusLabs\DoctrineMigrationsExtraBundle\SyliusLabsDoctrineMigrationsExtraBundle::class => ['all' => true], - BabDev\PagerfantaBundle\BabDevPagerfantaBundle::class => ['all' => true], - SyliusLabs\Polyfill\Symfony\Security\Bundle\SyliusLabsPolyfillSymfonySecurityBundle::class => ['all' => true], -]; diff --git a/tests/Application/config/jwt/private-test.pem b/tests/Application/config/jwt/private-test.pem deleted file mode 100644 index b5a5246db..000000000 --- a/tests/Application/config/jwt/private-test.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKAIBAAKCAgEAxuS1SudSNkjTQcP4H5SjzrdO29upko9KYZgUH6z5n+weDtIo -5tysdm7xY3nNAU9ixo7wrBvttuf7T1fDCVJjhzqX5iewaCZks7q9kYygCbvmrAoc -bx5D9EPZPH0sQQoa9gMuNou2nqWpVdTYCMAjxzVpqa2krioUzkBJzaWGDYiijv9q -KbjWvRUUoYFNOFIFXHFFDrK5ISBC155XiETKyBYhB1wZVWX1tHe1nDW609BHAAsr -1Ve3uiodzYzQ7S9Rw9Q6RCRSRgZRzFV1GTJEuyMpCCD51DA4otYeEPQf+8hvV3aK -bSNydzrQICY95kfB0p9HxorBPh8QHq0qKZOIle1Aglp0UV3OWgXLWncNc0m8e8hT -2I55lYkLio99/4PGfalAdJBPhKtTzbJllaERHOnlMkEvwk7eggkbbXEN/Ay6usi0 -R8mRaxhMkS9i8MxubgQBDsOomtegRqA1EzSGU/FJMS5g/I/gO9bjFu2l2LJwd6B4 -t/FZt/9mAIGYbIj5/Ykd0E1WIKYAIRUoyW1gTrGe70yxdHEPEILnZZMRVJzDbkVY -fgKAFUpAUqbHTtS+YI6p9tjFuxBrc6GZR/kppL/MkEARDX0ZX3n4sPLQf/vdR7+K -s8Yqws1IqsZ3h8iP8WpykbEwnd1w49A1ZBBinIXU3idf41EtQgawbpK5Oy0CAwEA -AQKCAgAEdUnvBOJd3yIFHlxocM9/KbK10OWrKFUVfPAuiZUK1aMS1/kcu6OOAAyf -GzLSLbJcGwYgBXw9llOWwrPXeKZMeK7A9PDKVNn7AVuQcKOBtFmGT6+1eesyBXdQ -GMouJwjVrNqTVGxif/oct2mkQJJMu9DDgeXoFX9j5CMDXgt0MDTcmbMKfl8p29gb -iqdtdME0AkH3A2CM8oktBhqWLlyRQZW58YGL3X41bl1+w+GNL+T3hkiUPqQaoykJ -23cvadkeV5p6vomtkiSxPNUkHHFX9IDN8tdGv1H1rHD+FkrFPQfp4PlXWu0M6R+T -KOhISiF5FCLqu197gfy9g0onpmvwTkQW0ap5kTMfmryhc2fSbGeQO1bDjnCQQ05/ -yXpu9dRvQQCbXsAIUaJUyOsgJy4tpOlmra9mFK0/+ObN7ZJDxYA1tHPKoArcSFnC -L0nbMs7C5b5Njky10nD6d1hu+HBEB1g6wBmsOCEMNBd6AF9ABmA/TIGSS2rsjdf6 -eYytYSAlSVlYwel3mvtpwfq5/q1mPtr38ND192/FDCMoVWoUbpYTKQHvAYdodOyS -DJNH6upchKdCMItv6K6rv4Rc+lN9lp/XGYlxO8BpXVO+IF/dD+6Rs5vv3Hk5lU3D -aX/ALBTTN1Xe8JKaCHd4ji1rgsIOxRmXDqdy05kiQtt7LtkCDQKCAQEA5Xw9EjXw -Wsb42j+Ew3ISAC92VBIBmSz6hnwX27vON51Bm3j86LV1GHl0PvEl0A7c46+scumG -pKgY5qad1Z7FtyCi78a9cr7HVbX03QndFZpzD14oIugN7y3ecSppPI/hfmOwfOAa -O8b2lg2A6s4QBG70SAGFVeTozxovL/V4EWly/NW/2gAe6ZAWU8c3XyJBMMhA5Ez8 -aZONHipHi+uAiOwsLth7rGZbjyF4/QpXJuKzZ8p8e43yJiCkV7F588akp13NDmIB -qXHNLEUcE1SD4GGoFGEacOkVkXO5Bqyn15LACjyluhhzsl5IWdClRUfjgbg3ieWO -wdHq3bL6TgFuBwKCAQEA3d+drcpzMUOJRrDr08yNr6ygkeIvRtX7De3ilhg0Y3G3 -L2/rexfe1i5yrSc5N5hTWjLOSUtT4AD4tIgvNwW4YRtdQ9eRr/aoFHy5w8oXm0z5 -TJta7yBgcXTc4xQE9XtVEL0OUOKXxrh+Y5HHh5do5blsNfutHqRjtKWvPp1kCHba -GjMiLkleivE4WGMkuBXPa13GUe3UfxOQp3KfaNcWAlNhnbc/8ljQJ1YZEj3XDodB -RvorGzw8m8U+yZ/rhslPiq2MD0IrKUg5/r1C8k6ZVTrcG82A3Su1gacpS4b3q84W -r1AwM3iajzBnPkiRc98H7p07vwYCeLV2Lm/w/LZAKwKCAQEAqx0HYKPNk7KXbg08 -zoso9vBs9+TxQijyqQKwu4x/CKL+f5IoatCa/mPZlPE0872RYUjlek28stwQrTOB -rv6TiKgSNl3ndz7f3X4ulf671lbzAnt/y/9iHH0ERzeLfrf+OMLWn1Zu2THTPjHV -db+u289r4KEZreFg4sQweT88hycssXAkfMXoRtnEfDWoiQw+tcQr9s+cypBWAi8e -aCtzDSWlEE0lcnhkPwaDc5KZR4p0oaivR2WhMGLYh/by6x2sOovL0bSsbo9HoIHr -nFJBfzbyIDgDgjuadHlodpyZDjoDbd6o6GlBI7f/lNDp2w3uixQ0fWMpHkaLLUI+ -N5oDUwKCAQAIjvemHIkU/WXuNCTkpp9Qh3gqKG9qbBajEuoKoCRlMZ2/VrHera0K -1f/Wbgzm+Bk/AXaznRQ/L8poLFil5rKWDFgspcQY5YrWP3lq9AC1HOMA8X0wfC88 -MSXUHJGUZo2Bd8l1lUgFglhdvuHTeSOyuNRTwMGMzQqLjViVMb0KFouTNyW6Y1oi -QevKfQiNkUnO+m8L+gCYZkjOLL25bZKLxGufidINpx9gZRHSglApX05FTqEbC9fK -qnEhlemf6WQIFWmxrPu9O+wAx4wtjJqdjweuit7NqUH3HluZbjtfhTOaz50MXzqX -C2bwIBx8O74ylh4X4EN4JIfKgsbo+J7BAoIBAAJ72s6YSoHaQCYXynLhRGenVHtI -rj6wNkwnBEsIDk9j8vt5fMJA1xRNZ0kA1mDAgDqq9ad2RpGcZH1jjvI5IPdB0dKE -5fyKR5okRMNRMc2Sn5LOiLsSqnhHwZo1nEZP/UTcZvIKDqajy8t4cIvBEO1ol4D5 -DxiclH7UMAgwKemYbsBHOOscbN2Z3o41uzSKUhNlLV5GP3ZPMau+MHinIXtCjHdi -Xu9eGA3GDD4/sU4JZTl1g/Rs48JEn2H800pVgyzkn9Q01hJZ0dSqy+Agcu3Yw6Sr -XRaqXN38pEInJ+GAU6y+6/RsiHdF3YOOUOPUX6PCfu8BMLRFASAdMbwNBXk= ------END RSA PRIVATE KEY----- diff --git a/tests/Application/config/jwt/public-test.pem b/tests/Application/config/jwt/public-test.pem deleted file mode 100644 index e21c0559e..000000000 --- a/tests/Application/config/jwt/public-test.pem +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxuS1SudSNkjTQcP4H5Sj -zrdO29upko9KYZgUH6z5n+weDtIo5tysdm7xY3nNAU9ixo7wrBvttuf7T1fDCVJj -hzqX5iewaCZks7q9kYygCbvmrAocbx5D9EPZPH0sQQoa9gMuNou2nqWpVdTYCMAj -xzVpqa2krioUzkBJzaWGDYiijv9qKbjWvRUUoYFNOFIFXHFFDrK5ISBC155XiETK -yBYhB1wZVWX1tHe1nDW609BHAAsr1Ve3uiodzYzQ7S9Rw9Q6RCRSRgZRzFV1GTJE -uyMpCCD51DA4otYeEPQf+8hvV3aKbSNydzrQICY95kfB0p9HxorBPh8QHq0qKZOI -le1Aglp0UV3OWgXLWncNc0m8e8hT2I55lYkLio99/4PGfalAdJBPhKtTzbJllaER -HOnlMkEvwk7eggkbbXEN/Ay6usi0R8mRaxhMkS9i8MxubgQBDsOomtegRqA1EzSG -U/FJMS5g/I/gO9bjFu2l2LJwd6B4t/FZt/9mAIGYbIj5/Ykd0E1WIKYAIRUoyW1g -TrGe70yxdHEPEILnZZMRVJzDbkVYfgKAFUpAUqbHTtS+YI6p9tjFuxBrc6GZR/kp -pL/MkEARDX0ZX3n4sPLQf/vdR7+Ks8Yqws1IqsZ3h8iP8WpykbEwnd1w49A1ZBBi -nIXU3idf41EtQgawbpK5Oy0CAwEAAQ== ------END PUBLIC KEY----- diff --git a/tests/Application/config/packages/_sylius.yaml b/tests/Application/config/packages/_sylius.yaml deleted file mode 100644 index 12be5a2c3..000000000 --- a/tests/Application/config/packages/_sylius.yaml +++ /dev/null @@ -1,29 +0,0 @@ -imports: - - { resource: "@SyliusCoreBundle/Resources/config/app/config.yml" } - - - { resource: "@SyliusAdminBundle/Resources/config/app/config.yml" } - - - { resource: "@SyliusShopBundle/Resources/config/app/config.yml" } - - - { resource: "@SyliusApiBundle/Resources/config/app/config.yaml" } - - - { resource: "@SyliusAdminOrderCreationPlugin/Resources/config/app/config.yml" } - -parameters: - sylius_core.public_dir: '%kernel.project_dir%/public' - -sylius_shop: - product_grid: - include_all_descendants: true - -sylius_customer: - resources: - customer: - classes: - repository: Tests\Sylius\AdminOrderCreationPlugin\Application\Doctrine\ORM\CustomerRepository - -sylius_product: - resources: - product_variant: - classes: - repository: Tests\Sylius\AdminOrderCreationPlugin\Application\Doctrine\ORM\ProductVariantRepository diff --git a/tests/Application/config/packages/dev/framework.yaml b/tests/Application/config/packages/dev/framework.yaml deleted file mode 100644 index 4b116defd..000000000 --- a/tests/Application/config/packages/dev/framework.yaml +++ /dev/null @@ -1,2 +0,0 @@ -framework: - profiler: { only_exceptions: false } diff --git a/tests/Application/config/packages/dev/jms_serializer.yaml b/tests/Application/config/packages/dev/jms_serializer.yaml deleted file mode 100644 index 2f32a9b18..000000000 --- a/tests/Application/config/packages/dev/jms_serializer.yaml +++ /dev/null @@ -1,12 +0,0 @@ -jms_serializer: - visitors: - json_serialization: - options: - - JSON_PRETTY_PRINT - - JSON_UNESCAPED_SLASHES - - JSON_PRESERVE_ZERO_FRACTION - json_deserialization: - options: - - JSON_PRETTY_PRINT - - JSON_UNESCAPED_SLASHES - - JSON_PRESERVE_ZERO_FRACTION diff --git a/tests/Application/config/packages/dev/monolog.yaml b/tests/Application/config/packages/dev/monolog.yaml deleted file mode 100644 index da2b092de..000000000 --- a/tests/Application/config/packages/dev/monolog.yaml +++ /dev/null @@ -1,9 +0,0 @@ -monolog: - handlers: - main: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: debug - firephp: - type: firephp - level: info diff --git a/tests/Application/config/packages/dev/routing.yaml b/tests/Application/config/packages/dev/routing.yaml deleted file mode 100644 index 4116679a2..000000000 --- a/tests/Application/config/packages/dev/routing.yaml +++ /dev/null @@ -1,3 +0,0 @@ -framework: - router: - strict_requirements: true diff --git a/tests/Application/config/packages/dev/swiftmailer.yaml b/tests/Application/config/packages/dev/swiftmailer.yaml deleted file mode 100644 index f43807805..000000000 --- a/tests/Application/config/packages/dev/swiftmailer.yaml +++ /dev/null @@ -1,2 +0,0 @@ -swiftmailer: - disable_delivery: true diff --git a/tests/Application/config/packages/dev/web_profiler.yaml b/tests/Application/config/packages/dev/web_profiler.yaml deleted file mode 100644 index 1f1cb2bb4..000000000 --- a/tests/Application/config/packages/dev/web_profiler.yaml +++ /dev/null @@ -1,3 +0,0 @@ -web_profiler: - toolbar: true - intercept_redirects: false diff --git a/tests/Application/config/packages/doctrine.yaml b/tests/Application/config/packages/doctrine.yaml deleted file mode 100644 index f51ba5a22..000000000 --- a/tests/Application/config/packages/doctrine.yaml +++ /dev/null @@ -1,14 +0,0 @@ -parameters: - # Adds a fallback DATABASE_URL if the env var is not set. - # This allows you to run cache:warmup even if your - # environment variables are not available yet. - # You should not need to change this value. - env(DATABASE_URL): '' - -doctrine: - dbal: - driver: 'pdo_mysql' - server_version: '5.7' - charset: UTF8 - - url: '%env(resolve:DATABASE_URL)%' diff --git a/tests/Application/config/packages/doctrine_migrations.yaml b/tests/Application/config/packages/doctrine_migrations.yaml deleted file mode 100644 index c2456bfdb..000000000 --- a/tests/Application/config/packages/doctrine_migrations.yaml +++ /dev/null @@ -1,6 +0,0 @@ -doctrine_migrations: - storage: - table_storage: - table_name: sylius_migrations - migrations_paths: - 'DoctrineMigrations': '%kernel.project_dir%/src/Migrations' diff --git a/tests/Application/config/packages/fos_rest.yaml b/tests/Application/config/packages/fos_rest.yaml deleted file mode 100644 index eaebb2776..000000000 --- a/tests/Application/config/packages/fos_rest.yaml +++ /dev/null @@ -1,11 +0,0 @@ -fos_rest: - exception: true - view: - formats: - json: true - xml: true - empty_content: 204 - format_listener: - rules: - - { path: '^/api/v1/.*', priorities: ['json', 'xml'], fallback_format: json, prefer_extension: true } - - { path: '^/', stop: true } diff --git a/tests/Application/config/packages/framework.yaml b/tests/Application/config/packages/framework.yaml deleted file mode 100644 index 9b445011c..000000000 --- a/tests/Application/config/packages/framework.yaml +++ /dev/null @@ -1,6 +0,0 @@ -framework: - secret: '%env(APP_SECRET)%' - form: true - csrf_protection: true - session: - handler_id: ~ diff --git a/tests/Application/config/packages/jms_serializer.yaml b/tests/Application/config/packages/jms_serializer.yaml deleted file mode 100644 index ed7bc613f..000000000 --- a/tests/Application/config/packages/jms_serializer.yaml +++ /dev/null @@ -1,4 +0,0 @@ -jms_serializer: - visitors: - xml_serialization: - format_output: '%kernel.debug%' diff --git a/tests/Application/config/packages/lexik_jwt_authentication.yaml b/tests/Application/config/packages/lexik_jwt_authentication.yaml deleted file mode 100644 index edfb69dc8..000000000 --- a/tests/Application/config/packages/lexik_jwt_authentication.yaml +++ /dev/null @@ -1,4 +0,0 @@ -lexik_jwt_authentication: - secret_key: '%env(resolve:JWT_SECRET_KEY)%' - public_key: '%env(resolve:JWT_PUBLIC_KEY)%' - pass_phrase: '%env(JWT_PASSPHRASE)%' diff --git a/tests/Application/config/packages/liip_imagine.yaml b/tests/Application/config/packages/liip_imagine.yaml deleted file mode 100644 index bb2e7ceb9..000000000 --- a/tests/Application/config/packages/liip_imagine.yaml +++ /dev/null @@ -1,6 +0,0 @@ -liip_imagine: - resolvers: - default: - web_path: - web_root: "%kernel.project_dir%/public" - cache_prefix: "media/cache" diff --git a/tests/Application/config/packages/prod/doctrine.yaml b/tests/Application/config/packages/prod/doctrine.yaml deleted file mode 100644 index 2f16f0fde..000000000 --- a/tests/Application/config/packages/prod/doctrine.yaml +++ /dev/null @@ -1,31 +0,0 @@ -doctrine: - orm: - metadata_cache_driver: - type: service - id: doctrine.system_cache_provider - query_cache_driver: - type: service - id: doctrine.system_cache_provider - result_cache_driver: - type: service - id: doctrine.result_cache_provider - -services: - doctrine.result_cache_provider: - class: Symfony\Component\Cache\DoctrineProvider - public: false - arguments: - - '@doctrine.result_cache_pool' - doctrine.system_cache_provider: - class: Symfony\Component\Cache\DoctrineProvider - public: false - arguments: - - '@doctrine.system_cache_pool' - -framework: - cache: - pools: - doctrine.result_cache_pool: - adapter: cache.app - doctrine.system_cache_pool: - adapter: cache.system diff --git a/tests/Application/config/packages/prod/jms_serializer.yaml b/tests/Application/config/packages/prod/jms_serializer.yaml deleted file mode 100644 index c2881820f..000000000 --- a/tests/Application/config/packages/prod/jms_serializer.yaml +++ /dev/null @@ -1,10 +0,0 @@ -jms_serializer: - visitors: - json_serialization: - options: - - JSON_UNESCAPED_SLASHES - - JSON_PRESERVE_ZERO_FRACTION - json_deserialization: - options: - - JSON_UNESCAPED_SLASHES - - JSON_PRESERVE_ZERO_FRACTION diff --git a/tests/Application/config/packages/prod/monolog.yaml b/tests/Application/config/packages/prod/monolog.yaml deleted file mode 100644 index 646121143..000000000 --- a/tests/Application/config/packages/prod/monolog.yaml +++ /dev/null @@ -1,10 +0,0 @@ -monolog: - handlers: - main: - type: fingers_crossed - action_level: error - handler: nested - nested: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: debug diff --git a/tests/Application/config/packages/routing.yaml b/tests/Application/config/packages/routing.yaml deleted file mode 100644 index 368bc7f49..000000000 --- a/tests/Application/config/packages/routing.yaml +++ /dev/null @@ -1,3 +0,0 @@ -framework: - router: - strict_requirements: ~ diff --git a/tests/Application/config/packages/security.yaml b/tests/Application/config/packages/security.yaml deleted file mode 100644 index efa460155..000000000 --- a/tests/Application/config/packages/security.yaml +++ /dev/null @@ -1,126 +0,0 @@ -security: - providers: - sylius_admin_user_provider: - id: sylius.admin_user_provider.email_or_name_based - sylius_shop_user_provider: - id: sylius.shop_user_provider.email_or_name_based - sylius_api_admin_user_provider: - id: sylius.admin_user_provider.email_or_name_based - sylius_api_shop_user_provider: - id: sylius.shop_user_provider.email_or_name_based - - encoders: - Sylius\Component\User\Model\UserInterface: argon2i - - firewalls: - admin: - switch_user: true - context: admin - pattern: "%sylius.security.admin_regex%" - provider: sylius_admin_user_provider - form_login: - provider: sylius_admin_user_provider - login_path: sylius_admin_login - check_path: sylius_admin_login_check - failure_path: sylius_admin_login - default_target_path: sylius_admin_dashboard - use_forward: false - use_referer: true - csrf_token_generator: security.csrf.token_manager - csrf_parameter: _csrf_admin_security_token - csrf_token_id: admin_authenticate - remember_me: - secret: "%env(APP_SECRET)%" - path: "/%sylius_admin.path_name%" - name: APP_ADMIN_REMEMBER_ME - lifetime: 31536000 - remember_me_parameter: _remember_me - logout: - path: sylius_admin_logout - target: sylius_admin_login - anonymous: true - - new_api_admin_user: - pattern: "%sylius.security.new_api_admin_regex%/.*" - provider: sylius_api_admin_user_provider - stateless: true - anonymous: true - json_login: - check_path: "%sylius.security.new_api_admin_route%/authentication-token" - username_path: email - password_path: password - success_handler: lexik_jwt_authentication.handler.authentication_success - failure_handler: lexik_jwt_authentication.handler.authentication_failure - guard: - authenticators: - - lexik_jwt_authentication.jwt_token_authenticator - - new_api_shop_user: - pattern: "%sylius.security.new_api_shop_regex%/.*" - provider: sylius_api_shop_user_provider - stateless: true - anonymous: true - json_login: - check_path: "%sylius.security.new_api_shop_route%/authentication-token" - username_path: email - password_path: password - success_handler: lexik_jwt_authentication.handler.authentication_success - failure_handler: lexik_jwt_authentication.handler.authentication_failure - guard: - authenticators: - - lexik_jwt_authentication.jwt_token_authenticator - - shop: - switch_user: { role: ROLE_ALLOWED_TO_SWITCH } - context: shop - pattern: "%sylius.security.shop_regex%" - provider: sylius_shop_user_provider - form_login: - success_handler: sylius.authentication.success_handler - failure_handler: sylius.authentication.failure_handler - provider: sylius_shop_user_provider - login_path: sylius_shop_login - check_path: sylius_shop_login_check - failure_path: sylius_shop_login - default_target_path: sylius_shop_homepage - use_forward: false - use_referer: true - csrf_token_generator: security.csrf.token_manager - csrf_parameter: _csrf_shop_security_token - csrf_token_id: shop_authenticate - remember_me: - secret: "%env(APP_SECRET)%" - name: APP_SHOP_REMEMBER_ME - lifetime: 31536000 - remember_me_parameter: _remember_me - logout: - path: sylius_shop_logout - target: sylius_shop_login - invalidate_session: false - success_handler: sylius.handler.shop_user_logout - anonymous: true - - dev: - pattern: ^/(_(profiler|wdt)|css|images|js)/ - security: false - - access_control: - - { path: "%sylius.security.admin_regex%/_partial", role: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1] } - - { path: "%sylius.security.admin_regex%/_partial", role: ROLE_NO_ACCESS } - - { path: "%sylius.security.shop_regex%/_partial", role: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1] } - - { path: "%sylius.security.shop_regex%/_partial", role: ROLE_NO_ACCESS } - - - { path: "%sylius.security.admin_regex%/login", role: IS_AUTHENTICATED_ANONYMOUSLY } - - { path: "%sylius.security.shop_regex%/login", role: IS_AUTHENTICATED_ANONYMOUSLY } - - - { path: "%sylius.security.shop_regex%/register", role: IS_AUTHENTICATED_ANONYMOUSLY } - - { path: "%sylius.security.shop_regex%/verify", role: IS_AUTHENTICATED_ANONYMOUSLY } - - - { path: "%sylius.security.admin_regex%", role: ROLE_ADMINISTRATION_ACCESS } - - { path: "%sylius.security.shop_regex%/account", role: ROLE_USER } - - - { path: "%sylius.security.new_api_admin_regex%/.*", role: ROLE_API_ACCESS } - - { path: "%sylius.security.new_api_admin_route%/authentication-token", role: IS_AUTHENTICATED_ANONYMOUSLY } - - { path: "%sylius.security.new_api_user_account_regex%/.*", role: ROLE_USER } - - { path: "%sylius.security.new_api_shop_route%/authentication-token", role: IS_AUTHENTICATED_ANONYMOUSLY } - - { path: "%sylius.security.new_api_shop_regex%/.*", role: IS_AUTHENTICATED_ANONYMOUSLY } diff --git a/tests/Application/config/packages/staging/monolog.yaml b/tests/Application/config/packages/staging/monolog.yaml deleted file mode 100644 index 646121143..000000000 --- a/tests/Application/config/packages/staging/monolog.yaml +++ /dev/null @@ -1,10 +0,0 @@ -monolog: - handlers: - main: - type: fingers_crossed - action_level: error - handler: nested - nested: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: debug diff --git a/tests/Application/config/packages/staging/swiftmailer.yaml b/tests/Application/config/packages/staging/swiftmailer.yaml deleted file mode 100644 index f43807805..000000000 --- a/tests/Application/config/packages/staging/swiftmailer.yaml +++ /dev/null @@ -1,2 +0,0 @@ -swiftmailer: - disable_delivery: true diff --git a/tests/Application/config/packages/stof_doctrine_extensions.yaml b/tests/Application/config/packages/stof_doctrine_extensions.yaml deleted file mode 100644 index 7770f74e1..000000000 --- a/tests/Application/config/packages/stof_doctrine_extensions.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html -# See the official DoctrineExtensions documentation for more details: https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc/ -stof_doctrine_extensions: - default_locale: '%locale%' diff --git a/tests/Application/config/packages/swiftmailer.yaml b/tests/Application/config/packages/swiftmailer.yaml deleted file mode 100644 index 3bab0d32f..000000000 --- a/tests/Application/config/packages/swiftmailer.yaml +++ /dev/null @@ -1,2 +0,0 @@ -swiftmailer: - url: '%env(MAILER_URL)%' diff --git a/tests/Application/config/packages/test/framework.yaml b/tests/Application/config/packages/test/framework.yaml deleted file mode 100644 index 76d7e5e11..000000000 --- a/tests/Application/config/packages/test/framework.yaml +++ /dev/null @@ -1,4 +0,0 @@ -framework: - test: ~ - session: - storage_id: session.storage.mock_file diff --git a/tests/Application/config/packages/test/monolog.yaml b/tests/Application/config/packages/test/monolog.yaml deleted file mode 100644 index 7e2b9e3aa..000000000 --- a/tests/Application/config/packages/test/monolog.yaml +++ /dev/null @@ -1,6 +0,0 @@ -monolog: - handlers: - main: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: error diff --git a/tests/Application/config/packages/test/swiftmailer.yaml b/tests/Application/config/packages/test/swiftmailer.yaml deleted file mode 100644 index c438f4b25..000000000 --- a/tests/Application/config/packages/test/swiftmailer.yaml +++ /dev/null @@ -1,6 +0,0 @@ -swiftmailer: - disable_delivery: true - logging: true - spool: - type: file - path: "%kernel.cache_dir%/spool" diff --git a/tests/Application/config/packages/test/sylius_theme.yaml b/tests/Application/config/packages/test/sylius_theme.yaml deleted file mode 100644 index 4d34199f5..000000000 --- a/tests/Application/config/packages/test/sylius_theme.yaml +++ /dev/null @@ -1,3 +0,0 @@ -sylius_theme: - sources: - test: ~ diff --git a/tests/Application/config/packages/test/web_profiler.yaml b/tests/Application/config/packages/test/web_profiler.yaml deleted file mode 100644 index 03752de21..000000000 --- a/tests/Application/config/packages/test/web_profiler.yaml +++ /dev/null @@ -1,6 +0,0 @@ -web_profiler: - toolbar: false - intercept_redirects: false - -framework: - profiler: { collect: false } diff --git a/tests/Application/config/packages/test_cached/doctrine.yaml b/tests/Application/config/packages/test_cached/doctrine.yaml deleted file mode 100644 index 49528606d..000000000 --- a/tests/Application/config/packages/test_cached/doctrine.yaml +++ /dev/null @@ -1,16 +0,0 @@ -doctrine: - orm: - entity_managers: - default: - result_cache_driver: - type: memcached - host: localhost - port: 11211 - query_cache_driver: - type: memcached - host: localhost - port: 11211 - metadata_cache_driver: - type: memcached - host: localhost - port: 11211 diff --git a/tests/Application/config/packages/test_cached/fos_rest.yaml b/tests/Application/config/packages/test_cached/fos_rest.yaml deleted file mode 100644 index 2b4189da0..000000000 --- a/tests/Application/config/packages/test_cached/fos_rest.yaml +++ /dev/null @@ -1,3 +0,0 @@ -fos_rest: - exception: - debug: true diff --git a/tests/Application/config/packages/test_cached/framework.yaml b/tests/Application/config/packages/test_cached/framework.yaml deleted file mode 100644 index 76d7e5e11..000000000 --- a/tests/Application/config/packages/test_cached/framework.yaml +++ /dev/null @@ -1,4 +0,0 @@ -framework: - test: ~ - session: - storage_id: session.storage.mock_file diff --git a/tests/Application/config/packages/test_cached/monolog.yaml b/tests/Application/config/packages/test_cached/monolog.yaml deleted file mode 100644 index 7e2b9e3aa..000000000 --- a/tests/Application/config/packages/test_cached/monolog.yaml +++ /dev/null @@ -1,6 +0,0 @@ -monolog: - handlers: - main: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: error diff --git a/tests/Application/config/packages/test_cached/swiftmailer.yaml b/tests/Application/config/packages/test_cached/swiftmailer.yaml deleted file mode 100644 index c438f4b25..000000000 --- a/tests/Application/config/packages/test_cached/swiftmailer.yaml +++ /dev/null @@ -1,6 +0,0 @@ -swiftmailer: - disable_delivery: true - logging: true - spool: - type: file - path: "%kernel.cache_dir%/spool" diff --git a/tests/Application/config/packages/test_cached/sylius_channel.yaml b/tests/Application/config/packages/test_cached/sylius_channel.yaml deleted file mode 100644 index bab83ef25..000000000 --- a/tests/Application/config/packages/test_cached/sylius_channel.yaml +++ /dev/null @@ -1,2 +0,0 @@ -sylius_channel: - debug: true diff --git a/tests/Application/config/packages/test_cached/sylius_theme.yaml b/tests/Application/config/packages/test_cached/sylius_theme.yaml deleted file mode 100644 index 4d34199f5..000000000 --- a/tests/Application/config/packages/test_cached/sylius_theme.yaml +++ /dev/null @@ -1,3 +0,0 @@ -sylius_theme: - sources: - test: ~ diff --git a/tests/Application/config/packages/test_cached/twig.yaml b/tests/Application/config/packages/test_cached/twig.yaml deleted file mode 100644 index 8c6e0b401..000000000 --- a/tests/Application/config/packages/test_cached/twig.yaml +++ /dev/null @@ -1,2 +0,0 @@ -twig: - strict_variables: true diff --git a/tests/Application/config/packages/translation.yaml b/tests/Application/config/packages/translation.yaml deleted file mode 100644 index 1f4f96646..000000000 --- a/tests/Application/config/packages/translation.yaml +++ /dev/null @@ -1,8 +0,0 @@ -framework: - default_locale: '%locale%' - translator: - paths: - - '%kernel.project_dir%/translations' - fallbacks: - - '%locale%' - - 'en' diff --git a/tests/Application/config/packages/twig.yaml b/tests/Application/config/packages/twig.yaml deleted file mode 100644 index 3b315dcc1..000000000 --- a/tests/Application/config/packages/twig.yaml +++ /dev/null @@ -1,4 +0,0 @@ -twig: - paths: ['%kernel.project_dir%/templates'] - debug: '%kernel.debug%' - strict_variables: '%kernel.debug%' diff --git a/tests/Application/config/packages/twig_extensions.yaml b/tests/Application/config/packages/twig_extensions.yaml deleted file mode 100644 index c39fa7fab..000000000 --- a/tests/Application/config/packages/twig_extensions.yaml +++ /dev/null @@ -1,12 +0,0 @@ -services: - _defaults: - public: false - autowire: true - autoconfigure: true - - # Uncomment any lines below to activate that Twig extension - #Twig\Extensions\ArrayExtension: ~ - #Twig\Extensions\DateExtension: ~ - #Twig\Extensions\IntlExtension: ~ - #Twig\Extensions\TextExtension: ~ - Twig\Extra\Intl\IntlExtension: ~ diff --git a/tests/Application/config/packages/validator.yaml b/tests/Application/config/packages/validator.yaml deleted file mode 100644 index 61807db62..000000000 --- a/tests/Application/config/packages/validator.yaml +++ /dev/null @@ -1,3 +0,0 @@ -framework: - validation: - enable_annotations: true diff --git a/tests/Application/config/routes.yaml b/tests/Application/config/routes.yaml deleted file mode 100644 index 5f0779259..000000000 --- a/tests/Application/config/routes.yaml +++ /dev/null @@ -1,2 +0,0 @@ -sylius_admin_order_creation: - resource: "@SyliusAdminOrderCreationPlugin/Resources/config/app/routing.yml" diff --git a/tests/Application/config/routes/dev/web_profiler.yaml b/tests/Application/config/routes/dev/web_profiler.yaml deleted file mode 100644 index 3e79dc212..000000000 --- a/tests/Application/config/routes/dev/web_profiler.yaml +++ /dev/null @@ -1,7 +0,0 @@ -_wdt: - resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" - prefix: /_wdt - -_profiler: - resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" - prefix: /_profiler diff --git a/tests/Application/config/routes/liip_imagine.yaml b/tests/Application/config/routes/liip_imagine.yaml deleted file mode 100644 index 201cbd5d4..000000000 --- a/tests/Application/config/routes/liip_imagine.yaml +++ /dev/null @@ -1,2 +0,0 @@ -_liip_imagine: - resource: "@LiipImagineBundle/Resources/config/routing.yaml" diff --git a/tests/Application/config/routes/sylius_admin.yaml b/tests/Application/config/routes/sylius_admin.yaml deleted file mode 100644 index 1ba48d6cf..000000000 --- a/tests/Application/config/routes/sylius_admin.yaml +++ /dev/null @@ -1,3 +0,0 @@ -sylius_admin: - resource: "@SyliusAdminBundle/Resources/config/routing.yml" - prefix: /admin diff --git a/tests/Application/config/routes/sylius_api.yaml b/tests/Application/config/routes/sylius_api.yaml deleted file mode 100644 index ae01ffce8..000000000 --- a/tests/Application/config/routes/sylius_api.yaml +++ /dev/null @@ -1,3 +0,0 @@ -sylius_api: - resource: "@SyliusApiBundle/Resources/config/routing.yml" - prefix: "%sylius.security.new_api_route%" diff --git a/tests/Application/config/routes/sylius_shop.yaml b/tests/Application/config/routes/sylius_shop.yaml deleted file mode 100644 index 2bb3bd2e1..000000000 --- a/tests/Application/config/routes/sylius_shop.yaml +++ /dev/null @@ -1,23 +0,0 @@ -sylius_shop: - resource: "@SyliusShopBundle/Resources/config/routing.yml" - prefix: /{_locale} - requirements: - _locale: ^[a-z]{2}(?:_[A-Z]{2})?$ - -sylius_shop_payum: - resource: "@SyliusShopBundle/Resources/config/routing/payum.yml" - -sylius_shop_default_locale: - path: / - methods: [GET] - defaults: - _controller: sylius.controller.shop.locale_switch:switchAction - -# see https://web.dev/change-password-url/ -sylius_shop_request_password_reset_token_redirect: - path: /.well-known/change-password - methods: [GET] - controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::redirectAction - defaults: - route: sylius_shop_request_password_reset_token - permanent: false diff --git a/tests/Application/config/services.yaml b/tests/Application/config/services.yaml deleted file mode 100644 index 615506eb5..000000000 --- a/tests/Application/config/services.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Put parameters here that don't need to change on each machine where the app is deployed -# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration -parameters: - locale: en_US diff --git a/tests/Application/config/services_test.yaml b/tests/Application/config/services_test.yaml deleted file mode 100644 index 8d1e8c8d1..000000000 --- a/tests/Application/config/services_test.yaml +++ /dev/null @@ -1,3 +0,0 @@ -imports: - - { resource: "../../../vendor/sylius/sylius/src/Sylius/Behat/Resources/config/services.xml" } - - { resource: "../../Behat/Resources/services.xml" } diff --git a/tests/Application/gulpfile.babel.js b/tests/Application/gulpfile.babel.js deleted file mode 100644 index bbf20669f..000000000 --- a/tests/Application/gulpfile.babel.js +++ /dev/null @@ -1,60 +0,0 @@ -import chug from 'gulp-chug'; -import gulp from 'gulp'; -import yargs from 'yargs'; - -const { argv } = yargs - .options({ - rootPath: { - description: ' path to web assets directory', - type: 'string', - requiresArg: true, - required: false, - }, - nodeModulesPath: { - description: ' path to node_modules directory', - type: 'string', - requiresArg: true, - required: false, - }, - }); - -const config = [ - '--rootPath', - argv.rootPath || '../../../../../../../tests/Application/public/assets', - '--nodeModulesPath', - argv.nodeModulesPath || '../../../../../../../tests/Application/node_modules', -]; - -export const buildAdmin = function buildAdmin() { - return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/AdminBundle/gulpfile.babel.js', { read: false }) - .pipe(chug({ args: config, tasks: 'build' })); -}; -buildAdmin.description = 'Build admin assets.'; - -export const watchAdmin = function watchAdmin() { - return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/AdminBundle/gulpfile.babel.js', { read: false }) - .pipe(chug({ args: config, tasks: 'watch' })); -}; -watchAdmin.description = 'Watch admin asset sources and rebuild on changes.'; - -export const buildShop = function buildShop() { - return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/ShopBundle/gulpfile.babel.js', { read: false }) - .pipe(chug({ args: config, tasks: 'build' })); -}; -buildShop.description = 'Build shop assets.'; - -export const watchShop = function watchShop() { - return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/ShopBundle/gulpfile.babel.js', { read: false }) - .pipe(chug({ args: config, tasks: 'watch' })); -}; -watchShop.description = 'Watch shop asset sources and rebuild on changes.'; - -export const build = gulp.parallel(buildAdmin, buildShop); -build.description = 'Build assets.'; - -gulp.task('admin', buildAdmin); -gulp.task('admin-watch', watchAdmin); -gulp.task('shop', buildShop); -gulp.task('shop-watch', watchShop); - -export default build; diff --git a/tests/Application/package.json b/tests/Application/package.json deleted file mode 100644 index 9271063f9..000000000 --- a/tests/Application/package.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "dependencies": { - "babel-polyfill": "^6.26.0", - "chart.js": "^2.9.3", - "jquery": "^3.2.0", - "lightbox2": "^2.9.0", - "semantic-ui-css": "^2.2.0", - "slick-carousel": "^1.8.1" - }, - "devDependencies": { - "@symfony/webpack-encore": "^0.28.0", - "babel-core": "^6.26.3", - "babel-plugin-external-helpers": "^6.22.0", - "babel-plugin-module-resolver": "^3.1.1", - "babel-plugin-transform-object-rest-spread": "^6.26.0", - "babel-preset-env": "^1.7.0", - "babel-register": "^6.26.0", - "dedent": "^0.7.0", - "eslint": "^4.19.1", - "eslint-config-airbnb-base": "^12.1.0", - "eslint-import-resolver-babel-module": "^4.0.0", - "eslint-plugin-import": "^2.12.0", - "fast-async": "^6.3.7", - "gulp": "^4.0.0", - "gulp-chug": "^0.5", - "gulp-concat": "^2.6.0", - "gulp-debug": "^2.1.2", - "gulp-if": "^2.0.0", - "gulp-livereload": "^3.8.1", - "gulp-order": "^1.1.1", - "gulp-sass": "^4.0.1", - "gulp-sourcemaps": "^1.6.0", - "gulp-uglifycss": "^1.0.5", - "merge-stream": "^1.0.0", - "rollup": "^0.60.7", - "rollup-plugin-babel": "^3.0.4", - "rollup-plugin-commonjs": "^9.1.3", - "rollup-plugin-inject": "^2.0.0", - "rollup-plugin-node-resolve": "^3.3.0", - "rollup-plugin-uglify": "^4.0.0", - "sass-loader": "^7.0.1", - "upath": "^1.1.0", - "yargs": "^6.4.0" - }, - "scripts": { - "build": "gulp build", - "gulp": "gulp build", - "lint": "yarn lint:js", - "lint:js": "eslint gulpfile.babel.js" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/Sylius/Sylius.git" - }, - "author": "Paweł Jędrzejewski", - "license": "MIT" -} diff --git a/tests/Application/public/.htaccess b/tests/Application/public/.htaccess deleted file mode 100644 index 99ed00df8..000000000 --- a/tests/Application/public/.htaccess +++ /dev/null @@ -1,25 +0,0 @@ -DirectoryIndex app.php - - - RewriteEngine On - - RewriteCond %{HTTP:Authorization} ^(.*) - RewriteRule .* - [e=HTTP_AUTHORIZATION:%1] - - RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$ - RewriteRule ^(.*) - [E=BASE:%1] - - RewriteCond %{ENV:REDIRECT_STATUS} ^$ - RewriteRule ^index\.php(/(.*)|$) %{ENV:BASE}/$2 [R=301,L] - - RewriteCond %{REQUEST_FILENAME} -f - RewriteRule .? - [L] - - RewriteRule .? %{ENV:BASE}/index.php [L] - - - - - RedirectMatch 302 ^/$ /index.php/ - - diff --git a/tests/Application/public/index.php b/tests/Application/public/index.php deleted file mode 100644 index c8a547664..000000000 --- a/tests/Application/public/index.php +++ /dev/null @@ -1,27 +0,0 @@ -handle($request); -$response->send(); -$kernel->terminate($request, $response); diff --git a/tests/Application/public/robots.txt b/tests/Application/public/robots.txt deleted file mode 100644 index 214e41196..000000000 --- a/tests/Application/public/robots.txt +++ /dev/null @@ -1,4 +0,0 @@ -# www.robotstxt.org/ -# www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 - -User-agent: * diff --git a/tests/Application/templates/bundles/SyliusAdminBundle/Order/Show/Summary/_item.html.twig b/tests/Application/templates/bundles/SyliusAdminBundle/Order/Show/Summary/_item.html.twig deleted file mode 100644 index fe177a554..000000000 --- a/tests/Application/templates/bundles/SyliusAdminBundle/Order/Show/Summary/_item.html.twig +++ /dev/null @@ -1,52 +0,0 @@ -{% import "@SyliusAdmin/Common/Macro/money.html.twig" as money %} - -{% set orderPromotionAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_PROMOTION_ADJUSTMENT') %} -{% set unitPromotionAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_UNIT_PROMOTION_ADJUSTMENT') %} -{% set shippingAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::SHIPPING_ADJUSTMENT') %} -{% set taxAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::TAX_ADJUSTMENT') %} -{% set orderItemDiscountAdjustment = constant('Sylius\\AdminOrderCreationPlugin\\Form\\Type\\AdjustmentType::ORDER_ITEM_DISCOUNT_ADJUSTMENT') %} - -{% set variant = item.variant %} -{% set product = variant.product %} - -{% set unitDiscount = item.units.first.adjustmentsTotal(unitPromotionAdjustment) + item.getAdjustmentsTotalRecursively(orderItemDiscountAdjustment) / item.quantity %} -{% set discountedUnitPrice = item.fullDiscountedUnitPrice + item.getAdjustmentsTotalRecursively(orderItemDiscountAdjustment) / item.quantity %} -{% set subtotal = item.quantity * (item.unitPrice + item.units.first.adjustmentsTotal(unitPromotionAdjustment) + item.units.first.adjustmentsTotal(orderPromotionAdjustment)) + item.getAdjustmentsTotalRecursively(orderItemDiscountAdjustment) %} - -{% set taxIncluded = sylius_admin_order_unit_tax_included(item) %} -{% set taxExcluded = sylius_admin_order_unit_tax_excluded(item) %} - - - - {% include '@SyliusAdmin/Product/_info.html.twig' %} - - - {{ money.format(item.unitPrice, order.currencyCode) }} - - - {{ money.format(unitDiscount, order.currencyCode) }} - - - {{ money.format(item.units.first.adjustmentsTotal(orderPromotionAdjustment), order.currencyCode) }} - - - {{ money.format(discountedUnitPrice, order.currencyCode) }} - - - {{ item.quantity }} - - - {{ money.format(subtotal, order.currencyCode) }} - - -
{{ money.format(taxExcluded, order.currencyCode) }}
-
-
{{ money.format(taxIncluded, order.currencyCode) }} -
- ({{ 'sylius.ui.included_in_price'|trans }}) -
- - - {{ money.format(item.total, order.currencyCode) }} - - diff --git a/tests/Application/templates/bundles/SyliusAdminBundle/Order/Show/Summary/_totals.html.twig b/tests/Application/templates/bundles/SyliusAdminBundle/Order/Show/Summary/_totals.html.twig deleted file mode 100644 index 54e45f6a0..000000000 --- a/tests/Application/templates/bundles/SyliusAdminBundle/Order/Show/Summary/_totals.html.twig +++ /dev/null @@ -1,70 +0,0 @@ -{% import "@SyliusAdmin/Common/Macro/money.html.twig" as money %} - -{% set orderPromotionAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_PROMOTION_ADJUSTMENT') %} -{% set orderShippingPromotionAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_SHIPPING_PROMOTION_ADJUSTMENT') %} -{% set itemPromotionAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_ITEM_PROMOTION_ADJUSTMENT') %} -{% set shippingAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::SHIPPING_ADJUSTMENT') %} -{% set taxAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::TAX_ADJUSTMENT') %} -{% set orderDiscountAdjustment = constant('Sylius\\AdminOrderCreationPlugin\\Form\\Type\\AdjustmentType::ORDER_DISCOUNT_ADJUSTMENT') %} - -{% set orderShippingPromotions = sylius_aggregate_adjustments(order.getAdjustmentsRecursively(orderShippingPromotionAdjustment)) %} - - - - - {{ 'sylius.ui.tax_total'|trans }}: - {{ money.format(order.taxTotal, order.currencyCode) }} - - - {{ 'sylius.ui.items_total'|trans }}: - {{ money.format(order.itemsTotal, order.currencyCode) }} - - - - - {% if not order.adjustments(shippingAdjustment).isEmpty() %} -
-
{{ 'sylius.ui.shipping'|trans }}:
- {% for adjustment in order.adjustments(shippingAdjustment) %} -
-
{{ money.format(adjustment.amount, order.currencyCode) }}
-
-
- {{ adjustment.label }}: -
-
-
- {% endfor %} -
- {% else %} -

{{ 'sylius.ui.no_shipping_charges'|trans }}

- {% endif %} - - {% if not orderShippingPromotions is empty %} - -
-
{{ 'sylius.ui.shipping_discount'|trans }}:
- {% for label, amount in orderShippingPromotions %} -
-
- {{ money.format(amount, order.currencyCode) }} -
-
- {% endfor %} -
- - {% endif %} - - {{ 'sylius.ui.shipping_total'|trans }}: - {{ money.format(order.shippingTotal, order.currencyCode) }} - - - -{% include '@SyliusAdmin/Order/Show/Summary/_totalsPromotions.html.twig' %} - - - - {{ 'sylius.ui.order_total'|trans }}: - {{ money.format(order.total, order.currencyCode) }} - - diff --git a/tests/Application/templates/bundles/SyliusAdminBundle/Order/Show/_payment.html.twig b/tests/Application/templates/bundles/SyliusAdminBundle/Order/Show/_payment.html.twig deleted file mode 100644 index 72e0eaa99..000000000 --- a/tests/Application/templates/bundles/SyliusAdminBundle/Order/Show/_payment.html.twig +++ /dev/null @@ -1,44 +0,0 @@ -{% import "@SyliusAdmin/Common/Macro/money.html.twig" as money %} -{% import '@SyliusUi/Macro/labels.html.twig' as label %} - -
-
- {% include '@SyliusAdmin/Common/Label/paymentState.html.twig' with {'data': payment.state} %} -
- -
-
- {{ payment.method }} -
-
- {{ money.format(payment.amount, payment.order.currencyCode) }} -
-
- {% if sm_can(payment, 'complete', 'sylius_payment') %} -
-
- - - -
-
- {% if payment.details['payment-link'] is defined %} - - {% endif %} - {% endif %} - {% if sm_can(payment, 'refund', 'sylius_payment') %} -
-
- - - -
-
- {% endif %} -
diff --git a/tests/Behat/Context/Admin/ManagingOrdersContext.php b/tests/Behat/Context/Admin/ManagingOrdersContext.php index b3a0b4a0b..b8201d941 100644 --- a/tests/Behat/Context/Admin/ManagingOrdersContext.php +++ b/tests/Behat/Context/Admin/ManagingOrdersContext.php @@ -2,23 +2,23 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Behat\Context\Admin; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Context\Admin; use Behat\Behat\Context\Context; use Sylius\Behat\NotificationType; +use Sylius\Behat\Service\Checker\EmailCheckerInterface; use Sylius\Behat\Service\NotificationCheckerInterface; use Sylius\Component\Addressing\Comparator\AddressComparatorInterface; use Sylius\Component\Core\Model\AddressInterface; use Sylius\Component\Core\Model\CustomerInterface; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\ProductInterface; -use Sylius\Component\Core\Test\Services\EmailCheckerInterface; -use Tests\Sylius\AdminOrderCreationPlugin\Behat\Element\Admin\OrderCreateFormElementInterface; -use Tests\Sylius\AdminOrderCreationPlugin\Behat\Page\Admin\NewOrderCustomerPageInterface; -use Tests\Sylius\AdminOrderCreationPlugin\Behat\Page\Admin\OrderIndexPageInterface; -use Tests\Sylius\AdminOrderCreationPlugin\Behat\Page\Admin\OrderPreviewPageInterface; -use Tests\Sylius\AdminOrderCreationPlugin\Behat\Page\Admin\OrderShowPageInterface; -use Tests\Sylius\AdminOrderCreationPlugin\Behat\Page\Admin\ReorderPageInterface; +use Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Element\Admin\OrderCreateFormElementInterface; +use Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Page\Admin\NewOrderCustomerPageInterface; +use Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Page\Admin\OrderIndexPageInterface; +use Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Page\Admin\OrderPreviewPageInterface; +use Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Page\Admin\OrderShowPageInterface; +use Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Page\Admin\ReorderPageInterface; use Webmozart\Assert\Assert; final class ManagingOrdersContext implements Context @@ -59,7 +59,7 @@ public function __construct( OrderCreateFormElementInterface $orderCreateFormElement, NotificationCheckerInterface $notificationChecker, EmailCheckerInterface $emailChecker, - AddressComparatorInterface $addressComparator + AddressComparatorInterface $addressComparator, ) { $this->orderIndexPage = $orderIndexPage; $this->newOrderCustomerPage = $newOrderCustomerPage; @@ -237,7 +237,7 @@ public function lowerItemWithProductPriceBy(ProductInterface $product, string $d { $this->orderPreviewPage->lowerItemWithProductPriceBy( $product->getCode(), - str_replace(['$', '€', '£'], '', $discount) + str_replace(['$', '€', '£'], '', $discount), ); } @@ -270,6 +270,14 @@ public function confirmThisOrder(): void $this->orderPreviewPage->confirm(); } + /** + * @When I want to send a payment link email to the customer + */ + public function iWantToSendAPaymentLinkEmailToTheCustomer(): void + { + $this->orderPreviewPage->checkSendPaymentLinkEmail(); + } + /** * @When I go back to the order creation */ @@ -322,7 +330,7 @@ public function shouldBeNotifiedAboutShippingMethodsSelectionRequirements(): voi { Assert::same( 'You need to add some items and shipping address to select from eligible shipping method', - $this->orderCreateFormElement->getShippingMethodsValidationMessage() + $this->orderCreateFormElement->getShippingMethodsValidationMessage(), ); } @@ -333,7 +341,7 @@ public function shouldBeNotifiedThatOrderHasBeenSuccessfullyCreated(): void { $this->notificationChecker->checkNotification( 'Order has been successfully created', - NotificationType::success() + NotificationType::success(), ); } @@ -351,7 +359,7 @@ public function shouldBeNotifiedThatOrderDiscountCannotBeBelow0(): void public function shouldBeNotifiedThatItemWithProductDiscountCannotBeBelow0(ProductInterface $product): void { Assert::true( - $this->orderPreviewPage->hasItemDiscountValidationMessage($product->getCode(), 'Discount cannot be below 0') + $this->orderPreviewPage->hasItemDiscountValidationMessage($product->getCode(), 'Discount cannot be below 0'), ); } @@ -386,7 +394,7 @@ public function thereShouldBePaymentLinkSentTo(string $email): void { Assert::true($this->emailChecker->hasMessageTo( 'New order has been created for you in Admin panel. Check it out in your orders history. To pay for this order, click', - $email + $email, )); } @@ -395,13 +403,10 @@ public function thereShouldBePaymentLinkSentTo(string $email): void */ public function thereShouldBeNoPaymentLinkSentTo(string $email): void { - try { - $this->emailChecker->countMessagesTo($email); - } catch (\InvalidArgumentException $exception) { - return; - } - - throw new \Exception('There should be no messages exception thrown'); + Assert::false($this->emailChecker->hasMessageTo( + 'New order has been created for you in Admin panel. Check it out in your orders history. To pay for this order, click', + $email, + )); } /** @@ -416,7 +421,7 @@ public function thereShouldBeOneOrderForInTheRegistry(string $channelName, Custo 'state' => 'New', 'paymentState' => 'Awaiting payment', 'shippingState' => 'Ready', - 'channel' => $channelName + 'channel' => $channelName, ])); } @@ -434,7 +439,7 @@ public function thereShouldBeOneOrdersForInTheRegistry(int $amountOfOrders, Cust 'state' => 'New', 'paymentState' => 'Awaiting payment', 'shippingState' => 'Ready', - ]) + ]), ); } @@ -446,7 +451,7 @@ public function thisOrderShippingAddressShouldBe( string $street, string $postcode, string $city, - string $countryName + string $countryName, ): void { Assert::true($this->orderShowPage->hasShippingAddress($customerName, $street, $postcode, $city, $countryName)); } @@ -459,7 +464,7 @@ public function thisOrderBillingAddressShouldBe( string $street, string $postcode, string $city, - string $countryName + string $countryName, ): void { Assert::true($this->orderShowPage->hasBillingAddress($customerName, $street, $postcode, $city, $countryName)); } @@ -536,6 +541,22 @@ public function shouldBeAbleToConfirmOrderCreation(): void Assert::true($this->orderPreviewPage->hasConfirmButton()); } + /** + * @Then I should see a validation error + */ + public function iShouldSeeAValidationError(): void + { + Assert::true($this->orderCreateFormElement->hasValidationErrors()); + } + + /** + * @Then I should still be on the order creation form + */ + public function iShouldStillBeOnTheOrderCreationForm(): void + { + Assert::true($this->orderCreateFormElement->isDisplayed()); + } + /** * @Then the product named :productName should not be in the items list */ diff --git a/tests/Behat/Context/Setup/PaymentContext.php b/tests/Behat/Context/Setup/PaymentContext.php new file mode 100644 index 000000000..ba8424eb5 --- /dev/null +++ b/tests/Behat/Context/Setup/PaymentContext.php @@ -0,0 +1,44 @@ +paymentMethodExampleFactory->create([ + 'name' => $paymentMethodName, + 'code' => $paymentMethodCode, + 'gatewayName' => $gatewayLabel, + 'gatewayFactory' => StringInflector::nameToLowercaseCode($gatewayLabel), + 'enabled' => true, + 'channels' => $this->sharedStorage->has('channel') ? [$this->sharedStorage->get('channel')] : [], + ]); + + $this->sharedStorage->set('payment_method', $paymentMethod); + $this->paymentMethodRepository->add($paymentMethod); + } +} diff --git a/tests/Behat/Element/Admin/OrderCreateFormElement.php b/tests/Behat/Element/Admin/OrderCreateFormElement.php index a9444fa2b..dca5e6317 100644 --- a/tests/Behat/Element/Admin/OrderCreateFormElement.php +++ b/tests/Behat/Element/Admin/OrderCreateFormElement.php @@ -2,270 +2,290 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Behat\Element\Admin; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Element\Admin; -use Behat\Mink\Driver\Selenium2Driver; use Behat\Mink\Element\NodeElement; -use Behat\Mink\Exception\Exception; use Behat\Mink\Session; -use DMore\ChromeDriver\ChromeDriver; +use Sylius\Behat\Service\Helper\AutocompleteHelperInterface; use Sylius\Component\Core\Model\AddressInterface; -use Tests\Sylius\AdminOrderCreationPlugin\Behat\Element\Element; -use Tests\Sylius\AdminOrderCreationPlugin\Behat\Service\AutoCompleteSelector; +use Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Element\Element; class OrderCreateFormElement extends Element implements OrderCreateFormElementInterface { - public const TYPE_BILLING = 'billing'; - public const TYPE_SHIPPING = 'shipping'; - - /** @var AutoCompleteSelector */ - private $autoCompleteSelector; public function __construct( Session $session, $parameters, - AutoCompleteSelector $autoCompleteSelector + private readonly AutocompleteHelperInterface $autocompleteHelper, ) { parent::__construct($session, $parameters); - - $this->autoCompleteSelector = $autoCompleteSelector; } public function addProduct(string $productVariantDescriptor): void { - $this->clickOnTabAndWait('Items'); $item = $this->addItemAndWaitForIt(); - $this->autoCompleteSelector->selectOption($item, $productVariantDescriptor); + $this->autocompleteHelper->selectByName( + $this->getDriver(), + $this->getVariantSelect($item)->getXpath(), + $productVariantDescriptor, + ); + $this->waitForComponentIdle(); } public function addMultipleProducts(string $productVariantDescriptor, int $quantity): void { - $this->clickOnTabAndWait('Items'); - $item = $this->addItemAndWaitForIt(); - $this->autoCompleteSelector->selectOption($item, $productVariantDescriptor); - $item->fillField('Quantity', $quantity); + $this->autocompleteHelper->selectByName( + $this->getDriver(), + $this->getVariantSelect($item)->getXpath(), + $productVariantDescriptor, + ); + $this->waitForComponentIdle(); + $item->fillField('Quantity', (string) $quantity); + $this->waitForComponentIdle(); } public function removeProduct(string $productVariantDescriptor): void { $item = $this->getItemWithProductSelected($productVariantDescriptor); - $item->focus(); - - $item->clickLink('Delete'); + $item->pressButton('Delete'); + $this->waitForComponentIdle(); } public function areProductsVisible(): bool { - $this->clickOnTabAndWait('Items'); - + $this->clickOnTab('items'); $item = $this->addItemAndWaitForIt(); - return $this->autoCompleteSelector->areItemsVisible($item); + $results = $this->autocompleteHelper->search( + $this->getDriver(), + $this->getVariantSelect($item)->getXpath(), + 'a', + ); + + return [] !== $results; } public function specifyShippingAddress(AddressInterface $address): void { - $this->clickOnTabAndWait('Shipping address & Billing address'); + $this->clickOnTab('addresses'); $this->fillAddressData( $this->getDocument()->find('css', 'div[id*="shippingAddress"]'), - $address + $address, ); } public function specifyBillingAddress(AddressInterface $address): void { + $this->clickOnTab('addresses'); + $this->fillAddressData( $this->getDocument()->find('css', 'div[id*="billingAddress"]'), - $address + $address, ); } public function getAvailableShippingMethods(): array { - $this->clickOnTabAndWait('Shipments & Payments'); + $shipmentRow = $this->addShipmentRowAndWaitForIt(); - $shipmentsCollection = $this->getDocument()->find('css', '#sylius_admin_order_creation_new_order_shipments'); + $shippingMethods = $shipmentRow->findAll('css', 'select[name$="[method]"] option'); - if (count($shipmentsCollection->findAll('css', '[data-form-collection="item"]')) === 0) { - $shipmentsCollection->clickLink('Add'); - } - - $this->waitForFormToLoad(); - - $shippingMethods = $this->getDocument()->findAll( - 'css', '#sylius_admin_order_creation_new_order_shipments [data-form-collection="item"]:last-child select option' - ); - - $shippingMethods = array_map(function(NodeElement $option) : string { - return $option->getText(); - }, $shippingMethods); - - return $shippingMethods; + return array_map(static fn (NodeElement $option): string => $option->getText(), $shippingMethods); } public function moveToShippingAndPaymentsSection(): void { - $this->clickOnTabAndWait('Shipments & Payments'); + $this->clickOnTab('shipping-payment'); } public function selectShippingMethod(string $shippingMethodName): void { - $this->selectMethod('shipments', 'Shipping Method', $shippingMethodName, true); + $shipmentRow = $this->addShipmentRowAndWaitForIt(); + $shipmentRow->selectFieldOption('Shipping Method', $shippingMethodName); } public function changeShippingMethod(string $shippingMethodName): void { - $this->selectMethod('shipments', 'Shipping Method', $shippingMethodName, false); + $this->clickOnTab('shipping-payment'); + $this->waitForComponentIdle(); + + $shipmentRow = $this->findLast('[data-test-shipment-row]'); + $shipmentRow->selectFieldOption('Shipping Method', $shippingMethodName); } public function selectPaymentMethod(string $paymentMethodName): void { - $this->selectMethod('payments', 'Payment Method', $paymentMethodName, true); + $paymentRow = $this->addPaymentRowAndWaitForIt(); + $paymentRow->selectFieldOption('Payment Method', $paymentMethodName); } public function changePaymentMethod(string $paymentMethodName): void { - $this->selectMethod('payments', 'Payment Method', $paymentMethodName, false); + $this->clickOnTab('shipping-payment'); + $this->waitForComponentIdle(); + + $paymentRow = $this->findLast('[data-test-payment-row]'); + $paymentRow->selectFieldOption('Payment Method', $paymentMethodName); } public function specifyQuantity(string $productVariantDescriptor, int $quantity): void { $item = $this->getItemWithProductSelected($productVariantDescriptor); - $item->fillField('Quantity', $quantity); + $item->fillField('Quantity', (string) $quantity); } public function placeOrder(): void { - $this->getDocument()->waitFor(10, function() { - try { - $this->getDocument()->pressButton('Create'); - - return true; - } catch (Exception $exception) { - return false; - } - }); - + $this->getDocument()->pressButton('Order preview'); } public function selectLocale(string $localeName): void { - $this->clickOnTabAndWait('Locale & Currency'); + $this->clickOnTab('locale'); - $this->getElement('locale')->selectOption($localeName); + $this->getDocument()->selectFieldOption('Locale', $localeName); } public function selectCurrency(string $currencyName): void { - $this->clickOnTabAndWait('Locale & Currency'); + $this->clickOnTab('locale'); - $this->getElement('currency')->selectOption($currencyName); + $this->getDocument()->selectFieldOption('Currency', $currencyName); } public function getShippingMethodsValidationMessage(): string { return $this ->getDocument() - ->find('css', '#shipmentsAndPayments .invalid-data-message') + ->find('css', '[data-test-shipping-methods-requirement]') ->getText() ; } - protected function getDefinedElements(): array - { - return array_merge(parent::getDefinedElements(), [ - 'billing_city' => '#sylius_admin_order_creation_new_order_billingAddress_city', - 'billing_country' => '#sylius_admin_order_creation_new_order_billingAddress_countryCode', - 'billing_first_name' => '#sylius_admin_order_creation_new_order_billingAddress_firstName', - 'billing_last_name' => '#sylius_admin_order_creation_new_order_billingAddress_lastName', - 'billing_postcode' => '#sylius_admin_order_creation_new_order_billingAddress_postcode', - 'billing_street' => '#sylius_admin_order_creation_new_order_billingAddress_street', - 'currency' => '#sylius_admin_order_creation_new_order_currencyCode', - 'locale' => '#sylius_admin_order_creation_new_order_localeCode', - 'payments' => '#sylius_admin_order_creation_new_order_payments', - 'shipments' => '#sylius_admin_order_creation_new_order_shipments', - 'shipping_city' => '#sylius_admin_order_creation_new_order_shippingAddress_city', - 'shipping_country' => '#sylius_admin_order_creation_new_order_shippingAddress_countryCode', - 'shipping_first_name' => '#sylius_admin_order_creation_new_order_shippingAddress_firstName', - 'shipping_last_name' => '#sylius_admin_order_creation_new_order_shippingAddress_lastName', - 'shipping_postcode' => '#sylius_admin_order_creation_new_order_shippingAddress_postcode', - 'shipping_street' => '#sylius_admin_order_creation_new_order_shippingAddress_street', - ]); + public function isAddPaymentButtonVisible(): bool + { + $this->clickOnTab('shipping-payment'); + + $addPaymentButton = $this->getDocument()->findButton('Add payment'); + + return $addPaymentButton !== null && $addPaymentButton->isVisible(); + } + + public function hasValidationErrors(): bool + { + return $this->getDocument()->has('css', '.invalid-feedback'); + } + + public function isDisplayed(): bool + { + return $this->getDocument()->findButton('Order preview') !== null; } private function fillAddressData(NodeElement $addressForm, AddressInterface $address): void { + $countryCode = $address->getCountryCode(); + \assert($countryCode !== null); + $addressForm->fillField('First name', $address->getFirstName()); $addressForm->fillField('Last name', $address->getLastName()); $addressForm->fillField('Street', $address->getStreet()); - $addressForm->fillField('Country', $address->getCountryCode()); + $addressForm->selectFieldOption('Country', $countryCode); $addressForm->fillField('City', $address->getCity()); $addressForm->fillField('Postcode', $address->getPostcode()); } - private function selectMethod(string $type, string $field, string $name, bool $addNew): void + private function addItemAndWaitForIt(): NodeElement + { + $this->clickOnTab('items'); + $this->waitForComponentIdle(); + + $itemsCount = $this->countItems(); + $this->getDocument()->pressButton('Add item'); + + return $this->waitForLast('[data-test-item-row]', $itemsCount); + } + + private function addShipmentRowAndWaitForIt(): NodeElement { - $this->clickOnTabAndWait('Shipments & Payments'); - $this->waitForFormToLoad(); + $this->clickOnTab('shipping-payment'); + $this->waitForComponentIdle(); - $collection = $this->getElement($type); + $shipmentsCount = $this->countShipments(); - if ($addNew) { - $this->getDocument()->waitFor(10, function () use ($collection) { - try { - $collection->clickLink('Add'); + if (0 === $shipmentsCount) { + $this->getDocument()->pressButton('Add shipment'); - return true; - } catch (Exception $exception) { - return false; - } - }); - $this->waitForFormToLoad(); + return $this->waitForLast('[data-test-shipment-row]', $shipmentsCount); } - $this->getDocument()->waitFor(1, function () use ($collection) { - return $collection->has('css', '[data-form-collection="item"]'); - }); + $this->waitForComponentIdle(); - $collection->selectFieldOption($field, $name); + return $this->findLast('[data-test-shipment-row]'); } - private function addItemAndWaitForIt(): NodeElement + private function addPaymentRowAndWaitForIt(): NodeElement { - $itemsCount = $this->countItems(); - $this->getDocument()->waitFor(10, function() { - try { - $this->getDocument()->clickLink('Add'); + $this->clickOnTab('shipping-payment'); + $this->waitForComponentIdle(); - return true; - } catch (Exception $exception) { - return false; - } - }); + $paymentsCount = $this->countPayments(); + $this->getDocument()->pressButton('Add payment'); - $this->getDocument()->waitFor(1, function () use ($itemsCount) { - return $this->countItems() > $itemsCount; + return $this->waitForLast('[data-test-payment-row]', $paymentsCount); + } + + private function waitForLast(string $cssSelector, int $previousCount): NodeElement + { + $result = $this->getDocument()->waitFor(15, function () use ($cssSelector, $previousCount) { + $elements = $this->getDocument()->findAll('css', $cssSelector); + + return count($elements) > $previousCount ? end($elements) : null; }); - return $this->getDocument()->find('css', '#items [data-form-collection="item"]:last-child'); + \assert($result instanceof NodeElement); + + return $result; } private function countItems(): int { - return count($this->getDocument()->findAll('css', '#items [data-form-collection="item"]')); + return count($this->getDocument()->findAll('css', '[data-test-item-row]')); + } + + private function countShipments(): int + { + return count($this->getDocument()->findAll('css', '[data-test-shipment-row]')); + } + + private function countPayments(): int + { + return count($this->getDocument()->findAll('css', '[data-test-payment-row]')); + } + + private function waitForComponentIdle(): void + { + // Live Component debounces model updates (150ms by default) before the + // "busy" attribute appears, so a check right after a field change can + // race ahead of a request that hasn't started yet. + $this->getSession()->wait(300); + + $this->getDocument()->waitFor(15, function () { + return $this->getDocument()->find('css', '[busy]') === null; + }); } private function getItemWithProductSelected(string $productVariantDescriptor): NodeElement { - /** @var NodeElement $item */ - foreach ($this->getDocument()->findAll('css', '#items [data-form-collection="item"]') as $item) { - $selectedProduct = $item->find('css', '.sylius-autocomplete .text')->getText(); + $this->waitForComponentIdle(); + + foreach ($this->getDocument()->findAll('css', '[data-test-item-row]') as $item) { + $selectedOption = $this->getVariantSelect($item)->find('css', 'option[selected]'); - if (strpos($selectedProduct, $productVariantDescriptor) !== false) { + if ($selectedOption !== null && str_contains($selectedOption->getText(), $productVariantDescriptor)) { return $item; } } @@ -273,13 +293,25 @@ private function getItemWithProductSelected(string $productVariantDescriptor): N throw new \InvalidArgumentException(sprintf('There is no item with product with descriptor "%s" selected', $productVariantDescriptor)); } - private function clickOnTabAndWait(string $tabName): void + private function findLast(string $cssSelector): NodeElement { - if (!$this->getDriver() instanceof Selenium2Driver && !$this->getDriver() instanceof ChromeDriver) { - return; - } + $elements = $this->getDocument()->findAll('css', $cssSelector); + \assert([] !== $elements); + + return end($elements); + } + + private function getVariantSelect(NodeElement $item): NodeElement + { + $select = $item->find('css', 'select[name$="[variant]"]'); + \assert($select !== null); + + return $select; + } - $tab = $this->getDocument()->find('css', sprintf('.title:contains("%s")', $tabName)); + private function clickOnTab(string $tabName): void + { + $tab = $this->getDocument()->find('css', sprintf('[data-test-tab="%s"]', $tabName)); if ($tab->hasClass('active')) { return; @@ -290,24 +322,9 @@ private function clickOnTabAndWait(string $tabName): void $this->getDocument()->waitFor(5, function () use ($tabName) { return $this ->getDocument() - ->find('css', sprintf('.title:contains("%s") + .content', $tabName)) + ->find('css', sprintf('[data-test-tab="%s"]', $tabName)) ->hasClass('active') ; }); } - - private function waitForFormToLoad(): void - { - $form = $this->getDocument()->find('css', '[name="sylius_admin_order_creation_new_order"]'); - $this->getDocument()->waitFor(1000, function () use ($form) { - return !$form->hasClass('loading'); - }); - } - - public function isAddPaymentButtonVisible(): bool - { - return - $this->getElement('payments')->find('css', '[data-form-collection="add"]')->isVisible() - ; - } } diff --git a/tests/Behat/Element/Admin/OrderCreateFormElementInterface.php b/tests/Behat/Element/Admin/OrderCreateFormElementInterface.php index 43de22cc5..32e23d94d 100644 --- a/tests/Behat/Element/Admin/OrderCreateFormElementInterface.php +++ b/tests/Behat/Element/Admin/OrderCreateFormElementInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Behat\Element\Admin; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Element\Admin; use Sylius\Component\Core\Model\AddressInterface; @@ -44,4 +44,8 @@ public function placeOrder(): void; public function getShippingMethodsValidationMessage(): string; public function isAddPaymentButtonVisible(): bool; + + public function hasValidationErrors(): bool; + + public function isDisplayed(): bool; } diff --git a/tests/Behat/Element/Element.php b/tests/Behat/Element/Element.php index 693e85167..cd20dc53d 100644 --- a/tests/Behat/Element/Element.php +++ b/tests/Behat/Element/Element.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Behat\Element; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Element; use Behat\Mink\Driver\DriverInterface; use Behat\Mink\Element\DocumentElement; @@ -15,7 +15,7 @@ abstract class Element { /** @var Session */ private $session; - + private $parameters; /** @var DocumentElement|null */ @@ -49,7 +49,7 @@ protected function getElement(string $name, array $parameters = []): NodeElement $this->getSession(), sprintf('Element named "%s" with parameters %s', $name, implode(', ', $parameters)), 'xpath', - $element->getXpath() + $element->getXpath(), ); } @@ -88,7 +88,7 @@ private function createElement(string $name, array $parameters = []): NodeElemen throw new \InvalidArgumentException(sprintf( 'Could not find a defined element with name "%s". The defined ones are: %s.', $name, - implode(', ', array_keys($definedElements)) + implode(', ', array_keys($definedElements)), )); } @@ -96,7 +96,7 @@ private function createElement(string $name, array $parameters = []): NodeElemen return new NodeElement( $this->getSelectorAsXpath($elementSelector, $this->session->getSelectorsHandler()), - $this->session + $this->session, ); } @@ -117,7 +117,8 @@ private function resolveParameters(string $name, array $parameters, array $defin array_map( function ($definedElement) use ($parameters): string { return strtr($definedElement, $parameters); - }, $definedElements[$name] + }, + $definedElements[$name], ); return $definedElements[$name]; diff --git a/tests/Behat/Page/Admin/NewOrderCustomerPage.php b/tests/Behat/Page/Admin/NewOrderCustomerPage.php index 613b13b39..0920ab26f 100644 --- a/tests/Behat/Page/Admin/NewOrderCustomerPage.php +++ b/tests/Behat/Page/Admin/NewOrderCustomerPage.php @@ -2,27 +2,22 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Behat\Page\Admin; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Page\Admin; use Behat\Mink\Session; use FriendsOfBehat\PageObjectExtension\Page\SymfonyPage; +use Sylius\Behat\Service\Helper\AutocompleteHelperInterface; use Symfony\Component\Routing\RouterInterface; -use Tests\Sylius\AdminOrderCreationPlugin\Behat\Service\AutoCompleteSelector; final class NewOrderCustomerPage extends SymfonyPage implements NewOrderCustomerPageInterface { - /** @var AutoCompleteSelector */ - private $autoCompleteSelector; - public function __construct( Session $session, $parameters, RouterInterface $router, - AutoCompleteSelector $autoCompleteSelector + private readonly AutocompleteHelperInterface $autocompleteHelper, ) { parent::__construct($session, $parameters, $router); - - $this->autoCompleteSelector = $autoCompleteSelector; } public function getRouteName(): string @@ -32,7 +27,11 @@ public function getRouteName(): string public function selectCustomer(string $customerEmail): void { - $this->autoCompleteSelector->selectOption($this->getDocument(), $customerEmail); + $this->autocompleteHelper->selectByName( + $this->getDriver(), + $this->getElement('customer_autocomplete')->getXpath(), + $customerEmail, + ); } public function next(): void @@ -48,16 +47,26 @@ public function createCustomer(string $email): void public function selectChannel(string $channelName): void { - $this->getDocument()->selectFieldOption( - 'sylius_admin_order_creation_new_order_customer_create_channel', - $channelName - ); + foreach ($this->getDocument()->findAll('css', 'select[name$="[channel]"]') as $select) { + $select->selectOption($channelName); + } } public function hasCustomerEmailValidationMessage(string $message): bool { - $validationMessage = $this->getDocument()->find('css', 'form .sylius-validation-error'); + foreach ($this->getDocument()->findAll('css', '.invalid-feedback') as $validationMessage) { + if (trim($validationMessage->getText()) === $message) { + return true; + } + } - return $validationMessage !== null && $validationMessage->getText() === $message; + return false; + } + + protected function getDefinedElements(): array + { + return array_merge(parent::getDefinedElements(), [ + 'customer_autocomplete' => 'select[name$="[customer]"]', + ]); } } diff --git a/tests/Behat/Page/Admin/NewOrderCustomerPageInterface.php b/tests/Behat/Page/Admin/NewOrderCustomerPageInterface.php index baeb0ad40..ef1e0ab49 100644 --- a/tests/Behat/Page/Admin/NewOrderCustomerPageInterface.php +++ b/tests/Behat/Page/Admin/NewOrderCustomerPageInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Behat\Page\Admin; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Page\Admin; interface NewOrderCustomerPageInterface { diff --git a/tests/Behat/Page/Admin/OrderIndexPage.php b/tests/Behat/Page/Admin/OrderIndexPage.php index 7598be65d..831893618 100644 --- a/tests/Behat/Page/Admin/OrderIndexPage.php +++ b/tests/Behat/Page/Admin/OrderIndexPage.php @@ -2,30 +2,12 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Behat\Page\Admin; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Page\Admin; -use Behat\Mink\Session; use Sylius\Behat\Page\Admin\Order\IndexPage; -use Sylius\Behat\Service\Accessor\TableAccessorInterface; -use Symfony\Component\Routing\RouterInterface; final class OrderIndexPage extends IndexPage implements OrderIndexPageInterface { - /** @var TableAccessorInterface */ - private $tableAccessor; - - public function __construct( - Session $session, - $parameters, - RouterInterface $router, - TableAccessorInterface $tableAccessor, - $routeName - ) { - parent::__construct($session, $parameters, $router, $tableAccessor, $routeName); - - $this->tableAccessor = $tableAccessor; - } - public function createOrder(): void { $this->getDocument()->clickLink('Create'); diff --git a/tests/Behat/Page/Admin/OrderIndexPageInterface.php b/tests/Behat/Page/Admin/OrderIndexPageInterface.php index 976b753a3..d01a8a7f4 100644 --- a/tests/Behat/Page/Admin/OrderIndexPageInterface.php +++ b/tests/Behat/Page/Admin/OrderIndexPageInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Behat\Page\Admin; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Page\Admin; use Sylius\Behat\Page\Admin\Order\IndexPageInterface; diff --git a/tests/Behat/Page/Admin/OrderPreviewPage.php b/tests/Behat/Page/Admin/OrderPreviewPage.php index 6f2eacd1b..ad503fd6e 100644 --- a/tests/Behat/Page/Admin/OrderPreviewPage.php +++ b/tests/Behat/Page/Admin/OrderPreviewPage.php @@ -2,11 +2,8 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Behat\Page\Admin; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Page\Admin; -use Behat\Mink\Driver\Selenium2Driver; -use Behat\Mink\Element\NodeElement; -use DMore\ChromeDriver\ChromeDriver; use FriendsOfBehat\PageObjectExtension\Page\SymfonyPage; final class OrderPreviewPage extends SymfonyPage implements OrderPreviewPageInterface @@ -18,12 +15,12 @@ public function getRouteName(): string public function getTotal(): string { - return str_replace('Order total: ', '', $this->getDocument()->find('css', 'td#total')->getText()); + return trim($this->getDocument()->find('css', 'td#total')->getText()); } public function getShippingTotal(): string { - return str_replace('Shipping total: ', '', $this->getDocument()->find('css', 'td#shipping-total')->getText()); + return trim($this->getDocument()->find('css', 'td#shipping-total')->getText()); } public function hasProduct(string $productName): bool @@ -43,85 +40,79 @@ public function hasConfirmButton(): bool public function hasOrderDiscountValidationMessage(string $message): bool { - $orderDiscountValidationMessage = $this + $validationMessage = $this ->getDocument() - ->find('css', '#sylius_admin_order_creation_new_order_adjustments .sylius-validation-error') + ->find('css', '[data-test-order-discount] .invalid-feedback') ; - return - $orderDiscountValidationMessage !== null && - $orderDiscountValidationMessage->getText() === $message - ; + return $validationMessage !== null && trim($validationMessage->getText()) === $message; } public function hasItemDiscountValidationMessage(string $productCode, string $message): bool { - $item = $this->getDocument()->find('css', sprintf('table tr:contains("%s") + tr', $productCode)); + $row = $this->getDocument()->find('css', sprintf('[data-test-item]:contains("%s")', $productCode)); + + if ($row === null) { + return false; + } + + $validationMessage = $row->find('css', '[data-test-item-discount] .invalid-feedback'); - return null !== $item->find('css', sprintf('.sylius-validation-error:contains("%s")', $message)); + return $validationMessage !== null && trim($validationMessage->getText()) === $message; } public function hasLocale(string $localeName): bool { - /** @var NodeElement $localeElement */ $localeElement = $this->getDocument()->find('css', '#sylius-order-locale-code'); - return strpos($localeElement->getText(), $localeName) !== false; + return $localeElement !== null && strpos($localeElement->getText(), $localeName) !== false; } public function hasCurrency(string $currencyName): bool { - /** @var NodeElement $localeElement */ - $localeElement = $this->getDocument()->find('css', '#sylius-order-currency'); + $currencyElement = $this->getDocument()->find('css', '#sylius-order-currency'); - return strpos($localeElement->getText(), $currencyName) !== false; + return $currencyElement !== null && strpos($currencyElement->getText(), $currencyName) !== false; } public function lowerOrderPriceBy(string $discount): void { - $discountCollection = $this->getDocument()->find('css', '#sylius_admin_order_creation_new_order_adjustments'); + $discountCard = $this->getDocument()->find('css', '[data-test-order-discount]'); + \assert($discountCard !== null); + $discountCard->pressButton('Add discount'); - $discountCollection->clickLink('Add discount'); - $this->getDocument()->waitFor(1, function () use ($discountCollection) { - return $discountCollection->has('css', '[data-form-collection="item"]'); + $this->getDocument()->waitFor(5, function () use ($discountCard) { + return $discountCard->hasField('Order discount'); }); - $discountCollection->fillField('Order discount', $discount); + $discountCard->fillField('Order discount', $discount); } public function lowerItemWithProductPriceBy(string $productCode, string $discount): void { - $item = $this->getDocument()->find('css', sprintf('table tr:contains("%s") + tr', $productCode)); - $item->clickLink('Add discount'); + $row = $this->getDocument()->find('css', sprintf('[data-test-item]:contains("%s")', $productCode)); + \assert($row !== null); + $row->pressButton('Add discount'); - $discountCollection = $item->find('css', '[data-form-type="collection"]'); - - $this->getDocument()->waitFor(1, function () use ($discountCollection) { - return $discountCollection->has('css', '[data-form-collection="item"]'); + $this->getDocument()->waitFor(5, function () use ($row) { + return $row->hasField('Item discount'); }); - $discountCollection->fillField('Item discount', $discount); + $row->fillField('Item discount', $discount); } - public function confirm(): void + public function checkSendPaymentLinkEmail(): void { - $confirmButton = $this->getDocument()->findButton('Confirm'); - - if ($this->getDriver() instanceof Selenium2Driver || $this->getDriver() instanceof ChromeDriver) { - $confirmButton->focus(); - } + $this->getDocument()->checkField('Send a payment link to the customer via email'); + } - $confirmButton->press(); + public function confirm(): void + { + $this->getDocument()->pressButton('Confirm'); } public function goBack(): void { - $backButton = $this->getDocument()->findButton('Back'); - - if ($this->getDriver() instanceof Selenium2Driver || $this->getDriver() instanceof ChromeDriver) { - $backButton->focus(); - } - - $backButton->press(); + $this->getDocument()->pressButton('Back'); } } diff --git a/tests/Behat/Page/Admin/OrderPreviewPageInterface.php b/tests/Behat/Page/Admin/OrderPreviewPageInterface.php index d93390f6a..838829385 100644 --- a/tests/Behat/Page/Admin/OrderPreviewPageInterface.php +++ b/tests/Behat/Page/Admin/OrderPreviewPageInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Behat\Page\Admin; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Page\Admin; interface OrderPreviewPageInterface { @@ -28,6 +28,8 @@ public function lowerOrderPriceBy(string $discount): void; public function lowerItemWithProductPriceBy(string $productCode, string $discount): void; + public function checkSendPaymentLinkEmail(): void; + public function confirm(): void; public function goBack(): void; diff --git a/tests/Behat/Page/Admin/OrderShowPage.php b/tests/Behat/Page/Admin/OrderShowPage.php index 242caf0ef..89f30ccca 100644 --- a/tests/Behat/Page/Admin/OrderShowPage.php +++ b/tests/Behat/Page/Admin/OrderShowPage.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Behat\Page\Admin; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Page\Admin; use Sylius\Behat\Page\Admin\Order\ShowPage; @@ -10,20 +10,24 @@ final class OrderShowPage extends ShowPage implements OrderShowPageInterface { public function hasPaymentLink(): bool { - $lastPayment = $this->getElement('payments')->find('css', '.item:last-child'); + $lastPayment = $this->getElement('payments')->find('css', '[data-test-payment]:last-child'); - return null !== $lastPayment->find('css', '#payment-link'); + if (null === $lastPayment) { + return false; + } + + return null !== $lastPayment->find('css', '[data-test-pay-via-payment-link]'); } public function hasNoPaymentBlock(): bool { - return null !== $this->getElement('no-payments'); + return null !== $this->getDocument()->find('css', $this->getDefinedElements()['no-payments']); } protected function getDefinedElements(): array { return array_merge(parent::getDefinedElements(), [ - 'no-payments' => '#no-payments', + 'no-payments' => '[data-test-no-payments]', ]); } } diff --git a/tests/Behat/Page/Admin/OrderShowPageInterface.php b/tests/Behat/Page/Admin/OrderShowPageInterface.php index de91651f7..61cb58ea9 100644 --- a/tests/Behat/Page/Admin/OrderShowPageInterface.php +++ b/tests/Behat/Page/Admin/OrderShowPageInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Behat\Page\Admin; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Page\Admin; use Sylius\Behat\Page\Admin\Order\ShowPageInterface; diff --git a/tests/Behat/Page/Admin/ReorderPage.php b/tests/Behat/Page/Admin/ReorderPage.php index 26078b67a..eee5cf379 100644 --- a/tests/Behat/Page/Admin/ReorderPage.php +++ b/tests/Behat/Page/Admin/ReorderPage.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Behat\Page\Admin; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Page\Admin; use FriendsOfBehat\PageObjectExtension\Page\SymfonyPage; diff --git a/tests/Behat/Page/Admin/ReorderPageInterface.php b/tests/Behat/Page/Admin/ReorderPageInterface.php index 2a5598b6c..07e52b55e 100644 --- a/tests/Behat/Page/Admin/ReorderPageInterface.php +++ b/tests/Behat/Page/Admin/ReorderPageInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Behat\Page\Admin; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Page\Admin; use FriendsOfBehat\PageObjectExtension\Page\SymfonyPageInterface; diff --git a/tests/Behat/Resources/services.xml b/tests/Behat/Resources/services.xml index 015d4763f..afe8f901b 100644 --- a/tests/Behat/Resources/services.xml +++ b/tests/Behat/Resources/services.xml @@ -4,35 +4,39 @@ - - + + - - - - + + + + - - - - - - - - - - + + + + - + + + + + + + + + + + - + - - + + diff --git a/tests/Behat/Resources/suites/admin_reordering.yml b/tests/Behat/Resources/suites/admin_reordering.yml index 3fba425e8..9333c3ad0 100644 --- a/tests/Behat/Resources/suites/admin_reordering.yml +++ b/tests/Behat/Resources/suites/admin_reordering.yml @@ -3,7 +3,7 @@ default: ui_admin_order_creation_admin_reordering: contexts: - sylius.behat.context.hook.doctrine_orm - - sylius.behat.context.hook.email_spool + - sylius.behat.context.hook.mailer - sylius.behat.context.setup.admin_security - sylius.behat.context.setup.admin_user @@ -36,6 +36,7 @@ default: - sylius.behat.context.ui.channel - sylius.behat.context.ui.email - - Tests\Sylius\AdminOrderCreationPlugin\Behat\Context\Admin\ManagingOrdersContext + - Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Context\Admin\ManagingOrdersContext + - Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Context\Setup\PaymentContext filters: tags: "@admin_order_creation_admin_reordering && @ui" diff --git a/tests/Behat/Resources/suites/managing_orders.yml b/tests/Behat/Resources/suites/managing_orders.yml index 04061b6b2..6e38429cd 100644 --- a/tests/Behat/Resources/suites/managing_orders.yml +++ b/tests/Behat/Resources/suites/managing_orders.yml @@ -3,7 +3,7 @@ default: ui_admin_order_creation_managing_orders: contexts: - sylius.behat.context.hook.doctrine_orm - - sylius.behat.context.hook.email_spool + - sylius.behat.context.hook.mailer - sylius.behat.context.setup.admin_security - sylius.behat.context.setup.admin_user @@ -54,6 +54,7 @@ default: - sylius.behat.context.ui.shop.checkout.complete - sylius.behat.context.ui.shop.currency - - Tests\Sylius\AdminOrderCreationPlugin\Behat\Context\Admin\ManagingOrdersContext + - Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Context\Admin\ManagingOrdersContext + - Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Behat\Context\Setup\PaymentContext filters: tags: "@admin_order_creation_managing_orders && @ui" diff --git a/tests/Behat/Service/AutoCompleteSelector.php b/tests/Behat/Service/AutoCompleteSelector.php deleted file mode 100644 index 8b9b0fd60..000000000 --- a/tests/Behat/Service/AutoCompleteSelector.php +++ /dev/null @@ -1,45 +0,0 @@ -waitForItemsToLoad($scope); - - $scope->waitFor(10, function () use ($scope, $optionName) { - try { - $scope->find('css', sprintf('.sylius-autocomplete .menu .item:contains("%s")', $optionName))->click(); - - return true; - } catch (FatalThrowableError $exception) { - return false; - } - }); - } - - public function areItemsVisible(ElementInterface $scope): bool - { - $this->waitForItemsToLoad($scope); - - return strpos($scope->find('css', '.sylius-autocomplete .menu')->getText(), 'No results found') !== false; - } - - private function waitForItemsToLoad(ElementInterface $scope): void - { - $scope->find('css', '.sylius-autocomplete .icon')->click(); - - $scope->waitFor(10, function() use ($scope) { - return $scope - ->find('css', '.sylius-autocomplete .menu') - ->hasClass('visible') - ; - }); - } -} diff --git a/tests/Application/src/Migrations/.gitkeep b/tests/Functional/.gitkeep similarity index 100% rename from tests/Application/src/Migrations/.gitkeep rename to tests/Functional/.gitkeep diff --git a/tests/Application/var/.gitkeep b/tests/Integration/.gitkeep similarity index 100% rename from tests/Application/var/.gitkeep rename to tests/Integration/.gitkeep diff --git a/tests/Integration/DependencyInjection/OrderFactoryServiceWiringTest.php b/tests/Integration/DependencyInjection/OrderFactoryServiceWiringTest.php new file mode 100644 index 000000000..d9b54e9a5 --- /dev/null +++ b/tests/Integration/DependencyInjection/OrderFactoryServiceWiringTest.php @@ -0,0 +1,23 @@ +getContainer(); + + $orderFactory = $container->get(OrderFactoryInterface::class); + + self::assertInstanceOf(OrderFactoryInterface::class, $orderFactory); + } +} diff --git a/tests/TestApplication/.env b/tests/TestApplication/.env new file mode 100644 index 000000000..2d764e838 --- /dev/null +++ b/tests/TestApplication/.env @@ -0,0 +1,8 @@ +DATABASE_URL=mysql://root@127.0.0.1/sylius_admin_order_creation_plugin_%kernel.environment% + +BEHAT_BASE_URL="https://127.0.0.1:8080/" +BEHAT_CHROME_URL="http://127.0.0.1:9222" + +SYLIUS_TEST_APP_BUNDLES_PATH="tests/TestApplication/config/bundles.php" +SYLIUS_TEST_APP_CONFIGS_TO_IMPORT="@WebgriffeSyliusAdminOrderCreationPlugin/tests/TestApplication/config/config.yaml" +SYLIUS_TEST_APP_ROUTES_TO_IMPORT="@WebgriffeSyliusAdminOrderCreationPlugin/tests/TestApplication/config/routes.yaml" diff --git a/tests/TestApplication/.env.test b/tests/TestApplication/.env.test new file mode 100644 index 000000000..81ecc6533 --- /dev/null +++ b/tests/TestApplication/.env.test @@ -0,0 +1 @@ +DATABASE_URL=mysql://root@127.0.0.1/sylius_admin_order_creation_plugin_%kernel.environment% diff --git a/tests/TestApplication/config/bundles.php b/tests/TestApplication/config/bundles.php new file mode 100644 index 000000000..21325d35f --- /dev/null +++ b/tests/TestApplication/config/bundles.php @@ -0,0 +1,7 @@ + ['all' => true], +]; diff --git a/tests/TestApplication/config/config.yaml b/tests/TestApplication/config/config.yaml new file mode 100644 index 000000000..9f6f528b1 --- /dev/null +++ b/tests/TestApplication/config/config.yaml @@ -0,0 +1,16 @@ +imports: + - { resource: "packages/*.yaml" } + - { resource: "@WebgriffeSyliusAdminOrderCreationPlugin/config/config.yaml" } + - { resource: "services_test.php" } + +sylius_customer: + resources: + customer: + classes: + repository: Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Repository\CustomerRepository + +sylius_product: + resources: + product_variant: + classes: + repository: Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Repository\ProductVariantRepository diff --git a/tests/TestApplication/config/packages/monolog.yaml b/tests/TestApplication/config/packages/monolog.yaml new file mode 100644 index 000000000..62e6ab019 --- /dev/null +++ b/tests/TestApplication/config/packages/monolog.yaml @@ -0,0 +1,10 @@ +when@dev: + monolog: + handlers: + main: + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" + level: warning + firephp: + type: firephp + level: info diff --git a/tests/TestApplication/config/routes.yaml b/tests/TestApplication/config/routes.yaml new file mode 100644 index 000000000..92612b9b5 --- /dev/null +++ b/tests/TestApplication/config/routes.yaml @@ -0,0 +1,2 @@ +sylius_admin_order_creation_plugin: + resource: "@WebgriffeSyliusAdminOrderCreationPlugin/config/routing.yaml" diff --git a/tests/TestApplication/config/services_test.php b/tests/TestApplication/config/services_test.php new file mode 100644 index 000000000..ced5c42b7 --- /dev/null +++ b/tests/TestApplication/config/services_test.php @@ -0,0 +1,12 @@ +env(), 'test')) { + $container->import('../../../vendor/sylius/sylius/src/Sylius/Behat/Resources/config/services.xml'); + $container->import('@WebgriffeSyliusAdminOrderCreationPlugin/tests/Behat/Resources/services.xml'); + } +}; diff --git a/tests/Application/Doctrine/ORM/CustomerRepository.php b/tests/TestApplication/src/Repository/CustomerRepository.php similarity index 52% rename from tests/Application/Doctrine/ORM/CustomerRepository.php rename to tests/TestApplication/src/Repository/CustomerRepository.php index 77d7b6d6b..12c674c71 100644 --- a/tests/Application/Doctrine/ORM/CustomerRepository.php +++ b/tests/TestApplication/src/Repository/CustomerRepository.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Application\Doctrine\ORM; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Repository; -use Sylius\AdminOrderCreationPlugin\Doctrine\ORM\CustomerRepositoryInterface; -use Sylius\AdminOrderCreationPlugin\Doctrine\ORM\CustomerRepositoryTrait; +use Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM\CustomerRepositoryInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM\CustomerRepositoryTrait; use Sylius\Bundle\CoreBundle\Doctrine\ORM\CustomerRepository as BaseCustomerRepository; final class CustomerRepository extends BaseCustomerRepository implements CustomerRepositoryInterface diff --git a/tests/Application/Doctrine/ORM/ProductVariantRepository.php b/tests/TestApplication/src/Repository/ProductVariantRepository.php similarity index 54% rename from tests/Application/Doctrine/ORM/ProductVariantRepository.php rename to tests/TestApplication/src/Repository/ProductVariantRepository.php index a7719f127..27a00a6cc 100644 --- a/tests/Application/Doctrine/ORM/ProductVariantRepository.php +++ b/tests/TestApplication/src/Repository/ProductVariantRepository.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Tests\Sylius\AdminOrderCreationPlugin\Application\Doctrine\ORM; +namespace Tests\Webgriffe\SyliusAdminOrderCreationPlugin\Repository; -use Sylius\AdminOrderCreationPlugin\Doctrine\ORM\ProductVariantRepositoryInterface; -use Sylius\AdminOrderCreationPlugin\Doctrine\ORM\ProductVariantRepositoryTrait; +use Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM\ProductVariantRepositoryInterface; +use Webgriffe\SyliusAdminOrderCreationPlugin\Doctrine\ORM\ProductVariantRepositoryTrait; use Sylius\Bundle\CoreBundle\Doctrine\ORM\ProductVariantRepository as BaseProductVariantRepository; final class ProductVariantRepository extends BaseProductVariantRepository implements ProductVariantRepositoryInterface diff --git a/tests/Application/translations/.gitignore b/tests/Unit/.gitkeep similarity index 100% rename from tests/Application/translations/.gitignore rename to tests/Unit/.gitkeep diff --git a/tests/Unit/DependencyInjection/ConfigurationTest.php b/tests/Unit/DependencyInjection/ConfigurationTest.php new file mode 100644 index 000000000..35b62aa9f --- /dev/null +++ b/tests/Unit/DependencyInjection/ConfigurationTest.php @@ -0,0 +1,44 @@ +processConfiguration(new Configuration(), []); + + self::assertSame(['offline'], $processedConfiguration['offline_gateway_names']); + } + + public function testItAllowsConfiguringCustomOfflineGatewayNames(): void + { + $processedConfiguration = (new Processor())->processConfiguration(new Configuration(), [ + ['offline_gateway_names' => ['offline', 'bank_transfer']], + ]); + + self::assertSame(['offline', 'bank_transfer'], $processedConfiguration['offline_gateway_names']); + } + + public function testItDefaultsPaymentLinkGenerationToEnabled(): void + { + $processedConfiguration = (new Processor())->processConfiguration(new Configuration(), []); + + self::assertTrue($processedConfiguration['payment_link_generation_enabled']); + } + + public function testItAllowsDisablingPaymentLinkGeneration(): void + { + $processedConfiguration = (new Processor())->processConfiguration(new Configuration(), [ + ['payment_link_generation_enabled' => false], + ]); + + self::assertFalse($processedConfiguration['payment_link_generation_enabled']); + } +} diff --git a/src/Resources/translations/messages.de.yml b/translations/messages.de.yaml similarity index 100% rename from src/Resources/translations/messages.de.yml rename to translations/messages.de.yaml diff --git a/src/Resources/translations/messages.en.yml b/translations/messages.en.yaml similarity index 76% rename from src/Resources/translations/messages.en.yml rename to translations/messages.en.yaml index 4dbb17792..63a4bf3d8 100644 --- a/src/Resources/translations/messages.en.yml +++ b/translations/messages.en.yaml @@ -8,14 +8,21 @@ sylius_admin_order_creation: order_price: 'Order price' ui: add_discount: 'Add discount' + add_item: 'Add item' + add_payment: 'Add payment' + add_shipment: 'Add shipment' confirm: 'Confirm' create_new: 'Create new' customer_email: 'Customer email cannot be empty' customer_selection: 'Customer selection' + existing_customer: 'Existing customer' item_discount: 'Item discount' new_customer_email: 'New customer email' no_customer_selected: "You haven't selected a customer" + no_discount: 'No discount applied' order_discount: 'Order discount' order_preview: 'Order preview' + pay: 'Pay' reorder: 'Reorder' + send_payment_link_email: 'Send a payment link to the customer via email' shipping_methods_selection_requirement: 'You need to add some items and shipping address to select from eligible shipping method' diff --git a/src/Resources/translations/messages.fr.yml b/translations/messages.fr.yaml similarity index 100% rename from src/Resources/translations/messages.fr.yml rename to translations/messages.fr.yaml diff --git a/src/Resources/translations/messages.ru.yml b/translations/messages.ru.yaml similarity index 100% rename from src/Resources/translations/messages.ru.yml rename to translations/messages.ru.yaml diff --git a/src/Resources/translations/validators.de.yml b/translations/validators.de.yaml similarity index 71% rename from src/Resources/translations/validators.de.yml rename to translations/validators.de.yaml index d1af7ea58..8c1d8c003 100644 --- a/src/Resources/translations/validators.de.yml +++ b/translations/validators.de.yaml @@ -1,3 +1,4 @@ sylius_admin_order_creation: order_discount: 'Der Rabatt kann nicht unter 0 liegen' customer_email: 'Die E-Mail Adresse des Kunden darf nicht leer sein' + no_customer_selected: 'Sie haben keinen Kunden ausgewählt' diff --git a/src/Resources/translations/validators.en.yml b/translations/validators.en.yaml similarity index 68% rename from src/Resources/translations/validators.en.yml rename to translations/validators.en.yaml index 4cf738657..5acdad121 100644 --- a/src/Resources/translations/validators.en.yml +++ b/translations/validators.en.yaml @@ -1,3 +1,4 @@ sylius_admin_order_creation: order_discount: 'Discount cannot be below 0' customer_email: 'Customer email cannot be empty' + no_customer_selected: "You haven't selected a customer" diff --git a/src/Resources/translations/validators.fr.yml b/translations/validators.fr.yaml similarity index 71% rename from src/Resources/translations/validators.fr.yml rename to translations/validators.fr.yaml index e7425c39d..6b6dcd7b6 100644 --- a/src/Resources/translations/validators.fr.yml +++ b/translations/validators.fr.yaml @@ -1,3 +1,4 @@ sylius_admin_order_creation: order_discount: La réduction ne peut pas être inférieure à zéro customer_email: L'email du client ne peut pas être vide + no_customer_selected: Vous n'avez sélectionné aucun client diff --git a/src/Resources/translations/validators.ru.yml b/translations/validators.ru.yaml similarity index 100% rename from src/Resources/translations/validators.ru.yml rename to translations/validators.ru.yaml