From 17d8a9388cf76a01f13c83dfa55bc9ed18e11a78 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Tue, 30 Jul 2024 15:29:20 +0200 Subject: [PATCH 01/40] OP-289: Update readme --- README.md | 80 ++++++++++++++++++++++++---------------------- UPGRADE-1.3.md | 28 ---------------- UPGRADE-1.4.md | 86 -------------------------------------------------- 3 files changed, 43 insertions(+), 151 deletions(-) delete mode 100644 UPGRADE-1.3.md delete mode 100644 UPGRADE-1.4.md diff --git a/README.md b/README.md index 993371ac..02a9466c 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ This **open-source plugin was developed to help the Sylius community**. If you h composer require bitbag/product-bundle-plugin --no-scripts ``` -2. Add plugin dependencies to your `config/bundles.php` file after `Sylius\Bundle\ApiBundle\SyliusApiBundle`. +2. (optional) Add plugin dependencies to your `config/bundles.php` file after `Sylius\Bundle\ApiBundle\SyliusApiBundle`. ```php return [ @@ -59,7 +59,7 @@ This **open-source plugin was developed to help the Sylius community**. If you h ]; ``` -3. Import required config in your `config/packages/_sylius.yaml` file: +3. (optional) Import required config in your `config/packages/_sylius.yaml` file: ```yaml # config/packages/_sylius.yaml @@ -70,7 +70,7 @@ This **open-source plugin was developed to help the Sylius community**. If you h - { resource: "@BitBagSyliusProductBundlePlugin/Resources/config/config.yml" } ``` -4. Import routing in your `config/routes.yaml` file: +4. (optional) Import routing in your `config/routes.yaml` file: ```yaml @@ -105,6 +105,10 @@ This **open-source plugin was developed to help the Sylius community**. If you h **Note.** If you're using Attributes Mapping, please use your `ProductTrait` in your `Product` entity instead of plugins `ProductBundlesAwareTrait`. ```php + ['test' => true, 'test_cached' => true], - ]; - ``` -* If you use our Travis CI configuration, follow [these changes](https://github.com/Sylius/PluginSkeleton/pull/156/files#diff-354f30a63fb0907d4ad57269548329e3) introduced in `.travis.yml` file -* Create `tests/Application/config/services_test.yaml` file with the following code and add these your own Behat services as well: - ```yaml - imports: - - { resource: "../../../vendor/sylius/sylius/src/Sylius/Behat/Resources/config/services.xml" } - ``` -* Remove all `__symfony__` prefixes in your Behat services -* Remove all `` tags from your Behat services -* Make your Behat services public by default with `` -* Change `contexts_services ` in your suite definitions to `contexts` -* Take a look at [SymfonyExtension UPGRADE guide](https://github.com/FriendsOfBehat/SymfonyExtension/blob/master/UPGRADE-2.0.md) if you have any more problems - -### Phpstan - -* Fix the container XML path parameter in the `phpstan.neon` file as done [here](https://github.com/Sylius/PluginSkeleton/commit/37fa614dbbcf8eb31b89eaf202b4bd4d89a5c7b3) - -# UPGRADE FROM `v1.2.X` TO `v1.4.0` - -Firstly, check out the [PluginSkeleton 1.3 upgrade guide](https://github.com/Sylius/PluginSkeleton/blob/1.4/UPGRADE-1.3.md) to update Sylius version step by step. -To upgrade to Sylius 1.4 follow instructions from [the previous section](https://github.com/Sylius/PluginSkeleton/blob/1.4/UPGRADE-1.4.md#upgrade-from-v13x-to-v140) with following changes: - -### Doctrine migrations - -* Change namespaces of copied migrations to `Sylius\Migrations` - -### Dotenv - -* These changes are not required, but can be done as well, if you've changed application directory structure in `1.2.x` to `1.3` update - -### Behat - -* Add `\FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle()` to your bundles lists in `tests/Application/AppKernel.php` (preferably only in `test` environment) -* Import Sylius Behat services in `tests/Application/config/config_test.yml` and your own Behat services as well: - ```yaml - imports: - - { resource: "../../../../vendor/sylius/sylius/src/Sylius/Behat/Resources/config/services.xml" } - ``` -* Specify test application's kernel path in `behat.yml`: - ```yaml - FriendsOfBehat\SymfonyExtension: - kernel: - class: AppKernel - path: tests/Application/app/AppKernel.php - ``` From 16e4782c81d77954eba1eb3a37f615273f072d4c Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Tue, 30 Jul 2024 15:31:03 +0200 Subject: [PATCH 02/40] OP-289: Add docker support --- .docker/fpm.conf | 21 ++++++++++++ .docker/nginx.conf | 48 ++++++++++++++++++++++++++ .docker/php.ini | 21 ++++++++++++ .docker/supervisord.conf | 14 ++++++++ Dockerfile | 73 ++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 46 +++++++++++++++++++++++++ 6 files changed, 223 insertions(+) create mode 100644 .docker/fpm.conf create mode 100644 .docker/nginx.conf create mode 100644 .docker/php.ini create mode 100644 .docker/supervisord.conf create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/.docker/fpm.conf b/.docker/fpm.conf new file mode 100644 index 00000000..4f0c372e --- /dev/null +++ b/.docker/fpm.conf @@ -0,0 +1,21 @@ +[www] +user = www-data +group = www-data + +listen = /var/run/php-www.sock +listen.owner = www-data +listen.group = www-data +listen.mode = 0660 + +clear_env = no + +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 + +pm.status_path = /status +catch_workers_output = yes + +security.limit_extensions = .php diff --git a/.docker/nginx.conf b/.docker/nginx.conf new file mode 100644 index 00000000..7fa27004 --- /dev/null +++ b/.docker/nginx.conf @@ -0,0 +1,48 @@ +user www-data; +worker_processes auto; +daemon off; +pid /run/nginx.pid; + +include /etc/nginx/modules-enabled/*.conf; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + server_tokens off; + + client_max_body_size 64m; + sendfile on; + tcp_nodelay on; + tcp_nopush on; + + gzip_vary on; + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + server { + listen 80; + + root /app/tests/Application/public; + index index.php; + + location / { + try_files $uri /index.php$is_args$args; + } + + location ~ \.php$ { + include fastcgi_params; + + fastcgi_pass unix:/var/run/php-www.sock; + fastcgi_split_path_info ^(.+\.php)(/.*)$; + + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + fastcgi_param DOCUMENT_ROOT $realpath_root; + } + } +} diff --git a/.docker/php.ini b/.docker/php.ini new file mode 100644 index 00000000..dfe91c67 --- /dev/null +++ b/.docker/php.ini @@ -0,0 +1,21 @@ +[PHP] +memory_limit=512M + +[date] +date.timezone=${PHP_DATE_TIMEZONE} + +[opcache] +opcache.enable=0 +opcache.memory_consumption=256 +opcache.max_accelerated_files=20000 +opcache.validate_timestamps=0 +;opcache.preload=/app/config/preload.php +opcache.preload_user=www-data +opcache.jit=1255 +opcache.jit_buffer_size=256M + +;[xdebug] +;xdebug.mode=debug +;xdebug.client_host=host.docker.internal +;xdebug.start_with_request = yes +;xdebug.client_port = 9003 diff --git a/.docker/supervisord.conf b/.docker/supervisord.conf new file mode 100644 index 00000000..913adb67 --- /dev/null +++ b/.docker/supervisord.conf @@ -0,0 +1,14 @@ +[supervisord] +nodaemon = true +user = root +pidfile = /run/supervisord.pid + +[program:nginx] +command = /usr/sbin/nginx +user = root +autostart = true + +[program:php-fpm] +command = /usr/sbin/php-fpm -F +user = root +autostart = true diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..834732c5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,73 @@ +FROM ubuntu:20.04 +ARG DEBIAN_FRONTEND=noninteractive +ARG PHP_VERSION=8.1 +ENV LC_ALL=C.UTF-8 + +# Install basic tools +RUN apt-get update && apt-get install -y \ + software-properties-common \ + curl \ + make \ + supervisor \ + unzip \ + python2 \ + g++ + +# Append NODE, NGINX and PHP repositories +RUN add-apt-repository ppa:ondrej/php \ + && add-apt-repository ppa:ondrej/nginx \ + && curl -sL https://deb.nodesource.com/setup_14.x | bash - + +# Install required PHP extensions +RUN apt-get update && apt-get install -y \ + nodejs \ + nginx \ + php${PHP_VERSION} \ + php${PHP_VERSION}-apcu \ + php${PHP_VERSION}-calendar \ + php${PHP_VERSION}-common \ + php${PHP_VERSION}-cli \ + php${PHP_VERSION}-ctype \ + php${PHP_VERSION}-curl \ + php${PHP_VERSION}-dom \ + php${PHP_VERSION}-exif \ + php${PHP_VERSION}-fpm \ + php${PHP_VERSION}-gd \ + php${PHP_VERSION}-intl \ + php${PHP_VERSION}-mbstring \ + php${PHP_VERSION}-mysql \ + php${PHP_VERSION}-opcache \ + php${PHP_VERSION}-pdo \ + php${PHP_VERSION}-pgsql \ + php${PHP_VERSION}-sqlite \ + php${PHP_VERSION}-xml \ + php${PHP_VERSION}-xsl \ + php${PHP_VERSION}-yaml \ + php${PHP_VERSION}-zip + +# Install Composer +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename composer + +# Cleanup +RUN apt-get remove --purge -y software-properties-common curl && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* /usr/share/man/* + +# Create directory for php-fpm socket +# Link php-fpm binary file without version +# -p Creates missing intermediate path name directories +RUN ln -s /usr/sbin/php-fpm${PHP_VERSION} /usr/sbin/php-fpm && mkdir -p /run/php + +# Install yarn +RUN npm install -g yarn && npm cache clean --force + +# Initialize config files +COPY .docker/supervisord.conf /etc/supervisor/conf.d/supervisor.conf +COPY .docker/nginx.conf /etc/nginx/nginx.conf +COPY .docker/fpm.conf /etc/php/${PHP_VERSION}/fpm/pool.d/www.conf +COPY .docker/php.ini /etc/php/${PHP_VERSION}/fpm/php.ini +COPY .docker/php.ini /etc/php/${PHP_VERSION}/cli/php.ini + +WORKDIR /app + +EXPOSE 80 + +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..8cbe025b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,46 @@ +services: + app: + container_name: app + build: + context: . + environment: + APP_ENV: "dev" + DATABASE_URL: "mysql://root:mysql@mysql/sylius_%kernel.environment%?charset=utf8mb4" +# DATABASE_URL: "pgsql://root:postgres@postgres/sylius_%kernel.environment%?charset=utf8" # When using postgres + PHP_DATE_TIMEZONE: "Europe/Warsaw" +# PHP_IDE_CONFIG: serverName=wishlist + volumes: + - ./:/app:delegated + - ./.docker/php.ini:/etc/php8/php.ini:delegated + - ./.docker/nginx.conf:/etc/nginx/nginx.conf:delegated + ports: + - 80:80 + depends_on: + - mysql + networks: + - sylius + + mysql: + container_name: mysql + image: mysql:8.0 + platform: linux/amd64 + environment: + MYSQL_ROOT_PASSWORD: mysql + ports: + - ${MYSQL_PORT:-3306}:3306 + networks: + - sylius + +# postgres: +# image: postgres:14-alpine +# environment: +# POSTGRES_USER: root +# POSTGRES_PASSWORD: postgres +# ports: +# - ${POSTGRES_PORT:-5432}:5432 +# networks: +# - sylius + +networks: + sylius: + driver: bridge From 8dac202275b7241c5b33b4baac0bcf81aaeaaca0 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Wed, 31 Jul 2024 13:24:48 +0200 Subject: [PATCH 03/40] OP-289: Add mapping configuration to traits --- .../ProductBundleOrderItemsAwareTrait.php | 16 +++++++++++++++- src/Entity/ProductBundlesAwareTrait.php | 17 ++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/Entity/ProductBundleOrderItemsAwareTrait.php b/src/Entity/ProductBundleOrderItemsAwareTrait.php index c35892ce..7ae8bd27 100644 --- a/src/Entity/ProductBundleOrderItemsAwareTrait.php +++ b/src/Entity/ProductBundleOrderItemsAwareTrait.php @@ -12,10 +12,24 @@ namespace BitBag\SyliusProductBundlePlugin\Entity; use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\ORM\Mapping as ORM; trait ProductBundleOrderItemsAwareTrait { - /** @var ArrayCollection|ProductBundleOrderItemInterface[] */ + /** + * @var ArrayCollection|ProductBundleOrderItemInterface[] + * + * @ORM\OneToMany( + * targetEntity=ProductBundleOrderItemInterface::class, + * mappedBy="orderItem", + * cascade={"all"} + * ) + */ + #[ORM\OneToMany( + targetEntity: ProductBundleOrderItemInterface::class, + mappedBy: 'orderItem', + cascade: ['all'], + )] protected $productBundleOrderItems; protected function init(): void diff --git a/src/Entity/ProductBundlesAwareTrait.php b/src/Entity/ProductBundlesAwareTrait.php index e054f480..24b8dbc9 100644 --- a/src/Entity/ProductBundlesAwareTrait.php +++ b/src/Entity/ProductBundlesAwareTrait.php @@ -11,9 +11,24 @@ namespace BitBag\SyliusProductBundlePlugin\Entity; +use Doctrine\ORM\Mapping as ORM; + trait ProductBundlesAwareTrait { - /** @var ProductBundleInterface */ + /** + * @var ProductBundleInterface + * + * @ORM\OneToOne( + * targetEntity=ProductBundleInterface::class, + * mappedBy="product", + * cascade={"all"} + * ) + */ + #[ORM\OneToOne( + targetEntity: ProductBundleInterface::class, + mappedBy: 'product', + cascade: ['all'], + )] protected $productBundle; public function getProductBundle(): ?ProductBundleInterface From c451f99a881fd06ffb59283093eb1bbdcdca30de Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Wed, 31 Jul 2024 15:26:20 +0200 Subject: [PATCH 04/40] OP-289: Remove ProductVariantRepository --- README.md | 6 +-- src/Repository/ProductVariantRepository.php | 50 ------------------- .../ProductVariantRepositoryInterface.php | 25 ---------- .../Application/config/packages/_sylius.yaml | 3 -- 4 files changed, 2 insertions(+), 82 deletions(-) delete mode 100644 src/Repository/ProductVariantRepository.php delete mode 100644 src/Repository/ProductVariantRepositoryInterface.php diff --git a/README.md b/README.md index 02a9466c..54b5690b 100644 --- a/README.md +++ b/README.md @@ -223,7 +223,7 @@ This **open-source plugin was developed to help the Sylius community**. If you h ``` -9. Add configuration for extended product, order item and product variant repository: +9. Add configuration for extended product and order item: ```yaml # config/packages/_sylius.yaml @@ -233,9 +233,7 @@ This **open-source plugin was developed to help the Sylius community**. If you h product: classes: model: App\Entity\Product\Product - product_variant: - classes: - repository: BitBag\SyliusProductBundlePlugin\Repository\ProductVariantRepository + sylius_order: resources: order_item: diff --git a/src/Repository/ProductVariantRepository.php b/src/Repository/ProductVariantRepository.php deleted file mode 100644 index 8341b5a7..00000000 --- a/src/Repository/ProductVariantRepository.php +++ /dev/null @@ -1,50 +0,0 @@ -getEntityManager()->getExpressionBuilder(); - - return $this->createQueryBuilder('o') - ->leftJoin('o.translations', 'translation', 'WITH', 'translation.locale = :locale') - ->innerJoin('o.product', 'product') - ->andWhere($expr->orX( - 'translation.name LIKE :phrase', - 'o.code LIKE :phrase', - 'product.code LIKE :phrase', - )) - ->setParameter('phrase', '%' . $phrase . '%') - ->setParameter('locale', $locale) - ->setMaxResults(20) - ->getQuery() - ->getResult() - ; - } - - public function findByCodes(array $codes): array - { - return $this->createQueryBuilder('o') - ->andWhere('o.code IN (:codes)') - ->setParameter('codes', $codes) - ->getQuery() - ->getResult() - ; - } -} diff --git a/src/Repository/ProductVariantRepositoryInterface.php b/src/Repository/ProductVariantRepositoryInterface.php deleted file mode 100644 index 739c16f3..00000000 --- a/src/Repository/ProductVariantRepositoryInterface.php +++ /dev/null @@ -1,25 +0,0 @@ - Date: Thu, 1 Aug 2024 11:43:49 +0200 Subject: [PATCH 05/40] OP-289: Add grid config to plugin resources --- README.md | 46 +++---------------- src/Resources/config/config.yml | 1 + src/Resources/config/grids.yml | 27 +++++++++++ .../Application/config/packages/_sylius.yaml | 28 ----------- 4 files changed, 34 insertions(+), 68 deletions(-) create mode 100644 src/Resources/config/grids.yml diff --git a/README.md b/README.md index 54b5690b..d1f9de68 100644 --- a/README.md +++ b/README.md @@ -242,41 +242,7 @@ This **open-source plugin was developed to help the Sylius community**. If you h ``` -10. Add 'Create/Bundle' to product grid configuration: - - ```yaml - # config/packages/_sylius.yaml - - sylius_grid: - grids: - sylius_admin_product: - actions: - main: - create: - type: links - label: sylius.ui.create - options: - class: primary - icon: plus - header: - icon: cube - label: sylius.ui.type - links: - simple: - label: sylius.ui.simple_product - icon: plus - route: sylius_admin_product_create_simple - configurable: - label: sylius.ui.configurable_product - icon: plus - route: sylius_admin_product_create - bundle: - label: bitbag_sylius_product_bundle.ui.bundle - icon: plus - route: bitbag_product_bundle_admin_product_create_bundle - - ``` -11. If you have full configuration in xml override doctrine config : +10. If you have full configuration in xml override doctrine config : ```yaml # config/packages/doctrine.yaml @@ -293,26 +259,26 @@ doctrine: alias: App ``` - -12. Copy plugin templates into your project `templates/bundles` directory: + +11. Copy plugin templates into your project `templates/bundles` directory: ```bash cp -R vendor/bitbag/product-bundle-plugin/tests/Application/templates/bundles/* templates/bundles/ ``` -13. Please clear application cache by running command below: +12. Please clear application cache by running command below: ```bash bin/console cache:clear ``` -14. Finish the installation by updating the database schema and installing assets: +13. Finish the installation by updating the database schema and installing assets: ```bash bin/console doctrine:migrations:diff bin/console doctrine:migrations:migrate ``` -15. Add plugin assets to your project: +14. Add plugin assets to your project: [Import webpack config](./README_webpack-config.md)* ## Testing diff --git a/src/Resources/config/config.yml b/src/Resources/config/config.yml index a8236304..bc83332f 100644 --- a/src/Resources/config/config.yml +++ b/src/Resources/config/config.yml @@ -1,5 +1,6 @@ imports: - { resource: resources.yml } + - { resource: grids.yml } - { resource: services.xml } framework: diff --git a/src/Resources/config/grids.yml b/src/Resources/config/grids.yml new file mode 100644 index 00000000..92cdc9a0 --- /dev/null +++ b/src/Resources/config/grids.yml @@ -0,0 +1,27 @@ +sylius_grid: + grids: + sylius_admin_product: + actions: + main: + create: + type: links + label: sylius.ui.create + options: + class: primary + icon: plus + header: + icon: cube + label: sylius.ui.type + links: + simple: + label: sylius.ui.simple_product + icon: plus + route: sylius_admin_product_create_simple + configurable: + label: sylius.ui.configurable_product + icon: plus + route: sylius_admin_product_create + bundle: + label: bitbag_sylius_product_bundle.ui.bundle + icon: plus + route: bitbag_product_bundle_admin_product_create_bundle diff --git a/tests/Application/config/packages/_sylius.yaml b/tests/Application/config/packages/_sylius.yaml index 7304b89a..0c0e3df8 100644 --- a/tests/Application/config/packages/_sylius.yaml +++ b/tests/Application/config/packages/_sylius.yaml @@ -28,31 +28,3 @@ sylius_order: sylius_shop: product_grid: include_all_descendants: true - -sylius_grid: - grids: - sylius_admin_product: - actions: - main: - create: - type: links - label: sylius.ui.create - options: - class: primary - icon: plus - header: - icon: cube - label: sylius.ui.type - links: - simple: - label: sylius.ui.simple_product - icon: plus - route: sylius_admin_product_create_simple - configurable: - label: sylius.ui.configurable_product - icon: plus - route: sylius_admin_product_create - bundle: - label: bitbag_sylius_product_bundle.ui.bundle - icon: plus - route: bitbag_product_bundle_admin_product_create_bundle From a90ff5a6d0fdd69483d99186fa91496685ba2b69 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Thu, 1 Aug 2024 12:06:21 +0200 Subject: [PATCH 06/40] OP-289: Update FE build --- tests/Application/.babelrc | 15 -------- tests/Application/gulpfile.babel.js | 60 ----------------------------- tests/Application/package.json | 47 ++++------------------ 3 files changed, 8 insertions(+), 114 deletions(-) delete mode 100644 tests/Application/.babelrc delete mode 100644 tests/Application/gulpfile.babel.js diff --git a/tests/Application/.babelrc b/tests/Application/.babelrc deleted file mode 100644 index e563a62e..00000000 --- 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/gulpfile.babel.js b/tests/Application/gulpfile.babel.js deleted file mode 100644 index 5920316f..00000000 --- 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 public 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 index 16de6eea..dfe32583 100644 --- a/tests/Application/package.json +++ b/tests/Application/package.json @@ -1,44 +1,13 @@ { - "dependencies": { - "babel-polyfill": "^6.26.0", - "jquery": "^3.4.0", - "jquery.dirtyforms": "^2.0.0", - "lightbox2": "^2.9.0", - "semantic-ui-css": "^2.2.0", - "slick-carousel": "^1.8.1", - "chart.js": "^3.7.1" - }, - "devDependencies": { - "@symfony/webpack-encore": "^1.6.1", - "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.11.0", - "fast-async": "^6.3.7", - "merge-stream": "^1.0.0", - "sass": "^1.50.0", - "sass-loader": "^7.0.1", - "upath": "^1.1.0", - "webpack": "^5.76.1", - "yargs": "^6.4.0" - }, + "license": "MIT", "scripts": { - "build": "encore production", - "dev": "encore dev", + "build": "encore dev", + "build:prod": "encore production", + "postinstall": "semantic-ui-css-patch", "lint": "yarn lint:js", - "lint:js": "eslint gulpfile.babel.js" + "watch": "encore dev --watch" }, - "repository": { - "type": "git", - "url": "git+https://github.com/Sylius/Sylius.git" - }, - "author": "Paweł Jędrzejewski", - "license": "MIT" + "devDependencies": { + "@sylius-ui/frontend": "^1.0" + } } From 42b46655906e611085541408a401112ca889884f Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Thu, 1 Aug 2024 14:59:02 +0200 Subject: [PATCH 07/40] OP-289: Add a migration for creating plugin tables --- README.md | 1 - .../BitBagSyliusProductBundleExtension.php | 46 +++++++++++++++++++ src/Migrations/Version20240801112208.php | 43 +++++++++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/DependencyInjection/BitBagSyliusProductBundleExtension.php create mode 100644 src/Migrations/Version20240801112208.php diff --git a/README.md b/README.md index d1f9de68..70281f39 100644 --- a/README.md +++ b/README.md @@ -275,7 +275,6 @@ doctrine: 13. Finish the installation by updating the database schema and installing assets: ```bash - bin/console doctrine:migrations:diff bin/console doctrine:migrations:migrate ``` 14. Add plugin assets to your project: diff --git a/src/DependencyInjection/BitBagSyliusProductBundleExtension.php b/src/DependencyInjection/BitBagSyliusProductBundleExtension.php new file mode 100644 index 00000000..4936305b --- /dev/null +++ b/src/DependencyInjection/BitBagSyliusProductBundleExtension.php @@ -0,0 +1,46 @@ +prependDoctrineMigrations($container); + } + + protected function getMigrationsNamespace(): string + { + return 'BitBag\SyliusProductBundlePlugin\Migrations'; + } + + protected function getMigrationsDirectory(): string + { + return '@BitBagSyliusProductBundlePlugin/Migrations'; + } + + protected function getNamespacesOfMigrationsExecutedBefore(): array + { + return ['Sylius\Bundle\CoreBundle\Migrations']; + } +} diff --git a/src/Migrations/Version20240801112208.php b/src/Migrations/Version20240801112208.php new file mode 100644 index 00000000..81d7b79d --- /dev/null +++ b/src/Migrations/Version20240801112208.php @@ -0,0 +1,43 @@ +addSql('CREATE TABLE bitbag_product_bundle (id INT AUTO_INCREMENT NOT NULL, product_id INT NOT NULL, is_packed_product TINYINT(1) NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME DEFAULT NULL, UNIQUE INDEX UNIQ_9EBE7ABF4584665A (product_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE bitbag_product_bundle_item (id INT AUTO_INCREMENT NOT NULL, product_variant_id INT NOT NULL, product_bundle_id INT NOT NULL, quantity INT NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME DEFAULT NULL, INDEX IDX_F429FEB6A80EF684 (product_variant_id), INDEX IDX_F429FEB69F5A6F5E (product_bundle_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE bitbag_product_bundle_order_item (id INT AUTO_INCREMENT NOT NULL, product_variant_id INT NOT NULL, order_item_id INT NOT NULL, product_bundle_item_id INT NOT NULL, quantity INT NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME DEFAULT NULL, INDEX IDX_A615CDA9A80EF684 (product_variant_id), INDEX IDX_A615CDA9E415FB15 (order_item_id), INDEX IDX_A615CDA9B7FE950B (product_bundle_item_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE bitbag_product_bundle ADD CONSTRAINT FK_9EBE7ABF4584665A FOREIGN KEY (product_id) REFERENCES sylius_product (id)'); + $this->addSql('ALTER TABLE bitbag_product_bundle_item ADD CONSTRAINT FK_F429FEB6A80EF684 FOREIGN KEY (product_variant_id) REFERENCES sylius_product_variant (id)'); + $this->addSql('ALTER TABLE bitbag_product_bundle_item ADD CONSTRAINT FK_F429FEB69F5A6F5E FOREIGN KEY (product_bundle_id) REFERENCES bitbag_product_bundle (id)'); + $this->addSql('ALTER TABLE bitbag_product_bundle_order_item ADD CONSTRAINT FK_A615CDA9A80EF684 FOREIGN KEY (product_variant_id) REFERENCES sylius_product_variant (id)'); + $this->addSql('ALTER TABLE bitbag_product_bundle_order_item ADD CONSTRAINT FK_A615CDA9E415FB15 FOREIGN KEY (order_item_id) REFERENCES sylius_order_item (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE bitbag_product_bundle_order_item ADD CONSTRAINT FK_A615CDA9B7FE950B FOREIGN KEY (product_bundle_item_id) REFERENCES bitbag_product_bundle_item (id)'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE bitbag_product_bundle DROP FOREIGN KEY FK_9EBE7ABF4584665A'); + $this->addSql('ALTER TABLE bitbag_product_bundle_item DROP FOREIGN KEY FK_F429FEB6A80EF684'); + $this->addSql('ALTER TABLE bitbag_product_bundle_item DROP FOREIGN KEY FK_F429FEB69F5A6F5E'); + $this->addSql('ALTER TABLE bitbag_product_bundle_order_item DROP FOREIGN KEY FK_A615CDA9A80EF684'); + $this->addSql('ALTER TABLE bitbag_product_bundle_order_item DROP FOREIGN KEY FK_A615CDA9E415FB15'); + $this->addSql('ALTER TABLE bitbag_product_bundle_order_item DROP FOREIGN KEY FK_A615CDA9B7FE950B'); + $this->addSql('DROP TABLE bitbag_product_bundle'); + $this->addSql('DROP TABLE bitbag_product_bundle_item'); + $this->addSql('DROP TABLE bitbag_product_bundle_order_item'); + } +} From dd20f781591bb709991a671564fa7f1572002c6a Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Thu, 1 Aug 2024 15:04:41 +0200 Subject: [PATCH 08/40] OP-289: Use test application build action from SyliusLabs --- .github/workflows/build.yml | 138 +++++------------------------------- tests/Application/.env | 7 ++ 2 files changed, 25 insertions(+), 120 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 38ade654..3a3b1d0d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,10 +6,9 @@ on: - 'dependabot/**' pull_request: ~ release: - types: [created] + types: [ created ] schedule: - - - cron: "0 1 * * 6" # Run at 1am every Saturday + - cron: "0 1 * * 6" # Run at 1am every Saturday workflow_dispatch: ~ jobs: @@ -39,11 +38,9 @@ jobs: DATABASE_URL: "mysql://root:root@127.0.0.1/sylius?serverVersion=${{ matrix.mysql }}" steps: - - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - - name: Setup PHP + - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: "${{ matrix.php }}" @@ -51,130 +48,32 @@ jobs: tools: symfony coverage: none - - - name: Setup Node - uses: actions/setup-node@v4 + - name: Build test application + uses: SyliusLabs/BuildTestAppAction@v1.3 with: + sylius-version: "${{ matrix.sylius }}" + symfony-version: "${{ matrix.symfony }}" + mysql-version: "${{ matrix.mysql }}" node-version: "${{ matrix.node }}" + working-directory: "." + plugin-build: "yes" - - - name: Shutdown default MySQL - run: sudo service mysql stop - - - - name: Setup MySQL - uses: mirromutth/mysql-action@v1.1 - with: - mysql version: "${{ matrix.mysql }}" - mysql root password: "root" - - - - name: Output PHP version for Symfony CLI - 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: Get Composer cache directory - id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" - - - - name: Cache Composer - uses: actions/cache@v4 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-php-${{ matrix.php }}-composer-${{ hashFiles('**/composer.json **/composer.lock') }} - restore-keys: | - ${{ runner.os }}-php-${{ matrix.php }}-composer- - - - name: Restrict Symfony version - if: matrix.symfony != '' - run: | - composer global config --no-plugins allow-plugins.symfony/flex true - composer global require --no-progress --no-scripts --no-plugins "symfony/flex:^1.10" - composer config extra.symfony.require "${{ matrix.symfony }}" - - - name: Restrict Sylius version - if: matrix.sylius != '' - run: composer require "sylius/sylius:${{ matrix.sylius }}" --no-update --no-scripts --no-interaction - - - - name: Install PHP dependencies - run: composer install --no-interaction - env: - SYMFONY_REQUIRE: ${{ matrix.symfony }} - - - name: Install Behat driver - run: vendor/bin/bdi browser:google-chrome drivers - - - - name: Get Yarn cache directory - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - - - name: Cache Yarn - uses: actions/cache@v4 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-node-${{ matrix.node }}-yarn-${{ hashFiles('**/package.json **/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-node-${{ matrix.node }}-yarn- - - - name: Install JS dependencies - run: (cd tests/Application && yarn install) - - - - name: Prepare test application database - run: | - (cd tests/Application && bin/console doctrine:database:create -vvv) - (cd tests/Application && bin/console doctrine:schema:create -vvv) - - - name: Prepare test application assets - run: | - (cd tests/Application && bin/console assets:install public -vvv) - (cd tests/Application && yarn encore dev) - - - name: Prepare test application cache - run: (cd tests/Application && 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 + - name: Validate composer.json run: composer validate --ansi --strict - - - name: Validate database schema + - name: Validate database schema run: (cd tests/Application && bin/console doctrine:schema:validate) - - - name: Run PHPSpec + - name: Run PHPSpec run: vendor/bin/phpspec run --ansi -f progress --no-interaction - - - name: Run PHPUnit + - name: Run PHPUnit run: vendor/bin/phpunit --colors=always - - - name: Run Behat + - name: Run Behat run: vendor/bin/behat --colors --strict -vvv --no-interaction || vendor/bin/behat --colors --strict -vvv --no-interaction --rerun - - - name: Upload Behat logs + - name: Upload Behat logs uses: actions/upload-artifact@v3 if: failure() with: @@ -182,8 +81,7 @@ jobs: path: etc/build/ if-no-files-found: ignore - - - name: Failed build Slack notification + - name: Failed build Slack notification uses: rtCamp/action-slack-notify@v2 if: ${{ failure() && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') }} env: diff --git a/tests/Application/.env b/tests/Application/.env index 6fa8b962..8ca196db 100644 --- a/tests/Application/.env +++ b/tests/Application/.env @@ -23,6 +23,13 @@ DATABASE_URL=sqlite:///%kernel.project_dir%/var/data_%kernel.environment%.db MESSENGER_TRANSPORT_DSN=sync:// ###< symfony/messenger ### +###> sylius/sylius >= 12.4 ### +SYLIUS_MESSENGER_TRANSPORT_MAIN_DSN=${MESSENGER_TRANSPORT_DSN} +SYLIUS_MESSENGER_TRANSPORT_MAIN_FAILED_DSN=${MESSENGER_TRANSPORT_DSN} +SYLIUS_MESSENGER_TRANSPORT_CATALOG_PROMOTION_REMOVAL_DSN=${MESSENGER_TRANSPORT_DSN} +SYLIUS_MESSENGER_TRANSPORT_CATALOG_PROMOTION_REMOVAL_FAILED_DSN=${MESSENGER_TRANSPORT_DSN} +###< sylius/sylius ### + ###> lexik/jwt-authentication-bundle ### JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem From ed6d75b899f59546895be656146db1e3d505a9fe Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Fri, 2 Aug 2024 09:36:47 +0200 Subject: [PATCH 09/40] OP-289: Drop php 8.0 support, bump minimal symfony 6 version to 6.4 --- .github/workflows/build.yml | 9 +-------- .github/workflows/coding_standard.yml | 9 +-------- composer.json | 20 +++++++------------- 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3a3b1d0d..509d94ee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,19 +20,12 @@ jobs: strategy: fail-fast: false matrix: - php: [ "8.0", "8.1", "8.2", "8.3" ] + php: [ "8.1", "8.2", "8.3" ] symfony: [ "^5.4", "^6.4" ] sylius: [ "^1.12", "^1.13" ] node: [ "18.x", "20.x" ] mysql: [ "8.0" ] - exclude: - - sylius: ^1.13 - php: 8.0 - - sylius: ^1.12 - php: 8.0 - symfony: ^6.4 - env: APP_ENV: test DATABASE_URL: "mysql://root:root@127.0.0.1/sylius?serverVersion=${{ matrix.mysql }}" diff --git a/.github/workflows/coding_standard.yml b/.github/workflows/coding_standard.yml index 02942c52..2e7d9fe8 100644 --- a/.github/workflows/coding_standard.yml +++ b/.github/workflows/coding_standard.yml @@ -18,18 +18,11 @@ jobs: strategy: fail-fast: false matrix: - php: [ "8.0", "8.1", "8.2", "8.3" ] + php: [ "8.1", "8.2", "8.3" ] symfony: [ "^5.4", "^6.4" ] sylius: [ "^1.12", "^1.13" ] node: [ "18.x", "20.x" ] - exclude: - - sylius: ^1.13 - php: 8.0 - - sylius: ^1.12 - php: 8.0 - symfony: ^6.4 - steps: - uses: actions/checkout@v3 diff --git a/composer.json b/composer.json index 4efdb997..56b0a86b 100644 --- a/composer.json +++ b/composer.json @@ -5,9 +5,8 @@ "description": "Product bundle for Sylius.", "license": "MIT", "require": { - "php": "^8.0", - "sylius/sylius": "~1.12.0 || ~1.13.0", - "symfony/webpack-encore-bundle": "^1.12" + "php": "^8.1", + "sylius/sylius": "~1.12.0 || ~1.13.0" }, "require-dev": { "ext-json": "*", @@ -33,11 +32,11 @@ "phpstan/phpstan-webmozart-assert": "^1.2.0", "phpunit/phpunit": "^9.5", "sylius-labs/coding-standard": "^4.0", - "symfony/browser-kit": "^5.4 || ^6.0", - "symfony/debug-bundle": "^5.4 || ^6.0", - "symfony/dotenv": "^5.4 || ^6.0", - "symfony/intl": "^5.4 || ^6.0", - "symfony/web-profiler-bundle": "^5.4 || ^6.0", + "symfony/browser-kit": "^5.4 || ^6.4", + "symfony/debug-bundle": "^5.4 || ^6.4", + "symfony/dotenv": "^5.4 || ^6.4", + "symfony/intl": "^5.4 || ^6.4", + "symfony/web-profiler-bundle": "^5.4 || ^6.4", "vimeo/psalm": "^4.12", "composer/xdebug-handler": "^2.0", "friendsofphp/php-cs-fixer": "^3.0", @@ -47,11 +46,6 @@ "robertfausk/behat-panther-extension": "^1.1" }, "conflict": { - "symfony/symfony": "4.1.8", - "symfony/browser-kit": "4.1.8", - "symfony/dom-crawler": "4.1.8", - "symfony/routing": "4.1.8", - "symfony/doctrine-bridge": "4.4.16", "symfony/framework-bundle": "6.2.8", "behat/mink-selenium2-driver": ">=1.7.0" }, From 5366129dcd4f4a4529d3749e9ac0a3bd17a88811 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Fri, 2 Aug 2024 15:14:34 +0200 Subject: [PATCH 10/40] OP-289: Refactor templates --- README.md | 37 ++++++++++++- README_webpack-config.md | 18 +------ .../Admin/Order/Show/Summary/_item.html.twig | 52 +++++++++++++++++++ .../Resources/views/Admin}/_scripts.html.twig | 1 - .../Resources/views/Admin}/_styles.html.twig | 1 - .../Shop}/Product/Show/_inventory.html.twig | 2 +- .../Shop/Product/_variantSelection.html.twig | 7 +++ .../Resources/views/Shop}/_scripts.html.twig | 1 - .../Resources/views/Shop}/_styles.html.twig | 1 - .../config/packages/sylius_ui.yaml | 27 ++++++++++ .../SyliusAdminBundle/Layout/_logo.html.twig | 5 -- .../Order/Show/Summary/_item.html.twig | 39 +------------- .../Layout/Header/_logo.html.twig | 5 -- 13 files changed, 124 insertions(+), 72 deletions(-) create mode 100644 src/Resources/views/Admin/Order/Show/Summary/_item.html.twig rename {tests/Application/templates/bundles/SyliusAdminBundle => src/Resources/views/Admin}/_scripts.html.twig (59%) rename {tests/Application/templates/bundles/SyliusAdminBundle => src/Resources/views/Admin}/_styles.html.twig (60%) rename {tests/Application/templates/bundles/SyliusShopBundle => src/Resources/views/Shop}/Product/Show/_inventory.html.twig (80%) create mode 100644 src/Resources/views/Shop/Product/_variantSelection.html.twig rename {tests/Application/templates/bundles/SyliusShopBundle => src/Resources/views/Shop}/_scripts.html.twig (60%) rename {tests/Application/templates/bundles/SyliusShopBundle => src/Resources/views/Shop}/_styles.html.twig (60%) create mode 100644 tests/Application/config/packages/sylius_ui.yaml delete mode 100644 tests/Application/templates/bundles/SyliusAdminBundle/Layout/_logo.html.twig delete mode 100644 tests/Application/templates/bundles/SyliusShopBundle/Layout/Header/_logo.html.twig diff --git a/README.md b/README.md index 70281f39..7636f319 100644 --- a/README.md +++ b/README.md @@ -260,10 +260,43 @@ doctrine: ``` -11. Copy plugin templates into your project `templates/bundles` directory: +11. Add plugin templates: +- Inject blocks: + +```yaml +sylius_ui: + events: + sylius.shop.product.show.right_sidebar: + blocks: + variant_selection: + template: "@BitBagSyliusProductBundlePlugin/Shop/Product/_variantSelection.html.twig" + priority: 10 + sylius.shop.layout.javascripts: + blocks: + plugin_scripts: + template: "@BitBagSyliusProductBundlePlugin/Shop/_scripts.html.twig" + priority: 20 + sylius.shop.layout.stylesheets: + blocks: + plugin_stylesheets: + template: "@BitBagSyliusProductBundlePlugin/Shop/_styles.html.twig" + priority: 20 + sylius.admin.layout.javascripts: + blocks: + plugin_scripts: + template: "@BitBagSyliusProductBundlePlugin/Admin/_scripts.html.twig" + priority: 20 + sylius.admin.layout.stylesheets: + blocks: + plugin_stylesheets: + template: "@BitBagSyliusProductBundlePlugin/Admin/_styles.html.twig" + priority: 20 + +``` +- Copy plugin templates into your project `templates/bundles` directory: ```bash - cp -R vendor/bitbag/product-bundle-plugin/tests/Application/templates/bundles/* templates/bundles/ + cp vendor/bitbag/product-bundle-plugin/src/Resources/views/Admin/Order/Show/Summary/_item.html.twig templates/bundles/SyliusAdminBundle/Order/Show/Summary ``` 12. Please clear application cache by running command below: diff --git a/README_webpack-config.md b/README_webpack-config.md index fdd6326e..62b2eccc 100644 --- a/README_webpack-config.md +++ b/README_webpack-config.md @@ -43,20 +43,4 @@ webpack_encore: product_bundle_admin: '%kernel.project_dir%/public/build/bitbag/productBundle/admin' ``` -4. Add encore functions to your templates - -```twig -{# @SyliusShopBundle/_scripts.html.twig #} -{{ encore_entry_script_tags('bitbag-productBundle-shop', null, 'product_bundle_shop') }} - -{# @SyliusShopBundle/_styles.html.twig #} -{{ encore_entry_link_tags('bitbag-productBundle-shop', null, 'product_bundle_shop') }} - -{# @SyliusAdminBundle/_scripts.html.twig #} -{{ encore_entry_script_tags('bitbag-productBundle-admin', null, 'product_bundle_admin') }} - -{# @SyliusAdminBundle/_styles.html.twig #} -{{ encore_entry_link_tags('bitbag-productBundle-admin', null, 'product_bundle_admin') }} -``` - -5. Run `yarn encore dev` or `yarn encore production` +4. Run `yarn encore dev` or `yarn encore production` diff --git a/src/Resources/views/Admin/Order/Show/Summary/_item.html.twig b/src/Resources/views/Admin/Order/Show/Summary/_item.html.twig new file mode 100644 index 00000000..b88d3dc3 --- /dev/null +++ b/src/Resources/views/Admin/Order/Show/Summary/_item.html.twig @@ -0,0 +1,52 @@ +{% 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_ITEM_PROMOTION_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 %} + +{% set aggregatedUnitPromotionAdjustments = item.getAdjustmentsTotalRecursively(unitPromotionAdjustment) + item.getAdjustmentsTotalRecursively(orderPromotionAdjustment) %} +{% set subtotal = (item.unitPrice * item.quantity) + aggregatedUnitPromotionAdjustments %} + +{% set taxIncluded = sylius_admin_order_unit_tax_included(item) %} +{% set taxExcluded = sylius_admin_order_unit_tax_excluded(item) %} + + + + {% include '@SyliusAdmin/Product/_info.html.twig' %} + + {{ bitbag_render_product_bundle_order_items(item) }} + + + {{ money.format(item.unitPrice, order.currencyCode) }} + + + {{ money.format(item.units.first.adjustmentsTotal(unitPromotionAdjustment), order.currencyCode) }} + + + ~ {{ money.format(item.units.first.adjustmentsTotal(orderPromotionAdjustment), order.currencyCode) }} + + + {{ money.format(item.fullDiscountedUnitPrice, 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/_scripts.html.twig b/src/Resources/views/Admin/_scripts.html.twig similarity index 59% rename from tests/Application/templates/bundles/SyliusAdminBundle/_scripts.html.twig rename to src/Resources/views/Admin/_scripts.html.twig index 54be86fa..ba1e4025 100644 --- a/tests/Application/templates/bundles/SyliusAdminBundle/_scripts.html.twig +++ b/src/Resources/views/Admin/_scripts.html.twig @@ -1,2 +1 @@ -{{ encore_entry_script_tags('admin-entry', null, 'admin') }} {{ encore_entry_script_tags('bitbag-productBundle-admin', null, 'product_bundle_admin') }} diff --git a/tests/Application/templates/bundles/SyliusAdminBundle/_styles.html.twig b/src/Resources/views/Admin/_styles.html.twig similarity index 60% rename from tests/Application/templates/bundles/SyliusAdminBundle/_styles.html.twig rename to src/Resources/views/Admin/_styles.html.twig index d87343da..66909a90 100644 --- a/tests/Application/templates/bundles/SyliusAdminBundle/_styles.html.twig +++ b/src/Resources/views/Admin/_styles.html.twig @@ -1,2 +1 @@ -{{ encore_entry_link_tags('admin-entry', null, 'admin') }} {{ encore_entry_link_tags('bitbag-productBundle-admin', null, 'product_bundle_admin') }} diff --git a/tests/Application/templates/bundles/SyliusShopBundle/Product/Show/_inventory.html.twig b/src/Resources/views/Shop/Product/Show/_inventory.html.twig similarity index 80% rename from tests/Application/templates/bundles/SyliusShopBundle/Product/Show/_inventory.html.twig rename to src/Resources/views/Shop/Product/Show/_inventory.html.twig index 31efa961..6019f8aa 100644 --- a/tests/Application/templates/bundles/SyliusShopBundle/Product/Show/_inventory.html.twig +++ b/src/Resources/views/Shop/Product/Show/_inventory.html.twig @@ -1,4 +1,4 @@ -{% if product.variants.empty() or product.simple and not sylius_inventory_is_available(product.variants.first) %} +{% if product.enabledVariants.empty() or product.simple and not sylius_inventory_is_available(product.enabledVariants.first) %} {% include '@SyliusShop/Product/Show/_outOfStock.html.twig' %} {% else %} {% if product.isBundle %} diff --git a/src/Resources/views/Shop/Product/_variantSelection.html.twig b/src/Resources/views/Shop/Product/_variantSelection.html.twig new file mode 100644 index 00000000..4f0c160d --- /dev/null +++ b/src/Resources/views/Shop/Product/_variantSelection.html.twig @@ -0,0 +1,7 @@ +{% if product.isConfigurable() and product.getVariantSelectionMethod() == 'match' and not product.enabledVariants.empty() %} + {% include '@SyliusShop/Product/Show/_variantsPricing.html.twig' with {'pricing': sylius_product_variants_map(product, {'channel': sylius.channel}), 'variants': product.enabledVariants} %} +{% endif %} + +{% include '@BitBagSyliusProductBundlePlugin/Shop/Product/Show/_inventory.html.twig' %} + + diff --git a/tests/Application/templates/bundles/SyliusShopBundle/_scripts.html.twig b/src/Resources/views/Shop/_scripts.html.twig similarity index 60% rename from tests/Application/templates/bundles/SyliusShopBundle/_scripts.html.twig rename to src/Resources/views/Shop/_scripts.html.twig index 278c091e..658945c3 100644 --- a/tests/Application/templates/bundles/SyliusShopBundle/_scripts.html.twig +++ b/src/Resources/views/Shop/_scripts.html.twig @@ -1,2 +1 @@ -{{ encore_entry_script_tags('shop-entry', null, 'shop') }} {{ encore_entry_script_tags('bitbag-productBundle-shop', null, 'product_bundle_shop') }} diff --git a/tests/Application/templates/bundles/SyliusShopBundle/_styles.html.twig b/src/Resources/views/Shop/_styles.html.twig similarity index 60% rename from tests/Application/templates/bundles/SyliusShopBundle/_styles.html.twig rename to src/Resources/views/Shop/_styles.html.twig index 5365a60e..94657919 100644 --- a/tests/Application/templates/bundles/SyliusShopBundle/_styles.html.twig +++ b/src/Resources/views/Shop/_styles.html.twig @@ -1,2 +1 @@ -{{ encore_entry_link_tags('shop-entry', null, 'shop') }} {{ encore_entry_link_tags('bitbag-productBundle-shop', null, 'product_bundle_shop') }} diff --git a/tests/Application/config/packages/sylius_ui.yaml b/tests/Application/config/packages/sylius_ui.yaml new file mode 100644 index 00000000..404f275e --- /dev/null +++ b/tests/Application/config/packages/sylius_ui.yaml @@ -0,0 +1,27 @@ +sylius_ui: + events: + sylius.shop.product.show.right_sidebar: + blocks: + variant_selection: + template: "@BitBagSyliusProductBundlePlugin/Shop/Product/_variantSelection.html.twig" + priority: 10 + sylius.shop.layout.javascripts: + blocks: + plugin_scripts: + template: "@BitBagSyliusProductBundlePlugin/Shop/_scripts.html.twig" + priority: 20 + sylius.shop.layout.stylesheets: + blocks: + plugin_stylesheets: + template: "@BitBagSyliusProductBundlePlugin/Shop/_styles.html.twig" + priority: 20 + sylius.admin.layout.javascripts: + blocks: + plugin_scripts: + template: "@BitBagSyliusProductBundlePlugin/Admin/_scripts.html.twig" + priority: 20 + sylius.admin.layout.stylesheets: + blocks: + plugin_stylesheets: + template: "@BitBagSyliusProductBundlePlugin/Admin/_styles.html.twig" + priority: 20 diff --git a/tests/Application/templates/bundles/SyliusAdminBundle/Layout/_logo.html.twig b/tests/Application/templates/bundles/SyliusAdminBundle/Layout/_logo.html.twig deleted file mode 100644 index 1d9fa7d0..00000000 --- a/tests/Application/templates/bundles/SyliusAdminBundle/Layout/_logo.html.twig +++ /dev/null @@ -1,5 +0,0 @@ - -
- -
-
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 index 6d94509c..3c48d043 100644 --- a/tests/Application/templates/bundles/SyliusAdminBundle/Order/Show/Summary/_item.html.twig +++ b/tests/Application/templates/bundles/SyliusAdminBundle/Order/Show/Summary/_item.html.twig @@ -1,38 +1 @@ -{% 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 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' %} - - {{ bitbag_render_product_bundle_order_items(item) }} - - - {{ money.format(item.unitPrice, order.currencyCode) }} - - - {{ money.format(item.discountedUnitPrice, order.currencyCode) }} - - - {{ item.quantity }} - - - {{ money.format(item.subtotal, order.currencyCode) }} - - - {{ money.format(item.getAdjustmentsTotalRecursively(orderPromotionAdjustment), order.currencyCode) }} - - - {{ money.format(item.taxTotal, order.currencyCode) }} - - - {{ money.format(item.total, order.currencyCode) }} - - +{% include '@BitBagSyliusProductBundlePlugin/Admin/Order/Show/Summary/_item.html.twig' %} diff --git a/tests/Application/templates/bundles/SyliusShopBundle/Layout/Header/_logo.html.twig b/tests/Application/templates/bundles/SyliusShopBundle/Layout/Header/_logo.html.twig deleted file mode 100644 index 84b8df56..00000000 --- a/tests/Application/templates/bundles/SyliusShopBundle/Layout/Header/_logo.html.twig +++ /dev/null @@ -1,5 +0,0 @@ - From 4903ee265d4b33d7fe65f80c857e4aa3fae91949 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Mon, 12 Aug 2024 17:08:10 +0200 Subject: [PATCH 11/40] OP-289: Add grid type and filter to handle bundles --- src/Grid/Filter/IsBundleFilter.php | 34 +++++++++++++++++++ .../ProductBundlesAwareInterface.php | 19 +++++++++++ src/Repository/ProductBundlesAwareTrait.php | 27 +++++++++++++++ src/Resources/config/grids.yml | 20 +++++++++++ src/Resources/config/services.xml | 1 + src/Resources/config/services/filter.xml | 10 ++++++ src/Resources/translations/messages.en.yml | 1 + .../Application/config/packages/_sylius.yaml | 1 + .../src/Repository/ProductRepository.php | 21 ++++++++++++ 9 files changed, 134 insertions(+) create mode 100644 src/Grid/Filter/IsBundleFilter.php create mode 100644 src/Repository/ProductBundlesAwareInterface.php create mode 100644 src/Repository/ProductBundlesAwareTrait.php create mode 100644 src/Resources/config/services/filter.xml create mode 100644 tests/Application/src/Repository/ProductRepository.php diff --git a/src/Grid/Filter/IsBundleFilter.php b/src/Grid/Filter/IsBundleFilter.php new file mode 100644 index 00000000..3a562d70 --- /dev/null +++ b/src/Grid/Filter/IsBundleFilter.php @@ -0,0 +1,34 @@ +restrict('productBundleItems IS NOT NULL'); + + break; + case BooleanFilter::FALSE: + $dataSource->restrict('productBundleItems IS NULL'); + + break; + } + } +} diff --git a/src/Repository/ProductBundlesAwareInterface.php b/src/Repository/ProductBundlesAwareInterface.php new file mode 100644 index 00000000..2f94668c --- /dev/null +++ b/src/Repository/ProductBundlesAwareInterface.php @@ -0,0 +1,19 @@ +createListQueryBuilder($locale, $taxonId) + ->leftJoin('o.productBundle', 'productBundle') + ->leftJoin('productBundle.productBundleItems', 'productBundleItems'); + } +} diff --git a/src/Resources/config/grids.yml b/src/Resources/config/grids.yml index 92cdc9a0..e6355667 100644 --- a/src/Resources/config/grids.yml +++ b/src/Resources/config/grids.yml @@ -1,6 +1,23 @@ sylius_grid: grids: sylius_admin_product: + driver: + name: doctrine/orm + options: + class: "%sylius.model.product.class%" + repository: + method: createSearchListQueryBuilder + arguments: [ "expr:service('sylius.context.locale').getLocaleCode()", $taxonId ] + fields: + bundle: + type: twig + label: bitbag_sylius_product_bundle.ui.is_bundle + options: + template: "@SyliusUi/Grid/Field/yesNo.html.twig" + filters: + bundle: + type: is_bundle + label: bitbag_sylius_product_bundle.ui.is_bundle actions: main: create: @@ -25,3 +42,6 @@ sylius_grid: label: bitbag_sylius_product_bundle.ui.bundle icon: plus route: bitbag_product_bundle_admin_product_create_bundle + templates: + filter: + is_bundle: "@SyliusUi/Grid/Filter/boolean.html.twig" diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 0c4f960c..ea17a5b4 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -5,6 +5,7 @@ + diff --git a/src/Resources/config/services/filter.xml b/src/Resources/config/services/filter.xml new file mode 100644 index 00000000..f3543982 --- /dev/null +++ b/src/Resources/config/services/filter.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/src/Resources/translations/messages.en.yml b/src/Resources/translations/messages.en.yml index cecb9d56..e6e23f97 100644 --- a/src/Resources/translations/messages.en.yml +++ b/src/Resources/translations/messages.en.yml @@ -6,3 +6,4 @@ bitbag_sylius_product_bundle: delete: Delete products_in_bundle: Products in bundle is_packed_product: Is packed product + is_bundle: Is bundle diff --git a/tests/Application/config/packages/_sylius.yaml b/tests/Application/config/packages/_sylius.yaml index 0c0e3df8..00de5680 100644 --- a/tests/Application/config/packages/_sylius.yaml +++ b/tests/Application/config/packages/_sylius.yaml @@ -18,6 +18,7 @@ sylius_product: product: classes: model: Tests\BitBag\SyliusProductBundlePlugin\Entity\Product + repository: Tests\BitBag\SyliusProductBundlePlugin\Application\src\Repository\ProductRepository sylius_order: resources: diff --git a/tests/Application/src/Repository/ProductRepository.php b/tests/Application/src/Repository/ProductRepository.php new file mode 100644 index 00000000..cee09d27 --- /dev/null +++ b/tests/Application/src/Repository/ProductRepository.php @@ -0,0 +1,21 @@ + Date: Tue, 13 Aug 2024 14:51:59 +0200 Subject: [PATCH 12/40] OP-289: Add xdebug support for docker --- Dockerfile => .docker/Dockerfile | 0 .docker/Dockerfile-xdebug | 74 ++++++++++++++++++++++++++++++++ .docker/php-xdebug.ini | 22 ++++++++++ .docker/php.ini | 6 --- docker-compose.yml | 9 +++- 5 files changed, 104 insertions(+), 7 deletions(-) rename Dockerfile => .docker/Dockerfile (100%) create mode 100644 .docker/Dockerfile-xdebug create mode 100644 .docker/php-xdebug.ini diff --git a/Dockerfile b/.docker/Dockerfile similarity index 100% rename from Dockerfile rename to .docker/Dockerfile diff --git a/.docker/Dockerfile-xdebug b/.docker/Dockerfile-xdebug new file mode 100644 index 00000000..0faea0ef --- /dev/null +++ b/.docker/Dockerfile-xdebug @@ -0,0 +1,74 @@ +FROM ubuntu:20.04 +ARG DEBIAN_FRONTEND=noninteractive +ARG PHP_VERSION=8.1 +ENV LC_ALL=C.UTF-8 + +# Install basic tools +RUN apt-get update && apt-get install -y \ + software-properties-common \ + curl \ + make \ + supervisor \ + unzip \ + python2 \ + g++ + +# Append NODE, NGINX and PHP repositories +RUN add-apt-repository ppa:ondrej/php \ + && add-apt-repository ppa:ondrej/nginx \ + && curl -sL https://deb.nodesource.com/setup_14.x | bash - + +# Install required PHP extensions +RUN apt-get update && apt-get install -y \ + nodejs \ + nginx \ + php${PHP_VERSION} \ + php${PHP_VERSION}-apcu \ + php${PHP_VERSION}-calendar \ + php${PHP_VERSION}-common \ + php${PHP_VERSION}-cli \ + php${PHP_VERSION}-ctype \ + php${PHP_VERSION}-curl \ + php${PHP_VERSION}-dom \ + php${PHP_VERSION}-exif \ + php${PHP_VERSION}-fpm \ + php${PHP_VERSION}-gd \ + php${PHP_VERSION}-intl \ + php${PHP_VERSION}-mbstring \ + php${PHP_VERSION}-mysql \ + php${PHP_VERSION}-opcache \ + php${PHP_VERSION}-pdo \ + php${PHP_VERSION}-pgsql \ + php${PHP_VERSION}-sqlite \ + php${PHP_VERSION}-xml \ + php${PHP_VERSION}-xsl \ + php${PHP_VERSION}-yaml \ + php${PHP_VERSION}-xdebug \ + php${PHP_VERSION}-zip + +# Install Composer +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename composer + +# Cleanup +RUN apt-get remove --purge -y software-properties-common curl && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* /usr/share/man/* + +# Create directory for php-fpm socket +# Link php-fpm binary file without version +# -p Creates missing intermediate path name directories +RUN ln -s /usr/sbin/php-fpm${PHP_VERSION} /usr/sbin/php-fpm && mkdir -p /run/php + +# Install yarn +RUN npm install -g yarn && npm cache clean --force + +# Initialize config files +COPY .docker/supervisord.conf /etc/supervisor/conf.d/supervisor.conf +COPY .docker/nginx.conf /etc/nginx/nginx.conf +COPY .docker/fpm.conf /etc/php/${PHP_VERSION}/fpm/pool.d/www.conf +COPY .docker/php-xdebug.ini /etc/php/${PHP_VERSION}/fpm/php.ini +COPY .docker/php-xdebug.ini /etc/php/${PHP_VERSION}/cli/php.ini + +WORKDIR /app + +EXPOSE 80 + +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] diff --git a/.docker/php-xdebug.ini b/.docker/php-xdebug.ini new file mode 100644 index 00000000..717a6bc2 --- /dev/null +++ b/.docker/php-xdebug.ini @@ -0,0 +1,22 @@ +[PHP] +memory_limit=512M + +[date] +date.timezone=${PHP_DATE_TIMEZONE} + +[opcache] +opcache.enable=0 +opcache.memory_consumption=256 +opcache.max_accelerated_files=20000 +opcache.validate_timestamps=0 +;opcache.preload=/app/config/preload.php +opcache.preload_user=www-data +opcache.jit=1255 +opcache.jit_buffer_size=256M + +[xdebug] +xdebug.mode=debug +xdebug.client_host=host.docker.internal +xdebug.start_with_request = yes +xdebug.client_port = 9003 +xdebug.discover_client_host = 1 diff --git a/.docker/php.ini b/.docker/php.ini index dfe91c67..c283a534 100644 --- a/.docker/php.ini +++ b/.docker/php.ini @@ -13,9 +13,3 @@ opcache.validate_timestamps=0 opcache.preload_user=www-data opcache.jit=1255 opcache.jit_buffer_size=256M - -;[xdebug] -;xdebug.mode=debug -;xdebug.client_host=host.docker.internal -;xdebug.start_with_request = yes -;xdebug.client_port = 9003 diff --git a/docker-compose.yml b/docker-compose.yml index 8cbe025b..483d2140 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,12 +3,14 @@ services: container_name: app build: context: . + dockerfile: .docker/Dockerfile +# dockerfile: .docker/Dockerfile-xdebug environment: APP_ENV: "dev" DATABASE_URL: "mysql://root:mysql@mysql/sylius_%kernel.environment%?charset=utf8mb4" # DATABASE_URL: "pgsql://root:postgres@postgres/sylius_%kernel.environment%?charset=utf8" # When using postgres PHP_DATE_TIMEZONE: "Europe/Warsaw" -# PHP_IDE_CONFIG: serverName=wishlist + PHP_IDE_CONFIG: serverName=PHPSTORM volumes: - ./:/app:delegated - ./.docker/php.ini:/etc/php8/php.ini:delegated @@ -24,6 +26,8 @@ services: container_name: mysql image: mysql:8.0 platform: linux/amd64 + volumes: + - mysql-data:/var/lib/mysql:rw environment: MYSQL_ROOT_PASSWORD: mysql ports: @@ -44,3 +48,6 @@ services: networks: sylius: driver: bridge + +volumes: + mysql-data: From d8a0d970a80c905bb683d334ed0c2d36c5709dd6 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Tue, 13 Aug 2024 17:43:00 +0200 Subject: [PATCH 13/40] OP-289: Fix bundle filter handling --- src/Entity/ProductBundlesAwareInterface.php | 2 + src/Entity/ProductBundlesAwareTrait.php | 11 +++++ src/Grid/Filter/IsBundleFilter.php | 12 ++++-- src/Repository/ProductBundleRepository.php | 42 +++++++++++++++++++ .../ProductBundleRepositoryInterface.php | 24 +++++++++++ .../ProductBundlesAwareInterface.php | 19 --------- src/Repository/ProductBundlesAwareTrait.php | 27 ------------ src/Resources/config/grids.yml | 9 +--- .../config/resources/product_bundle.yml | 1 + src/Resources/config/services/filter.xml | 1 + .../Application/config/packages/_sylius.yaml | 1 - .../src/Repository/ProductRepository.php | 21 ---------- 12 files changed, 91 insertions(+), 79 deletions(-) create mode 100644 src/Repository/ProductBundleRepository.php create mode 100644 src/Repository/ProductBundleRepositoryInterface.php delete mode 100644 src/Repository/ProductBundlesAwareInterface.php delete mode 100644 src/Repository/ProductBundlesAwareTrait.php delete mode 100644 tests/Application/src/Repository/ProductRepository.php diff --git a/src/Entity/ProductBundlesAwareInterface.php b/src/Entity/ProductBundlesAwareInterface.php index c4ed4eb9..e805a249 100644 --- a/src/Entity/ProductBundlesAwareInterface.php +++ b/src/Entity/ProductBundlesAwareInterface.php @@ -18,4 +18,6 @@ public function getProductBundle(): ?ProductBundleInterface; public function setProductBundle(?ProductBundleInterface $productBundle): void; public function isBundle(): bool; + + public function hasProductBundleItems(): bool; } diff --git a/src/Entity/ProductBundlesAwareTrait.php b/src/Entity/ProductBundlesAwareTrait.php index 24b8dbc9..b3485136 100644 --- a/src/Entity/ProductBundlesAwareTrait.php +++ b/src/Entity/ProductBundlesAwareTrait.php @@ -11,6 +11,7 @@ namespace BitBag\SyliusProductBundlePlugin\Entity; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; trait ProductBundlesAwareTrait @@ -45,4 +46,14 @@ public function isBundle(): bool { return null !== $this->getProductBundle(); } + + public function hasProductBundleItems(): bool + { + $items = $this->getProductBundle()?->getProductBundleItems(); + if (!$items instanceof Collection) { + return false; + } + + return 0 < $items->count(); + } } diff --git a/src/Grid/Filter/IsBundleFilter.php b/src/Grid/Filter/IsBundleFilter.php index 3a562d70..41fa3a5d 100644 --- a/src/Grid/Filter/IsBundleFilter.php +++ b/src/Grid/Filter/IsBundleFilter.php @@ -11,22 +11,28 @@ namespace BitBag\SyliusProductBundlePlugin\Grid\Filter; +use BitBag\SyliusProductBundlePlugin\Repository\ProductBundleRepositoryInterface; use Sylius\Component\Grid\Data\DataSourceInterface; use Sylius\Component\Grid\Filter\BooleanFilter; use Sylius\Component\Grid\Filtering\FilterInterface; final class IsBundleFilter implements FilterInterface { - //TODO fix handling bundles with no products added + public function __construct(private ProductBundleRepositoryInterface $productBundleRepository) + { + } + public function apply(DataSourceInterface $dataSource, string $name, $data, array $options): void { + $productBundleIds = $this->productBundleRepository->getProductIds(); + switch ($data) { case BooleanFilter::TRUE: - $dataSource->restrict('productBundleItems IS NOT NULL'); + $dataSource->restrict($dataSource->getExpressionBuilder()->in('id', $productBundleIds)); break; case BooleanFilter::FALSE: - $dataSource->restrict('productBundleItems IS NULL'); + $dataSource->restrict($dataSource->getExpressionBuilder()->notIn('id', $productBundleIds)); break; } diff --git a/src/Repository/ProductBundleRepository.php b/src/Repository/ProductBundleRepository.php new file mode 100644 index 00000000..1a817839 --- /dev/null +++ b/src/Repository/ProductBundleRepository.php @@ -0,0 +1,42 @@ +createQueryBuilder('pb') + ->select('product.id') + ->leftJoin('pb.product', 'product') + ->leftJoin('pb.productBundleItems', 'items') + ->andWhere('items.id IS NOT NULL') + ->getQuery() + ->getSingleColumnResult(); + } + + /** @param Collection $variants */ + public function findBundlesByVariants(Collection $variants): array + { + return $this->createQueryBuilder('pb') + ->select('pb') + ->leftJoin('pb.productBundleItems', 'items') + ->andWhere('items.productVariant IN (:variants)') + ->setParameter('variants', $variants) + ->getQuery() + ->getResult(); + } +} diff --git a/src/Repository/ProductBundleRepositoryInterface.php b/src/Repository/ProductBundleRepositoryInterface.php new file mode 100644 index 00000000..b408b3f8 --- /dev/null +++ b/src/Repository/ProductBundleRepositoryInterface.php @@ -0,0 +1,24 @@ + $variants */ + public function findBundlesByVariants(Collection $variants): array; +} diff --git a/src/Repository/ProductBundlesAwareInterface.php b/src/Repository/ProductBundlesAwareInterface.php deleted file mode 100644 index 2f94668c..00000000 --- a/src/Repository/ProductBundlesAwareInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -createListQueryBuilder($locale, $taxonId) - ->leftJoin('o.productBundle', 'productBundle') - ->leftJoin('productBundle.productBundleItems', 'productBundleItems'); - } -} diff --git a/src/Resources/config/grids.yml b/src/Resources/config/grids.yml index e6355667..54a30cde 100644 --- a/src/Resources/config/grids.yml +++ b/src/Resources/config/grids.yml @@ -1,15 +1,8 @@ sylius_grid: grids: sylius_admin_product: - driver: - name: doctrine/orm - options: - class: "%sylius.model.product.class%" - repository: - method: createSearchListQueryBuilder - arguments: [ "expr:service('sylius.context.locale').getLocaleCode()", $taxonId ] fields: - bundle: + productBundleItems: type: twig label: bitbag_sylius_product_bundle.ui.is_bundle options: diff --git a/src/Resources/config/resources/product_bundle.yml b/src/Resources/config/resources/product_bundle.yml index 767218f8..d426be8c 100644 --- a/src/Resources/config/resources/product_bundle.yml +++ b/src/Resources/config/resources/product_bundle.yml @@ -6,3 +6,4 @@ sylius_resource: model: BitBag\SyliusProductBundlePlugin\Entity\ProductBundle interface: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleInterface form: BitBag\SyliusProductBundlePlugin\Form\Type\ProductBundleType + repository: BitBag\SyliusProductBundlePlugin\Repository\ProductBundleRepository diff --git a/src/Resources/config/services/filter.xml b/src/Resources/config/services/filter.xml index f3543982..70cc729d 100644 --- a/src/Resources/config/services/filter.xml +++ b/src/Resources/config/services/filter.xml @@ -4,6 +4,7 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> + diff --git a/tests/Application/config/packages/_sylius.yaml b/tests/Application/config/packages/_sylius.yaml index 00de5680..0c0e3df8 100644 --- a/tests/Application/config/packages/_sylius.yaml +++ b/tests/Application/config/packages/_sylius.yaml @@ -18,7 +18,6 @@ sylius_product: product: classes: model: Tests\BitBag\SyliusProductBundlePlugin\Entity\Product - repository: Tests\BitBag\SyliusProductBundlePlugin\Application\src\Repository\ProductRepository sylius_order: resources: diff --git a/tests/Application/src/Repository/ProductRepository.php b/tests/Application/src/Repository/ProductRepository.php deleted file mode 100644 index cee09d27..00000000 --- a/tests/Application/src/Repository/ProductRepository.php +++ /dev/null @@ -1,21 +0,0 @@ - Date: Mon, 19 Aug 2024 12:40:54 +0200 Subject: [PATCH 14/40] OP-289: List bundles on product detail admin page --- src/Resources/config/services/filter.xml | 6 ++- src/Resources/config/services/twig.xml | 17 +++++--- src/Resources/translations/messages.en.yml | 1 + .../views/Admin/Product/show.html.twig | 26 +++++++++++++ .../Extension/ProductBundlesExtension.php | 39 +++++++++++++++++++ .../SyliusAdminBundle/Product/show.html.twig | 1 + 6 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 src/Resources/views/Admin/Product/show.html.twig create mode 100644 src/Twig/Extension/ProductBundlesExtension.php create mode 100644 tests/Application/templates/bundles/SyliusAdminBundle/Product/show.html.twig diff --git a/src/Resources/config/services/filter.xml b/src/Resources/config/services/filter.xml index 70cc729d..5d9c7bae 100644 --- a/src/Resources/config/services/filter.xml +++ b/src/Resources/config/services/filter.xml @@ -3,9 +3,11 @@ - + - + diff --git a/src/Resources/config/services/twig.xml b/src/Resources/config/services/twig.xml index e67c3568..e2e9a173 100644 --- a/src/Resources/config/services/twig.xml +++ b/src/Resources/config/services/twig.xml @@ -1,11 +1,18 @@ - + - - - - + + + + + + + + diff --git a/src/Resources/translations/messages.en.yml b/src/Resources/translations/messages.en.yml index e6e23f97..61f378c3 100644 --- a/src/Resources/translations/messages.en.yml +++ b/src/Resources/translations/messages.en.yml @@ -1,6 +1,7 @@ bitbag_sylius_product_bundle: ui: bundle: Bundle + bundles: Bundles product_variant: Product variant quantity: Quantity delete: Delete diff --git a/src/Resources/views/Admin/Product/show.html.twig b/src/Resources/views/Admin/Product/show.html.twig new file mode 100644 index 00000000..2c19840a --- /dev/null +++ b/src/Resources/views/Admin/Product/show.html.twig @@ -0,0 +1,26 @@ +{% extends '@SyliusAdmin/layout.html.twig' %} + +{% block title %}{{ 'sylius.ui.show_product'|trans }} | {{ product.name }}{% endblock %} + +{% block content %} + {% if product.variants|length == 1 %} + {% include "@SyliusAdmin/Product/Show/_simpleProduct.html.twig" %} + {% else %} + {% include "@SyliusAdmin/Product/Show/_configurableProduct.html.twig" %} + {% endif %} + + {% set bundles = bitbag_get_bundles_containing_product(product) %} + {% if bundles|length > 0 %} + +
+

{{ 'bitbag_sylius_product_bundle.ui.bundles'|trans }}

+ {% for bundle in bundles %} + + {% endfor %} +
+ {% endif %} +{% endblock %} diff --git a/src/Twig/Extension/ProductBundlesExtension.php b/src/Twig/Extension/ProductBundlesExtension.php new file mode 100644 index 00000000..eb2824a8 --- /dev/null +++ b/src/Twig/Extension/ProductBundlesExtension.php @@ -0,0 +1,39 @@ + ['html']]), + ]; + } + + /** @return ProductBundleInterface[] */ + public function getBundlesForProduct(ProductInterface $product): array + { + return $this->productBundleRepository->findBundlesByVariants($product->getVariants()); + } +} diff --git a/tests/Application/templates/bundles/SyliusAdminBundle/Product/show.html.twig b/tests/Application/templates/bundles/SyliusAdminBundle/Product/show.html.twig new file mode 100644 index 00000000..054123dd --- /dev/null +++ b/tests/Application/templates/bundles/SyliusAdminBundle/Product/show.html.twig @@ -0,0 +1 @@ +{% include '@BitBagSyliusProductBundlePlugin/Admin/Product/show.html.twig' %} From 9124743c95bce691b1f8aaaacb45211ec070fa1b Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Tue, 20 Aug 2024 15:59:58 +0200 Subject: [PATCH 15/40] OP-289: Add fixtures --- .../Factory/ProductBundleFixtureFactory.php | 72 +++++++++++++++++++ src/Fixture/ProductBundleFixture.php | 32 +++++++++ src/Resources/config/services.xml | 1 + src/Resources/config/services/fixture.xml | 20 ++++++ .../bitbag_sylius_product_bundle_plugin.yml | 46 +++++++++++- 5 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 src/Fixture/Factory/ProductBundleFixtureFactory.php create mode 100644 src/Fixture/ProductBundleFixture.php create mode 100644 src/Resources/config/services/fixture.xml diff --git a/src/Fixture/Factory/ProductBundleFixtureFactory.php b/src/Fixture/Factory/ProductBundleFixtureFactory.php new file mode 100644 index 00000000..9661bffd --- /dev/null +++ b/src/Fixture/Factory/ProductBundleFixtureFactory.php @@ -0,0 +1,72 @@ +optionsResolver = new OptionsResolver(); + $this->configureOptions($this->optionsResolver); + } + + private function configureOptions(OptionsResolver $resolver): void + { + $resolver + ->setDefault('bundle', '') + ->setAllowedTypes('bundle', 'string') + ->setDefault('items', []) + ->setAllowedTypes('items', 'array') + ->setDefault('is_packed', '') + ->setAllowedTypes('is_packed', 'bool') + ; + } + + public function create(array $options = []): ProductBundleInterface + { + $options = $this->optionsResolver->resolve($options); + + $bundleProduct = $this->productRepository->findOneByCode($options['bundle']); + /** @var ProductBundleInterface $productBundle */ + $productBundle = $this->productBundleFactory->createNew(); + $productBundle->setProduct($bundleProduct); + $productBundle->setIsPackedProduct($options['is_packed']); + + foreach ($options['items'] ?? [] as $item) { + /** @var ProductVariantInterface $productVariant */ + $productVariant = $this->productVariantRepository->findOneBy(['code' => $item]); + /** @var ProductBundleItemInterface $bundleItem */ + $bundleItem = $this->productBundleItemFactory->createNew(); + $bundleItem->setProductVariant($productVariant); + $bundleItem->setQuantity(1); + $bundleItem->setProductBundle($productBundle); + $productBundle->addProductBundleItem($bundleItem); + } + + return $productBundle; + } +} diff --git a/src/Fixture/ProductBundleFixture.php b/src/Fixture/ProductBundleFixture.php new file mode 100644 index 00000000..0e568b45 --- /dev/null +++ b/src/Fixture/ProductBundleFixture.php @@ -0,0 +1,32 @@ +children(); + $resourceNodeChildren->scalarNode('bundle')->end(); + $resourceNodeChildren->arrayNode('items')->scalarPrototype()->end(); + $resourceNodeChildren->booleanNode('is_packed')->end(); + } + + public function getName(): string + { + return 'product_bundle'; + } +} diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index ea17a5b4..145bb073 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -6,6 +6,7 @@ + diff --git a/src/Resources/config/services/fixture.xml b/src/Resources/config/services/fixture.xml new file mode 100644 index 00000000..d86eb6e2 --- /dev/null +++ b/src/Resources/config/services/fixture.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/tests/Application/config/packages/bitbag_sylius_product_bundle_plugin.yml b/tests/Application/config/packages/bitbag_sylius_product_bundle_plugin.yml index bd17eb83..71204aee 100644 --- a/tests/Application/config/packages/bitbag_sylius_product_bundle_plugin.yml +++ b/tests/Application/config/packages/bitbag_sylius_product_bundle_plugin.yml @@ -1,2 +1,46 @@ imports: - - { resource: "@BitBagSyliusProductBundlePlugin/Resources/config/config.yml" } + - { resource: "@BitBagSyliusProductBundlePlugin/Resources/config/config.yml" } + +sylius_fixtures: + suites: + default: + fixtures: + tshirt_bundle_products: + name: product + options: + custom: + - name: 'Packed T-Shirt bundle' + tax_category: 'clothing' + channels: + - 'FASHION_WEB' + main_taxon: 'mens_t_shirts' + taxons: + - 't_shirts' + - 'mens_t_shirts' + + - name: 'Not packed T-Shirt bundle' + tax_category: 'clothing' + channels: + - 'FASHION_WEB' + main_taxon: 'womens_t_shirts' + taxons: + - 't_shirts' + - 'womens_t_shirts' + + tshirt_bundles: + name: product_bundle + options: + custom: + - bundle: 'Packed_T_Shirt_bundle' + items: + - 'Sport_basic_white_T_Shirt-variant-0' + - 'Raglan_grey_&_black_Tee-variant-0' + - 'Oversize_white_cotton_T_Shirt-variant-0' + is_packed: true + + - bundle: 'Not_packed_T_Shirt_bundle' + items: + - 'Everyday_white_basic_T_Shirt-variant-0' + - 'Loose_white_designer_T_Shirt-variant-0' + - 'Ribbed_copper_slim_fit_Tee-variant-0' + is_packed: false From def83b39021c4828c86bb6d4bd1fc91f6b19a20b Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Wed, 21 Aug 2024 15:24:20 +0200 Subject: [PATCH 16/40] OP-289: Disable bundle form for simple and configurable products --- src/Menu/AdminProductFormMenuListener.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Menu/AdminProductFormMenuListener.php b/src/Menu/AdminProductFormMenuListener.php index f461e37d..18bf0eed 100644 --- a/src/Menu/AdminProductFormMenuListener.php +++ b/src/Menu/AdminProductFormMenuListener.php @@ -11,12 +11,19 @@ namespace BitBag\SyliusProductBundlePlugin\Menu; +use BitBag\SyliusProductBundlePlugin\Entity\ProductInterface; use Sylius\Bundle\AdminBundle\Event\ProductMenuBuilderEvent; final class AdminProductFormMenuListener { public function addItems(ProductMenuBuilderEvent $event): void { + /** @var ProductInterface $product */ + $product = $event->getProduct(); + if (null === $product->getProductBundle()) { + return; + } + $menu = $event->getMenu(); $menu From 208659e72438c02845ae4e8f07773607aa59d4c9 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Thu, 22 Aug 2024 11:10:28 +0200 Subject: [PATCH 17/40] OP-289: Don't create bundle entities for simple and configurable products --- README.md | 16 +++++++++------- src/Entity/ProductBundlesAwareInterface.php | 2 -- src/Entity/ProductBundlesAwareTrait.php | 15 +++------------ src/Form/Extension/ProductTypeExtension.php | 1 + src/Repository/ProductBundleRepository.php | 2 -- .../config/doctrine/ProductBundle.orm.xml | 9 ++++++--- .../config/doctrine/ProductBundleItem.orm.xml | 11 +++++++---- .../doctrine/ProductBundleOrderItem.orm.xml | 11 +++++++---- src/Resources/config/grids.yml | 2 +- .../Resources/config/doctrine/Product.orm.xml | 3 ++- 10 files changed, 36 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 7636f319..6ee3c44a 100644 --- a/README.md +++ b/README.md @@ -121,9 +121,10 @@ This **open-source plugin was developed to help the Sylius community**. If you h * @var ProductBundleInterface */ #[ORM\OneToOne( - targetEntity: "BitBag\SyliusProductBundlePlugin\Entity\ProductBundleInterface", - mappedBy: "product", - cascade: ["all"] + targetEntity: 'BitBag\SyliusProductBundlePlugin\Entity\ProductBundleInterface', + mappedBy: 'product', + cascade: ['all'], + orphanRemoval: true, )] protected $productBundle; @@ -140,10 +141,11 @@ This **open-source plugin was developed to help the Sylius community**. If you h http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd" > - - - - + + + +
diff --git a/src/Entity/ProductBundlesAwareInterface.php b/src/Entity/ProductBundlesAwareInterface.php index e805a249..c4ed4eb9 100644 --- a/src/Entity/ProductBundlesAwareInterface.php +++ b/src/Entity/ProductBundlesAwareInterface.php @@ -18,6 +18,4 @@ public function getProductBundle(): ?ProductBundleInterface; public function setProductBundle(?ProductBundleInterface $productBundle): void; public function isBundle(): bool; - - public function hasProductBundleItems(): bool; } diff --git a/src/Entity/ProductBundlesAwareTrait.php b/src/Entity/ProductBundlesAwareTrait.php index b3485136..5a76aaa1 100644 --- a/src/Entity/ProductBundlesAwareTrait.php +++ b/src/Entity/ProductBundlesAwareTrait.php @@ -11,7 +11,6 @@ namespace BitBag\SyliusProductBundlePlugin\Entity; -use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; trait ProductBundlesAwareTrait @@ -22,13 +21,15 @@ trait ProductBundlesAwareTrait * @ORM\OneToOne( * targetEntity=ProductBundleInterface::class, * mappedBy="product", - * cascade={"all"} + * cascade={"all"}, + * orphanRemoval=true, * ) */ #[ORM\OneToOne( targetEntity: ProductBundleInterface::class, mappedBy: 'product', cascade: ['all'], + orphanRemoval: true, )] protected $productBundle; @@ -46,14 +47,4 @@ public function isBundle(): bool { return null !== $this->getProductBundle(); } - - public function hasProductBundleItems(): bool - { - $items = $this->getProductBundle()?->getProductBundleItems(); - if (!$items instanceof Collection) { - return false; - } - - return 0 < $items->count(); - } } diff --git a/src/Form/Extension/ProductTypeExtension.php b/src/Form/Extension/ProductTypeExtension.php index 4e1122ba..b211678b 100644 --- a/src/Form/Extension/ProductTypeExtension.php +++ b/src/Form/Extension/ProductTypeExtension.php @@ -25,6 +25,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ->add('productBundle', ProductBundleType::class, [ 'label' => false, 'constraints' => [new Valid()], + 'required' => false, ]) ; } diff --git a/src/Repository/ProductBundleRepository.php b/src/Repository/ProductBundleRepository.php index 1a817839..c8089a3b 100644 --- a/src/Repository/ProductBundleRepository.php +++ b/src/Repository/ProductBundleRepository.php @@ -22,8 +22,6 @@ public function getProductIds(): array return $this->createQueryBuilder('pb') ->select('product.id') ->leftJoin('pb.product', 'product') - ->leftJoin('pb.productBundleItems', 'items') - ->andWhere('items.id IS NOT NULL') ->getQuery() ->getSingleColumnResult(); } diff --git a/src/Resources/config/doctrine/ProductBundle.orm.xml b/src/Resources/config/doctrine/ProductBundle.orm.xml index ad1214ec..d58497f2 100644 --- a/src/Resources/config/doctrine/ProductBundle.orm.xml +++ b/src/Resources/config/doctrine/ProductBundle.orm.xml @@ -18,16 +18,19 @@ - + - + - + diff --git a/src/Resources/config/doctrine/ProductBundleItem.orm.xml b/src/Resources/config/doctrine/ProductBundleItem.orm.xml index e2645073..2e3a20f4 100644 --- a/src/Resources/config/doctrine/ProductBundleItem.orm.xml +++ b/src/Resources/config/doctrine/ProductBundleItem.orm.xml @@ -3,7 +3,8 @@ - + @@ -18,11 +19,13 @@ - + - - + + diff --git a/src/Resources/config/doctrine/ProductBundleOrderItem.orm.xml b/src/Resources/config/doctrine/ProductBundleOrderItem.orm.xml index 47a6eeb6..389967d1 100644 --- a/src/Resources/config/doctrine/ProductBundleOrderItem.orm.xml +++ b/src/Resources/config/doctrine/ProductBundleOrderItem.orm.xml @@ -3,7 +3,8 @@ - + @@ -18,17 +19,19 @@ - + - + - + diff --git a/src/Resources/config/grids.yml b/src/Resources/config/grids.yml index 54a30cde..746428d3 100644 --- a/src/Resources/config/grids.yml +++ b/src/Resources/config/grids.yml @@ -2,7 +2,7 @@ sylius_grid: grids: sylius_admin_product: fields: - productBundleItems: + bundle: type: twig label: bitbag_sylius_product_bundle.ui.is_bundle options: diff --git a/tests/Application/src/Resources/config/doctrine/Product.orm.xml b/tests/Application/src/Resources/config/doctrine/Product.orm.xml index 974072b7..d291029b 100644 --- a/tests/Application/src/Resources/config/doctrine/Product.orm.xml +++ b/tests/Application/src/Resources/config/doctrine/Product.orm.xml @@ -5,7 +5,8 @@ http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd" > - + From 4678d0587306c4fcc6f4d9a18f2660bfcf2be126 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Thu, 22 Aug 2024 11:26:10 +0200 Subject: [PATCH 18/40] OP-289: Reformat configs for consistency --- README.md | 46 +++++----- src/Resources/config/api_resources/Order.xml | 41 +++++---- .../config/api_resources/Product.xml | 26 +++--- src/Resources/config/config.yml | 12 +-- src/Resources/config/resources.yml | 6 +- .../config/resources/product_bundle.yml | 16 ++-- .../config/resources/product_bundle_item.yml | 14 +-- .../resources/product_bundle_order_item.yml | 12 +-- src/Resources/config/routing.yml | 8 +- src/Resources/config/routing/admin.yml | 92 +++++++++---------- src/Resources/config/routing/shop.yml | 66 ++++++------- src/Resources/config/services.xml | 3 +- src/Resources/config/services/controller.xml | 13 ++- .../config/services/event_listener.xml | 8 +- src/Resources/config/services/factory.xml | 17 ++-- src/Resources/config/services/form.xml | 18 ++-- src/Resources/config/services/handler.xml | 12 ++- src/Resources/config/services/menu.xml | 8 +- src/Resources/config/services/processor.xml | 11 ++- src/Resources/config/services/transformer.xml | 5 +- src/Resources/config/services/validator.xml | 30 +++--- .../validation/AddProductBundleToCartDto.xml | 2 +- .../Application/config/packages/_sylius.yaml | 32 +++---- .../config/packages/dev/framework.yaml | 2 +- .../config/packages/dev/monolog.yaml | 16 ++-- .../config/packages/dev/routing.yaml | 4 +- .../config/packages/dev/web_profiler.yaml | 4 +- .../Application/config/packages/doctrine.yaml | 40 ++++---- .../config/packages/doctrine_migrations.yaml | 6 +- .../Application/config/packages/fos_rest.yaml | 20 ++-- .../config/packages/framework.yaml | 10 +- .../packages/lexik_jwt_authentication.yaml | 6 +- .../config/packages/liip_imagine.yaml | 10 +- .../config/packages/prod/doctrine.yaml | 52 +++++------ .../config/packages/prod/monolog.yaml | 18 ++-- .../Application/config/packages/routing.yaml | 4 +- .../config/packages/staging/monolog.yaml | 18 ++-- .../packages/stof_doctrine_extensions.yaml | 2 +- .../config/packages/test/framework.yaml | 6 +- .../config/packages/test/monolog.yaml | 10 +- .../config/packages/test/sylius_theme.yaml | 4 +- .../config/packages/test/sylius_uploader.yaml | 4 +- .../config/packages/test/web_profiler.yaml | 6 +- .../config/packages/test_cached/doctrine.yaml | 30 +++--- .../config/packages/test_cached/fos_rest.yaml | 4 +- .../packages/test_cached/framework.yaml | 6 +- .../config/packages/test_cached/monolog.yaml | 10 +- .../packages/test_cached/sylius_channel.yaml | 2 +- .../packages/test_cached/sylius_theme.yaml | 4 +- .../packages/test_cached/sylius_uploader.yaml | 2 +- .../config/packages/test_cached/twig.yaml | 2 +- .../config/packages/translation.yaml | 14 +-- tests/Application/config/packages/twig.yaml | 16 ++-- .../config/packages/validator.yaml | 4 +- tests/Application/config/routes.yaml | 2 +- .../config/routes/dev/web_profiler.yaml | 8 +- .../config/routes/liip_imagine.yaml | 2 +- .../config/routes/sylius_admin.yaml | 4 +- .../Application/config/routes/sylius_api.yaml | 4 +- .../config/routes/sylius_shop.yaml | 18 ++-- .../routes/test/sylius_test_plugin.yaml | 8 +- .../config/routes/test_cached/routing.yaml | 8 +- .../test_cached/sylius_test_plugin.yaml | 8 +- tests/Application/config/services.yaml | 2 +- tests/Application/config/services_test.yaml | 6 +- .../config/services_test_cached.yaml | 2 +- .../sylius/1.12/packages/jms_serializer.yaml | 6 +- .../config/sylius/1.12/packages/security.yaml | 4 +- .../sylius/1.13/packages/jms_serializer.yaml | 6 +- .../config/sylius/1.13/packages/security.yaml | 4 +- .../symfony/4.4/packages/framework.yaml | 2 +- .../config/doctrine/OrderItem.orm.xml | 4 +- 72 files changed, 480 insertions(+), 452 deletions(-) diff --git a/README.md b/README.md index 6ee3c44a..62f450bc 100644 --- a/README.md +++ b/README.md @@ -65,9 +65,9 @@ This **open-source plugin was developed to help the Sylius community**. If you h # config/packages/_sylius.yaml imports: - ... + ... - - { resource: "@BitBagSyliusProductBundlePlugin/Resources/config/config.yml" } + - { resource: "@BitBagSyliusProductBundlePlugin/Resources/config/config.yml" } ``` 4. (optional) Import routing in your `config/routes.yaml` file: @@ -78,7 +78,7 @@ This **open-source plugin was developed to help the Sylius community**. If you h ... bitbag_sylius_product_bundle_plugin: - resource: "@BitBagSyliusProductBundlePlugin/Resources/config/routing.yml" + resource: "@BitBagSyliusProductBundlePlugin/Resources/config/routing.yml" ``` 5. Extend `Product`(including Doctrine mapping): @@ -231,16 +231,16 @@ This **open-source plugin was developed to help the Sylius community**. If you h # config/packages/_sylius.yaml sylius_product: - resources: - product: - classes: - model: App\Entity\Product\Product - - sylius_order: - resources: - order_item: - classes: - model: App\Entity\Order\OrderItem + resources: + product: + classes: + model: App\Entity\Product\Product + + sylius_order: + resources: + order_item: + classes: + model: App\Entity\Order\OrderItem ``` @@ -249,16 +249,16 @@ This **open-source plugin was developed to help the Sylius community**. If you h ```yaml # config/packages/doctrine.yaml doctrine: - orm: - entity_managers: - default: - mappings: - App: - is_bundle: false - type: xml - dir: '%kernel.project_dir%/src/Resources/config/doctrine' - prefix: 'App\Entity' - alias: App + orm: + entity_managers: + default: + mappings: + App: + is_bundle: false + type: xml + dir: '%kernel.project_dir%/src/Resources/config/doctrine' + prefix: 'App\Entity' + alias: App ``` diff --git a/src/Resources/config/api_resources/Order.xml b/src/Resources/config/api_resources/Order.xml index 47cbbefa..8a2fef40 100644 --- a/src/Resources/config/api_resources/Order.xml +++ b/src/Resources/config/api_resources/Order.xml @@ -34,7 +34,8 @@ shop:order:create - Pickups a new cart. Provided locale code has to be one of available for a particular channel. + Pickups a new cart. Provided locale code has to be one of available for a particular channel. + @@ -414,33 +415,33 @@ - - - - - + + + + + - + - + - - - - - + + + + + - + - - + + - + - - - + + + diff --git a/src/Resources/config/api_resources/Product.xml b/src/Resources/config/api_resources/Product.xml index 854c50ed..74bfe012 100644 --- a/src/Resources/config/api_resources/Product.xml +++ b/src/Resources/config/api_resources/Product.xml @@ -99,10 +99,10 @@ - - - - + + + + object @@ -115,17 +115,17 @@ - - - - - - + + + + + + - + - + - + diff --git a/src/Resources/config/config.yml b/src/Resources/config/config.yml index bc83332f..11f741f3 100644 --- a/src/Resources/config/config.yml +++ b/src/Resources/config/config.yml @@ -1,9 +1,9 @@ imports: - - { resource: resources.yml } - - { resource: grids.yml } - - { resource: services.xml } + - { resource: resources.yml } + - { resource: grids.yml } + - { resource: services.xml } framework: - messenger: - buses: - bitbag_sylius_product_bundle.command_bus: ~ + messenger: + buses: + bitbag_sylius_product_bundle.command_bus: ~ diff --git a/src/Resources/config/resources.yml b/src/Resources/config/resources.yml index 564c562c..f9720b2f 100644 --- a/src/Resources/config/resources.yml +++ b/src/Resources/config/resources.yml @@ -1,4 +1,4 @@ imports: - - { resource: resources/product_bundle.yml } - - { resource: resources/product_bundle_item.yml } - - { resource: resources/product_bundle_order_item.yml } + - { resource: resources/product_bundle.yml } + - { resource: resources/product_bundle_item.yml } + - { resource: resources/product_bundle_order_item.yml } diff --git a/src/Resources/config/resources/product_bundle.yml b/src/Resources/config/resources/product_bundle.yml index d426be8c..603e2023 100644 --- a/src/Resources/config/resources/product_bundle.yml +++ b/src/Resources/config/resources/product_bundle.yml @@ -1,9 +1,9 @@ sylius_resource: - resources: - bitbag_sylius_product_bundle.product_bundle: - driver: doctrine/orm - classes: - model: BitBag\SyliusProductBundlePlugin\Entity\ProductBundle - interface: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleInterface - form: BitBag\SyliusProductBundlePlugin\Form\Type\ProductBundleType - repository: BitBag\SyliusProductBundlePlugin\Repository\ProductBundleRepository + resources: + bitbag_sylius_product_bundle.product_bundle: + driver: doctrine/orm + classes: + model: BitBag\SyliusProductBundlePlugin\Entity\ProductBundle + interface: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleInterface + form: BitBag\SyliusProductBundlePlugin\Form\Type\ProductBundleType + repository: BitBag\SyliusProductBundlePlugin\Repository\ProductBundleRepository diff --git a/src/Resources/config/resources/product_bundle_item.yml b/src/Resources/config/resources/product_bundle_item.yml index da9bd09e..40ddaa70 100644 --- a/src/Resources/config/resources/product_bundle_item.yml +++ b/src/Resources/config/resources/product_bundle_item.yml @@ -1,8 +1,8 @@ sylius_resource: - resources: - bitbag_sylius_product_bundle.product_bundle_item: - driver: doctrine/orm - classes: - model: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItem - interface: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface - form: BitBag\SyliusProductBundlePlugin\Form\Type\ProductBundleItemType + resources: + bitbag_sylius_product_bundle.product_bundle_item: + driver: doctrine/orm + classes: + model: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItem + interface: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface + form: BitBag\SyliusProductBundlePlugin\Form\Type\ProductBundleItemType diff --git a/src/Resources/config/resources/product_bundle_order_item.yml b/src/Resources/config/resources/product_bundle_order_item.yml index c11d81dd..4b7c9867 100644 --- a/src/Resources/config/resources/product_bundle_order_item.yml +++ b/src/Resources/config/resources/product_bundle_order_item.yml @@ -1,7 +1,7 @@ sylius_resource: - resources: - bitbag_sylius_product_bundle.product_bundle_order_item: - driver: doctrine/orm - classes: - model: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItem - interface: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItemInterface + resources: + bitbag_sylius_product_bundle.product_bundle_order_item: + driver: doctrine/orm + classes: + model: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItem + interface: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItemInterface diff --git a/src/Resources/config/routing.yml b/src/Resources/config/routing.yml index 1a87e161..e35d5a36 100644 --- a/src/Resources/config/routing.yml +++ b/src/Resources/config/routing.yml @@ -1,7 +1,7 @@ bitbag_sylius_product_bundle_admin: - resource: routing/admin.yml - prefix: /admin + resource: routing/admin.yml + prefix: /admin bitbag_sylius_product_bundle_shop: - resource: routing/shop.yml - prefix: /{_locale} + resource: routing/shop.yml + prefix: /{_locale} diff --git a/src/Resources/config/routing/admin.yml b/src/Resources/config/routing/admin.yml index a8681f25..547111be 100644 --- a/src/Resources/config/routing/admin.yml +++ b/src/Resources/config/routing/admin.yml @@ -1,51 +1,51 @@ bitbag_product_bundle_admin_product_create_bundle: - path: /products/new/bundle - methods: [GET, POST] - defaults: - _controller: sylius.controller.product::createAction - _sylius: - section: admin - permission: true - factory: - method: createWithVariantAndBundle - template: "@SyliusAdmin/Crud/create.html.twig" - redirect: sylius_admin_product_update - form: - options: - validation_groups: - - sylius - - bitbag_sylius_product_bundle - vars: - subheader: sylius.ui.manage_your_product_catalog - templates: - form: "@SyliusAdmin/Product/_form.html.twig" - route: - name: bitbag_product_bundle_admin_product_create_bundle + path: /products/new/bundle + methods: [ GET, POST ] + defaults: + _controller: sylius.controller.product::createAction + _sylius: + section: admin + permission: true + factory: + method: createWithVariantAndBundle + template: "@SyliusAdmin/Crud/create.html.twig" + redirect: sylius_admin_product_update + form: + options: + validation_groups: + - sylius + - bitbag_sylius_product_bundle + vars: + subheader: sylius.ui.manage_your_product_catalog + templates: + form: "@SyliusAdmin/Product/_form.html.twig" + route: + name: bitbag_product_bundle_admin_product_create_bundle bitbag_product_bundle_admin_ajax_product_variants_by_phrase: - path: /ajax/product-variants/search-by-phrase - methods: [GET] - defaults: - _controller: sylius.controller.product_variant::indexAction - _format: json - _sylius: - serialization_groups: [Autocomplete] - permission: true - repository: - method: findByPhrase - arguments: - phrase: $phrase - locale: expr:service('sylius.context.locale').getLocaleCode() + path: /ajax/product-variants/search-by-phrase + methods: [ GET ] + defaults: + _controller: sylius.controller.product_variant::indexAction + _format: json + _sylius: + serialization_groups: [ Autocomplete ] + permission: true + repository: + method: findByPhrase + arguments: + phrase: $phrase + locale: expr:service('sylius.context.locale').getLocaleCode() bitbag_product_bundle_admin_ajax_product_variants_by_codes: - path: /ajax/product-variants/by-codes - methods: [GET] - defaults: - _controller: sylius.controller.product_variant::indexAction - _format: json - _sylius: - serialization_groups: [Autocomplete] - permission: true - repository: - method: findByCodes - arguments: [$code] + path: /ajax/product-variants/by-codes + methods: [ GET ] + defaults: + _controller: sylius.controller.product_variant::indexAction + _format: json + _sylius: + serialization_groups: [ Autocomplete ] + permission: true + repository: + method: findByCodes + arguments: [ $code ] diff --git a/src/Resources/config/routing/shop.yml b/src/Resources/config/routing/shop.yml index d8d693f4..8d5f3e4b 100644 --- a/src/Resources/config/routing/shop.yml +++ b/src/Resources/config/routing/shop.yml @@ -1,36 +1,36 @@ bitbag_sylius_product_bundle_shop_ajax_cart_add_product_bundle: - path: /ajax/cart/product-bundle/add - methods: [POST] - defaults: - _controller: bitbag_sylius_product_bundle.controller.order_item::addProductBundleAction - _format: json - _sylius: - factory: - method: createForProduct - arguments: [expr:notFoundOnNull(service('sylius.repository.product').find($productId))] - form: - type: BitBag\SyliusProductBundlePlugin\Form\Type\AddProductBundleToCartType - options: - product: expr:notFoundOnNull(service('sylius.repository.product').find($productId)) - redirect: - route: sylius_shop_cart_summary - parameters: {} - flash: sylius.cart.add_item + path: /ajax/cart/product-bundle/add + methods: [ POST ] + defaults: + _controller: bitbag_sylius_product_bundle.controller.order_item::addProductBundleAction + _format: json + _sylius: + factory: + method: createForProduct + arguments: [ expr:notFoundOnNull(service('sylius.repository.product').find($productId)) ] + form: + type: BitBag\SyliusProductBundlePlugin\Form\Type\AddProductBundleToCartType + options: + product: expr:notFoundOnNull(service('sylius.repository.product').find($productId)) + redirect: + route: sylius_shop_cart_summary + parameters: { } + flash: sylius.cart.add_item bitbag_sylius_product_bundle_shop_partial_cart_add_product_bundle: - path: /_partial/cart/product-bundle/add - methods: [GET] - defaults: - _controller: bitbag_sylius_product_bundle.controller.order_item::addProductBundleAction - _sylius: - template: $template - factory: - method: createForProduct - arguments: [expr:notFoundOnNull(service('sylius.repository.product').find($productId))] - form: - type: BitBag\SyliusProductBundlePlugin\Form\Type\AddProductBundleToCartType - options: - product: expr:notFoundOnNull(service('sylius.repository.product').find($productId)) - redirect: - route: sylius_shop_cart_summary - parameters: {} + path: /_partial/cart/product-bundle/add + methods: [ GET ] + defaults: + _controller: bitbag_sylius_product_bundle.controller.order_item::addProductBundleAction + _sylius: + template: $template + factory: + method: createForProduct + arguments: [ expr:notFoundOnNull(service('sylius.repository.product').find($productId)) ] + form: + type: BitBag\SyliusProductBundlePlugin\Form\Type\AddProductBundleToCartType + options: + product: expr:notFoundOnNull(service('sylius.repository.product').find($productId)) + redirect: + route: sylius_shop_cart_summary + parameters: { } diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 145bb073..a968c028 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -1,6 +1,7 @@ - + diff --git a/src/Resources/config/services/controller.xml b/src/Resources/config/services/controller.xml index c51fcb10..ec85cc4f 100644 --- a/src/Resources/config/services/controller.xml +++ b/src/Resources/config/services/controller.xml @@ -1,12 +1,15 @@ - + - + - - - + + + diff --git a/src/Resources/config/services/event_listener.xml b/src/Resources/config/services/event_listener.xml index 797d027c..95a149f8 100644 --- a/src/Resources/config/services/event_listener.xml +++ b/src/Resources/config/services/event_listener.xml @@ -2,9 +2,11 @@ - - - + + + diff --git a/src/Resources/config/services/factory.xml b/src/Resources/config/services/factory.xml index 26e912a5..0eff5d4e 100644 --- a/src/Resources/config/services/factory.xml +++ b/src/Resources/config/services/factory.xml @@ -1,17 +1,20 @@ - + - - - + + + - + - + - + diff --git a/src/Resources/config/services/form.xml b/src/Resources/config/services/form.xml index b64a68d5..06e50700 100644 --- a/src/Resources/config/services/form.xml +++ b/src/Resources/config/services/form.xml @@ -1,6 +1,7 @@ - + bitbag_sylius_product_bundle @@ -12,20 +13,23 @@ - + %bitbag_sylius_product_bundle.model.product_bundle.class% %bitbag_sylius_product_bundle.form.type.product_bundle.validation_groups% - + - + %bitbag_sylius_product_bundle.model.product_bundle_item.class% %bitbag_sylius_product_bundle.form.type.product_bundle_item.validation_groups% - + - - + + diff --git a/src/Resources/config/services/handler.xml b/src/Resources/config/services/handler.xml index 66cfaf68..bbcc3f66 100644 --- a/src/Resources/config/services/handler.xml +++ b/src/Resources/config/services/handler.xml @@ -1,11 +1,13 @@ - + - - - - + + + + diff --git a/src/Resources/config/services/menu.xml b/src/Resources/config/services/menu.xml index 11233699..a95e9905 100644 --- a/src/Resources/config/services/menu.xml +++ b/src/Resources/config/services/menu.xml @@ -1,9 +1,11 @@ - + - - + + diff --git a/src/Resources/config/services/processor.xml b/src/Resources/config/services/processor.xml index dec8a8cf..ac9c1ead 100644 --- a/src/Resources/config/services/processor.xml +++ b/src/Resources/config/services/processor.xml @@ -1,15 +1,16 @@ - + - - - - + + + + diff --git a/src/Resources/config/services/transformer.xml b/src/Resources/config/services/transformer.xml index 1cacdd6a..4f212350 100644 --- a/src/Resources/config/services/transformer.xml +++ b/src/Resources/config/services/transformer.xml @@ -1,12 +1,13 @@ - + - + diff --git a/src/Resources/config/services/validator.xml b/src/Resources/config/services/validator.xml index 9994250d..89bd7a66 100644 --- a/src/Resources/config/services/validator.xml +++ b/src/Resources/config/services/validator.xml @@ -1,32 +1,38 @@ - + - - - + + + - - + + - + - - + + - + - - + + diff --git a/src/Resources/config/validation/AddProductBundleToCartDto.xml b/src/Resources/config/validation/AddProductBundleToCartDto.xml index 73877de2..293524f1 100644 --- a/src/Resources/config/validation/AddProductBundleToCartDto.xml +++ b/src/Resources/config/validation/AddProductBundleToCartDto.xml @@ -12,6 +12,6 @@ https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd"> - + diff --git a/tests/Application/config/packages/_sylius.yaml b/tests/Application/config/packages/_sylius.yaml index 0c0e3df8..e39d353e 100644 --- a/tests/Application/config/packages/_sylius.yaml +++ b/tests/Application/config/packages/_sylius.yaml @@ -1,30 +1,30 @@ imports: - - { resource: "@SyliusCoreBundle/Resources/config/app/config.yml" } + - { resource: "@SyliusCoreBundle/Resources/config/app/config.yml" } - - { resource: "@SyliusAdminBundle/Resources/config/app/config.yml" } + - { resource: "@SyliusAdminBundle/Resources/config/app/config.yml" } - - { resource: "@SyliusShopBundle/Resources/config/app/config.yml" } + - { resource: "@SyliusShopBundle/Resources/config/app/config.yml" } - - { resource: "@SyliusApiBundle/Resources/config/app/config.yaml" } + - { resource: "@SyliusApiBundle/Resources/config/app/config.yaml" } sylius_api: - enabled: true + enabled: true parameters: - sylius_core.public_dir: '%kernel.project_dir%/public' + sylius_core.public_dir: '%kernel.project_dir%/public' sylius_product: - resources: - product: - classes: - model: Tests\BitBag\SyliusProductBundlePlugin\Entity\Product + resources: + product: + classes: + model: Tests\BitBag\SyliusProductBundlePlugin\Entity\Product sylius_order: - resources: - order_item: - classes: - model: Tests\BitBag\SyliusProductBundlePlugin\Entity\OrderItem + resources: + order_item: + classes: + model: Tests\BitBag\SyliusProductBundlePlugin\Entity\OrderItem sylius_shop: - product_grid: - include_all_descendants: true + product_grid: + include_all_descendants: true diff --git a/tests/Application/config/packages/dev/framework.yaml b/tests/Application/config/packages/dev/framework.yaml index 4b116def..1e2ebc1f 100644 --- a/tests/Application/config/packages/dev/framework.yaml +++ b/tests/Application/config/packages/dev/framework.yaml @@ -1,2 +1,2 @@ framework: - profiler: { only_exceptions: false } + profiler: { only_exceptions: false } diff --git a/tests/Application/config/packages/dev/monolog.yaml b/tests/Application/config/packages/dev/monolog.yaml index da2b092d..a8ae3c7d 100644 --- a/tests/Application/config/packages/dev/monolog.yaml +++ b/tests/Application/config/packages/dev/monolog.yaml @@ -1,9 +1,9 @@ monolog: - handlers: - main: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: debug - firephp: - type: firephp - level: info + 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 index 4116679a..a3d2503d 100644 --- a/tests/Application/config/packages/dev/routing.yaml +++ b/tests/Application/config/packages/dev/routing.yaml @@ -1,3 +1,3 @@ framework: - router: - strict_requirements: true + router: + strict_requirements: true diff --git a/tests/Application/config/packages/dev/web_profiler.yaml b/tests/Application/config/packages/dev/web_profiler.yaml index 1f1cb2bb..be5064e3 100644 --- a/tests/Application/config/packages/dev/web_profiler.yaml +++ b/tests/Application/config/packages/dev/web_profiler.yaml @@ -1,3 +1,3 @@ web_profiler: - toolbar: true - intercept_redirects: false + toolbar: true + intercept_redirects: false diff --git a/tests/Application/config/packages/doctrine.yaml b/tests/Application/config/packages/doctrine.yaml index b4126767..c74b2072 100644 --- a/tests/Application/config/packages/doctrine.yaml +++ b/tests/Application/config/packages/doctrine.yaml @@ -1,25 +1,25 @@ 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): '' + # 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 + dbal: + driver: 'pdo_mysql' + server_version: '5.7' + charset: UTF8 - url: '%env(resolve:DATABASE_URL)%' + url: '%env(resolve:DATABASE_URL)%' - orm: - auto_generate_proxy_classes: '%kernel.debug%' - naming_strategy: doctrine.orm.naming_strategy.underscore - auto_mapping: true - mappings: - App: - is_bundle: false - type: xml - dir: '%kernel.project_dir%/src/Resources/config/doctrine' - prefix: 'Tests\BitBag\SyliusProductBundlePlugin\Entity' + orm: + auto_generate_proxy_classes: '%kernel.debug%' + naming_strategy: doctrine.orm.naming_strategy.underscore + auto_mapping: true + mappings: + App: + is_bundle: false + type: xml + dir: '%kernel.project_dir%/src/Resources/config/doctrine' + prefix: 'Tests\BitBag\SyliusProductBundlePlugin\Entity' diff --git a/tests/Application/config/packages/doctrine_migrations.yaml b/tests/Application/config/packages/doctrine_migrations.yaml index cdbc01ae..2b3bacda 100644 --- a/tests/Application/config/packages/doctrine_migrations.yaml +++ b/tests/Application/config/packages/doctrine_migrations.yaml @@ -1,4 +1,4 @@ doctrine_migrations: - storage: - table_storage: - table_name: sylius_migrations + storage: + table_storage: + table_name: sylius_migrations diff --git a/tests/Application/config/packages/fos_rest.yaml b/tests/Application/config/packages/fos_rest.yaml index a72eef7c..6e54cbdc 100644 --- a/tests/Application/config/packages/fos_rest.yaml +++ b/tests/Application/config/packages/fos_rest.yaml @@ -1,11 +1,11 @@ fos_rest: - exception: true - view: - formats: - json: true - xml: true - empty_content: 204 - format_listener: - rules: - - { path: '^/api/.*', priorities: ['json', 'xml'], fallback_format: json, prefer_extension: true } - - { path: '^/', stop: true } + exception: true + view: + formats: + json: true + xml: true + empty_content: 204 + format_listener: + rules: + - { path: '^/api/.*', 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 index 9b445011..22e9f619 100644 --- a/tests/Application/config/packages/framework.yaml +++ b/tests/Application/config/packages/framework.yaml @@ -1,6 +1,6 @@ framework: - secret: '%env(APP_SECRET)%' - form: true - csrf_protection: true - session: - handler_id: ~ + secret: '%env(APP_SECRET)%' + form: true + csrf_protection: true + session: + handler_id: ~ diff --git a/tests/Application/config/packages/lexik_jwt_authentication.yaml b/tests/Application/config/packages/lexik_jwt_authentication.yaml index edfb69dc..20f1cb0c 100644 --- a/tests/Application/config/packages/lexik_jwt_authentication.yaml +++ b/tests/Application/config/packages/lexik_jwt_authentication.yaml @@ -1,4 +1,4 @@ lexik_jwt_authentication: - secret_key: '%env(resolve:JWT_SECRET_KEY)%' - public_key: '%env(resolve:JWT_PUBLIC_KEY)%' - pass_phrase: '%env(JWT_PASSPHRASE)%' + 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 index bb2e7ceb..28b2e2cf 100644 --- a/tests/Application/config/packages/liip_imagine.yaml +++ b/tests/Application/config/packages/liip_imagine.yaml @@ -1,6 +1,6 @@ liip_imagine: - resolvers: - default: - web_path: - web_root: "%kernel.project_dir%/public" - cache_prefix: "media/cache" + 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 index 2f16f0fd..a0f45e57 100644 --- a/tests/Application/config/packages/prod/doctrine.yaml +++ b/tests/Application/config/packages/prod/doctrine.yaml @@ -1,31 +1,31 @@ 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 + 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' + 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 + cache: + pools: + doctrine.result_cache_pool: + adapter: cache.app + doctrine.system_cache_pool: + adapter: cache.system diff --git a/tests/Application/config/packages/prod/monolog.yaml b/tests/Application/config/packages/prod/monolog.yaml index 64612114..f17d1996 100644 --- a/tests/Application/config/packages/prod/monolog.yaml +++ b/tests/Application/config/packages/prod/monolog.yaml @@ -1,10 +1,10 @@ monolog: - handlers: - main: - type: fingers_crossed - action_level: error - handler: nested - nested: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: debug + 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 index 368bc7f4..17ae5482 100644 --- a/tests/Application/config/packages/routing.yaml +++ b/tests/Application/config/packages/routing.yaml @@ -1,3 +1,3 @@ framework: - router: - strict_requirements: ~ + router: + strict_requirements: ~ diff --git a/tests/Application/config/packages/staging/monolog.yaml b/tests/Application/config/packages/staging/monolog.yaml index 64612114..f17d1996 100644 --- a/tests/Application/config/packages/staging/monolog.yaml +++ b/tests/Application/config/packages/staging/monolog.yaml @@ -1,10 +1,10 @@ monolog: - handlers: - main: - type: fingers_crossed - action_level: error - handler: nested - nested: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: debug + 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/stof_doctrine_extensions.yaml b/tests/Application/config/packages/stof_doctrine_extensions.yaml index 7770f74e..b2e32470 100644 --- a/tests/Application/config/packages/stof_doctrine_extensions.yaml +++ b/tests/Application/config/packages/stof_doctrine_extensions.yaml @@ -1,4 +1,4 @@ # 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%' + default_locale: '%locale%' diff --git a/tests/Application/config/packages/test/framework.yaml b/tests/Application/config/packages/test/framework.yaml index daf04d4e..2734c0ca 100644 --- a/tests/Application/config/packages/test/framework.yaml +++ b/tests/Application/config/packages/test/framework.yaml @@ -1,4 +1,4 @@ framework: - test: ~ - session: - handler_id: ~ + test: ~ + session: + handler_id: ~ diff --git a/tests/Application/config/packages/test/monolog.yaml b/tests/Application/config/packages/test/monolog.yaml index 7e2b9e3a..d5cefe75 100644 --- a/tests/Application/config/packages/test/monolog.yaml +++ b/tests/Application/config/packages/test/monolog.yaml @@ -1,6 +1,6 @@ monolog: - handlers: - main: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: error + handlers: + main: + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" + level: error diff --git a/tests/Application/config/packages/test/sylius_theme.yaml b/tests/Application/config/packages/test/sylius_theme.yaml index 4d34199f..3cc3d971 100644 --- a/tests/Application/config/packages/test/sylius_theme.yaml +++ b/tests/Application/config/packages/test/sylius_theme.yaml @@ -1,3 +1,3 @@ sylius_theme: - sources: - test: ~ + sources: + test: ~ diff --git a/tests/Application/config/packages/test/sylius_uploader.yaml b/tests/Application/config/packages/test/sylius_uploader.yaml index ab9d6ca0..cfc5d05b 100644 --- a/tests/Application/config/packages/test/sylius_uploader.yaml +++ b/tests/Application/config/packages/test/sylius_uploader.yaml @@ -1,3 +1,3 @@ services: - Sylius\Component\Core\Generator\ImagePathGeneratorInterface: - class: Sylius\Behat\Service\Generator\UploadedImagePathGenerator + Sylius\Component\Core\Generator\ImagePathGeneratorInterface: + class: Sylius\Behat\Service\Generator\UploadedImagePathGenerator diff --git a/tests/Application/config/packages/test/web_profiler.yaml b/tests/Application/config/packages/test/web_profiler.yaml index 03752de2..808c4cf9 100644 --- a/tests/Application/config/packages/test/web_profiler.yaml +++ b/tests/Application/config/packages/test/web_profiler.yaml @@ -1,6 +1,6 @@ web_profiler: - toolbar: false - intercept_redirects: false + toolbar: false + intercept_redirects: false framework: - profiler: { collect: false } + profiler: { collect: false } diff --git a/tests/Application/config/packages/test_cached/doctrine.yaml b/tests/Application/config/packages/test_cached/doctrine.yaml index 49528606..316da829 100644 --- a/tests/Application/config/packages/test_cached/doctrine.yaml +++ b/tests/Application/config/packages/test_cached/doctrine.yaml @@ -1,16 +1,16 @@ 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 + 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 index 2b4189da..c36c93f9 100644 --- a/tests/Application/config/packages/test_cached/fos_rest.yaml +++ b/tests/Application/config/packages/test_cached/fos_rest.yaml @@ -1,3 +1,3 @@ fos_rest: - exception: - debug: true + exception: + debug: true diff --git a/tests/Application/config/packages/test_cached/framework.yaml b/tests/Application/config/packages/test_cached/framework.yaml index daf04d4e..2734c0ca 100644 --- a/tests/Application/config/packages/test_cached/framework.yaml +++ b/tests/Application/config/packages/test_cached/framework.yaml @@ -1,4 +1,4 @@ framework: - test: ~ - session: - handler_id: ~ + test: ~ + session: + handler_id: ~ diff --git a/tests/Application/config/packages/test_cached/monolog.yaml b/tests/Application/config/packages/test_cached/monolog.yaml index 7e2b9e3a..d5cefe75 100644 --- a/tests/Application/config/packages/test_cached/monolog.yaml +++ b/tests/Application/config/packages/test_cached/monolog.yaml @@ -1,6 +1,6 @@ monolog: - handlers: - main: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: error + handlers: + main: + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" + level: error diff --git a/tests/Application/config/packages/test_cached/sylius_channel.yaml b/tests/Application/config/packages/test_cached/sylius_channel.yaml index bab83ef2..88ff5989 100644 --- a/tests/Application/config/packages/test_cached/sylius_channel.yaml +++ b/tests/Application/config/packages/test_cached/sylius_channel.yaml @@ -1,2 +1,2 @@ sylius_channel: - debug: true + debug: true diff --git a/tests/Application/config/packages/test_cached/sylius_theme.yaml b/tests/Application/config/packages/test_cached/sylius_theme.yaml index 4d34199f..3cc3d971 100644 --- a/tests/Application/config/packages/test_cached/sylius_theme.yaml +++ b/tests/Application/config/packages/test_cached/sylius_theme.yaml @@ -1,3 +1,3 @@ sylius_theme: - sources: - test: ~ + sources: + test: ~ diff --git a/tests/Application/config/packages/test_cached/sylius_uploader.yaml b/tests/Application/config/packages/test_cached/sylius_uploader.yaml index cfa727e2..fcb02b15 100644 --- a/tests/Application/config/packages/test_cached/sylius_uploader.yaml +++ b/tests/Application/config/packages/test_cached/sylius_uploader.yaml @@ -1,2 +1,2 @@ imports: - - { resource: "../test/sylius_uploader.yaml" } + - { resource: "../test/sylius_uploader.yaml" } diff --git a/tests/Application/config/packages/test_cached/twig.yaml b/tests/Application/config/packages/test_cached/twig.yaml index 8c6e0b40..d82243ff 100644 --- a/tests/Application/config/packages/test_cached/twig.yaml +++ b/tests/Application/config/packages/test_cached/twig.yaml @@ -1,2 +1,2 @@ twig: - strict_variables: true + strict_variables: true diff --git a/tests/Application/config/packages/translation.yaml b/tests/Application/config/packages/translation.yaml index 1f4f9664..5b162856 100644 --- a/tests/Application/config/packages/translation.yaml +++ b/tests/Application/config/packages/translation.yaml @@ -1,8 +1,8 @@ framework: - default_locale: '%locale%' - translator: - paths: - - '%kernel.project_dir%/translations' - fallbacks: - - '%locale%' - - 'en' + 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 index 8545473d..0d575c1c 100644 --- a/tests/Application/config/packages/twig.yaml +++ b/tests/Application/config/packages/twig.yaml @@ -1,12 +1,12 @@ twig: - paths: ['%kernel.project_dir%/templates'] - debug: '%kernel.debug%' - strict_variables: '%kernel.debug%' + paths: [ '%kernel.project_dir%/templates' ] + debug: '%kernel.debug%' + strict_variables: '%kernel.debug%' services: - _defaults: - public: false - autowire: true - autoconfigure: true + _defaults: + public: false + autowire: true + autoconfigure: true - Twig\Extra\Intl\IntlExtension: ~ + Twig\Extra\Intl\IntlExtension: ~ diff --git a/tests/Application/config/packages/validator.yaml b/tests/Application/config/packages/validator.yaml index 61807db6..e6553b33 100644 --- a/tests/Application/config/packages/validator.yaml +++ b/tests/Application/config/packages/validator.yaml @@ -1,3 +1,3 @@ framework: - validation: - enable_annotations: true + validation: + enable_annotations: true diff --git a/tests/Application/config/routes.yaml b/tests/Application/config/routes.yaml index 7b7ee8bd..d3e703e0 100644 --- a/tests/Application/config/routes.yaml +++ b/tests/Application/config/routes.yaml @@ -1,2 +1,2 @@ bitbag_sylius_product_bundle_plugin: - resource: "@BitBagSyliusProductBundlePlugin/Resources/config/routing.yml" + resource: "@BitBagSyliusProductBundlePlugin/Resources/config/routing.yml" diff --git a/tests/Application/config/routes/dev/web_profiler.yaml b/tests/Application/config/routes/dev/web_profiler.yaml index 3e79dc21..f54957a8 100644 --- a/tests/Application/config/routes/dev/web_profiler.yaml +++ b/tests/Application/config/routes/dev/web_profiler.yaml @@ -1,7 +1,7 @@ _wdt: - resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" - prefix: /_wdt + resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" + prefix: /_wdt _profiler: - resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" - prefix: /_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 index 201cbd5d..4e89a350 100644 --- a/tests/Application/config/routes/liip_imagine.yaml +++ b/tests/Application/config/routes/liip_imagine.yaml @@ -1,2 +1,2 @@ _liip_imagine: - resource: "@LiipImagineBundle/Resources/config/routing.yaml" + resource: "@LiipImagineBundle/Resources/config/routing.yaml" diff --git a/tests/Application/config/routes/sylius_admin.yaml b/tests/Application/config/routes/sylius_admin.yaml index 1ba48d6c..baa3afdc 100644 --- a/tests/Application/config/routes/sylius_admin.yaml +++ b/tests/Application/config/routes/sylius_admin.yaml @@ -1,3 +1,3 @@ sylius_admin: - resource: "@SyliusAdminBundle/Resources/config/routing.yml" - prefix: /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 index ae01ffce..4ed7e8f3 100644 --- a/tests/Application/config/routes/sylius_api.yaml +++ b/tests/Application/config/routes/sylius_api.yaml @@ -1,3 +1,3 @@ sylius_api: - resource: "@SyliusApiBundle/Resources/config/routing.yml" - prefix: "%sylius.security.new_api_route%" + 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 index fae46cbf..38d83337 100644 --- a/tests/Application/config/routes/sylius_shop.yaml +++ b/tests/Application/config/routes/sylius_shop.yaml @@ -1,14 +1,14 @@ sylius_shop: - resource: "@SyliusShopBundle/Resources/config/routing.yml" - prefix: /{_locale} - requirements: - _locale: ^[A-Za-z]{2,4}(_([A-Za-z]{4}|[0-9]{3}))?(_([A-Za-z]{2}|[0-9]{3}))?$ + resource: "@SyliusShopBundle/Resources/config/routing.yml" + prefix: /{_locale} + requirements: + _locale: ^[A-Za-z]{2,4}(_([A-Za-z]{4}|[0-9]{3}))?(_([A-Za-z]{2}|[0-9]{3}))?$ sylius_shop_payum: - resource: "@SyliusShopBundle/Resources/config/routing/payum.yml" + resource: "@SyliusShopBundle/Resources/config/routing/payum.yml" sylius_shop_default_locale: - path: / - methods: [GET] - defaults: - _controller: sylius.controller.shop.locale_switch::switchAction + path: / + methods: [ GET ] + defaults: + _controller: sylius.controller.shop.locale_switch::switchAction diff --git a/tests/Application/config/routes/test/sylius_test_plugin.yaml b/tests/Application/config/routes/test/sylius_test_plugin.yaml index 0ca57d9a..138859f8 100644 --- a/tests/Application/config/routes/test/sylius_test_plugin.yaml +++ b/tests/Application/config/routes/test/sylius_test_plugin.yaml @@ -1,5 +1,5 @@ sylius_test_plugin_main: - path: /test/main - controller: FrameworkBundle:Template:template - defaults: - template: "@SyliusTestPlugin/main.html.twig" + path: /test/main + controller: FrameworkBundle:Template:template + defaults: + template: "@SyliusTestPlugin/main.html.twig" diff --git a/tests/Application/config/routes/test_cached/routing.yaml b/tests/Application/config/routes/test_cached/routing.yaml index 0ca57d9a..138859f8 100644 --- a/tests/Application/config/routes/test_cached/routing.yaml +++ b/tests/Application/config/routes/test_cached/routing.yaml @@ -1,5 +1,5 @@ sylius_test_plugin_main: - path: /test/main - controller: FrameworkBundle:Template:template - defaults: - template: "@SyliusTestPlugin/main.html.twig" + path: /test/main + controller: FrameworkBundle:Template:template + defaults: + template: "@SyliusTestPlugin/main.html.twig" diff --git a/tests/Application/config/routes/test_cached/sylius_test_plugin.yaml b/tests/Application/config/routes/test_cached/sylius_test_plugin.yaml index 0ca57d9a..138859f8 100644 --- a/tests/Application/config/routes/test_cached/sylius_test_plugin.yaml +++ b/tests/Application/config/routes/test_cached/sylius_test_plugin.yaml @@ -1,5 +1,5 @@ sylius_test_plugin_main: - path: /test/main - controller: FrameworkBundle:Template:template - defaults: - template: "@SyliusTestPlugin/main.html.twig" + path: /test/main + controller: FrameworkBundle:Template:template + defaults: + template: "@SyliusTestPlugin/main.html.twig" diff --git a/tests/Application/config/services.yaml b/tests/Application/config/services.yaml index 615506eb..ee3b8248 100644 --- a/tests/Application/config/services.yaml +++ b/tests/Application/config/services.yaml @@ -1,4 +1,4 @@ # 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 + locale: en_US diff --git a/tests/Application/config/services_test.yaml b/tests/Application/config/services_test.yaml index 9edf553d..f90fe420 100644 --- a/tests/Application/config/services_test.yaml +++ b/tests/Application/config/services_test.yaml @@ -1,8 +1,8 @@ imports: - - { resource: "../../Behat/Resources/services.yml" } - - { resource: "../../../vendor/sylius/sylius/src/Sylius/Behat/Resources/config/services.xml" } + - { resource: "../../Behat/Resources/services.yml" } + - { resource: "../../../vendor/sylius/sylius/src/Sylius/Behat/Resources/config/services.xml" } # workaround needed for strange "test.client.history" problem # see https://github.com/FriendsOfBehat/SymfonyExtension/issues/88 services: - Symfony\Component\BrowserKit\AbstractBrowser: '@test.client' + Symfony\Component\BrowserKit\AbstractBrowser: '@test.client' diff --git a/tests/Application/config/services_test_cached.yaml b/tests/Application/config/services_test_cached.yaml index 0de380ea..5d2c7fa4 100644 --- a/tests/Application/config/services_test_cached.yaml +++ b/tests/Application/config/services_test_cached.yaml @@ -1,2 +1,2 @@ imports: - - { resource: "services_test.yaml" } + - { resource: "services_test.yaml" } diff --git a/tests/Application/config/sylius/1.12/packages/jms_serializer.yaml b/tests/Application/config/sylius/1.12/packages/jms_serializer.yaml index ed7bc613..fa43f5b6 100644 --- a/tests/Application/config/sylius/1.12/packages/jms_serializer.yaml +++ b/tests/Application/config/sylius/1.12/packages/jms_serializer.yaml @@ -1,4 +1,4 @@ jms_serializer: - visitors: - xml_serialization: - format_output: '%kernel.debug%' + visitors: + xml_serialization: + format_output: '%kernel.debug%' diff --git a/tests/Application/config/sylius/1.12/packages/security.yaml b/tests/Application/config/sylius/1.12/packages/security.yaml index 2b277f55..cb926ec4 100644 --- a/tests/Application/config/sylius/1.12/packages/security.yaml +++ b/tests/Application/config/sylius/1.12/packages/security.yaml @@ -113,9 +113,9 @@ security: security: false access_control: - - { path: "%sylius.security.admin_regex%/_partial", role: PUBLIC_ACCESS, ips: [127.0.0.1, ::1] } + - { path: "%sylius.security.admin_regex%/_partial", role: PUBLIC_ACCESS, ips: [ 127.0.0.1, ::1 ] } - { path: "%sylius.security.admin_regex%/_partial", role: ROLE_NO_ACCESS } - - { path: "%sylius.security.shop_regex%/_partial", role: PUBLIC_ACCESS, ips: [127.0.0.1, ::1] } + - { path: "%sylius.security.shop_regex%/_partial", role: PUBLIC_ACCESS, ips: [ 127.0.0.1, ::1 ] } - { path: "%sylius.security.shop_regex%/_partial", role: ROLE_NO_ACCESS } - { path: "%sylius.security.admin_regex%/login", role: PUBLIC_ACCESS } diff --git a/tests/Application/config/sylius/1.13/packages/jms_serializer.yaml b/tests/Application/config/sylius/1.13/packages/jms_serializer.yaml index ed7bc613..fa43f5b6 100644 --- a/tests/Application/config/sylius/1.13/packages/jms_serializer.yaml +++ b/tests/Application/config/sylius/1.13/packages/jms_serializer.yaml @@ -1,4 +1,4 @@ jms_serializer: - visitors: - xml_serialization: - format_output: '%kernel.debug%' + visitors: + xml_serialization: + format_output: '%kernel.debug%' diff --git a/tests/Application/config/sylius/1.13/packages/security.yaml b/tests/Application/config/sylius/1.13/packages/security.yaml index 7458c77a..6df8cb5a 100644 --- a/tests/Application/config/sylius/1.13/packages/security.yaml +++ b/tests/Application/config/sylius/1.13/packages/security.yaml @@ -102,9 +102,9 @@ security: security: false access_control: - - { path: "%sylius.security.admin_regex%/_partial", role: PUBLIC_ACCESS, ips: [127.0.0.1, ::1] } + - { path: "%sylius.security.admin_regex%/_partial", role: PUBLIC_ACCESS, ips: [ 127.0.0.1, ::1 ] } - { path: "%sylius.security.admin_regex%/_partial", role: ROLE_NO_ACCESS } - - { path: "%sylius.security.shop_regex%/_partial", role: PUBLIC_ACCESS, ips: [127.0.0.1, ::1] } + - { path: "%sylius.security.shop_regex%/_partial", role: PUBLIC_ACCESS, ips: [ 127.0.0.1, ::1 ] } - { path: "%sylius.security.shop_regex%/_partial", role: ROLE_NO_ACCESS } - { path: "%sylius.security.admin_regex%/login", role: PUBLIC_ACCESS } diff --git a/tests/Application/config/symfony/4.4/packages/framework.yaml b/tests/Application/config/symfony/4.4/packages/framework.yaml index 62f82d35..4cc0d5ae 100644 --- a/tests/Application/config/symfony/4.4/packages/framework.yaml +++ b/tests/Application/config/symfony/4.4/packages/framework.yaml @@ -1,2 +1,2 @@ framework: - templating: { engines: ["twig"] } + templating: { engines: [ "twig" ] } diff --git a/tests/Application/src/Resources/config/doctrine/OrderItem.orm.xml b/tests/Application/src/Resources/config/doctrine/OrderItem.orm.xml index 5f6eaa93..b87772f8 100644 --- a/tests/Application/src/Resources/config/doctrine/OrderItem.orm.xml +++ b/tests/Application/src/Resources/config/doctrine/OrderItem.orm.xml @@ -5,7 +5,9 @@ http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd" > - + From 2ec84391fdcbe68cd38722e6cacdde1969813b6f Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Thu, 22 Aug 2024 11:47:16 +0200 Subject: [PATCH 19/40] OP-289: Make properties readonly --- src/Fixture/Factory/ProductBundleFixtureFactory.php | 8 ++++---- src/Grid/Filter/IsBundleFilter.php | 2 +- src/Twig/Extension/ProductBundleOrderItemExtension.php | 4 ++-- src/Twig/Extension/ProductBundlesExtension.php | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Fixture/Factory/ProductBundleFixtureFactory.php b/src/Fixture/Factory/ProductBundleFixtureFactory.php index 9661bffd..c15cd0d4 100644 --- a/src/Fixture/Factory/ProductBundleFixtureFactory.php +++ b/src/Fixture/Factory/ProductBundleFixtureFactory.php @@ -25,10 +25,10 @@ final class ProductBundleFixtureFactory implements ExampleFactoryInterface private readonly OptionsResolver $optionsResolver; public function __construct( - private FactoryInterface $productBundleFactory, - private FactoryInterface $productBundleItemFactory, - private ProductRepositoryInterface $productRepository, - private ProductVariantRepositoryInterface $productVariantRepository, + private readonly FactoryInterface $productBundleFactory, + private readonly FactoryInterface $productBundleItemFactory, + private readonly ProductRepositoryInterface $productRepository, + private readonly ProductVariantRepositoryInterface $productVariantRepository, ) { $this->optionsResolver = new OptionsResolver(); $this->configureOptions($this->optionsResolver); diff --git a/src/Grid/Filter/IsBundleFilter.php b/src/Grid/Filter/IsBundleFilter.php index 41fa3a5d..f06d224f 100644 --- a/src/Grid/Filter/IsBundleFilter.php +++ b/src/Grid/Filter/IsBundleFilter.php @@ -18,7 +18,7 @@ final class IsBundleFilter implements FilterInterface { - public function __construct(private ProductBundleRepositoryInterface $productBundleRepository) + public function __construct(private readonly ProductBundleRepositoryInterface $productBundleRepository) { } diff --git a/src/Twig/Extension/ProductBundleOrderItemExtension.php b/src/Twig/Extension/ProductBundleOrderItemExtension.php index 5bc1eafd..39980b30 100644 --- a/src/Twig/Extension/ProductBundleOrderItemExtension.php +++ b/src/Twig/Extension/ProductBundleOrderItemExtension.php @@ -21,8 +21,8 @@ final class ProductBundleOrderItemExtension extends AbstractExtension { public function __construct( - private RepositoryInterface $productBundleOrderItemRepository, - private Environment $twig, + private readonly RepositoryInterface $productBundleOrderItemRepository, + private readonly Environment $twig, ) { } diff --git a/src/Twig/Extension/ProductBundlesExtension.php b/src/Twig/Extension/ProductBundlesExtension.php index eb2824a8..caf346cf 100644 --- a/src/Twig/Extension/ProductBundlesExtension.php +++ b/src/Twig/Extension/ProductBundlesExtension.php @@ -20,7 +20,7 @@ final class ProductBundlesExtension extends AbstractExtension { public function __construct( - private ProductBundleRepositoryInterface $productBundleRepository, + private readonly ProductBundleRepositoryInterface $productBundleRepository, ) { } From bb778ca07f626b90019c6e6c306e26dfd00bb4cc Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Mon, 26 Aug 2024 12:31:03 +0200 Subject: [PATCH 20/40] OP-289: Add accordion with bundled products on order view --- src/Resources/assets/admin/scss/main.scss | 8 +++ src/Resources/config/services/twig.xml | 2 +- .../Admin/Order/Show/Summary/_item.html.twig | 9 ++- .../Show/_productBundleOrderItems.html.twig | 63 ++++++++++--------- .../ProductBundleOrderItemExtension.php | 39 +++++++++--- 5 files changed, 81 insertions(+), 40 deletions(-) diff --git a/src/Resources/assets/admin/scss/main.scss b/src/Resources/assets/admin/scss/main.scss index e69de29b..998565b8 100644 --- a/src/Resources/assets/admin/scss/main.scss +++ b/src/Resources/assets/admin/scss/main.scss @@ -0,0 +1,8 @@ +.bundled-items { + margin-left: 3rem; + margin-top: 0.5rem; +} + +.bundled-item { + margin-bottom: 1rem; +} diff --git a/src/Resources/config/services/twig.xml b/src/Resources/config/services/twig.xml index e2e9a173..e91dea65 100644 --- a/src/Resources/config/services/twig.xml +++ b/src/Resources/config/services/twig.xml @@ -3,7 +3,7 @@ - diff --git a/src/Resources/views/Admin/Order/Show/Summary/_item.html.twig b/src/Resources/views/Admin/Order/Show/Summary/_item.html.twig index b88d3dc3..6ebaedbb 100644 --- a/src/Resources/views/Admin/Order/Show/Summary/_item.html.twig +++ b/src/Resources/views/Admin/Order/Show/Summary/_item.html.twig @@ -17,8 +17,6 @@ {% include '@SyliusAdmin/Product/_info.html.twig' %} - - {{ bitbag_render_product_bundle_order_items(item) }} {{ money.format(item.unitPrice, order.currencyCode) }} @@ -50,3 +48,10 @@ {{ money.format(item.total, order.currencyCode) }} +{% if product.bundle %} + + + {{ bitbag_render_admin_product_bundle_order_items(item) }} + + +{% endif %} diff --git a/src/Resources/views/Admin/Order/Show/_productBundleOrderItems.html.twig b/src/Resources/views/Admin/Order/Show/_productBundleOrderItems.html.twig index a4ad133f..1d4e667b 100644 --- a/src/Resources/views/Admin/Order/Show/_productBundleOrderItems.html.twig +++ b/src/Resources/views/Admin/Order/Show/_productBundleOrderItems.html.twig @@ -1,35 +1,40 @@ -

{{ 'bitbag_sylius_product_bundle.ui.products_in_bundle'|trans }}

+
+
+ {{ 'bitbag_sylius_product_bundle.ui.products_in_bundle'|trans }} +
-{% for item in items %} - {% set variant = item.productVariant %} - {% set product = variant.product %} +
+ {% for item in items %} + {% set variant = item.productVariant %} + {% set product = variant.product %} -
-
- {% include '@SyliusAdmin/Product/_mainImage.html.twig' with {'product': product, 'filter': 'sylius_admin_product_tiny_thumbnail'} %} -
-
{{ product.name }}
- - {{ variant.code }} - -
-
- {% if product.hasOptions() %} -
- {% for optionValue in variant.optionValues %} -
- {{ optionValue.value }} +
+
+ {% include '@SyliusAdmin/Product/_mainImage.html.twig' with {'product': product, 'filter': 'sylius_admin_product_tiny_thumbnail'} %} +
+
{{ product.name }}
+ + {{ variant.code }} + +
+
+
+ {% if product.hasOptions() %} + {% for optionValue in variant.optionValues %} +
+ {{ optionValue.value }} +
+ {% endfor %} + {% elseif variant.name is not null %} +
+ {{ variant.name }} +
+ {% endif %} +
+ {{ 'bitbag_sylius_product_bundle.ui.quantity'|trans }}: {{ item.quantity }}
- {% endfor %} -
- {% elseif variant.name is not null %} -
-
- {{ variant.name }}
- {% endif %} - -

{{ 'bitbag_sylius_product_bundle.ui.quantity'|trans }}: {{ item.quantity }}

+ {% endfor %}
-{% endfor %} +
diff --git a/src/Twig/Extension/ProductBundleOrderItemExtension.php b/src/Twig/Extension/ProductBundleOrderItemExtension.php index 39980b30..122c1111 100644 --- a/src/Twig/Extension/ProductBundleOrderItemExtension.php +++ b/src/Twig/Extension/ProductBundleOrderItemExtension.php @@ -29,25 +29,48 @@ public function __construct( public function getFunctions(): array { return [ - new TwigFunction('bitbag_render_product_bundle_order_items', [$this, 'renderProductBundleOrderItems'], ['is_safe' => ['html']]), + new TwigFunction('bitbag_render_admin_product_bundle_order_items', [$this, 'renderAdminProductBundleOrderItems'], ['is_safe' => ['html']]), + new TwigFunction('bitbag_render_shop_product_bundle_order_items', [$this, 'renderShopProductBundleOrderItems'], ['is_safe' => ['html']]), ]; } - public function renderProductBundleOrderItems(OrderItemInterface $orderItem): string + public function renderAdminProductBundleOrderItems(OrderItemInterface $orderItem): string { - /** @var ProductInterface $product */ - $product = $orderItem->getProduct(); + $items = $this->getItems($orderItem); - if (!$product->isBundle()) { + if ($items === []) { return ''; } - $items = $this->productBundleOrderItemRepository->findBy([ - 'orderItem' => $orderItem, + return $this->twig->render('@BitBagSyliusProductBundlePlugin/Admin/Order/Show/_productBundleOrderItems.html.twig', [ + 'items' => $items, ]); + } - return $this->twig->render('@BitBagSyliusProductBundlePlugin/Admin/Order/Show/_productBundleOrderItems.html.twig', [ + public function renderShopProductBundleOrderItems(OrderItemInterface $orderItem): string + { + $items = $this->getItems($orderItem); + + if ($items === []) { + return ''; + } + + return $this->twig->render('@BitBagSyliusProductBundlePlugin/Shop/Order/Show/_productBundleOrderItems.html.twig', [ 'items' => $items, ]); } + + private function getItems(OrderItemInterface $orderItem): array + { + /** @var ProductInterface $product */ + $product = $orderItem->getProduct(); + + if (!$product->isBundle()) { + return []; + } + + return $this->productBundleOrderItemRepository->findBy([ + 'orderItem' => $orderItem, + ]); + } } From d8df9438e83505ce67e282240aaee57f6d171580 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Mon, 26 Aug 2024 17:44:31 +0200 Subject: [PATCH 21/40] OP-289: Add accordion with bundled products on cart summary --- src/Resources/assets/shop/scss/main.scss | 11 +++++ .../views/Shop/Cart/Summary/_item.html.twig | 42 +++++++++++++++++++ .../views/Shop/Cart/Summary/_items.html.twig | 34 +++++++++++++++ .../Show/_productBundleOrderItems.html.twig | 27 ++++++++++++ .../ProductBundleOrderItemExtension.php | 4 +- .../config/packages/sylius_ui.yaml | 9 ++++ .../Cart/Summary/_item.html.twig | 1 + 7 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 src/Resources/views/Shop/Cart/Summary/_item.html.twig create mode 100644 src/Resources/views/Shop/Cart/Summary/_items.html.twig create mode 100644 src/Resources/views/Shop/Order/Show/_productBundleOrderItems.html.twig create mode 100644 tests/Application/templates/bundles/SyliusShopBundle/Cart/Summary/_item.html.twig diff --git a/src/Resources/assets/shop/scss/main.scss b/src/Resources/assets/shop/scss/main.scss index e69de29b..b2992f8a 100644 --- a/src/Resources/assets/shop/scss/main.scss +++ b/src/Resources/assets/shop/scss/main.scss @@ -0,0 +1,11 @@ +.bundled-items-header { + padding-top: 0 !important; +} + +.bundled-items { + margin-left: 3rem; +} + +.bundled-item { + margin-bottom: 1rem; +} diff --git a/src/Resources/views/Shop/Cart/Summary/_item.html.twig b/src/Resources/views/Shop/Cart/Summary/_item.html.twig new file mode 100644 index 00000000..6ce5b1d7 --- /dev/null +++ b/src/Resources/views/Shop/Cart/Summary/_item.html.twig @@ -0,0 +1,42 @@ +{% import "@SyliusShop/Common/Macro/money.html.twig" as money %} + +{% set product_variant = item.variant %} +{% set original_price_to_display = sylius_order_item_original_price_to_display(item) %} +{% set is_bundle = item.product.bundle %} + + + + {% include '@SyliusShop/Product/_info.html.twig' with {'variant': product_variant} %} + + + {% if original_price_to_display is not null %} + + {{ money.convertAndFormat(original_price_to_display) }} + + {% endif %} + {{ money.convertAndFormat(item.discountedUnitPrice) }} + + + + {{ form_widget(form.quantity, sylius_test_form_attribute('cart-item-quantity-input', item.productName)|sylius_merge_recursive({'attr': {'form': main_form}})) }} + {{ form_errors(form.quantity) }} + + + +
+ + + +
+ + + {{ money.convertAndFormat(item.subtotal) }} + + +{% if item.product.bundle %} + + + {{ bitbag_render_shop_product_bundle_order_items(item) }} + + +{% endif %} diff --git a/src/Resources/views/Shop/Cart/Summary/_items.html.twig b/src/Resources/views/Shop/Cart/Summary/_items.html.twig new file mode 100644 index 00000000..9d88f4df --- /dev/null +++ b/src/Resources/views/Shop/Cart/Summary/_items.html.twig @@ -0,0 +1,34 @@ +
+ {{ form_start(form, {'action': path('sylius_shop_cart_save'), 'attr': {'class': 'ui loadable form', 'novalidate': 'novalidate', 'id': form.vars.id}}) }} + {{ form_errors(form) }} + + {{ form_row(form._token) }} + {{ form_end(form, {'render_rest': false}) }} + + {{ sylius_template_event('sylius.shop.cart.summary.items', {'cart': cart, 'form': form}) }} + + + + + + + + + + + + + {% for key, item in cart.items %} + {% include '@SyliusShop/Cart/Summary/_item.html.twig' with {'item': item, 'form': form.items[key], 'main_form': form.vars.id, 'loop_index': loop.index} %} + {% endfor %} + +
{{ 'sylius.ui.item'|trans }}{{ 'sylius.ui.unit_price'|trans }}{{ 'sylius.ui.qty'|trans }}{{ 'sylius.ui.total'|trans }}
+ {% if form.promotionCoupon is defined %} + + + {{ sylius_template_event('sylius.shop.cart.coupon', {'cart': cart, 'form': form, 'main_form': form.vars.id}) }} + + {% endif %} + + {% include '@SyliusShop/Cart/Summary/_update.html.twig' with {'main_form': form.vars.id} %} +
diff --git a/src/Resources/views/Shop/Order/Show/_productBundleOrderItems.html.twig b/src/Resources/views/Shop/Order/Show/_productBundleOrderItems.html.twig new file mode 100644 index 00000000..4e835f98 --- /dev/null +++ b/src/Resources/views/Shop/Order/Show/_productBundleOrderItems.html.twig @@ -0,0 +1,27 @@ +
+
+ {{ 'bitbag_sylius_product_bundle.ui.products_in_bundle'|trans }} +
+ +
+ {% for item in items %} + {% set variant = item.productVariant %} + {% set product = variant.product %} + +
+
+ {% include '@SyliusAdmin/Product/_mainImage.html.twig' with {'product': product, 'filter': 'sylius_admin_product_tiny_thumbnail'} %} +
+
{{ product.name }}
+ + {{ variant.code }} + + + {{ 'sylius.ui.qty'|trans }}: {{ item.quantity }} + +
+
+
+ {% endfor %} +
+
diff --git a/src/Twig/Extension/ProductBundleOrderItemExtension.php b/src/Twig/Extension/ProductBundleOrderItemExtension.php index 122c1111..99d1f1bf 100644 --- a/src/Twig/Extension/ProductBundleOrderItemExtension.php +++ b/src/Twig/Extension/ProductBundleOrderItemExtension.php @@ -38,7 +38,7 @@ public function renderAdminProductBundleOrderItems(OrderItemInterface $orderItem { $items = $this->getItems($orderItem); - if ($items === []) { + if ([] === $items) { return ''; } @@ -51,7 +51,7 @@ public function renderShopProductBundleOrderItems(OrderItemInterface $orderItem) { $items = $this->getItems($orderItem); - if ($items === []) { + if ([] === $items) { return ''; } diff --git a/tests/Application/config/packages/sylius_ui.yaml b/tests/Application/config/packages/sylius_ui.yaml index 404f275e..cdcbb0d2 100644 --- a/tests/Application/config/packages/sylius_ui.yaml +++ b/tests/Application/config/packages/sylius_ui.yaml @@ -5,23 +5,32 @@ sylius_ui: variant_selection: template: "@BitBagSyliusProductBundlePlugin/Shop/Product/_variantSelection.html.twig" priority: 10 + sylius.shop.layout.javascripts: blocks: plugin_scripts: template: "@BitBagSyliusProductBundlePlugin/Shop/_scripts.html.twig" priority: 20 + sylius.shop.layout.stylesheets: blocks: plugin_stylesheets: template: "@BitBagSyliusProductBundlePlugin/Shop/_styles.html.twig" priority: 20 + sylius.admin.layout.javascripts: blocks: plugin_scripts: template: "@BitBagSyliusProductBundlePlugin/Admin/_scripts.html.twig" priority: 20 + sylius.admin.layout.stylesheets: blocks: plugin_stylesheets: template: "@BitBagSyliusProductBundlePlugin/Admin/_styles.html.twig" priority: 20 + + sylius.shop.cart.items: + blocks: + content: + template: "@BitBagSyliusProductBundlePlugin/Shop/Cart/Summary/_items.html.twig" diff --git a/tests/Application/templates/bundles/SyliusShopBundle/Cart/Summary/_item.html.twig b/tests/Application/templates/bundles/SyliusShopBundle/Cart/Summary/_item.html.twig new file mode 100644 index 00000000..36dd5077 --- /dev/null +++ b/tests/Application/templates/bundles/SyliusShopBundle/Cart/Summary/_item.html.twig @@ -0,0 +1 @@ +{% include '@BitBagSyliusProductBundlePlugin/Shop/Cart/Summary/_item.html.twig' %} From 4fe1f9b47b5250aa72708d41aaf03102b4c3e64b Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Tue, 27 Aug 2024 11:31:19 +0200 Subject: [PATCH 22/40] OP-289: Add accordion with bundled products on order complete view and order view in order history --- src/Resources/assets/shop/scss/main.scss | 2 +- .../Shop/Common/Order/Table/_item.html.twig | 31 +++++++++++++++++++ .../Common/Order/Table/_item.html.twig | 1 + 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/Resources/views/Shop/Common/Order/Table/_item.html.twig create mode 100644 tests/Application/templates/bundles/SyliusShopBundle/Common/Order/Table/_item.html.twig diff --git a/src/Resources/assets/shop/scss/main.scss b/src/Resources/assets/shop/scss/main.scss index b2992f8a..b884ba51 100644 --- a/src/Resources/assets/shop/scss/main.scss +++ b/src/Resources/assets/shop/scss/main.scss @@ -7,5 +7,5 @@ } .bundled-item { - margin-bottom: 1rem; + margin-bottom: 1.5rem; } diff --git a/src/Resources/views/Shop/Common/Order/Table/_item.html.twig b/src/Resources/views/Shop/Common/Order/Table/_item.html.twig new file mode 100644 index 00000000..de0185ba --- /dev/null +++ b/src/Resources/views/Shop/Common/Order/Table/_item.html.twig @@ -0,0 +1,31 @@ +{% import "@SyliusShop/Common/Macro/money.html.twig" as money %} + +{% set unitPromotionAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_UNIT_PROMOTION_ADJUSTMENT') %} +{% set unitPromotions = item.units.first.adjustments(unitPromotionAdjustment) %} +{% set is_bundle = item.product.bundle %} + + + {% include '@SyliusShop/Product/_info.html.twig' with {'variant': item.variant} %} + + + {% if item.unitPrice != item.discountedUnitPrice %} + {{ money.convertAndFormat(item.unitPrice) }} + {% endif %} + {{ money.convertAndFormat(item.discountedUnitPrice) }} + {% if item.unitPrice != item.discountedUnitPrice %} + + + {% endif %} + + + {{ item.quantity }} + {{ money.convertAndFormat(item.subtotal) }} + +{% if item.product.bundle %} + + + {{ bitbag_render_shop_product_bundle_order_items(item) }} + + +{% endif %} diff --git a/tests/Application/templates/bundles/SyliusShopBundle/Common/Order/Table/_item.html.twig b/tests/Application/templates/bundles/SyliusShopBundle/Common/Order/Table/_item.html.twig new file mode 100644 index 00000000..271a5aac --- /dev/null +++ b/tests/Application/templates/bundles/SyliusShopBundle/Common/Order/Table/_item.html.twig @@ -0,0 +1 @@ +{% include '@BitBagSyliusProductBundlePlugin/Shop/Common/Order/Table/_item.html.twig' %} From 0e91c5ef574a1f2e7f4eac22231a963b602f99dd Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Tue, 27 Aug 2024 11:44:20 +0200 Subject: [PATCH 23/40] OP-289: Update readme to cover new templates --- README.md | 9 ++++- .../views/Shop/Cart/Summary/_items.html.twig | 34 ------------------- .../config/packages/sylius_ui.yaml | 5 --- 3 files changed, 8 insertions(+), 40 deletions(-) delete mode 100644 src/Resources/views/Shop/Cart/Summary/_items.html.twig diff --git a/README.md b/README.md index 62f450bc..3c7c4dcc 100644 --- a/README.md +++ b/README.md @@ -244,7 +244,7 @@ This **open-source plugin was developed to help the Sylius community**. If you h ``` -10. If you have full configuration in xml override doctrine config : +10. If you have full configuration in xml override doctrine config: ```yaml # config/packages/doctrine.yaml @@ -273,21 +273,25 @@ sylius_ui: variant_selection: template: "@BitBagSyliusProductBundlePlugin/Shop/Product/_variantSelection.html.twig" priority: 10 + sylius.shop.layout.javascripts: blocks: plugin_scripts: template: "@BitBagSyliusProductBundlePlugin/Shop/_scripts.html.twig" priority: 20 + sylius.shop.layout.stylesheets: blocks: plugin_stylesheets: template: "@BitBagSyliusProductBundlePlugin/Shop/_styles.html.twig" priority: 20 + sylius.admin.layout.javascripts: blocks: plugin_scripts: template: "@BitBagSyliusProductBundlePlugin/Admin/_scripts.html.twig" priority: 20 + sylius.admin.layout.stylesheets: blocks: plugin_stylesheets: @@ -299,6 +303,9 @@ sylius_ui: ```bash cp vendor/bitbag/product-bundle-plugin/src/Resources/views/Admin/Order/Show/Summary/_item.html.twig templates/bundles/SyliusAdminBundle/Order/Show/Summary + cp vendor/bitbag/product-bundle-plugin/src/Resources/views/Admin/Product/show.html.twig templates/bundles/SyliusAdminBundle/Product + cp vendor/bitbag/product-bundle-plugin/src/Resources/views/Shop/Cart/Summary/_item.html.twig templates/bundles/SyliusShopBundle/Cart/Summary + cp vendor/bitbag/product-bundle-plugin/src/Resources/views/Shop/Common/Order/Table/_item.html.twig templates/bundles/SyliusShopBundle/Common/Order/Table ``` 12. Please clear application cache by running command below: diff --git a/src/Resources/views/Shop/Cart/Summary/_items.html.twig b/src/Resources/views/Shop/Cart/Summary/_items.html.twig deleted file mode 100644 index 9d88f4df..00000000 --- a/src/Resources/views/Shop/Cart/Summary/_items.html.twig +++ /dev/null @@ -1,34 +0,0 @@ -
- {{ form_start(form, {'action': path('sylius_shop_cart_save'), 'attr': {'class': 'ui loadable form', 'novalidate': 'novalidate', 'id': form.vars.id}}) }} - {{ form_errors(form) }} - - {{ form_row(form._token) }} - {{ form_end(form, {'render_rest': false}) }} - - {{ sylius_template_event('sylius.shop.cart.summary.items', {'cart': cart, 'form': form}) }} - - - - - - - - - - - - - {% for key, item in cart.items %} - {% include '@SyliusShop/Cart/Summary/_item.html.twig' with {'item': item, 'form': form.items[key], 'main_form': form.vars.id, 'loop_index': loop.index} %} - {% endfor %} - -
{{ 'sylius.ui.item'|trans }}{{ 'sylius.ui.unit_price'|trans }}{{ 'sylius.ui.qty'|trans }}{{ 'sylius.ui.total'|trans }}
- {% if form.promotionCoupon is defined %} - - - {{ sylius_template_event('sylius.shop.cart.coupon', {'cart': cart, 'form': form, 'main_form': form.vars.id}) }} - - {% endif %} - - {% include '@SyliusShop/Cart/Summary/_update.html.twig' with {'main_form': form.vars.id} %} -
diff --git a/tests/Application/config/packages/sylius_ui.yaml b/tests/Application/config/packages/sylius_ui.yaml index cdcbb0d2..2ef12fd6 100644 --- a/tests/Application/config/packages/sylius_ui.yaml +++ b/tests/Application/config/packages/sylius_ui.yaml @@ -29,8 +29,3 @@ sylius_ui: plugin_stylesheets: template: "@BitBagSyliusProductBundlePlugin/Admin/_styles.html.twig" priority: 20 - - sylius.shop.cart.items: - blocks: - content: - template: "@BitBagSyliusProductBundlePlugin/Shop/Cart/Summary/_items.html.twig" From 60c8461dfcc76bce6680f4d7fc66e88237f83c74 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Tue, 27 Aug 2024 15:14:33 +0200 Subject: [PATCH 24/40] OP-289: Install rector and add rules for extending entities, update readme --- README.md | 87 ++++++++++----------------------------------------- composer.json | 3 +- rector.php | 33 +++++++++++++++++++ 3 files changed, 51 insertions(+), 72 deletions(-) create mode 100644 rector.php diff --git a/README.md b/README.md index 3c7c4dcc..da9365a2 100644 --- a/README.md +++ b/README.md @@ -81,12 +81,17 @@ This **open-source plugin was developed to help the Sylius community**. If you h resource: "@BitBagSyliusProductBundlePlugin/Resources/config/routing.yml" ``` -5. Extend `Product`(including Doctrine mapping): +5. (applied if using Rector) Extend entities by running + ```bash + vendor/bin/rector process src --config=vendor/bitbag/product-bundle-plugin/rector.php + ``` + +6. (applied if not using Rector) Extend `Product`(including Doctrine mapping): ```php @@ -151,7 +125,7 @@ This **open-source plugin was developed to help the Sylius community**. If you h ``` -7. Extend `OrderItem` (including Doctrine mapping): +8. (applied if not using Rector) Extend `OrderItem` (including Doctrine mapping): ```php @@ -225,7 +170,7 @@ This **open-source plugin was developed to help the Sylius community**. If you h ``` -9. Add configuration for extended product and order item: +10. Add configuration for extended product and order item: ```yaml # config/packages/_sylius.yaml @@ -244,7 +189,7 @@ This **open-source plugin was developed to help the Sylius community**. If you h ``` -10. If you have full configuration in xml override doctrine config: +11. If you have full configuration in xml override doctrine config: ```yaml # config/packages/doctrine.yaml @@ -262,7 +207,7 @@ doctrine: ``` -11. Add plugin templates: +12. Add plugin templates: - Inject blocks: ```yaml @@ -308,18 +253,18 @@ sylius_ui: cp vendor/bitbag/product-bundle-plugin/src/Resources/views/Shop/Common/Order/Table/_item.html.twig templates/bundles/SyliusShopBundle/Common/Order/Table ``` -12. Please clear application cache by running command below: +13. Please clear application cache by running command below: ```bash bin/console cache:clear ``` -13. Finish the installation by updating the database schema and installing assets: +14. Finish the installation by updating the database schema and installing assets: ```bash bin/console doctrine:migrations:migrate ``` -14. Add plugin assets to your project: +15. Add plugin assets to your project: [Import webpack config](./README_webpack-config.md)* ## Testing diff --git a/composer.json b/composer.json index 56b0a86b..b7cd948b 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,8 @@ "lchrusciel/api-test-case": "^5.1", "polishsymfonycommunity/symfony-mocker-container": "^1.0", "php-http/message-factory": "^1.1", - "robertfausk/behat-panther-extension": "^1.1" + "robertfausk/behat-panther-extension": "^1.1", + "sylius/sylius-rector": "^2.0" }, "conflict": { "symfony/framework-bundle": "6.2.8", diff --git a/rector.php b/rector.php new file mode 100644 index 00000000..e74b85e4 --- /dev/null +++ b/rector.php @@ -0,0 +1,33 @@ +ruleWithConfiguration(AddInterfaceToClassExtendingTypeRector::class, [ + 'Sylius\Component\Core\Model\OrderItem' => [ + 'BitBag\SyliusProductBundlePlugin\Entity\OrderItemInterface', + ], + 'Sylius\Component\Core\Model\Product' => [ + 'BitBag\SyliusProductBundlePlugin\Entity\ProductInterface', + ], + ]); + $rectorConfig->ruleWithConfiguration(AddTraitToClassExtendingTypeRector::class, [ + 'Sylius\Component\Core\Model\OrderItem' => [ + 'BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItemsAwareTrait', + ], + 'Sylius\Component\Core\Model\Product' => [ + 'BitBag\SyliusProductBundlePlugin\Entity\ProductBundlesAwareTrait', + ], + ]); +}; From 1f8bc2011465c39e511b952796b606014db79ca7 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Wed, 28 Aug 2024 14:32:26 +0200 Subject: [PATCH 25/40] OP-289: Disable variant management buttons for bundles --- README.md | 8 ++--- src/Resources/assets/admin/scss/main.scss | 10 ++++++ src/Resources/config/grids.yml | 6 ++++ .../Admin/Grid/Action/product_links.html.twig | 31 +++++++++++++++++++ .../Admin/Product/Update/_toolbar.html.twig | 7 +++++ .../Product/Update/_toolbar.html.twig | 1 + 6 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 src/Resources/views/Admin/Grid/Action/product_links.html.twig create mode 100644 src/Resources/views/Admin/Product/Update/_toolbar.html.twig create mode 100644 tests/Application/templates/bundles/SyliusAdminBundle/Product/Update/_toolbar.html.twig diff --git a/README.md b/README.md index da9365a2..8f734986 100644 --- a/README.md +++ b/README.md @@ -247,10 +247,10 @@ sylius_ui: - Copy plugin templates into your project `templates/bundles` directory: ```bash - cp vendor/bitbag/product-bundle-plugin/src/Resources/views/Admin/Order/Show/Summary/_item.html.twig templates/bundles/SyliusAdminBundle/Order/Show/Summary - cp vendor/bitbag/product-bundle-plugin/src/Resources/views/Admin/Product/show.html.twig templates/bundles/SyliusAdminBundle/Product - cp vendor/bitbag/product-bundle-plugin/src/Resources/views/Shop/Cart/Summary/_item.html.twig templates/bundles/SyliusShopBundle/Cart/Summary - cp vendor/bitbag/product-bundle-plugin/src/Resources/views/Shop/Common/Order/Table/_item.html.twig templates/bundles/SyliusShopBundle/Common/Order/Table + cp -R vendor/bitbag/product-bundle-plugin/src/Resources/views/Admin/Order templates/bundles/SyliusAdminBundle + cp -R vendor/bitbag/product-bundle-plugin/src/Resources/views/Admin/Product templates/bundles/SyliusAdminBundle + cp -R vendor/bitbag/product-bundle-plugin/src/Resources/views/Shop/Cart templates/bundles/SyliusShopBundle + cp -R vendor/bitbag/product-bundle-plugin/src/Resources/views/Shop/Common templates/bundles/SyliusShopBundle ``` 13. Please clear application cache by running command below: diff --git a/src/Resources/assets/admin/scss/main.scss b/src/Resources/assets/admin/scss/main.scss index 998565b8..94ff545c 100644 --- a/src/Resources/assets/admin/scss/main.scss +++ b/src/Resources/assets/admin/scss/main.scss @@ -6,3 +6,13 @@ .bundled-item { margin-bottom: 1rem; } + +.disabled-variant-management-button { + background-color: rgb(248, 248, 248) !important; + cursor: not-allowed !important; + + &:hover { + color: rgba(0, 0, 0, 0.6) !important; + background-color: rgb(248, 248, 248) !important; + } +} diff --git a/src/Resources/config/grids.yml b/src/Resources/config/grids.yml index 746428d3..27efcc18 100644 --- a/src/Resources/config/grids.yml +++ b/src/Resources/config/grids.yml @@ -35,6 +35,12 @@ sylius_grid: label: bitbag_sylius_product_bundle.ui.bundle icon: plus route: bitbag_product_bundle_admin_product_create_bundle + subitem: + variants: + type: product_links + templates: filter: is_bundle: "@SyliusUi/Grid/Filter/boolean.html.twig" + action: + product_links: "@BitBagSyliusProductBundlePlugin/Admin/Grid/Action/product_links.html.twig" diff --git a/src/Resources/views/Admin/Grid/Action/product_links.html.twig b/src/Resources/views/Admin/Grid/Action/product_links.html.twig new file mode 100644 index 00000000..93187fb9 --- /dev/null +++ b/src/Resources/views/Admin/Grid/Action/product_links.html.twig @@ -0,0 +1,31 @@ +{% set visible = options.visible is defined ? options.visible : true %} + +{% if visible %} + {% if not data.bundle %} + + {% else %} + + {% endif %} +{% endif %} diff --git a/src/Resources/views/Admin/Product/Update/_toolbar.html.twig b/src/Resources/views/Admin/Product/Update/_toolbar.html.twig new file mode 100644 index 00000000..fbc86335 --- /dev/null +++ b/src/Resources/views/Admin/Product/Update/_toolbar.html.twig @@ -0,0 +1,7 @@ +
+ {% if not product.bundle %} + {% set menu = knp_menu_get('sylius.admin.product.update', [], {'product': product}) %} + {{ knp_menu_render(menu, {'template': '@SyliusUi/Menu/top.html.twig'}) }} + {% endif %} + {% include '@SyliusAdmin/Product/_showInShopButton.html.twig' %} +
diff --git a/tests/Application/templates/bundles/SyliusAdminBundle/Product/Update/_toolbar.html.twig b/tests/Application/templates/bundles/SyliusAdminBundle/Product/Update/_toolbar.html.twig new file mode 100644 index 00000000..ab8bbcf4 --- /dev/null +++ b/tests/Application/templates/bundles/SyliusAdminBundle/Product/Update/_toolbar.html.twig @@ -0,0 +1 @@ +{% include '@BitBagSyliusProductBundlePlugin/Admin/Product/Update/_toolbar.html.twig' %} From 4e50cc4f7827bda9c70a6709ce8a28678e64a363 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Thu, 29 Aug 2024 14:06:53 +0200 Subject: [PATCH 26/40] OP-289: Move resource definitions to Configuration tree --- .../BitBagSyliusProductBundleExtension.php | 18 ++- src/DependencyInjection/Configuration.php | 109 ++++++++++++++++++ src/Resources/config/config.yml | 1 - src/Resources/config/resources.yml | 4 - .../config/resources/product_bundle.yml | 9 -- .../config/resources/product_bundle_item.yml | 8 -- .../resources/product_bundle_order_item.yml | 7 -- 7 files changed, 125 insertions(+), 31 deletions(-) create mode 100644 src/DependencyInjection/Configuration.php delete mode 100644 src/Resources/config/resources.yml delete mode 100644 src/Resources/config/resources/product_bundle.yml delete mode 100644 src/Resources/config/resources/product_bundle_item.yml delete mode 100644 src/Resources/config/resources/product_bundle_order_item.yml diff --git a/src/DependencyInjection/BitBagSyliusProductBundleExtension.php b/src/DependencyInjection/BitBagSyliusProductBundleExtension.php index 4936305b..0dad1e66 100644 --- a/src/DependencyInjection/BitBagSyliusProductBundleExtension.php +++ b/src/DependencyInjection/BitBagSyliusProductBundleExtension.php @@ -12,11 +12,12 @@ namespace BitBag\SyliusProductBundlePlugin\DependencyInjection; use Sylius\Bundle\CoreBundle\DependencyInjection\PrependDoctrineMigrationsTrait; +use Sylius\Bundle\ResourceBundle\DependencyInjection\Extension\AbstractResourceExtension; +use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; -final class BitBagSyliusProductBundleExtension extends Extension implements PrependExtensionInterface +final class BitBagSyliusProductBundleExtension extends AbstractResourceExtension implements PrependExtensionInterface { use PrependDoctrineMigrationsTrait; @@ -27,6 +28,9 @@ public function load(array $configs, ContainerBuilder $container): void public function prepend(ContainerBuilder $container): void { $this->prependDoctrineMigrations($container); + + $config = $this->getCurrentConfiguration($container); + $this->registerResources('bitbag_sylius_product_bundle', 'doctrine/orm', $config['resources'], $container); } protected function getMigrationsNamespace(): string @@ -43,4 +47,14 @@ protected function getNamespacesOfMigrationsExecutedBefore(): array { return ['Sylius\Bundle\CoreBundle\Migrations']; } + + private function getCurrentConfiguration(ContainerBuilder $container): array + { + /** @var ConfigurationInterface $configuration */ + $configuration = $this->getConfiguration([], $container); + + $configs = $container->getExtensionConfig($this->getAlias()); + + return $this->processConfiguration($configuration, $configs); + } } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php new file mode 100644 index 00000000..569fa83a --- /dev/null +++ b/src/DependencyInjection/Configuration.php @@ -0,0 +1,109 @@ +getRootNode(); + + $rootNode + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('driver')->defaultValue(SyliusResourceBundle::DRIVER_DOCTRINE_ORM)->end() + ->end() + ; + + $this->addResourcesSection($rootNode); + + return $treeBuilder; + } + + private function addResourcesSection(ArrayNodeDefinition $node): void + { + $node + ->children() + ->arrayNode('resources') + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('product_bundle') + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('classes') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('model')->defaultValue(ProductBundle::class)->cannotBeEmpty()->end() + ->scalarNode('interface')->defaultValue(ProductBundleInterface::class)->cannotBeEmpty()->end() + ->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end() + ->scalarNode('factory')->defaultValue(Factory::class)->cannotBeEmpty()->end() + ->scalarNode('repository')->defaultValue(ProductBundleRepository::class)->cannotBeEmpty()->end() + ->scalarNode('form')->defaultValue(ProductBundleType::class)->cannotBeEmpty()->end() + ->end() + ->end() + ->end() + ->end() + ->arrayNode('product_bundle_item') + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('classes') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('model')->defaultValue(ProductBundleItem::class)->cannotBeEmpty()->end() + ->scalarNode('interface')->defaultValue(ProductBundleItemInterface::class)->cannotBeEmpty()->end() + ->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end() + ->scalarNode('factory')->defaultValue(Factory::class)->cannotBeEmpty()->end() + ->scalarNode('repository')->cannotBeEmpty()->end() + ->scalarNode('form')->defaultValue(ProductBundleItemType::class)->cannotBeEmpty()->end() + ->end() + ->end() + ->end() + ->end() + ->arrayNode('product_bundle_order_item') + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('classes') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('model')->defaultValue(ProductBundleOrderItem::class)->cannotBeEmpty()->end() + ->scalarNode('interface')->defaultValue(ProductBundleOrderItemInterface::class)->cannotBeEmpty()->end() + ->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end() + ->scalarNode('factory')->defaultValue(Factory::class)->cannotBeEmpty()->end() + ->scalarNode('repository')->cannotBeEmpty()->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } +} diff --git a/src/Resources/config/config.yml b/src/Resources/config/config.yml index 11f741f3..76f4bcc0 100644 --- a/src/Resources/config/config.yml +++ b/src/Resources/config/config.yml @@ -1,5 +1,4 @@ imports: - - { resource: resources.yml } - { resource: grids.yml } - { resource: services.xml } diff --git a/src/Resources/config/resources.yml b/src/Resources/config/resources.yml deleted file mode 100644 index f9720b2f..00000000 --- a/src/Resources/config/resources.yml +++ /dev/null @@ -1,4 +0,0 @@ -imports: - - { resource: resources/product_bundle.yml } - - { resource: resources/product_bundle_item.yml } - - { resource: resources/product_bundle_order_item.yml } diff --git a/src/Resources/config/resources/product_bundle.yml b/src/Resources/config/resources/product_bundle.yml deleted file mode 100644 index 603e2023..00000000 --- a/src/Resources/config/resources/product_bundle.yml +++ /dev/null @@ -1,9 +0,0 @@ -sylius_resource: - resources: - bitbag_sylius_product_bundle.product_bundle: - driver: doctrine/orm - classes: - model: BitBag\SyliusProductBundlePlugin\Entity\ProductBundle - interface: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleInterface - form: BitBag\SyliusProductBundlePlugin\Form\Type\ProductBundleType - repository: BitBag\SyliusProductBundlePlugin\Repository\ProductBundleRepository diff --git a/src/Resources/config/resources/product_bundle_item.yml b/src/Resources/config/resources/product_bundle_item.yml deleted file mode 100644 index 40ddaa70..00000000 --- a/src/Resources/config/resources/product_bundle_item.yml +++ /dev/null @@ -1,8 +0,0 @@ -sylius_resource: - resources: - bitbag_sylius_product_bundle.product_bundle_item: - driver: doctrine/orm - classes: - model: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItem - interface: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface - form: BitBag\SyliusProductBundlePlugin\Form\Type\ProductBundleItemType diff --git a/src/Resources/config/resources/product_bundle_order_item.yml b/src/Resources/config/resources/product_bundle_order_item.yml deleted file mode 100644 index 4b7c9867..00000000 --- a/src/Resources/config/resources/product_bundle_order_item.yml +++ /dev/null @@ -1,7 +0,0 @@ -sylius_resource: - resources: - bitbag_sylius_product_bundle.product_bundle_order_item: - driver: doctrine/orm - classes: - model: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItem - interface: BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItemInterface From f4db7d223a7dc760fa0415e923c17bdacd253da9 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Mon, 2 Sep 2024 13:04:18 +0200 Subject: [PATCH 27/40] OP-289: Convert API resource definition to yaml --- src/Resources/config/api_resources/Order.xml | 447 ------------------ src/Resources/config/api_resources/Order.yml | 18 + .../config/api_resources/Product.xml | 131 ----- .../config/api_resources/Product.yml | 13 + 4 files changed, 31 insertions(+), 578 deletions(-) delete mode 100644 src/Resources/config/api_resources/Order.xml create mode 100644 src/Resources/config/api_resources/Order.yml delete mode 100644 src/Resources/config/api_resources/Product.xml create mode 100644 src/Resources/config/api_resources/Product.yml diff --git a/src/Resources/config/api_resources/Order.xml b/src/Resources/config/api_resources/Order.xml deleted file mode 100644 index 8a2fef40..00000000 --- a/src/Resources/config/api_resources/Order.xml +++ /dev/null @@ -1,447 +0,0 @@ - - - - - - - - - admin:order:read - - - - sylius - - - - GET - admin/orders - - - - POST - /shop/orders - input - Sylius\Bundle\ApiBundle\Command\Cart\PickupCart - - shop:order:create - - - Pickups a new cart. Provided locale code has to be one of available for a particular channel. - - - - - GET - /shop/orders - - - shop:order:read - - - - - - - - GET - /admin/orders/{tokenValue} - - - - GET - /shop/orders/{tokenValue} - - shop:cart:read - - - - - DELETE - /shop/orders/{tokenValue} - - Deletes cart - - - shop:order:read - - - - - PATCH - /admin/orders/{tokenValue}/cancel - false - sylius.api.order_state_machine_transition_applicator:cancel - - admin:order:update - - - Cancels Order - - - - - PATCH - /shop/orders/{tokenValue}/items - input - Sylius\Bundle\ApiBundle\Command\Cart\AddItemToCart - - shop:cart:read - - - shop:cart:add_item - - - Adds Item to cart - - - - - PATCH - /shop/orders/{tokenValue}/product-bundle - 200 - input - BitBag\SyliusProductBundlePlugin\Dto\Api\AddProductBundleToCartDto - false - - Default - - - shop:cart:add_product_bundle - - - Adds Product Bundle to cart - - - Product bundle added to the cart - - - - - - - PATCH - /shop/orders/{tokenValue}/address - input - Sylius\Bundle\ApiBundle\Command\Checkout\UpdateCart - - shop:cart:address - - - shop:cart:read - - - Addresses cart to given location, logged in Customer does not have to provide an email - - - - - PATCH - - sylius - - /shop/orders/{tokenValue}/shipments/{shipmentId} - input - Sylius\Bundle\ApiBundle\Command\Checkout\ChooseShippingMethod - - shop:cart:select_shipping_method - - - shop:cart:read - - - Selects shipping methods for particular shipment - - - tokenValue - path - true - - string - - - - shipmentId - path - true - - string - - - - - - - - PATCH - /shop/orders/{tokenValue}/payments/{paymentId} - input - Sylius\Bundle\ApiBundle\Command\Checkout\ChoosePaymentMethod - - shop:cart:select_payment_method - - - shop:cart:read - - - Selects payment methods for particular payment - - - tokenValue - path - true - - string - - - - paymentId - path - true - - string - - - - - - - - PATCH - /shop/account/orders/{tokenValue}/payments/{paymentId} - input - Sylius\Bundle\ApiBundle\Command\Account\ChangePaymentMethod - - shop:order:account:change_payment_method - - - shop:order:account:read - - - Change the payment method as logged shop user - - - tokenValue - path - true - - string - - - - paymentId - path - true - - string - - - - - - - - GET - sylius.api.get_configuration_action - /shop/orders/{tokenValue}/payments/{paymentId}/configuration - - Retrieve payment method configuration - - - tokenValue - path - true - - string - - - - paymentId - path - true - - string - - - - - - - - PATCH - /shop/orders/{tokenValue}/complete - - sylius - sylius_checkout_complete - - input - Sylius\Bundle\ApiBundle\Command\Checkout\CompleteOrder - - shop:cart:complete - - - shop:cart:read - - - Completes checkout - - - - - DELETE - /shop/orders/{tokenValue}/items/{itemId} - input - Sylius\Bundle\ApiBundle\Controller\DeleteOrderItemAction - false - - shop:cart:remove_item - - - - - tokenValue - path - true - - string - - - - itemId - path - true - - string - - - - - - - - PATCH - /shop/orders/{tokenValue}/items/{orderItemId} - input - Sylius\Bundle\ApiBundle\Command\Cart\ChangeItemQuantityInCart - - shop:cart:change_quantity - - - Changes quantity of order item - - - tokenValue - path - true - - string - - - - orderItemId - path - true - - string - - - - - - - - PATCH - /shop/orders/{tokenValue}/apply-coupon - input - Sylius\Bundle\ApiBundle\Command\Checkout\UpdateCart - - shop:cart:apply_coupon - - - Applies coupon to cart - - - - - PUT - /shop/orders/{tokenValue} - - admin:cart:update - - - - - - - GET - /shop/orders/{tokenValue}/items - - - - GET - /admin/orders/{tokenValue}/shipments - - - - GET - /admin/orders/{tokenValue}/payments - - - - GET - /shop/orders/{tokenValue}/adjustments - - - - GET - /shop/orders/{tokenValue}/payments/{payments}/methods - - - - GET - /shop/orders/{tokenValue}/shipments/{shipments}/methods - - - - GET - /shop/orders/{tokenValue}/items/{items}/adjustments - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Resources/config/api_resources/Order.yml b/src/Resources/config/api_resources/Order.yml new file mode 100644 index 00000000..d6a25203 --- /dev/null +++ b/src/Resources/config/api_resources/Order.yml @@ -0,0 +1,18 @@ +'%sylius.model.order.class%': + shortName: Order + itemOperations: + shop_add_product_bundle: + method: PATCH + path: /shop/orders/{tokenValue}/product-bundle + status: 200 + messenger: input + input: BitBag\SyliusProductBundlePlugin\Dto\Api\AddProductBundleToCartDto + output: false + validation_groups: [ 'Default' ] + denormalization_context: + groups: [ 'shop:cart:add_product_bundle' ] + openapi_context: + summary: Adds Product Bundle to cart + responses: + '200': + description: Product bundle added to the cart diff --git a/src/Resources/config/api_resources/Product.xml b/src/Resources/config/api_resources/Product.xml deleted file mode 100644 index 74bfe012..00000000 --- a/src/Resources/config/api_resources/Product.xml +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - sylius - - - ASC - - - - - GET - /admin/products - - sylius.api.product_name_filter - sylius.api.product_order_filter - sylius.api.product_taxon_code_filter - sylius.api.translation_order_name_and_locale_filter - - - admin:product:read - - - - - GET - /shop/products - - sylius.api.product_name_filter - sylius.api.product_order_filter - sylius.api.product_taxon_code_filter - sylius.api.translation_order_name_and_locale_filter - sylius.api.product_taxon_filter - - - shop:product:read - - - - - POST - /admin/products - - admin:product:create - - - - - - - GET - /admin/products/{code} - - Use code to retrieve a product resource. - - - admin:product:read - - - - - GET - /shop/products/{code} - - Use code to retrieve a product resource. - - - shop:product:read - - - - - PUT - /admin/products/{code} - - admin:product:update - - - - - DELETE - /admin/products/{code} - - - - - - /shop/products/{code}/bundle - - - - - - - - - - object - - - string - string - string - - - - - - - - - - - - - - - - - - diff --git a/src/Resources/config/api_resources/Product.yml b/src/Resources/config/api_resources/Product.yml new file mode 100644 index 00000000..9d46f5e8 --- /dev/null +++ b/src/Resources/config/api_resources/Product.yml @@ -0,0 +1,13 @@ +'%sylius.model.product.class%': + shortName: Product + properties: + productBundle: + writable: false + subresource: + resourceClass: '%bitbag_sylius_product_bundle.model.product_bundle.class%' + collection: false + bundle: + writable: false + subresourceOperations: + product_bundle_get_subresource: + path: /shop/products/{code}/bundle From 9ac1c75cc5dad233ff47c441887375a7aa357da1 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Mon, 2 Sep 2024 16:40:08 +0200 Subject: [PATCH 28/40] OP-289: Convert phpspec test to phpunit --- .github/workflows/build.yml | 3 - ...WhenEditNormalProductEventListenerSpec.php | 39 ------- ...WhenEditNormalProductEventListenerTest.php | 110 ++++++++++++++++++ 3 files changed, 110 insertions(+), 42 deletions(-) delete mode 100644 spec/EventListener/AddProductToProductBundleWhenEditNormalProductEventListenerSpec.php create mode 100644 tests/Unit/EventListener/AddProductToProductBundleWhenEditNormalProductEventListenerTest.php diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 509d94ee..41ac7541 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -57,9 +57,6 @@ jobs: - name: Validate database schema run: (cd tests/Application && bin/console doctrine:schema:validate) - - name: Run PHPSpec - run: vendor/bin/phpspec run --ansi -f progress --no-interaction - - name: Run PHPUnit run: vendor/bin/phpunit --colors=always diff --git a/spec/EventListener/AddProductToProductBundleWhenEditNormalProductEventListenerSpec.php b/spec/EventListener/AddProductToProductBundleWhenEditNormalProductEventListenerSpec.php deleted file mode 100644 index 84df86b7..00000000 --- a/spec/EventListener/AddProductToProductBundleWhenEditNormalProductEventListenerSpec.php +++ /dev/null @@ -1,39 +0,0 @@ -shouldHaveType(AddProductToProductBundleWhenEditNormalProductEventListener::class); - } - - public function it_should_add_product_to_product_bundle_if_not_exist_on_pre_create_and_update_event( - ResourceControllerEvent $resourceControllerEvent, - ProductInterface $product, - ProductBundleInterface $productBundle - ): void { - $resourceControllerEvent->getSubject()->willReturn($product); - - $product->getProductBundle()->shouldBeCalled(); - - $product->getProductBundle()->willReturn($productBundle); - - $this->addProductToProductBundle($resourceControllerEvent); - } -} diff --git a/tests/Unit/EventListener/AddProductToProductBundleWhenEditNormalProductEventListenerTest.php b/tests/Unit/EventListener/AddProductToProductBundleWhenEditNormalProductEventListenerTest.php new file mode 100644 index 00000000..7f8fb7ab --- /dev/null +++ b/tests/Unit/EventListener/AddProductToProductBundleWhenEditNormalProductEventListenerTest.php @@ -0,0 +1,110 @@ +instance = new AddProductToProductBundleWhenEditNormalProductEventListener(); + $this->resourceControllerEvent = $this->createMock(ResourceControllerEvent::class); + $this->product = $this->createMock(ProductInterface::class); + $this->productBundle = $this->createMock(ProductBundleInterface::class); + } + + public function testAddProductToProductBundle(): void + { + $this->productBundle + ->expects(self::once()) + ->method('getProduct') + ->willReturn(null); + + $this->product + ->expects(self::exactly(3)) + ->method('getProductBundle') + ->willReturn($this->productBundle); + + $this->productBundle + ->expects(self::once()) + ->method('setProduct') + ->with($this->product); + + $this->resourceControllerEvent + ->expects(self::once()) + ->method('getSubject') + ->willReturn($this->product); + + $this->instance->addProductToProductBundle($this->resourceControllerEvent); + } + + public function testWillNotAddProductToProductBundleIfProductHasBundle(): void + { + $this->productBundle + ->expects(self::never()) + ->method('getProduct'); + + $this->product + ->expects(self::once()) + ->method('getProductBundle') + ->willReturn(null); + + $this->productBundle + ->expects(self::never()) + ->method('setProduct'); + + $this->resourceControllerEvent + ->expects(self::once()) + ->method('getSubject') + ->willReturn($this->product); + + $this->instance->addProductToProductBundle($this->resourceControllerEvent); + } + + public function testWillNotAddProductToProductBundleIfProductBundleHasProduct(): void + { + $this->productBundle + ->expects(self::once()) + ->method('getProduct') + ->willReturn($this->product); + + $this->product + ->expects(self::exactly(2)) + ->method('getProductBundle') + ->willReturn($this->productBundle); + + $this->productBundle + ->expects(self::never()) + ->method('setProduct'); + + $this->resourceControllerEvent + ->expects(self::once()) + ->method('getSubject') + ->willReturn($this->product); + + $this->instance->addProductToProductBundle($this->resourceControllerEvent); + } +} From 44bf4a076162d5ed4228ec9ed0ee0f3d7e208985 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Tue, 3 Sep 2024 13:54:35 +0200 Subject: [PATCH 29/40] OP-289: Add more behat scenarios --- features/creating_bundled_product.feature | 76 ++++++++----- .../having_bundled_product_in_store.feature | 94 ++++++++++------ ...ewing_products_from_ordered_bundle.feature | 37 +++++++ .../Context/Api/ProductBundleContext.php | 83 ++++++++++++++ .../Context/Setup/ProductBundleContext.php | 53 +++++++-- .../Behat/Context/Ui/ProductBundleContext.php | 71 ++++++++++++ .../Page/Admin/CreateBundledProductPage.php | 7 +- .../Shop/BundledProductsListPageInterface.php | 19 ++++ tests/Behat/Page/Shop/OrderShowPage.php | 35 ++++++ tests/Behat/Page/Shop/SummaryPage.php | 35 ++++++ tests/Behat/Resources/services.yml | 26 +++++ tests/Behat/Resources/suites.yml | 104 +++++++++++++++--- 12 files changed, 549 insertions(+), 91 deletions(-) create mode 100644 features/viewing_products_from_ordered_bundle.feature create mode 100644 tests/Behat/Context/Api/ProductBundleContext.php create mode 100644 tests/Behat/Page/Shop/BundledProductsListPageInterface.php create mode 100644 tests/Behat/Page/Shop/OrderShowPage.php create mode 100644 tests/Behat/Page/Shop/SummaryPage.php diff --git a/features/creating_bundled_product.feature b/features/creating_bundled_product.feature index be965851..edded4ff 100644 --- a/features/creating_bundled_product.feature +++ b/features/creating_bundled_product.feature @@ -1,34 +1,52 @@ @bundled_product Feature: Creating a product in store which is a bundle of other products - I want to be able to add bundled product to cart + As an Administrator + I want to be able to add bundled product to cart - Background: - Given the store operates on a single channel in "United States" - And I am logged in as an administrator - And the store has a product "Jack Daniels Gentleman" priced at "$10.00" - And the store has a product "Johny Walker Black" priced at "$10.00" - And the store has a product "Jim Beam Double Oak" priced at "$10.00" + Background: + Given the store operates on a single channel in "United States" + And I am logged in as an administrator + And the store has a product "Jack Daniels Gentleman" priced at "$10.00" + And the store has a product "Johny Walker Black" priced at "$10.00" + And the store has a product "Jim Beam Double Oak" priced at "$10.00" - @ui @javascript - Scenario: Creating a bundled product - When I want to create a new bundled product - And I specify its code as "WHISKEY_PACK" - And I name it "Whiskey double pack" in "English (United States)" - And I set its slug to "whiskey-double-pack" in "English (United States)" - And I set its price to "$10.00" for "United States" channel - And I set its original price to "$20.00" for "United States" channel - And I add product "Johny Walker Black" and "Jack Daniels Gentleman" to the bundle - And I add it - Then I should be notified that it has been successfully created + @ui @javascript + Scenario: Creating a bundled product + When I want to create a new bundled product + And I specify its code as "WHISKEY_PACK" + And I name it "Whiskey double pack" in "English (United States)" + And I set its slug to "whiskey-double-pack" in "English (United States)" + And I set its price to "$10.00" for "United States" channel + And I set its original price to "$20.00" for "United States" channel + And I add product "Johny Walker Black" and "Jack Daniels Gentleman" to the bundle + And I add it + Then I should be notified that it has been successfully created - @ui @javascript - Scenario: Creating a bundled product with more products - When I want to create a new bundled product - And I specify its code as "WHISKEY_BIG_PACK" - And I name it "Whiskey triple pack" in "English (United States)" - And I set its slug to "whiskey-triple-pack" in "English (United States)" - And I set its price to "$10.00" for "United States" channel - And I set its original price to "$20.00" for "United States" channel - And I add product "Johny Walker Black" and "Jack Daniels Gentleman" and "Jim Beam Double Oak" to the bundle - And I add it - Then I should be notified that it has been successfully created + @ui @javascript + Scenario: Creating a bundled product with more products + When I want to create a new bundled product + And I specify its code as "WHISKEY_BIG_PACK" + And I name it "Whiskey triple pack" in "English (United States)" + And I set its slug to "whiskey-triple-pack" in "English (United States)" + And I set its price to "$10.00" for "United States" channel + And I set its original price to "$20.00" for "United States" channel + And I add product "Johny Walker Black" and "Jack Daniels Gentleman" and "Jim Beam Double Oak" to the bundle + And I add it + Then I should be notified that it has been successfully created + And there should be a "WHISKEY_BIG_PACK" bundle containing "Johny Walker Black" with quantity 1 + And there should be a "WHISKEY_BIG_PACK" bundle containing "Jack Daniels Gentleman" with quantity 1 + And there should be a "WHISKEY_BIG_PACK" bundle containing "Jim Beam Double Oak" with quantity 1 + + @ui @javascript + Scenario: Creating a bundled product with higher quantity + When I want to create a new bundled product + And I specify its code as "WHISKEY_BIG_PACK" + And I name it "Whiskey triple pack" in "English (United States)" + And I set its slug to "whiskey-triple-pack" in "English (United States)" + And I set its price to "$10.00" for "United States" channel + And I set its original price to "$20.00" for "United States" channel + And I add product "Jim Beam Double Oak" with quantity 5 and "Jack Daniels Gentleman" with quantity 2 to the bundle + And I add it + Then I should be notified that it has been successfully created + And there should be a "WHISKEY_BIG_PACK" bundle containing "Jim Beam Double Oak" with quantity 5 + And there should be a "WHISKEY_BIG_PACK" bundle containing "Jack Daniels Gentleman" with quantity 2 diff --git a/features/having_bundled_product_in_store.feature b/features/having_bundled_product_in_store.feature index 1e9031d2..bc75045f 100644 --- a/features/having_bundled_product_in_store.feature +++ b/features/having_bundled_product_in_store.feature @@ -1,40 +1,66 @@ @bundled_product Feature: Having a product in store which is a bundle of other products - I want to be able to add bundled product to cart + As a Customer + I want to be able to order bundled products - Background: - Given the store operates on a single channel in "United States" - And I am a logged in customer - And the store ships everywhere for Free - And the store allows paying Offline + Background: + Given the store operates on a single channel in "United States" + And the store has "VAT" tax rate of 50% for "Coke" within the "US" zone + And I am a logged in customer + And the store ships everywhere for Free + And the store allows paying Offline + And the store has a product "Jim Beam" priced at "$10.00" + And the store has a product "Jim Beam Double Oak" priced at "$10.00" + And the store has a product "Coca-Cola" priced at "$5.00" + And the store has bundled product "Jim Beam double pack" priced at "$18.00" which contains "Jim Beam" and "Jim Beam Double Oak" + And the store has bundled product "Jim Beam&Coke" priced at "$12.00" which contains "Jim Beam" and "Coca-Cola" + And it belongs to "Coke" tax category + And all store products appear under a main taxonomy - @ui - Scenario: Adding a product bundle to the cart - Given the store has a product "Jack Daniels Gentleman" priced at "$10.00" - And the store has a product "Johny Walker Black" priced at "$10.00" - And the store has bundled product "Whiskey double pack" priced at "$18.00" which contains "Jack Daniels Gentleman" and "Johny Walker Black" - And all store products appear under a main taxonomy - Then I added product "Whiskey double pack" to the cart - And I should be on my cart summary page - And there should be one item in my cart + @ui + Scenario: Adding product bundles to cart + When I added product "Jim Beam double pack" to the cart + And I change product "Jim Beam double pack" quantity to 5 in my cart + Then I should see "Jim Beam double pack" with quantity 5 in my cart + And my cart total should be "$90.00" - @ui - Scenario: Adding a few product bundles to the cart - Given the store has a product "Jim Beam" priced at "$10.00" - And the store has a product "Jim Beam Double Oak" priced at "$10.00" - And the store has bundled product "Jim Beam double pack" priced at "$18.00" which contains "Jim Beam" and "Jim Beam Double Oak" - And all store products appear under a main taxonomy - Then I added product "Jim Beam double pack" to the cart - And I change product "Jim Beam double pack" quantity to 5 in my cart - And I should see "Jim Beam double pack" with quantity 5 in my cart + @ui + Scenario: Placing an order for bundled products + Given I have product "Jim Beam double pack" in the cart + And I have product "Jim Beam&Coke" in the cart + And my cart total should be "$30.00" + And I specified the billing address as "Ankh Morpork", "Frost Alley", "90210", "United States" for "Jon Snow" + And I proceed with "Free" shipping method and "Offline" payment + When I confirm my order + Then I should see the thank you page - @ui - Scenario: Placing an order for a bundled product - Given the store has a product "Jim Beam" priced at "$10.00" - And the store has a product "Jim Beam Double Oak" priced at "$10.00" - And the store has bundled product "Jim Beam double pack" priced at "$18.00" which contains "Jim Beam" and "Jim Beam Double Oak" - Given I have product "Jim Beam double pack" in the cart - And I specified the billing address as "Ankh Morpork", "Frost Alley", "90210", "United States" for "Jon Snow" - And I proceed with "Free" shipping method and "Offline" payment - And I confirm my order - Then I should see the thank you page + @ui + Scenario: Placing an order for bundled products with promotion applied + Given there is a promotion "Holiday promotion" + And this promotion gives "$1.00" off on every product with minimum price at "$15.00" + And I have product "Jim Beam double pack" in the cart + And I have product "Jim Beam&Coke" in the cart + And my cart total should be "$29.00" + And I specified the billing address as "Ankh Morpork", "Frost Alley", "90210", "United States" for "Jon Snow" + And I proceed with "Free" shipping method and "Offline" payment + When I confirm my order + Then I should see the thank you page + + @ui + Scenario: Placing an order for bundled products with tax applied + Given I have product "Jim Beam double pack" in the cart + And I have product "Jim Beam&Coke" in the cart + And I have product "Coca-Cola" in the cart + When I specified the billing address as "Ankh Morpork", "Frost Alley", "90210", "United States" for "Jon Snow" + And I proceed with "Free" shipping method and "Offline" payment + Then my cart total should be "$41.00" + And my cart taxes should be "$6.00" + + @api + Scenario: Adding product bundles to cart with API + When I pick up my cart + And I add bundle "Jim Beam&Coke" with quantity 5 to my cart + And I add bundle "Jim Beam&Coke" with quantity 5 to my cart + Then I should have bundle "Jim Beam&Coke" with quantity 10 in my cart + And I should have product "Jim Beam" in bundled items + And I should have product "Coca-Cola" in bundled items diff --git a/features/viewing_products_from_ordered_bundle.feature b/features/viewing_products_from_ordered_bundle.feature new file mode 100644 index 00000000..44051955 --- /dev/null +++ b/features/viewing_products_from_ordered_bundle.feature @@ -0,0 +1,37 @@ +@bundled_product +Feature: Reviewing products from ordered bundle + As a Customer + I want to be able to see products in the bundle I ordered + + Background: + Given the store operates on a single channel in "United States" + And the store ships everywhere for Free + And the store allows paying Offline + And the store has a product "Jim Beam" priced at "$10.00" + And the store has a product "Coca-Cola" priced at "$5.00" + And the store has bundled product "Jim Beam&Coke" priced at "$12.00" which contains "Jim Beam" and "Coca-Cola" + And all store products appear under a main taxonomy + And I am a logged in customer with name "Bundle Customer" + + @ui @shop + Scenario: Viewing bundled products in cart + Given I have product "Jim Beam&Coke" in the cart + When I see the summary of my cart + Then there should be one item in my cart + And this item should have name "Jim Beam&Coke" + And there should be bundled products listed + And the list should contain "Jim Beam" + And the list should contain "Coca-Cola" + + @ui @shop + Scenario: Viewing bundled products in order history + Given there is a customer "sylius@example.com" that placed an order "#1" later + And the customer bought a single bundle "Jim Beam&Coke" + And I addressed it to "Ankh Morpork", "Frost Alley", "90210" "Los Angeles" in the "United States" + And for the billing address of "Ankh Morpork" in the "Frost Alley", "90210" "Los Angeles", "United States" + And I chose "Free" shipping method with "Offline" payment + When I view the summary of my order "#1" + Then it should have the number "#1" + And there should be bundled products listed in order details + And the list should contain "Jim Beam" in order details + And the list should contain "Coca-Cola" in order details diff --git a/tests/Behat/Context/Api/ProductBundleContext.php b/tests/Behat/Context/Api/ProductBundleContext.php new file mode 100644 index 00000000..fc9182c9 --- /dev/null +++ b/tests/Behat/Context/Api/ProductBundleContext.php @@ -0,0 +1,83 @@ +requestFactory->customItemAction( + 'shop', + Resources::ORDERS, + $this->sharedStorage->get('cart_token'), + HttpRequest::METHOD_PATCH, + 'product-bundle', + ); + $request->updateContent([ + 'productCode' => $product->getCode(), + 'quantity' => $quantity, + ]); + + $this->client->executeCustomRequest($request); + } + + /** + * @When I should have bundle :product with quantity :quantity in my cart + */ + public function iShouldHaveBundleWithQuantityInMyCart(ProductInterface $product, int $quantity): void + { + $response = $this->client->show(Resources::ORDERS, $this->sharedStorage->get('cart_token')); + + $item = $this->responseChecker->getValue($response, 'items')[0]; + Assert::eq($item['productName'], $product->getName()); + Assert::eq($item['quantity'], $quantity); + } + + /** + * @When I should have product :product in bundled items + */ + public function iShouldHaveProductInBundledItems(ProductInterface $product): void + { + $response = $this->client->show(Resources::ORDERS, $this->sharedStorage->get('cart_token')); + + $productBundleOrderItems = $this->responseChecker->getValue($response, 'items')[0]['productBundleOrderItems']; + foreach ($productBundleOrderItems as $item) { + if ($item['productVariant']['code'] === $product->getCode()) { + return; + } + } + + throw new \InvalidArgumentException('Product not found in bundled items'); + } +} diff --git a/tests/Behat/Context/Setup/ProductBundleContext.php b/tests/Behat/Context/Setup/ProductBundleContext.php index ab406ff0..b7c1bb74 100644 --- a/tests/Behat/Context/Setup/ProductBundleContext.php +++ b/tests/Behat/Context/Setup/ProductBundleContext.php @@ -14,16 +14,21 @@ use Behat\Behat\Context\Context; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductInterface; +use BitBag\SyliusProductBundlePlugin\Factory\OrderItemFactoryInterface; +use BitBag\SyliusProductBundlePlugin\Factory\ProductBundleOrderItemFactoryInterface; use BitBag\SyliusProductBundlePlugin\Factory\ProductFactory; use Doctrine\ORM\EntityManagerInterface; use Sylius\Behat\Service\SharedStorageInterface; use Sylius\Component\Core\Formatter\StringInflector; use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\ChannelPricingInterface; +use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\ProductTaxonInterface; use Sylius\Component\Core\Model\ProductVariantInterface; use Sylius\Component\Core\Model\TaxonInterface; use Sylius\Component\Core\Repository\ProductRepositoryInterface; +use Sylius\Component\Order\Modifier\OrderItemQuantityModifierInterface; +use Sylius\Component\Order\Modifier\OrderModifierInterface; use Sylius\Component\Product\Generator\SlugGeneratorInterface; use Sylius\Component\Product\Resolver\ProductVariantResolverInterface; use Sylius\Component\Resource\Factory\FactoryInterface; @@ -31,16 +36,21 @@ final class ProductBundleContext implements Context { public function __construct( - private SharedStorageInterface $sharedStorage, - private FactoryInterface $taxonFactory, - private ProductRepositoryInterface $productRepository, - private FactoryInterface $productTaxonFactory, - private EntityManagerInterface $productTaxonManager, - private ProductFactory $productFactory, - private FactoryInterface $productBundleItemFactory, - private FactoryInterface $channelPricingFactory, - private ProductVariantResolverInterface $productVariantResolver, - private SlugGeneratorInterface $slugGenerator, + private readonly SharedStorageInterface $sharedStorage, + private readonly FactoryInterface $taxonFactory, + private readonly ProductRepositoryInterface $productRepository, + private readonly FactoryInterface $productTaxonFactory, + private readonly EntityManagerInterface $productTaxonManager, + private readonly ProductFactory $productFactory, + private readonly FactoryInterface $productBundleItemFactory, + private readonly FactoryInterface $channelPricingFactory, + private readonly ProductVariantResolverInterface $productVariantResolver, + private readonly SlugGeneratorInterface $slugGenerator, + private readonly EntityManagerInterface $objectManager, + private readonly OrderItemQuantityModifierInterface $orderItemQuantityModifier, + private readonly ProductBundleOrderItemFactoryInterface $productBundleOrderItemFactory, + private readonly OrderModifierInterface $orderModifier, + private readonly OrderItemFactoryInterface $cartItemFactory, ) { } @@ -149,4 +159,27 @@ private function createProduct( return $product; } + + /** + * @When the customer bought a single bundle :product + */ + public function theCustomerBoughtBundle(ProductInterface $product): void + { + /** @var OrderInterface|null $cart */ + $cart = $this->sharedStorage->get('order'); + /** @var ProductVariantInterface|null $variant */ + $variant = $product->getVariants()->first(); + + $cartItem = $this->cartItemFactory->createWithVariant($variant); + $this->orderItemQuantityModifier->modify($cartItem, 1); + + foreach ($product->getProductBundle()->getProductBundleItems() as $bundleItem) { + $productBundleOrderItem = $this->productBundleOrderItemFactory->createFromProductBundleItem($bundleItem); + $cartItem->addProductBundleOrderItem($productBundleOrderItem); + } + + $this->orderModifier->addToOrder($cart, $cartItem); + + $this->objectManager->flush(); + } } diff --git a/tests/Behat/Context/Ui/ProductBundleContext.php b/tests/Behat/Context/Ui/ProductBundleContext.php index 43058dc5..5087e04d 100644 --- a/tests/Behat/Context/Ui/ProductBundleContext.php +++ b/tests/Behat/Context/Ui/ProductBundleContext.php @@ -12,13 +12,21 @@ namespace Tests\BitBag\SyliusProductBundlePlugin\Behat\Context\Ui; use Behat\Behat\Context\Context; +use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface; +use BitBag\SyliusProductBundlePlugin\Entity\ProductInterface; use Sylius\Component\Core\Model\ChannelInterface; +use Sylius\Component\Core\Repository\ProductRepositoryInterface; use Tests\BitBag\SyliusProductBundlePlugin\Behat\Page\Admin\CreateBundledProductPageInterface; +use Tests\BitBag\SyliusProductBundlePlugin\Behat\Page\Shop\BundledProductsListPageInterface; +use Webmozart\Assert\Assert; class ProductBundleContext implements Context { public function __construct( private CreateBundledProductPageInterface $createBundledProductPage, + private ProductRepositoryInterface $productRepository, + private BundledProductsListPageInterface $summaryPage, + private BundledProductsListPageInterface $orderShowPage, ) { } @@ -90,4 +98,67 @@ public function iAddProductsToBundledProduct(...$productsNames) { $this->createBundledProductPage->addProductsToBundle($productsNames); } + + /** + * @When I add product :firstProductName with quantity :firstQuantity and :secondProductName with quantity :secondQuantity to the bundle + */ + public function iAddProductsWithQuantitiesToBundledProduct(string $firstProductName, string $secondProductName, int $firstQuantity, int $secondQuantity) + { + $this->createBundledProductPage->addProductsToBundle([$firstProductName, $secondProductName], [$firstQuantity, $secondQuantity]); + } + + /** + * @When there should be a :bundleName bundle containing :productName with quantity :quantity + */ + public function theProductBundleShouldContainProductWithQuantity(string $bundleCode, string $productName, int $quantity): void + { + /** @var ProductInterface $product */ + $product = $this->productRepository->findOneBy(['code' => $bundleCode]); + + Assert::notNull($product->getProductBundle()); + + $bundleItems = $product->getProductBundle()->getProductBundleItems(); + /** @var ProductBundleItemInterface $item */ + foreach ($bundleItems as $item) { + if ($item->getProductVariant()->getProduct()->getName() === $productName) { + Assert::same($item->getQuantity(), $quantity); + + return; + } + } + + throw new \InvalidArgumentException(sprintf('Product "%s" not found in bundle "%s"', $productName, $bundleCode)); + } + + /** + * @When there should be bundled products listed + */ + public function thereShouldBeBundledProductsListed(): void + { + Assert::true($this->summaryPage->hasBundledProductsList()); + } + + /** + * @When the list should contain :productName + */ + public function thereShouldBeAProductOnTheList(string $productName): void + { + Assert::true($this->summaryPage->hasBundledProduct($productName)); + } + + /** + * @When there should be bundled products listed in order details + */ + public function thereShouldBeBundledProductsListedOnOrderShowPage(): void + { + Assert::true($this->orderShowPage->hasBundledProductsList()); + } + + /** + * @When the list should contain :productName in order details + */ + public function thereShouldBeAProductOnTheListOnOrderShowPage(string $productName): void + { + Assert::true($this->orderShowPage->hasBundledProduct($productName)); + } } diff --git a/tests/Behat/Page/Admin/CreateBundledProductPage.php b/tests/Behat/Page/Admin/CreateBundledProductPage.php index eb2f8ae0..23067825 100644 --- a/tests/Behat/Page/Admin/CreateBundledProductPage.php +++ b/tests/Behat/Page/Admin/CreateBundledProductPage.php @@ -60,13 +60,13 @@ public function specifyOriginalPrice(ChannelInterface $channel, int $originalPri $this->getElement('original_price', ['%channelCode%' => $channel->getCode()])->setValue($originalPrice); } - public function addProductsToBundle(array $productsNames): void + public function addProductsToBundle(array $productsNames, array $quantites = []): void { $this->clickTabIfItsNotActive('bundle'); $productCounter = 0; - foreach ($productsNames as $productName) { + foreach ($productsNames as $i => $productName) { $addSelector = $this->getElement('add_product_to_bundle_button'); $addSelector->click(); $addSelector->waitFor(5, fn () => $this->hasElement('product_selector_dropdown')); @@ -80,7 +80,8 @@ public function addProductsToBundle(array $productsNames): void ]); $item->click(); - $this->getElement('product_selector_quantity', ['%productCounter%' => $productCounter])->setValue('1'); + $quantity = array_key_exists($i, $quantites) ? (string) $quantites[$i] : '1'; + $this->getElement('product_selector_quantity', ['%productCounter%' => $productCounter])->setValue($quantity); ++$productCounter; } diff --git a/tests/Behat/Page/Shop/BundledProductsListPageInterface.php b/tests/Behat/Page/Shop/BundledProductsListPageInterface.php new file mode 100644 index 00000000..971d19aa --- /dev/null +++ b/tests/Behat/Page/Shop/BundledProductsListPageInterface.php @@ -0,0 +1,19 @@ +hasElement('products_in_bundle'); + } + + public function hasBundledProduct(string $productName): bool + { + return $this->hasElement('bundled_product', ['%productName%' => $productName]); + } + + protected function getDefinedElements(): array + { + return array_merge(parent::getDefinedElements(), [ + 'products_in_bundle' => '#sylius-order > tbody > tr:nth-child(2) > td > div > div.title.bundled-items-header > strong:contains("Products in bundle")', + 'bundled_product' => '#sylius-order > tbody > tr:nth-child(2) > td > div > div.content.bundled-items > div > div > div > div:contains("%productName%")', + ]); + } +} diff --git a/tests/Behat/Page/Shop/SummaryPage.php b/tests/Behat/Page/Shop/SummaryPage.php new file mode 100644 index 00000000..8acb33fd --- /dev/null +++ b/tests/Behat/Page/Shop/SummaryPage.php @@ -0,0 +1,35 @@ +hasElement('products_in_bundle'); + } + + public function hasBundledProduct(string $productName): bool + { + return $this->hasElement('bundled_product', ['%productName%' => $productName]); + } + + protected function getDefinedElements(): array + { + return array_merge(parent::getDefinedElements(), [ + 'products_in_bundle' => '#sylius-cart-items > tbody > tr:nth-child(2) > td > div > div.title.bundled-items-header > strong:contains("Products in bundle")', + 'bundled_product' => '#sylius-cart-items > tbody > tr:nth-child(2) > td > div > div.content.bundled-items > div > div > div > div:contains("%productName%")', + ]); + } +} diff --git a/tests/Behat/Resources/services.yml b/tests/Behat/Resources/services.yml index f6ae41e7..db9940d3 100644 --- a/tests/Behat/Resources/services.yml +++ b/tests/Behat/Resources/services.yml @@ -14,6 +14,11 @@ services: - '@sylius.factory.channel_pricing' - '@sylius.product_variant_resolver.default' - '@sylius.generator.slug' + - '@doctrine.orm.entity_manager' + - '@sylius.order_item_quantity_modifier' + - '@bitbag_sylius_product_bundle.custom_factory.product_bundle_order_item' + - '@sylius.order_modifier' + - '@bitbag_sylius_product_bundle.custom_factory.order_item' bitbag_sylius_product_bundle_plugin.behat.page.create_bundled_product_page: class: Tests\BitBag\SyliusProductBundlePlugin\Behat\Page\Admin\CreateBundledProductPage @@ -22,7 +27,28 @@ services: arguments: - 'bitbag_product_bundle_admin_product_create_bundle' + bitbag_sylius_product_bundle_plugin.behat.page.summary_page: + class: Tests\BitBag\SyliusProductBundlePlugin\Behat\Page\Shop\SummaryPage + parent: sylius.behat.page.shop.cart_summary + public: false + + bitbag_sylius_product_bundle_plugin.behat.page.account.order_show_page: + class: Tests\BitBag\SyliusProductBundlePlugin\Behat\Page\Shop\OrderShowPage + parent: sylius.behat.page.shop.account.order.show + public: false + bitbag_sylius_product_bundle_plugin.behat.context.ui.product_bundle: class: Tests\BitBag\SyliusProductBundlePlugin\Behat\Context\Ui\ProductBundleContext arguments: - '@bitbag_sylius_product_bundle_plugin.behat.page.create_bundled_product_page' + - '@sylius.repository.product' + - '@bitbag_sylius_product_bundle_plugin.behat.page.summary_page' + - '@bitbag_sylius_product_bundle_plugin.behat.page.account.order_show_page' + + bitbag_sylius_product_bundle_plugin.behat.context.api.product_bundle: + class: Tests\BitBag\SyliusProductBundlePlugin\Behat\Context\Api\ProductBundleContext + arguments: + - '@sylius.behat.shared_storage' + - '@sylius.behat.api_platform_client.shop' + - '@sylius.behat.request_factory' + - '@Sylius\Behat\Client\ResponseCheckerInterface' diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index 70c3f6c8..1b818d96 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -4,24 +4,29 @@ default: contexts: - sylius.behat.context.hook.doctrine_orm - - sylius.behat.context.transform.lexical - - sylius.behat.context.transform.product - - sylius.behat.context.transform.address - - sylius.behat.context.transform.payment - - sylius.behat.context.transform.shipping_method - - sylius.behat.context.transform.zone - - sylius.behat.context.transform.locale - - sylius.behat.context.transform.channel - - - sylius.behat.context.setup.currency - - sylius.behat.context.setup.locale + - sylius.behat.context.setup.admin_security - sylius.behat.context.setup.channel + - sylius.behat.context.setup.currency - sylius.behat.context.setup.customer - - sylius.behat.context.setup.shop_security + - sylius.behat.context.setup.locale + - sylius.behat.context.setup.payment - sylius.behat.context.setup.product - - sylius.behat.context.setup.admin_security + - sylius.behat.context.setup.promotion + - sylius.behat.context.setup.taxation - sylius.behat.context.setup.shipping - - sylius.behat.context.setup.payment + - sylius.behat.context.setup.shop_security + + - sylius.behat.context.transform.address + - sylius.behat.context.transform.channel + - sylius.behat.context.transform.lexical + - sylius.behat.context.transform.locale + - sylius.behat.context.transform.payment + - sylius.behat.context.transform.product + - sylius.behat.context.transform.promotion + - sylius.behat.context.transform.shared_storage + - sylius.behat.context.transform.shipping_method + - sylius.behat.context.transform.tax_category + - sylius.behat.context.transform.zone - sylius.behat.context.ui.shop.cart - sylius.behat.context.ui.shop.checkout.addressing @@ -30,7 +35,76 @@ default: - sylius.behat.context.ui.shop.checkout.thank_you - sylius.behat.context.ui.admin.notification + - bitbag_sylius_product_bundle_plugin.behat.context.ui.product_bundle - bitbag_sylius_product_bundle_plugin.behat.context.setup.product_bundle + + filters: + tags: "@bundled_product&&@ui&&~@shop" + + shop_bundled_product: + contexts: + - sylius.behat.context.hook.doctrine_orm + + - sylius.behat.context.setup.channel + - sylius.behat.context.setup.customer + - sylius.behat.context.setup.order + - sylius.behat.context.setup.payment + - sylius.behat.context.setup.product + - sylius.behat.context.setup.shipping + - sylius.behat.context.setup.shop_security + + - sylius.behat.context.transform.address + - sylius.behat.context.transform.customer + - sylius.behat.context.transform.lexical + - sylius.behat.context.transform.order + - sylius.behat.context.transform.payment + - sylius.behat.context.transform.product + - sylius.behat.context.transform.shared_storage + - sylius.behat.context.transform.shipping_method + + - sylius.behat.context.ui.shop.account + - sylius.behat.context.ui.shop.cart + - bitbag_sylius_product_bundle_plugin.behat.context.ui.product_bundle + - bitbag_sylius_product_bundle_plugin.behat.context.setup.product_bundle + + filters: + tags: "@bundled_product&&@ui&&@shop" + + api_bundled_product: + contexts: + - sylius.behat.context.hook.doctrine_orm + + - sylius.behat.context.setup.admin_security + - sylius.behat.context.setup.channel + - sylius.behat.context.setup.currency + - sylius.behat.context.setup.customer + - sylius.behat.context.setup.locale + - sylius.behat.context.setup.payment + - sylius.behat.context.setup.product + - sylius.behat.context.setup.promotion + - sylius.behat.context.setup.taxation + - sylius.behat.context.setup.shipping + - sylius.behat.context.setup.shop_security + + - sylius.behat.context.transform.address + - sylius.behat.context.transform.channel + - sylius.behat.context.transform.lexical + - sylius.behat.context.transform.locale + - sylius.behat.context.transform.payment + - sylius.behat.context.transform.product + - sylius.behat.context.transform.promotion + - sylius.behat.context.transform.shared_storage + - sylius.behat.context.transform.shipping_method + - sylius.behat.context.transform.tax_category + - sylius.behat.context.transform.zone + + - sylius.behat.context.api.shop.cart + - sylius.behat.context.api.shop.checkout + - sylius.behat.context.api.shop.checkout.complete + + - bitbag_sylius_product_bundle_plugin.behat.context.api.product_bundle + - bitbag_sylius_product_bundle_plugin.behat.context.setup.product_bundle + filters: - tags: "@bundled_product" + tags: "@bundled_product&&@api" From 65e99829c2d4e8817ee7c6b3153c4516f3c165fc Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Mon, 9 Sep 2024 14:12:07 +0200 Subject: [PATCH 30/40] OP-289: Add a helpbox explaining what "Is packed product" does --- src/Resources/translations/messages.en.yml | 1 + src/Resources/views/Admin/Product/Tab/_bundle.html.twig | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/Resources/translations/messages.en.yml b/src/Resources/translations/messages.en.yml index 61f378c3..ab250ed3 100644 --- a/src/Resources/translations/messages.en.yml +++ b/src/Resources/translations/messages.en.yml @@ -8,3 +8,4 @@ bitbag_sylius_product_bundle: products_in_bundle: Products in bundle is_packed_product: Is packed product is_bundle: Is bundle + is_packed_help: If the bundle is not marked as packed, customers will be able to select different variants for products in the bundle diff --git a/src/Resources/views/Admin/Product/Tab/_bundle.html.twig b/src/Resources/views/Admin/Product/Tab/_bundle.html.twig index 586e8568..aa724a8e 100644 --- a/src/Resources/views/Admin/Product/Tab/_bundle.html.twig +++ b/src/Resources/views/Admin/Product/Tab/_bundle.html.twig @@ -3,6 +3,10 @@
{{ form_row(form.productBundle.isPackedProduct) }} + {{ form_help(form.productBundle.isPackedProduct, { + 'help': 'bitbag_sylius_product_bundle.ui.is_packed_help'|trans, + 'help_attr': {'class':'ui info message'} + }) }} {{ form_row(form.productBundle.productBundleItems) }} {{ sonata_block_render_event('sylius.admin.product.' ~ action ~ '.tab_bundle', {'form': form }) }} From 3dff2a8c91fa506547cf06647ee0f1ecffff2d39 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Mon, 9 Sep 2024 16:49:07 +0200 Subject: [PATCH 31/40] OP-289: Fix handling unpacked bundles in shop --- src/Command/AddProductBundleToCartCommand.php | 17 +++++++++++++++++ .../AddProductBundleToCartCommandFactory.php | 12 ++++++++++-- ...oductBundleToCartCommandFactoryInterface.php | 4 ++++ src/Factory/ProductBundleOrderItemFactory.php | 13 +++++++++++++ .../ProductBundleOrderItemFactoryInterface.php | 3 +++ src/Handler/AddProductBundleToCartHandler.php | 5 ++++- .../CartProcessor.php | 14 ++++++++------ .../CartProcessorInterface.php | 2 ++ 8 files changed, 61 insertions(+), 9 deletions(-) diff --git a/src/Command/AddProductBundleToCartCommand.php b/src/Command/AddProductBundleToCartCommand.php index 88c83ae0..564a7bb6 100644 --- a/src/Command/AddProductBundleToCartCommand.php +++ b/src/Command/AddProductBundleToCartCommand.php @@ -11,8 +11,13 @@ namespace BitBag\SyliusProductBundlePlugin\Command; +use Doctrine\Common\Collections\Collection; + final class AddProductBundleToCartCommand implements OrderIdentityAwareInterface, ProductCodeAwareInterface { + /** @var Collection */ + private Collection $productBundleItems; + public function __construct( private int $orderId, private string $productCode, @@ -34,4 +39,16 @@ public function getQuantity(): int { return $this->quantity; } + + /** @return Collection */ + public function getProductBundleItems(): Collection + { + return $this->productBundleItems; + } + + /** @param Collection $productBundleItems */ + public function setProductBundleItems(Collection $productBundleItems): void + { + $this->productBundleItems = $productBundleItems; + } } diff --git a/src/Factory/AddProductBundleToCartCommandFactory.php b/src/Factory/AddProductBundleToCartCommandFactory.php index 798e0f00..177082f0 100644 --- a/src/Factory/AddProductBundleToCartCommandFactory.php +++ b/src/Factory/AddProductBundleToCartCommandFactory.php @@ -11,17 +11,24 @@ namespace BitBag\SyliusProductBundlePlugin\Factory; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleToCartCommand; use BitBag\SyliusProductBundlePlugin\Dto\AddProductBundleToCartDtoInterface; +use Doctrine\Common\Collections\Collection; final class AddProductBundleToCartCommandFactory implements AddProductBundleToCartCommandFactoryInterface { + /** @param Collection */ public function createNew( int $orderId, string $productCode, int $quantity, + Collection $productBundleItems, ): AddProductBundleToCartCommand { - return new AddProductBundleToCartCommand($orderId, $productCode, $quantity); + $command = new AddProductBundleToCartCommand($orderId, $productCode, $quantity); + $command->setProductBundleItems($productBundleItems); + + return $command; } public function createFromDto(AddProductBundleToCartDtoInterface $dto): AddProductBundleToCartCommand @@ -29,7 +36,8 @@ public function createFromDto(AddProductBundleToCartDtoInterface $dto): AddProdu $cartId = $dto->getCart()->getId(); $productCode = $dto->getProduct()->getCode() ?? ''; $quantity = $dto->getCartItem()->getQuantity(); + $productBundleItems = $dto->getProductBundleItems(); - return $this->createNew($cartId, $productCode, $quantity); + return $this->createNew($cartId, $productCode, $quantity, $productBundleItems); } } diff --git a/src/Factory/AddProductBundleToCartCommandFactoryInterface.php b/src/Factory/AddProductBundleToCartCommandFactoryInterface.php index d2fc089c..383de508 100644 --- a/src/Factory/AddProductBundleToCartCommandFactoryInterface.php +++ b/src/Factory/AddProductBundleToCartCommandFactoryInterface.php @@ -13,13 +13,17 @@ use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleToCartCommand; use BitBag\SyliusProductBundlePlugin\Dto\AddProductBundleToCartDtoInterface; +use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItemInterface; +use Doctrine\Common\Collections\Collection; interface AddProductBundleToCartCommandFactoryInterface { + /** @param Collection $productBundleItems */ public function createNew( int $orderId, string $productCode, int $quantity, + Collection $productBundleItems, ): AddProductBundleToCartCommand; public function createFromDto(AddProductBundleToCartDtoInterface $dto): AddProductBundleToCartCommand; diff --git a/src/Factory/ProductBundleOrderItemFactory.php b/src/Factory/ProductBundleOrderItemFactory.php index c18a7e60..5d00e8c5 100644 --- a/src/Factory/ProductBundleOrderItemFactory.php +++ b/src/Factory/ProductBundleOrderItemFactory.php @@ -11,6 +11,7 @@ namespace BitBag\SyliusProductBundlePlugin\Factory; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItemInterface; use Sylius\Component\Resource\Factory\FactoryInterface; @@ -41,4 +42,16 @@ public function createFromProductBundleItem(ProductBundleItemInterface $bundleIt return $productBundleOrderItem; } + + public function createFromAddProductBundleItemToCartCommand(AddProductBundleItemToCartCommand $addItemToCartCommand): ProductBundleOrderItemInterface + { + /** @var ProductBundleOrderItemInterface $productBundleOrderItem */ + $productBundleOrderItem = $this->decoratedFactory->createNew(); + + $productBundleOrderItem->setProductBundleItem($addItemToCartCommand->getProductBundleItem()); + $productBundleOrderItem->setProductVariant($addItemToCartCommand->getProductVariant()); + $productBundleOrderItem->setQuantity($addItemToCartCommand->getQuantity()); + + return $productBundleOrderItem; + } } diff --git a/src/Factory/ProductBundleOrderItemFactoryInterface.php b/src/Factory/ProductBundleOrderItemFactoryInterface.php index 29669331..44b7d9f5 100644 --- a/src/Factory/ProductBundleOrderItemFactoryInterface.php +++ b/src/Factory/ProductBundleOrderItemFactoryInterface.php @@ -11,6 +11,7 @@ namespace BitBag\SyliusProductBundlePlugin\Factory; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItemInterface; use Sylius\Component\Resource\Factory\FactoryInterface; @@ -18,4 +19,6 @@ interface ProductBundleOrderItemFactoryInterface extends FactoryInterface { public function createFromProductBundleItem(ProductBundleItemInterface $bundleItem): ProductBundleOrderItemInterface; + + public function createFromAddProductBundleItemToCartCommand(AddProductBundleItemToCartCommand $addItemToCartCommand): ProductBundleOrderItemInterface; } diff --git a/src/Handler/AddProductBundleToCartHandler.php b/src/Handler/AddProductBundleToCartHandler.php index 4c4b9832..004965e1 100644 --- a/src/Handler/AddProductBundleToCartHandler.php +++ b/src/Handler/AddProductBundleToCartHandler.php @@ -46,7 +46,10 @@ public function __invoke(AddProductBundleToCartCommand $addProductBundleToCartCo $quantity = $addProductBundleToCartCommand->getQuantity(); Assert::greaterThan($quantity, 0); - $this->cartProcessor->process($cart, $productBundle, $quantity); + $items = $addProductBundleToCartCommand->getProductBundleItems(); + Assert::false($items->isEmpty()); + + $this->cartProcessor->process($cart, $productBundle, $quantity, $items); $this->orderRepository->add($cart); } } diff --git a/src/Handler/AddProductBundleToCartHandler/CartProcessor.php b/src/Handler/AddProductBundleToCartHandler/CartProcessor.php index 7c3b8237..5260b63d 100644 --- a/src/Handler/AddProductBundleToCartHandler/CartProcessor.php +++ b/src/Handler/AddProductBundleToCartHandler/CartProcessor.php @@ -14,6 +14,7 @@ use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleInterface; use BitBag\SyliusProductBundlePlugin\Factory\OrderItemFactoryInterface; use BitBag\SyliusProductBundlePlugin\Factory\ProductBundleOrderItemFactoryInterface; +use Doctrine\Common\Collections\Collection; use Sylius\Component\Core\Model\ProductVariantInterface; use Sylius\Component\Order\Model\OrderInterface; use Sylius\Component\Order\Modifier\OrderItemQuantityModifierInterface; @@ -23,10 +24,10 @@ final class CartProcessor implements CartProcessorInterface { public function __construct( - private OrderItemQuantityModifierInterface $orderItemQuantityModifier, - private ProductBundleOrderItemFactoryInterface $productBundleOrderItemFactory, - private OrderModifierInterface $orderModifier, - private OrderItemFactoryInterface $cartItemFactory, + private readonly OrderItemQuantityModifierInterface $orderItemQuantityModifier, + private readonly ProductBundleOrderItemFactoryInterface $productBundleOrderItemFactory, + private readonly OrderModifierInterface $orderModifier, + private readonly OrderItemFactoryInterface $cartItemFactory, ) { } @@ -34,6 +35,7 @@ public function process( OrderInterface $cart, ProductBundleInterface $productBundle, int $quantity, + Collection $productBundleOrderItems, ): void { Assert::greaterThan($quantity, 0); @@ -47,8 +49,8 @@ public function process( $cartItem = $this->cartItemFactory->createWithVariant($productVariant); $this->orderItemQuantityModifier->modify($cartItem, $quantity); - foreach ($productBundle->getProductBundleItems() as $bundleItem) { - $productBundleOrderItem = $this->productBundleOrderItemFactory->createFromProductBundleItem($bundleItem); + foreach ($productBundleOrderItems as $addBundleItemToCartCommand) { + $productBundleOrderItem = $this->productBundleOrderItemFactory->createFromAddProductBundleItemToCartCommand($addBundleItemToCartCommand); $cartItem->addProductBundleOrderItem($productBundleOrderItem); } diff --git a/src/Handler/AddProductBundleToCartHandler/CartProcessorInterface.php b/src/Handler/AddProductBundleToCartHandler/CartProcessorInterface.php index f7946c08..54e2a43e 100644 --- a/src/Handler/AddProductBundleToCartHandler/CartProcessorInterface.php +++ b/src/Handler/AddProductBundleToCartHandler/CartProcessorInterface.php @@ -12,6 +12,7 @@ namespace BitBag\SyliusProductBundlePlugin\Handler\AddProductBundleToCartHandler; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleInterface; +use Doctrine\Common\Collections\Collection; use Sylius\Component\Order\Model\OrderInterface; interface CartProcessorInterface @@ -20,5 +21,6 @@ public function process( OrderInterface $cart, ProductBundleInterface $productBundle, int $quantity, + Collection $productBundleOrderItems, ): void; } From e36d8a0d2fa39b14be9ad43787dc93915fc9b116 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Tue, 10 Sep 2024 16:09:13 +0200 Subject: [PATCH 32/40] OP-289: Implement handling unpacked bundles in API --- src/Command/AddProductBundleToCartCommand.php | 8 +- ...dProductBundleToCartDtoDataTransformer.php | 16 +++- src/DependencyInjection/Configuration.php | 3 +- src/Dto/Api/AddProductBundleToCartDto.php | 6 ++ .../AddProductBundleToCartCommandFactory.php | 2 +- ...uctBundleToCartCommandFactoryInterface.php | 4 +- src/Handler/AddProductBundleToCartHandler.php | 6 +- ...ProductBundleItemToCartCommandProvider.php | 87 +++++++++++++++++++ ...ndleItemToCartCommandProviderInterface.php | 21 +++++ .../ProductBundleItemRepository.php | 31 +++++++ .../ProductBundleItemRepositoryInterface.php | 21 +++++ src/Repository/ProductBundleRepository.php | 12 +++ .../ProductBundleRepositoryInterface.php | 3 + .../AddProductBundleToCartDto.xml | 3 + src/Resources/config/services.xml | 1 + src/Resources/config/services/provider.xml | 16 ++++ src/Resources/config/services/transformer.xml | 1 + 17 files changed, 230 insertions(+), 11 deletions(-) create mode 100644 src/Provider/AddProductBundleItemToCartCommandProvider.php create mode 100644 src/Provider/AddProductBundleItemToCartCommandProviderInterface.php create mode 100644 src/Repository/ProductBundleItemRepository.php create mode 100644 src/Repository/ProductBundleItemRepositoryInterface.php create mode 100644 src/Resources/config/services/provider.xml diff --git a/src/Command/AddProductBundleToCartCommand.php b/src/Command/AddProductBundleToCartCommand.php index 564a7bb6..6f594132 100644 --- a/src/Command/AddProductBundleToCartCommand.php +++ b/src/Command/AddProductBundleToCartCommand.php @@ -11,6 +11,7 @@ namespace BitBag\SyliusProductBundlePlugin\Command; +use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; final class AddProductBundleToCartCommand implements OrderIdentityAwareInterface, ProductCodeAwareInterface @@ -19,10 +20,11 @@ final class AddProductBundleToCartCommand implements OrderIdentityAwareInterface private Collection $productBundleItems; public function __construct( - private int $orderId, - private string $productCode, - private int $quantity = 1, + private readonly int $orderId, + private readonly string $productCode, + private readonly int $quantity = 1, ) { + $this->productBundleItems = new ArrayCollection(); } public function getOrderId(): int diff --git a/src/DataTransformer/AddProductBundleToCartDtoDataTransformer.php b/src/DataTransformer/AddProductBundleToCartDtoDataTransformer.php index 323a6b30..7c3fa895 100644 --- a/src/DataTransformer/AddProductBundleToCartDtoDataTransformer.php +++ b/src/DataTransformer/AddProductBundleToCartDtoDataTransformer.php @@ -14,6 +14,7 @@ use ApiPlatform\Core\DataTransformer\DataTransformerInterface; use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleToCartCommand; use BitBag\SyliusProductBundlePlugin\Dto\Api\AddProductBundleToCartDto; +use BitBag\SyliusProductBundlePlugin\Provider\AddProductBundleItemToCartCommandProviderInterface; use Sylius\Component\Order\Model\OrderInterface; use Webmozart\Assert\Assert; @@ -21,6 +22,11 @@ final class AddProductBundleToCartDtoDataTransformer implements DataTransformerI { public const OBJECT_TO_POPULATE = 'object_to_populate'; + public function __construct( + private readonly AddProductBundleItemToCartCommandProviderInterface $addProductBundleItemToCartCommandProvider, + ) { + } + /** * @param AddProductBundleToCartDto|object $object */ @@ -37,8 +43,16 @@ public function transform( $productCode = $object->getProductCode(); $quantity = $object->getQuantity(); + $overwrittenVariants = $object->getOverwrittenVariants(); + $addItemToCartCommands = $this->addProductBundleItemToCartCommandProvider->provide( + $productCode, + $overwrittenVariants, + ); + + $command = new AddProductBundleToCartCommand($cart->getId(), $productCode, $quantity); + $command->setProductBundleItems($addItemToCartCommands); - return new AddProductBundleToCartCommand($cart->getId(), $productCode, $quantity); + return $command; } public function supportsTransformation( diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 569fa83a..16abc3a3 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -19,6 +19,7 @@ use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItemInterface; use BitBag\SyliusProductBundlePlugin\Form\Type\ProductBundleItemType; use BitBag\SyliusProductBundlePlugin\Form\Type\ProductBundleType; +use BitBag\SyliusProductBundlePlugin\Repository\ProductBundleItemRepository; use BitBag\SyliusProductBundlePlugin\Repository\ProductBundleRepository; use Sylius\Bundle\ResourceBundle\Controller\ResourceController; use Sylius\Bundle\ResourceBundle\SyliusResourceBundle; @@ -80,7 +81,7 @@ private function addResourcesSection(ArrayNodeDefinition $node): void ->scalarNode('interface')->defaultValue(ProductBundleItemInterface::class)->cannotBeEmpty()->end() ->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end() ->scalarNode('factory')->defaultValue(Factory::class)->cannotBeEmpty()->end() - ->scalarNode('repository')->cannotBeEmpty()->end() + ->scalarNode('repository')->defaultValue(ProductBundleItemRepository::class)->cannotBeEmpty()->end() ->scalarNode('form')->defaultValue(ProductBundleItemType::class)->cannotBeEmpty()->end() ->end() ->end() diff --git a/src/Dto/Api/AddProductBundleToCartDto.php b/src/Dto/Api/AddProductBundleToCartDto.php index 010434d9..20ab2856 100644 --- a/src/Dto/Api/AddProductBundleToCartDto.php +++ b/src/Dto/Api/AddProductBundleToCartDto.php @@ -19,6 +19,7 @@ public function __construct( private string $productCode, private int $quantity = 1, private ?string $orderTokenValue = null, + private array $overwrittenVariants = [], ) { } @@ -41,4 +42,9 @@ public function getQuantity(): int { return $this->quantity; } + + public function getOverwrittenVariants(): array + { + return $this->overwrittenVariants; + } } diff --git a/src/Factory/AddProductBundleToCartCommandFactory.php b/src/Factory/AddProductBundleToCartCommandFactory.php index 177082f0..dd66796b 100644 --- a/src/Factory/AddProductBundleToCartCommandFactory.php +++ b/src/Factory/AddProductBundleToCartCommandFactory.php @@ -18,7 +18,7 @@ final class AddProductBundleToCartCommandFactory implements AddProductBundleToCartCommandFactoryInterface { - /** @param Collection */ + /** @param Collection $productBundleItems */ public function createNew( int $orderId, string $productCode, diff --git a/src/Factory/AddProductBundleToCartCommandFactoryInterface.php b/src/Factory/AddProductBundleToCartCommandFactoryInterface.php index 383de508..24cba89a 100644 --- a/src/Factory/AddProductBundleToCartCommandFactoryInterface.php +++ b/src/Factory/AddProductBundleToCartCommandFactoryInterface.php @@ -11,14 +11,14 @@ namespace BitBag\SyliusProductBundlePlugin\Factory; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleToCartCommand; use BitBag\SyliusProductBundlePlugin\Dto\AddProductBundleToCartDtoInterface; -use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItemInterface; use Doctrine\Common\Collections\Collection; interface AddProductBundleToCartCommandFactoryInterface { - /** @param Collection $productBundleItems */ + /** @param Collection $productBundleItems */ public function createNew( int $orderId, string $productCode, diff --git a/src/Handler/AddProductBundleToCartHandler.php b/src/Handler/AddProductBundleToCartHandler.php index 004965e1..fc88282e 100644 --- a/src/Handler/AddProductBundleToCartHandler.php +++ b/src/Handler/AddProductBundleToCartHandler.php @@ -23,9 +23,9 @@ final class AddProductBundleToCartHandler implements MessageHandlerInterface { public function __construct( - private OrderRepositoryInterface $orderRepository, - private ProductRepositoryInterface $productRepository, - private CartProcessorInterface $cartProcessor, + private readonly OrderRepositoryInterface $orderRepository, + private readonly ProductRepositoryInterface $productRepository, + private readonly CartProcessorInterface $cartProcessor, ) { } diff --git a/src/Provider/AddProductBundleItemToCartCommandProvider.php b/src/Provider/AddProductBundleItemToCartCommandProvider.php new file mode 100644 index 00000000..27fa1542 --- /dev/null +++ b/src/Provider/AddProductBundleItemToCartCommandProvider.php @@ -0,0 +1,87 @@ + + * + * @throws \Exception + */ + public function provide(string $bundleCode, array $overwrittenVariants): Collection + { + $bundle = $this->productBundleRepository->findOneByProductCode($bundleCode); + if (null === $bundle) { + throw new \Exception('Product bundle not found'); + } + + $bundleItems = $this->productBundleItemRepository->findByBundleCode($bundleCode); + + $commands = []; + foreach ($bundleItems as $bundleItem) { + $command = $this->addProductBundleItemToCartCommandFactory->createNew($bundleItem); + if (!$bundle->isPackedProduct() && [] !== $overwrittenVariants) { + $this->overwriteVariant($command, $bundleItem, $overwrittenVariants); + } + $commands[] = $command; + } + + return new ArrayCollection($commands); + } + + private function overwriteVariant( + AddProductBundleItemToCartCommand $command, + ProductBundleItemInterface $bundleItem, + array $overwrittenVariants, + ): void { + foreach ($overwrittenVariants as $overwrittenVariant) { + if (null !== $overwrittenVariant['from'] && null !== $overwrittenVariant['to'] && + $bundleItem->getProductVariant()?->getCode() === $overwrittenVariant['from'] && + $this->shouldOverwriteVariant($overwrittenVariant['from'], $overwrittenVariant['to']) + ) { + /** @var ProductVariantInterface $newVariant */ + $newVariant = $this->productVariantRepository->findOneBy(['code' => $overwrittenVariant['to']]); + $command->setProductVariant($newVariant); + } + } + } + + private function shouldOverwriteVariant(string $oldVariantCode, string $newVariantCode): bool + { + $oldVariant = $this->productVariantRepository->findOneBy(['code' => $oldVariantCode]); + $newVariant = $this->productVariantRepository->findOneBy(['code' => $newVariantCode]); + + return + $oldVariant instanceof ProductVariantInterface && + $newVariant instanceof ProductVariantInterface && + $oldVariant->getProduct() === $newVariant->getProduct(); + } +} diff --git a/src/Provider/AddProductBundleItemToCartCommandProviderInterface.php b/src/Provider/AddProductBundleItemToCartCommandProviderInterface.php new file mode 100644 index 00000000..1949b599 --- /dev/null +++ b/src/Provider/AddProductBundleItemToCartCommandProviderInterface.php @@ -0,0 +1,21 @@ + */ + public function provide(string $bundleCode, array $overwrittenVariants): Collection; +} diff --git a/src/Repository/ProductBundleItemRepository.php b/src/Repository/ProductBundleItemRepository.php new file mode 100644 index 00000000..29995866 --- /dev/null +++ b/src/Repository/ProductBundleItemRepository.php @@ -0,0 +1,31 @@ +createQueryBuilder('pbi') + ->leftJoin('pbi.productBundle', 'pb') + ->leftJoin('pb.product', 'p') + ->where('p.code = :code') + ->setParameter('code', $bundleCode) + ->getQuery() + ->getResult(); + } +} diff --git a/src/Repository/ProductBundleItemRepositoryInterface.php b/src/Repository/ProductBundleItemRepositoryInterface.php new file mode 100644 index 00000000..e431613f --- /dev/null +++ b/src/Repository/ProductBundleItemRepositoryInterface.php @@ -0,0 +1,21 @@ +getQuery() ->getResult(); } + + public function findOneByProductCode(string $productCode): ?ProductBundleInterface + { + return $this->createQueryBuilder('pb') + ->select('pb') + ->leftJoin('pb.product', 'p') + ->andWhere('p.code = :productCode') + ->setParameter('productCode', $productCode) + ->getQuery() + ->getSingleResult(); + } } diff --git a/src/Repository/ProductBundleRepositoryInterface.php b/src/Repository/ProductBundleRepositoryInterface.php index b408b3f8..6ee3fde9 100644 --- a/src/Repository/ProductBundleRepositoryInterface.php +++ b/src/Repository/ProductBundleRepositoryInterface.php @@ -11,6 +11,7 @@ namespace BitBag\SyliusProductBundlePlugin\Repository; +use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleInterface; use Doctrine\Common\Collections\Collection; use Sylius\Component\Product\Model\ProductVariantInterface; use Sylius\Component\Resource\Repository\RepositoryInterface; @@ -21,4 +22,6 @@ public function getProductIds(): array; /** @param Collection $variants */ public function findBundlesByVariants(Collection $variants): array; + + public function findOneByProductCode(string $productCode): ?ProductBundleInterface; } diff --git a/src/Resources/config/serialization/AddProductBundleToCartDto.xml b/src/Resources/config/serialization/AddProductBundleToCartDto.xml index 7d6fe771..548434c4 100644 --- a/src/Resources/config/serialization/AddProductBundleToCartDto.xml +++ b/src/Resources/config/serialization/AddProductBundleToCartDto.xml @@ -17,5 +17,8 @@ shop:cart:add_product_bundle + + shop:cart:add_product_bundle + diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index a968c028..064ba3c6 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -11,6 +11,7 @@ + diff --git a/src/Resources/config/services/provider.xml b/src/Resources/config/services/provider.xml new file mode 100644 index 00000000..2d70958a --- /dev/null +++ b/src/Resources/config/services/provider.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/src/Resources/config/services/transformer.xml b/src/Resources/config/services/transformer.xml index 4f212350..ccac7ace 100644 --- a/src/Resources/config/services/transformer.xml +++ b/src/Resources/config/services/transformer.xml @@ -7,6 +7,7 @@ id="bitbag_sylius_product_bundle.data_transformer.add_product_bundle_to_cart_dto" class="BitBag\SyliusProductBundlePlugin\DataTransformer\AddProductBundleToCartDtoDataTransformer" > + From 8d279a70a3894cc22f8a0d7231cc4fd067933e02 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Wed, 11 Sep 2024 16:04:54 +0200 Subject: [PATCH 33/40] OP-289: Adjust unit tests to handle unpacked bundles logic --- .../AddProductBundleItemToCartCommand.php | 2 +- ...roductBundleItemToCartCommandInterface.php | 26 ++++++++++ src/Command/AddProductBundleToCartCommand.php | 6 +-- ...dProductBundleItemToCartCommandFactory.php | 3 +- ...undleItemToCartCommandFactoryInterface.php | 4 +- .../AddProductBundleToCartCommandFactory.php | 4 +- ...uctBundleToCartCommandFactoryInterface.php | 4 +- .../AddProductBundleToCartDtoFactory.php | 4 +- src/Factory/ProductBundleOrderItemFactory.php | 7 +-- ...ProductBundleOrderItemFactoryInterface.php | 6 ++- .../Type/AddProductBundleItemToCartType.php | 3 +- .../CartProcessor.php | 4 +- .../CartProcessorInterface.php | 2 +- ...ProductBundleItemToCartCommandProvider.php | 6 +-- ...ndleItemToCartCommandProviderInterface.php | 4 +- ...ductBundleToCartDtoDataTransformerTest.php | 32 +++++++++++-- ...ductBundleItemToCartCommandFactoryTest.php | 4 +- ...dProductBundleToCartCommandFactoryTest.php | 8 +++- .../CartProcessorTest.php | 48 ++++++++----------- .../AddProductBundleToCartHandlerTest.php | 20 +++++--- ...ddProductBundleItemToCartCommandMother.php | 3 +- 21 files changed, 131 insertions(+), 69 deletions(-) create mode 100644 src/Command/AddProductBundleItemToCartCommandInterface.php diff --git a/src/Command/AddProductBundleItemToCartCommand.php b/src/Command/AddProductBundleItemToCartCommand.php index 369ab358..79d1c60f 100644 --- a/src/Command/AddProductBundleItemToCartCommand.php +++ b/src/Command/AddProductBundleItemToCartCommand.php @@ -14,7 +14,7 @@ use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface; use Sylius\Component\Core\Model\ProductVariantInterface; -final class AddProductBundleItemToCartCommand +final class AddProductBundleItemToCartCommand implements AddProductBundleItemToCartCommandInterface { public function __construct( private ProductBundleItemInterface $productBundleItem, diff --git a/src/Command/AddProductBundleItemToCartCommandInterface.php b/src/Command/AddProductBundleItemToCartCommandInterface.php new file mode 100644 index 00000000..9862a3f3 --- /dev/null +++ b/src/Command/AddProductBundleItemToCartCommandInterface.php @@ -0,0 +1,26 @@ + */ + /** @var Collection */ private Collection $productBundleItems; public function __construct( @@ -42,13 +42,13 @@ public function getQuantity(): int return $this->quantity; } - /** @return Collection */ + /** @return Collection */ public function getProductBundleItems(): Collection { return $this->productBundleItems; } - /** @param Collection $productBundleItems */ + /** @param Collection $productBundleItems */ public function setProductBundleItems(Collection $productBundleItems): void { $this->productBundleItems = $productBundleItems; diff --git a/src/Factory/AddProductBundleItemToCartCommandFactory.php b/src/Factory/AddProductBundleItemToCartCommandFactory.php index 7c2f8bd1..03682f53 100644 --- a/src/Factory/AddProductBundleItemToCartCommandFactory.php +++ b/src/Factory/AddProductBundleItemToCartCommandFactory.php @@ -12,11 +12,12 @@ namespace BitBag\SyliusProductBundlePlugin\Factory; use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface; final class AddProductBundleItemToCartCommandFactory implements AddProductBundleItemToCartCommandFactoryInterface { - public function createNew(ProductBundleItemInterface $bundleItem): AddProductBundleItemToCartCommand + public function createNew(ProductBundleItemInterface $bundleItem): AddProductBundleItemToCartCommandInterface { return new AddProductBundleItemToCartCommand($bundleItem); } diff --git a/src/Factory/AddProductBundleItemToCartCommandFactoryInterface.php b/src/Factory/AddProductBundleItemToCartCommandFactoryInterface.php index cc881b78..b36dac4a 100644 --- a/src/Factory/AddProductBundleItemToCartCommandFactoryInterface.php +++ b/src/Factory/AddProductBundleItemToCartCommandFactoryInterface.php @@ -11,10 +11,10 @@ namespace BitBag\SyliusProductBundlePlugin\Factory; -use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface; interface AddProductBundleItemToCartCommandFactoryInterface { - public function createNew(ProductBundleItemInterface $bundleItem): AddProductBundleItemToCartCommand; + public function createNew(ProductBundleItemInterface $bundleItem): AddProductBundleItemToCartCommandInterface; } diff --git a/src/Factory/AddProductBundleToCartCommandFactory.php b/src/Factory/AddProductBundleToCartCommandFactory.php index dd66796b..e0df023b 100644 --- a/src/Factory/AddProductBundleToCartCommandFactory.php +++ b/src/Factory/AddProductBundleToCartCommandFactory.php @@ -11,14 +11,14 @@ namespace BitBag\SyliusProductBundlePlugin\Factory; -use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleToCartCommand; use BitBag\SyliusProductBundlePlugin\Dto\AddProductBundleToCartDtoInterface; use Doctrine\Common\Collections\Collection; final class AddProductBundleToCartCommandFactory implements AddProductBundleToCartCommandFactoryInterface { - /** @param Collection $productBundleItems */ + /** @param Collection $productBundleItems */ public function createNew( int $orderId, string $productCode, diff --git a/src/Factory/AddProductBundleToCartCommandFactoryInterface.php b/src/Factory/AddProductBundleToCartCommandFactoryInterface.php index 24cba89a..01150104 100644 --- a/src/Factory/AddProductBundleToCartCommandFactoryInterface.php +++ b/src/Factory/AddProductBundleToCartCommandFactoryInterface.php @@ -11,14 +11,14 @@ namespace BitBag\SyliusProductBundlePlugin\Factory; -use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleToCartCommand; use BitBag\SyliusProductBundlePlugin\Dto\AddProductBundleToCartDtoInterface; use Doctrine\Common\Collections\Collection; interface AddProductBundleToCartCommandFactoryInterface { - /** @param Collection $productBundleItems */ + /** @param Collection $productBundleItems */ public function createNew( int $orderId, string $productCode, diff --git a/src/Factory/AddProductBundleToCartDtoFactory.php b/src/Factory/AddProductBundleToCartDtoFactory.php index b98af5d6..b30cd4c0 100644 --- a/src/Factory/AddProductBundleToCartDtoFactory.php +++ b/src/Factory/AddProductBundleToCartDtoFactory.php @@ -11,7 +11,7 @@ namespace BitBag\SyliusProductBundlePlugin\Factory; -use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Dto\AddProductBundleToCartDto; use BitBag\SyliusProductBundlePlugin\Dto\AddProductBundleToCartDtoInterface; use BitBag\SyliusProductBundlePlugin\Entity\OrderItemInterface; @@ -39,7 +39,7 @@ public function createNew( } /** - * @return AddProductBundleItemToCartCommand[] + * @return AddProductBundleItemToCartCommandInterface[] */ private function getProcessedProductBundleItems(ProductBundleInterface $productBundle): array { diff --git a/src/Factory/ProductBundleOrderItemFactory.php b/src/Factory/ProductBundleOrderItemFactory.php index 5d00e8c5..808220e0 100644 --- a/src/Factory/ProductBundleOrderItemFactory.php +++ b/src/Factory/ProductBundleOrderItemFactory.php @@ -11,7 +11,7 @@ namespace BitBag\SyliusProductBundlePlugin\Factory; -use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItemInterface; use Sylius\Component\Resource\Factory\FactoryInterface; @@ -43,8 +43,9 @@ public function createFromProductBundleItem(ProductBundleItemInterface $bundleIt return $productBundleOrderItem; } - public function createFromAddProductBundleItemToCartCommand(AddProductBundleItemToCartCommand $addItemToCartCommand): ProductBundleOrderItemInterface - { + public function createFromAddProductBundleItemToCartCommand( + AddProductBundleItemToCartCommandInterface $addItemToCartCommand, + ): ProductBundleOrderItemInterface { /** @var ProductBundleOrderItemInterface $productBundleOrderItem */ $productBundleOrderItem = $this->decoratedFactory->createNew(); diff --git a/src/Factory/ProductBundleOrderItemFactoryInterface.php b/src/Factory/ProductBundleOrderItemFactoryInterface.php index 44b7d9f5..3c32cb63 100644 --- a/src/Factory/ProductBundleOrderItemFactoryInterface.php +++ b/src/Factory/ProductBundleOrderItemFactoryInterface.php @@ -11,7 +11,7 @@ namespace BitBag\SyliusProductBundlePlugin\Factory; -use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItemInterface; use Sylius\Component\Resource\Factory\FactoryInterface; @@ -20,5 +20,7 @@ interface ProductBundleOrderItemFactoryInterface extends FactoryInterface { public function createFromProductBundleItem(ProductBundleItemInterface $bundleItem): ProductBundleOrderItemInterface; - public function createFromAddProductBundleItemToCartCommand(AddProductBundleItemToCartCommand $addItemToCartCommand): ProductBundleOrderItemInterface; + public function createFromAddProductBundleItemToCartCommand( + AddProductBundleItemToCartCommandInterface $addItemToCartCommand, + ): ProductBundleOrderItemInterface; } diff --git a/src/Form/Type/AddProductBundleItemToCartType.php b/src/Form/Type/AddProductBundleItemToCartType.php index f4e0f926..099da8cf 100644 --- a/src/Form/Type/AddProductBundleItemToCartType.php +++ b/src/Form/Type/AddProductBundleItemToCartType.php @@ -12,6 +12,7 @@ namespace BitBag\SyliusProductBundlePlugin\Form\Type; use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductInterface; use Sylius\Bundle\ProductBundle\Form\Type\ProductVariantChoiceType; use Sylius\Bundle\ProductBundle\Form\Type\ProductVariantMatchType; @@ -35,7 +36,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void } $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event): void { - /** @var AddProductBundleItemToCartCommand $data */ + /** @var AddProductBundleItemToCartCommandInterface $data */ $data = $event->getData(); $form = $event->getForm(); diff --git a/src/Handler/AddProductBundleToCartHandler/CartProcessor.php b/src/Handler/AddProductBundleToCartHandler/CartProcessor.php index 5260b63d..faf794d4 100644 --- a/src/Handler/AddProductBundleToCartHandler/CartProcessor.php +++ b/src/Handler/AddProductBundleToCartHandler/CartProcessor.php @@ -35,7 +35,7 @@ public function process( OrderInterface $cart, ProductBundleInterface $productBundle, int $quantity, - Collection $productBundleOrderItems, + Collection $addBundleItemToCartCommands, ): void { Assert::greaterThan($quantity, 0); @@ -49,7 +49,7 @@ public function process( $cartItem = $this->cartItemFactory->createWithVariant($productVariant); $this->orderItemQuantityModifier->modify($cartItem, $quantity); - foreach ($productBundleOrderItems as $addBundleItemToCartCommand) { + foreach ($addBundleItemToCartCommands as $addBundleItemToCartCommand) { $productBundleOrderItem = $this->productBundleOrderItemFactory->createFromAddProductBundleItemToCartCommand($addBundleItemToCartCommand); $cartItem->addProductBundleOrderItem($productBundleOrderItem); } diff --git a/src/Handler/AddProductBundleToCartHandler/CartProcessorInterface.php b/src/Handler/AddProductBundleToCartHandler/CartProcessorInterface.php index 54e2a43e..aacf225b 100644 --- a/src/Handler/AddProductBundleToCartHandler/CartProcessorInterface.php +++ b/src/Handler/AddProductBundleToCartHandler/CartProcessorInterface.php @@ -21,6 +21,6 @@ public function process( OrderInterface $cart, ProductBundleInterface $productBundle, int $quantity, - Collection $productBundleOrderItems, + Collection $addBundleItemToCartCommands, ): void; } diff --git a/src/Provider/AddProductBundleItemToCartCommandProvider.php b/src/Provider/AddProductBundleItemToCartCommandProvider.php index 27fa1542..11a0ebd2 100644 --- a/src/Provider/AddProductBundleItemToCartCommandProvider.php +++ b/src/Provider/AddProductBundleItemToCartCommandProvider.php @@ -11,7 +11,7 @@ namespace BitBag\SyliusProductBundlePlugin\Provider; -use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface; use BitBag\SyliusProductBundlePlugin\Factory\AddProductBundleItemToCartCommandFactoryInterface; use BitBag\SyliusProductBundlePlugin\Repository\ProductBundleItemRepositoryInterface; @@ -32,7 +32,7 @@ public function __construct( } /** - * @return Collection + * @return Collection * * @throws \Exception */ @@ -58,7 +58,7 @@ public function provide(string $bundleCode, array $overwrittenVariants): Collect } private function overwriteVariant( - AddProductBundleItemToCartCommand $command, + AddProductBundleItemToCartCommandInterface $command, ProductBundleItemInterface $bundleItem, array $overwrittenVariants, ): void { diff --git a/src/Provider/AddProductBundleItemToCartCommandProviderInterface.php b/src/Provider/AddProductBundleItemToCartCommandProviderInterface.php index 1949b599..fc355dbf 100644 --- a/src/Provider/AddProductBundleItemToCartCommandProviderInterface.php +++ b/src/Provider/AddProductBundleItemToCartCommandProviderInterface.php @@ -11,11 +11,11 @@ namespace BitBag\SyliusProductBundlePlugin\Provider; -use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use Doctrine\Common\Collections\Collection; interface AddProductBundleItemToCartCommandProviderInterface { - /** @return Collection */ + /** @return Collection */ public function provide(string $bundleCode, array $overwrittenVariants): Collection; } diff --git a/tests/Unit/DataTransformer/AddProductBundleToCartDtoDataTransformerTest.php b/tests/Unit/DataTransformer/AddProductBundleToCartDtoDataTransformerTest.php index f4ec9ad8..4b592b18 100644 --- a/tests/Unit/DataTransformer/AddProductBundleToCartDtoDataTransformerTest.php +++ b/tests/Unit/DataTransformer/AddProductBundleToCartDtoDataTransformerTest.php @@ -17,9 +17,13 @@ namespace Tests\BitBag\SyliusProductBundlePlugin\Unit\DataTransformer; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleToCartCommand; use BitBag\SyliusProductBundlePlugin\DataTransformer\AddProductBundleToCartDtoDataTransformer; use BitBag\SyliusProductBundlePlugin\Dto\Api\AddProductBundleToCartDto; +use BitBag\SyliusProductBundlePlugin\Provider\AddProductBundleItemToCartCommandProviderInterface; +use Doctrine\Common\Collections\ArrayCollection; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Tests\BitBag\SyliusProductBundlePlugin\Unit\MotherObject\Api\AddProductBundleToCartDtoMother; use Tests\BitBag\SyliusProductBundlePlugin\Unit\MotherObject\OrderMother; @@ -28,6 +32,16 @@ final class AddProductBundleToCartDtoDataTransformerTest extends TestCase { + private AddProductBundleItemToCartCommandProviderInterface|MockObject $provider; + + private AddProductBundleItemToCartCommandInterface|MockObject $addProductBundleItemToCartCommand; + + public function setUp(): void + { + $this->provider = $this->createMock(AddProductBundleItemToCartCommandProviderInterface::class); + $this->addProductBundleItemToCartCommand = $this->createMock(AddProductBundleItemToCartCommandInterface::class); + } + public function testThrowErrorIfObjectIsNotInstanceOfAddProductBundleToCartDto(): void { $this->expectException(InvalidArgumentException::class); @@ -36,7 +50,8 @@ public function testThrowErrorIfObjectIsNotInstanceOfAddProductBundleToCartDto() ); $object = new \stdClass(); - $dataTransformer = new AddProductBundleToCartDtoDataTransformer(); + $this->provider->expects(self::never())->method(self::anything()); + $dataTransformer = new AddProductBundleToCartDtoDataTransformer($this->provider); $dataTransformer->transform($object, ''); } @@ -47,7 +62,8 @@ public function testThrowIfObjectToPopulateDoesntExist(): void $this->expectExceptionMessage(TypeExceptionMessage::EXPECTED_VALUE_OTHER_THAN_NULL); $object = AddProductBundleToCartDtoMother::create('PRODUCT_CODE'); - $dataTransformer = new AddProductBundleToCartDtoDataTransformer(); + $this->provider->expects(self::never())->method(self::anything()); + $dataTransformer = new AddProductBundleToCartDtoDataTransformer($this->provider); $dataTransformer->transform($object, ''); } @@ -58,7 +74,16 @@ public function testReturnAddProductBundleToCart(): void $context = [ AddProductBundleToCartDtoDataTransformer::OBJECT_TO_POPULATE => OrderMother::createWithId(3), ]; - $dataTransformer = new AddProductBundleToCartDtoDataTransformer(); + + $addProductBundleItemToCartCommands = new ArrayCollection([$this->addProductBundleItemToCartCommand]); + + $this->provider + ->expects(self::once()) + ->method('provide') + ->with('PRODUCT_CODE', []) + ->willReturn($addProductBundleItemToCartCommands); + + $dataTransformer = new AddProductBundleToCartDtoDataTransformer($this->provider); $addProductBundleToCartCommand = $dataTransformer->transform($object, '', $context); @@ -66,5 +91,6 @@ public function testReturnAddProductBundleToCart(): void self::assertSame('PRODUCT_CODE', $addProductBundleToCartCommand->getProductCode()); self::assertSame(2, $addProductBundleToCartCommand->getQuantity()); self::assertSame(3, $addProductBundleToCartCommand->getOrderId()); + self::assertSame($addProductBundleItemToCartCommands, $addProductBundleToCartCommand->getProductBundleItems()); } } diff --git a/tests/Unit/Factory/AddProductBundleItemToCartCommandFactoryTest.php b/tests/Unit/Factory/AddProductBundleItemToCartCommandFactoryTest.php index 2a3f0764..e77b6425 100644 --- a/tests/Unit/Factory/AddProductBundleItemToCartCommandFactoryTest.php +++ b/tests/Unit/Factory/AddProductBundleItemToCartCommandFactoryTest.php @@ -11,7 +11,7 @@ namespace Tests\BitBag\SyliusProductBundlePlugin\Unit\Factory; -use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Factory\AddProductBundleItemToCartCommandFactory; use PHPUnit\Framework\TestCase; use Tests\BitBag\SyliusProductBundlePlugin\Unit\MotherObject\ProductBundleItemMother; @@ -25,6 +25,6 @@ public function testCreateAddProductBundleItemToCartCommand(): void $factory = new AddProductBundleItemToCartCommandFactory(); $command = $factory->createNew($productBundleItem); - self::assertInstanceOf(AddProductBundleItemToCartCommand::class, $command); + self::assertInstanceOf(AddProductBundleItemToCartCommandInterface::class, $command); } } diff --git a/tests/Unit/Factory/AddProductBundleToCartCommandFactoryTest.php b/tests/Unit/Factory/AddProductBundleToCartCommandFactoryTest.php index 4df89fd4..7e4c0bb3 100644 --- a/tests/Unit/Factory/AddProductBundleToCartCommandFactoryTest.php +++ b/tests/Unit/Factory/AddProductBundleToCartCommandFactoryTest.php @@ -11,8 +11,10 @@ namespace Tests\BitBag\SyliusProductBundlePlugin\Unit\Factory; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleToCartCommand; use BitBag\SyliusProductBundlePlugin\Factory\AddProductBundleToCartCommandFactory; +use Doctrine\Common\Collections\ArrayCollection; use PHPUnit\Framework\TestCase; use Tests\BitBag\SyliusProductBundlePlugin\Unit\MotherObject\AddProductBundleToCartDtoMother; @@ -26,13 +28,17 @@ final class AddProductBundleToCartCommandFactoryTest extends TestCase public function testCreateAddProductBundleToCartCommandObject(): void { + $addProductBundleItemToCartCommand = $this->createMock(AddProductBundleItemToCartCommandInterface::class); + $commands = new ArrayCollection([$addProductBundleItemToCartCommand]); + $factory = new AddProductBundleToCartCommandFactory(); - $command = $factory->createNew(self::ORDER_ID, self::PRODUCT_CODE, self::QUANTITY); + $command = $factory->createNew(self::ORDER_ID, self::PRODUCT_CODE, self::QUANTITY, $commands); self::assertInstanceOf(AddProductBundleToCartCommand::class, $command); self::assertEquals(self::ORDER_ID, $command->getOrderId()); self::assertEquals(self::PRODUCT_CODE, $command->getProductCode()); self::assertEquals(self::QUANTITY, $command->getQuantity()); + self::assertEquals($commands, $command->getProductBundleItems()); } public function testCreateAddProductBundleToCartCommandObjectFromDto(): void diff --git a/tests/Unit/Handler/AddProductBundleToCartHandler/CartProcessorTest.php b/tests/Unit/Handler/AddProductBundleToCartHandler/CartProcessorTest.php index 3a492db4..64bea086 100644 --- a/tests/Unit/Handler/AddProductBundleToCartHandler/CartProcessorTest.php +++ b/tests/Unit/Handler/AddProductBundleToCartHandler/CartProcessorTest.php @@ -11,11 +11,10 @@ namespace Tests\BitBag\SyliusProductBundlePlugin\Unit\Handler\AddProductBundleToCartHandler; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Entity\OrderItemInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundle; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleInterface; -use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItem; -use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItem; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleOrderItemInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductInterface; @@ -23,6 +22,7 @@ use BitBag\SyliusProductBundlePlugin\Factory\ProductBundleOrderItemFactoryInterface; use BitBag\SyliusProductBundlePlugin\Handler\AddProductBundleToCartHandler\CartProcessor; use BitBag\SyliusProductBundlePlugin\Handler\AddProductBundleToCartHandler\CartProcessorInterface; +use Doctrine\Common\Collections\ArrayCollection; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Sylius\Component\Core\Model\ProductVariant; @@ -37,17 +37,15 @@ final class CartProcessorTest extends TestCase { - /** @var mixed|MockObject|OrderItemQuantityModifierInterface */ - private $orderItemQuantityModifier; + private OrderItemQuantityModifierInterface|MockObject $orderItemQuantityModifier; - /** @var ProductBundleOrderItemFactoryInterface|mixed|MockObject */ - private $productBundleOrderItemFactory; + private ProductBundleOrderItemFactoryInterface|MockObject $productBundleOrderItemFactory; - /** @var mixed|MockObject|OrderModifierInterface */ - private $orderModifier; + private OrderModifierInterface|MockObject $orderModifier; - /** @var OrderItemFactoryInterface|mixed|MockObject */ - private $cartItemFactory; + private OrderItemFactoryInterface|MockObject $cartItemFactory; + + private AddProductBundleItemToCartCommandInterface|MockObject $addProductBundleItemToCartCommand; protected function setUp(): void { @@ -55,6 +53,7 @@ protected function setUp(): void $this->productBundleOrderItemFactory = $this->createMock(ProductBundleOrderItemFactoryInterface::class); $this->orderModifier = $this->createMock(OrderModifierInterface::class); $this->cartItemFactory = $this->createMock(OrderItemFactoryInterface::class); + $this->addProductBundleItemToCartCommand = $this->createMock(AddProductBundleItemToCartCommandInterface::class); } public function testThrowExceptionIfQuantityNotGreaterThanZero(): void @@ -65,7 +64,7 @@ public function testThrowExceptionIfQuantityNotGreaterThanZero(): void $productBundle = $this->createProductBundle(); $processor = $this->createProcessor(); - $processor->process($cart, $productBundle, 0); + $processor->process($cart, $productBundle, 0, new ArrayCollection()); } public function testThrowExceptionIfProductIsNull(): void @@ -76,7 +75,7 @@ public function testThrowExceptionIfProductIsNull(): void $productBundle = $this->createProductBundle(); $processor = $this->createProcessor(); - $processor->process($cart, $productBundle, 1); + $processor->process($cart, $productBundle, 1, new ArrayCollection()); } public function testThrowExceptionIfProductHasNoVariant(): void @@ -87,7 +86,7 @@ public function testThrowExceptionIfProductHasNoVariant(): void $productBundle = $this->createProductBundleWithProduct(); $processor = $this->createProcessor(); - $processor->process($cart, $productBundle, 1); + $processor->process($cart, $productBundle, 1, new ArrayCollection()); } public function testCreateCartItem(): void @@ -103,7 +102,7 @@ public function testCreateCartItem(): void ; $processor = $this->createProcessor(); - $processor->process($cart, $productBundle, 2); + $processor->process($cart, $productBundle, 2, new ArrayCollection([$this->addProductBundleItemToCartCommand])); } public function testModifyCartItemQuantity(): void @@ -124,13 +123,13 @@ public function testModifyCartItemQuantity(): void ; $processor = $this->createProcessor(); - $processor->process($cart, $productBundle, 2); + $processor->process($cart, $productBundle, 2, new ArrayCollection([$this->addProductBundleItemToCartCommand])); } public function testCreateBundleOrderItemsFromBundleItems(): void { - $bundleItem1 = $this->createProductBundleItem(); - $bundleItem2 = $this->createProductBundleItem(); + $addBundleItemToCartCommand1 = $this->createMock(AddProductBundleItemToCartCommandInterface::class); + $addBundleItemToCartCommand2 = $this->createMock(AddProductBundleItemToCartCommandInterface::class); $productBundleOrderItem1 = $this->createProductBundleOrderItem(); $productBundleOrderItem2 = $this->createProductBundleOrderItem(); @@ -138,8 +137,6 @@ public function testCreateBundleOrderItemsFromBundleItems(): void $cart = $this->createCart(); $product = $this->createProductWithVariant(); $productBundle = $this->createProductBundleWithProduct($product); - $productBundle->addProductBundleItem($bundleItem1); - $productBundle->addProductBundleItem($bundleItem2); $cartItem = $this->createMock(OrderItemInterface::class); $cartItem->expects(self::exactly(2)) @@ -152,13 +149,13 @@ public function testCreateBundleOrderItemsFromBundleItems(): void ->willReturn($cartItem) ; $this->productBundleOrderItemFactory->expects(self::exactly(2)) - ->method('createFromProductBundleItem') - ->withConsecutive([$bundleItem1], [$bundleItem2]) + ->method('createFromAddProductBundleItemToCartCommand') + ->withConsecutive([$addBundleItemToCartCommand1], [$addBundleItemToCartCommand2]) ->willReturn($productBundleOrderItem1, $productBundleOrderItem2) ; $processor = $this->createProcessor(); - $processor->process($cart, $productBundle, 1); + $processor->process($cart, $productBundle, 1, new ArrayCollection([$addBundleItemToCartCommand1, $addBundleItemToCartCommand2])); } public function testAddCartItemToOrder(): void @@ -178,7 +175,7 @@ public function testAddCartItemToOrder(): void ; $processor = $this->createProcessor(); - $processor->process($cart, $productBundle, 1); + $processor->process($cart, $productBundle, 1, new ArrayCollection([$this->addProductBundleItemToCartCommand])); } private function createProcessor(): CartProcessorInterface @@ -230,11 +227,6 @@ private function createProductBundleWithProduct(?ProductInterface $product = nul return $productBundle; } - private function createProductBundleItem(): ProductBundleItemInterface - { - return new ProductBundleItem(); - } - private function createProductBundleOrderItem(): ProductBundleOrderItemInterface { return new ProductBundleOrderItem(); diff --git a/tests/Unit/Handler/AddProductBundleToCartHandlerTest.php b/tests/Unit/Handler/AddProductBundleToCartHandlerTest.php index 922efa27..4df1d108 100644 --- a/tests/Unit/Handler/AddProductBundleToCartHandlerTest.php +++ b/tests/Unit/Handler/AddProductBundleToCartHandlerTest.php @@ -11,10 +11,12 @@ namespace Tests\BitBag\SyliusProductBundlePlugin\Unit\Handler; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleToCartCommand; use BitBag\SyliusProductBundlePlugin\Entity\ProductInterface; use BitBag\SyliusProductBundlePlugin\Handler\AddProductBundleToCartHandler; use BitBag\SyliusProductBundlePlugin\Handler\AddProductBundleToCartHandler\CartProcessorInterface; +use Doctrine\Common\Collections\ArrayCollection; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Sylius\Component\Core\Model\OrderInterface; @@ -28,20 +30,20 @@ final class AddProductBundleToCartHandlerTest extends TestCase { - /** @var mixed|MockObject|OrderRepositoryInterface */ - private $orderRepository; + private OrderRepositoryInterface|MockObject $orderRepository; - /** @var mixed|MockObject|ProductRepositoryInterface */ - private $productRepository; + private ProductRepositoryInterface|MockObject $productRepository; - /** @var CartProcessorInterface|mixed|MockObject */ - private $cartProcessor; + private CartProcessorInterface|MockObject $cartProcessor; + + private AddProductBundleItemToCartCommandInterface|MockObject $addProductBundleItemToCartCommand; protected function setUp(): void { $this->orderRepository = $this->createMock(OrderRepositoryInterface::class); $this->productRepository = $this->createMock(ProductRepositoryInterface::class); $this->cartProcessor = $this->createMock(CartProcessorInterface::class); + $this->addProductBundleItemToCartCommand = $this->createMock(AddProductBundleItemToCartCommandInterface::class); } public function testThrowExceptionIfCartDoesntExist(): void @@ -122,10 +124,12 @@ public function testProcessCart(): void $this->cartProcessor->expects(self::once()) ->method('process') - ->with($cart, $productBundle, 2) + ->with($cart, $productBundle, 2, new ArrayCollection([$this->addProductBundleItemToCartCommand])) ; $command = new AddProductBundleToCartCommand(1, '', 2); + $command->setProductBundleItems(new ArrayCollection([$this->addProductBundleItemToCartCommand])); + $handler = $this->createHandler(); $handler($command); } @@ -149,6 +153,8 @@ public function testAddCartToRepository(): void ; $command = new AddProductBundleToCartCommand(1, '', 1); + $command->setProductBundleItems(new ArrayCollection([$this->addProductBundleItemToCartCommand])); + $handler = $this->createHandler(); $handler($command); } diff --git a/tests/Unit/MotherObject/AddProductBundleItemToCartCommandMother.php b/tests/Unit/MotherObject/AddProductBundleItemToCartCommandMother.php index 6912c547..0d296c5d 100644 --- a/tests/Unit/MotherObject/AddProductBundleItemToCartCommandMother.php +++ b/tests/Unit/MotherObject/AddProductBundleItemToCartCommandMother.php @@ -12,11 +12,12 @@ namespace Tests\BitBag\SyliusProductBundlePlugin\Unit\MotherObject; use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommand; +use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface; final class AddProductBundleItemToCartCommandMother { - public static function create(ProductBundleItemInterface $bundleItem): AddProductBundleItemToCartCommand + public static function create(ProductBundleItemInterface $bundleItem): AddProductBundleItemToCartCommandInterface { return new AddProductBundleItemToCartCommand($bundleItem); } From 32a5795361d7fd95a4189272501f09ffce8bb293 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Wed, 11 Sep 2024 16:19:56 +0200 Subject: [PATCH 34/40] OP-289: Add running unit tests to build --- .github/workflows/build.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 41ac7541..0d6ee0f5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -57,8 +57,11 @@ jobs: - name: Validate database schema run: (cd tests/Application && bin/console doctrine:schema:validate) - - name: Run PHPUnit - run: vendor/bin/phpunit --colors=always + - name: Run API tests + run: vendor/bin/phpunit tests/Api --colors=always + + - name: Run PHPUnit tests + run: vendor/bin/phpunit tests/Unit --colors=always - name: Run Behat run: vendor/bin/behat --colors --strict -vvv --no-interaction || vendor/bin/behat --colors --strict -vvv --no-interaction --rerun From 0391920d148ff808ee57511d493567a9b8ffa672 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Wed, 11 Sep 2024 17:35:41 +0200 Subject: [PATCH 35/40] OP-289: Add unit tests to command provider --- ...ProductBundleItemToCartCommandProvider.php | 23 ++- src/Resources/config/services/provider.xml | 1 - ...ductBundleToCartDtoDataTransformerTest.php | 6 - ...uctBundleItemToCartCommandProviderTest.php | 189 ++++++++++++++++++ 4 files changed, 202 insertions(+), 17 deletions(-) create mode 100644 tests/Unit/Provider/AddProductBundleItemToCartCommandProviderTest.php diff --git a/src/Provider/AddProductBundleItemToCartCommandProvider.php b/src/Provider/AddProductBundleItemToCartCommandProvider.php index 11a0ebd2..c6632f54 100644 --- a/src/Provider/AddProductBundleItemToCartCommandProvider.php +++ b/src/Provider/AddProductBundleItemToCartCommandProvider.php @@ -14,7 +14,6 @@ use BitBag\SyliusProductBundlePlugin\Command\AddProductBundleItemToCartCommandInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface; use BitBag\SyliusProductBundlePlugin\Factory\AddProductBundleItemToCartCommandFactoryInterface; -use BitBag\SyliusProductBundlePlugin\Repository\ProductBundleItemRepositoryInterface; use BitBag\SyliusProductBundlePlugin\Repository\ProductBundleRepositoryInterface; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; @@ -23,9 +22,12 @@ final class AddProductBundleItemToCartCommandProvider implements AddProductBundleItemToCartCommandProviderInterface { + public const TO = 'to'; + + public const FROM = 'from'; + public function __construct( private readonly AddProductBundleItemToCartCommandFactoryInterface $addProductBundleItemToCartCommandFactory, - private readonly ProductBundleItemRepositoryInterface $productBundleItemRepository, private readonly ProductBundleRepositoryInterface $productBundleRepository, private readonly ProductVariantRepositoryInterface $productVariantRepository, ) { @@ -43,8 +45,7 @@ public function provide(string $bundleCode, array $overwrittenVariants): Collect throw new \Exception('Product bundle not found'); } - $bundleItems = $this->productBundleItemRepository->findByBundleCode($bundleCode); - + $bundleItems = $bundle->getProductBundleItems(); $commands = []; foreach ($bundleItems as $bundleItem) { $command = $this->addProductBundleItemToCartCommandFactory->createNew($bundleItem); @@ -63,12 +64,12 @@ private function overwriteVariant( array $overwrittenVariants, ): void { foreach ($overwrittenVariants as $overwrittenVariant) { - if (null !== $overwrittenVariant['from'] && null !== $overwrittenVariant['to'] && - $bundleItem->getProductVariant()?->getCode() === $overwrittenVariant['from'] && - $this->shouldOverwriteVariant($overwrittenVariant['from'], $overwrittenVariant['to']) + if (null !== $overwrittenVariant[self::FROM] && null !== $overwrittenVariant[self::TO] && + $bundleItem->getProductVariant()?->getCode() === $overwrittenVariant[self::FROM] && + $this->shouldOverwriteVariant($overwrittenVariant[self::FROM], $overwrittenVariant[self::TO]) ) { /** @var ProductVariantInterface $newVariant */ - $newVariant = $this->productVariantRepository->findOneBy(['code' => $overwrittenVariant['to']]); + $newVariant = $this->productVariantRepository->findOneBy(['code' => $overwrittenVariant[self::TO]]); $command->setProductVariant($newVariant); } } @@ -76,12 +77,14 @@ private function overwriteVariant( private function shouldOverwriteVariant(string $oldVariantCode, string $newVariantCode): bool { + /** @var ?ProductVariantInterface $oldVariant */ $oldVariant = $this->productVariantRepository->findOneBy(['code' => $oldVariantCode]); + /** @var ?ProductVariantInterface $oldVariant */ $newVariant = $this->productVariantRepository->findOneBy(['code' => $newVariantCode]); return - $oldVariant instanceof ProductVariantInterface && - $newVariant instanceof ProductVariantInterface && + null !== $oldVariant && + null !== $newVariant && $oldVariant->getProduct() === $newVariant->getProduct(); } } diff --git a/src/Resources/config/services/provider.xml b/src/Resources/config/services/provider.xml index 2d70958a..e04757ca 100644 --- a/src/Resources/config/services/provider.xml +++ b/src/Resources/config/services/provider.xml @@ -8,7 +8,6 @@ class="BitBag\SyliusProductBundlePlugin\Provider\AddProductBundleItemToCartCommandProvider" > - diff --git a/tests/Unit/DataTransformer/AddProductBundleToCartDtoDataTransformerTest.php b/tests/Unit/DataTransformer/AddProductBundleToCartDtoDataTransformerTest.php index 4b592b18..a7fc87ec 100644 --- a/tests/Unit/DataTransformer/AddProductBundleToCartDtoDataTransformerTest.php +++ b/tests/Unit/DataTransformer/AddProductBundleToCartDtoDataTransformerTest.php @@ -7,12 +7,6 @@ * an email on hello@bitbag.io. */ -/* - * This file was created by developers working at BitBag - * Do you need more information about us and what we do? Visit our https://bitbag.io website! - * We are hiring developers from all over the world. Join us and start your new, exciting adventure and become part of us: https://bitbag.io/career - */ - declare(strict_types=1); namespace Tests\BitBag\SyliusProductBundlePlugin\Unit\DataTransformer; diff --git a/tests/Unit/Provider/AddProductBundleItemToCartCommandProviderTest.php b/tests/Unit/Provider/AddProductBundleItemToCartCommandProviderTest.php new file mode 100644 index 00000000..de5b8492 --- /dev/null +++ b/tests/Unit/Provider/AddProductBundleItemToCartCommandProviderTest.php @@ -0,0 +1,189 @@ +addProductBundleItemToCartCommandFactory = $this->createMock(AddProductBundleItemToCartCommandFactoryInterface::class); + $this->productBundleRepository = $this->createMock(ProductBundleRepositoryInterface::class); + $this->productVariantRepository = $this->createMock(ProductVariantRepositoryInterface::class); + + $this->bundleItem1 = $this->createMock(ProductBundleItemInterface::class); + $this->bundleItem2 = $this->createMock(ProductBundleItemInterface::class); + $this->bundle = $this->createMock(ProductBundleInterface::class); + $this->bundle + ->expects(self::any()) + ->method('getProductBundleItems') + ->willReturn(new ArrayCollection([$this->bundleItem1, $this->bundleItem2])); + + $this->provider = new AddProductBundleItemToCartCommandProvider( + $this->addProductBundleItemToCartCommandFactory, + $this->productBundleRepository, + $this->productVariantRepository, + ); + } + + public function testItThrowsExceptionIfBundleIsNotFound(): void + { + self::expectException(\Exception::class); + self::expectExceptionMessage('Product bundle not found'); + + $this->productBundleRepository + ->expects(self::once()) + ->method('findOneByProductCode') + ->with('BUNDLE_CODE') + ->willReturn(null); + + $this->provider->provide('BUNDLE_CODE', []); + } + + public function testItWillNotOverwriteIfBundleIsPacked(): void + { + $this->bundle + ->expects(self::exactly(2)) + ->method('isPackedProduct') + ->willReturn(true); + + $this->productBundleRepository + ->expects(self::once()) + ->method('findOneByProductCode') + ->with('BUNDLE_CODE') + ->willReturn($this->bundle); + + $addProductBundleItemToCartCommand = $this->createMock(AddProductBundleItemToCartCommandInterface::class); + + $this->addProductBundleItemToCartCommandFactory + ->expects(self::exactly(2)) + ->method('createNew') + ->withConsecutive([$this->bundleItem1], [$this->bundleItem2]) + ->willReturn($addProductBundleItemToCartCommand); + + $this->productVariantRepository->expects(self::never())->method(self::anything()); + + $this->provider->provide('BUNDLE_CODE', []); + } + + public function testItWillNotOverwriteIfOverwrittenVariantsIsEmpty(): void + { + $this->bundle + ->expects(self::exactly(2)) + ->method('isPackedProduct') + ->willReturn(false); + + $this->productBundleRepository + ->expects(self::once()) + ->method('findOneByProductCode') + ->with('BUNDLE_CODE') + ->willReturn($this->bundle); + + $addProductBundleItemToCartCommand = $this->createMock(AddProductBundleItemToCartCommandInterface::class); + + $this->addProductBundleItemToCartCommandFactory + ->expects(self::exactly(2)) + ->method('createNew') + ->withConsecutive([$this->bundleItem1], [$this->bundleItem2]) + ->willReturn($addProductBundleItemToCartCommand); + + $this->productVariantRepository->expects(self::never())->method(self::anything()); + + $this->provider->provide('BUNDLE_CODE', []); + } + + public function testItOverwrites(): void + { + $this->bundle + ->expects(self::exactly(2)) + ->method('isPackedProduct') + ->willReturn(false); + + $this->productBundleRepository + ->expects(self::once()) + ->method('findOneByProductCode') + ->with('BUNDLE_CODE') + ->willReturn($this->bundle); + + $product = $this->createMock(ProductInterface::class); + + $oldProductVariant = $this->createMock(ProductVariantInterface::class); + $oldProductVariant + ->expects(self::once()) + ->method('getCode') + ->willReturn('OLD_VARIANT_CODE'); + $oldProductVariant + ->expects(self::once()) + ->method('getProduct') + ->willReturn($product); + + $newProductVariant = $this->createMock(ProductVariantInterface::class); + $newProductVariant + ->expects(self::once()) + ->method('getProduct') + ->willReturn($product); + + $this->bundleItem1 + ->expects(self::once()) + ->method('getProductVariant') + ->willReturn($oldProductVariant); + + $this->productVariantRepository + ->expects(self::exactly(3)) + ->method('findOneBy') + ->willReturnOnConsecutiveCalls($oldProductVariant, $newProductVariant, $newProductVariant); + + $addProductBundleItemToCartCommand = $this->createMock(AddProductBundleItemToCartCommandInterface::class); + + $this->addProductBundleItemToCartCommandFactory + ->expects(self::exactly(2)) + ->method('createNew') + ->withConsecutive([$this->bundleItem1], [$this->bundleItem2]) + ->willReturn($addProductBundleItemToCartCommand); + + $overwrittenVariants = [ + [ + 'from' => 'OLD_VARIANT_CODE', + 'to' => 'NEW_VARIANT_CODE', + ], + ]; + + $this->provider->provide('BUNDLE_CODE', $overwrittenVariants); + } +} From 11b768e880df89c0214e78777a00cd10288a52bd Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Thu, 12 Sep 2024 10:44:54 +0200 Subject: [PATCH 36/40] OP-289: Cover overwriting variants with behat scenarios --- .../having_bundled_product_in_store.feature | 29 +++++++++ .../Context/Api/ProductBundleContext.php | 64 +++++++++++++++++++ .../Context/Setup/ProductBundleContext.php | 12 ++++ tests/Behat/Resources/services.yml | 1 + tests/Behat/Resources/suites.yml | 1 + 5 files changed, 107 insertions(+) diff --git a/features/having_bundled_product_in_store.feature b/features/having_bundled_product_in_store.feature index bc75045f..20ea435a 100644 --- a/features/having_bundled_product_in_store.feature +++ b/features/having_bundled_product_in_store.feature @@ -10,6 +10,7 @@ Feature: Having a product in store which is a bundle of other products And the store ships everywhere for Free And the store allows paying Offline And the store has a product "Jim Beam" priced at "$10.00" + And this product has "Jim Beam 1L" variant priced at "$15.00" identified by "JIM_BEAM_1L" And the store has a product "Jim Beam Double Oak" priced at "$10.00" And the store has a product "Coca-Cola" priced at "$5.00" And the store has bundled product "Jim Beam double pack" priced at "$18.00" which contains "Jim Beam" and "Jim Beam Double Oak" @@ -64,3 +65,31 @@ Feature: Having a product in store which is a bundle of other products Then I should have bundle "Jim Beam&Coke" with quantity 10 in my cart And I should have product "Jim Beam" in bundled items And I should have product "Coca-Cola" in bundled items + + @api + Scenario: Adding unpacked product bundles to cart and overwriting variants with API + When I pick up my cart + And I add bundle "Jim Beam&Coke" with quantity 5 to my cart and overwrite "JIM_BEAM" with "JIM_BEAM_1L" + Then I should have bundle "Jim Beam&Coke" with quantity 5 in my cart + And I should have product variant "Jim Beam 1L" in bundled items + And I should not have product variant "Jim Beam" in bundled items + And I should have product "Coca-Cola" in bundled items + + @api + Scenario: Adding unpacked product bundles to cart and overwriting variants with invalid variant with API + When I pick up my cart + And I add bundle "Jim Beam&Coke" with quantity 5 to my cart and overwrite "COCA_COLA" with "JIM_BEAM_1L" + Then I should have bundle "Jim Beam&Coke" with quantity 5 in my cart + And I should not have product variant "Jim Beam 1L" in bundled items + And I should have product variant "Jim Beam" in bundled items + And I should have product "Coca-Cola" in bundled items + + @api + Scenario: Adding packed product bundles to cart and overwriting varians with API + Given product bundle "JIM_BEAM&COKE" is packed + When I pick up my cart + And I add bundle "Jim Beam&Coke" with quantity 5 to my cart and overwrite "JIM_BEAM" with "JIM_BEAM_1L" + Then I should have bundle "Jim Beam&Coke" with quantity 5 in my cart + And I should not have product variant "Jim Beam 1L" in bundled items + And I should have product variant "Jim Beam" in bundled items + And I should have product "Coca-Cola" in bundled items diff --git a/tests/Behat/Context/Api/ProductBundleContext.php b/tests/Behat/Context/Api/ProductBundleContext.php index fc9182c9..e08ee862 100644 --- a/tests/Behat/Context/Api/ProductBundleContext.php +++ b/tests/Behat/Context/Api/ProductBundleContext.php @@ -13,11 +13,13 @@ use Behat\Behat\Context\Context; use BitBag\SyliusProductBundlePlugin\Entity\ProductInterface; +use BitBag\SyliusProductBundlePlugin\Provider\AddProductBundleItemToCartCommandProvider; use Sylius\Behat\Client\ApiClientInterface; use Sylius\Behat\Client\RequestFactoryInterface; use Sylius\Behat\Client\ResponseCheckerInterface; use Sylius\Behat\Context\Api\Resources; use Sylius\Behat\Service\SharedStorageInterface; +use Sylius\Component\Core\Model\ProductVariantInterface; use Symfony\Component\HttpFoundation\Request as HttpRequest; use Webmozart\Assert\Assert; @@ -52,6 +54,36 @@ public function iAddProductBundleToMyCart(ProductInterface $product, int $quanti $this->client->executeCustomRequest($request); } + /** + * @When I add bundle :product with quantity :quantity to my cart and overwrite :oldVariant with :newVariant + */ + public function iAddProductBundleToMyCartAndOverwriteVariant( + ProductInterface $product, + int $quantity, + string $oldVariant, + string $newVariant, + ): void { + $request = $this->requestFactory->customItemAction( + 'shop', + Resources::ORDERS, + $this->sharedStorage->get('cart_token'), + HttpRequest::METHOD_PATCH, + 'product-bundle', + ); + $request->updateContent([ + 'productCode' => $product->getCode(), + 'quantity' => $quantity, + 'overwrittenVariants' => [ + [ + AddProductBundleItemToCartCommandProvider::FROM => $oldVariant, + AddProductBundleItemToCartCommandProvider::TO => $newVariant, + ], + ], + ]); + + $this->client->executeCustomRequest($request); + } + /** * @When I should have bundle :product with quantity :quantity in my cart */ @@ -80,4 +112,36 @@ public function iShouldHaveProductInBundledItems(ProductInterface $product): voi throw new \InvalidArgumentException('Product not found in bundled items'); } + + /** + * @When I should have product variant :productVariant in bundled items + */ + public function iShouldHaveProductVariantInBundledItems(ProductVariantInterface $productVariant): void + { + $response = $this->client->show(Resources::ORDERS, $this->sharedStorage->get('cart_token')); + + $productBundleOrderItems = $this->responseChecker->getValue($response, 'items')[0]['productBundleOrderItems']; + foreach ($productBundleOrderItems as $item) { + if ($item['productVariant']['code'] === $productVariant->getCode()) { + return; + } + } + + throw new \InvalidArgumentException('Product not found in bundled items'); + } + + /** + * @When I should not have product variant :productVariant in bundled items + */ + public function iShouldNotHaveProductVariantInBundledItems(ProductVariantInterface $productVariant): void + { + $response = $this->client->show(Resources::ORDERS, $this->sharedStorage->get('cart_token')); + + $productBundleOrderItems = $this->responseChecker->getValue($response, 'items')[0]['productBundleOrderItems']; + foreach ($productBundleOrderItems as $item) { + if ($item['productVariant']['code'] === $productVariant->getCode()) { + throw new \InvalidArgumentException(\sprintf('Product variant %s found in bundled items', $productVariant->getName())); + } + } + } } diff --git a/tests/Behat/Context/Setup/ProductBundleContext.php b/tests/Behat/Context/Setup/ProductBundleContext.php index b7c1bb74..6b689bd2 100644 --- a/tests/Behat/Context/Setup/ProductBundleContext.php +++ b/tests/Behat/Context/Setup/ProductBundleContext.php @@ -17,6 +17,7 @@ use BitBag\SyliusProductBundlePlugin\Factory\OrderItemFactoryInterface; use BitBag\SyliusProductBundlePlugin\Factory\ProductBundleOrderItemFactoryInterface; use BitBag\SyliusProductBundlePlugin\Factory\ProductFactory; +use BitBag\SyliusProductBundlePlugin\Repository\ProductBundleRepositoryInterface; use Doctrine\ORM\EntityManagerInterface; use Sylius\Behat\Service\SharedStorageInterface; use Sylius\Component\Core\Formatter\StringInflector; @@ -51,6 +52,7 @@ public function __construct( private readonly ProductBundleOrderItemFactoryInterface $productBundleOrderItemFactory, private readonly OrderModifierInterface $orderModifier, private readonly OrderItemFactoryInterface $cartItemFactory, + private readonly ProductBundleRepositoryInterface $productBundleRepository, ) { } @@ -182,4 +184,14 @@ public function theCustomerBoughtBundle(ProductInterface $product): void $this->objectManager->flush(); } + + /** + * @Given product bundle :productBundleCode is packed + */ + public function productBundleIsPacked(string $productBundleCode): void + { + $bundle = $this->productBundleRepository->findOneByProductCode($productBundleCode); + $bundle->setIsPackedProduct(true); + $this->objectManager->flush(); + } } diff --git a/tests/Behat/Resources/services.yml b/tests/Behat/Resources/services.yml index db9940d3..cf7ea5a4 100644 --- a/tests/Behat/Resources/services.yml +++ b/tests/Behat/Resources/services.yml @@ -19,6 +19,7 @@ services: - '@bitbag_sylius_product_bundle.custom_factory.product_bundle_order_item' - '@sylius.order_modifier' - '@bitbag_sylius_product_bundle.custom_factory.order_item' + - '@bitbag_sylius_product_bundle.repository.product_bundle' bitbag_sylius_product_bundle_plugin.behat.page.create_bundled_product_page: class: Tests\BitBag\SyliusProductBundlePlugin\Behat\Page\Admin\CreateBundledProductPage diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index 1b818d96..719e3a2f 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -93,6 +93,7 @@ default: - sylius.behat.context.transform.locale - sylius.behat.context.transform.payment - sylius.behat.context.transform.product + - sylius.behat.context.transform.product_variant - sylius.behat.context.transform.promotion - sylius.behat.context.transform.shared_storage - sylius.behat.context.transform.shipping_method From eec3b65c2b32edb37b72dbc889e0b8cfd0f3e1de Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Thu, 12 Sep 2024 17:03:04 +0200 Subject: [PATCH 37/40] OP-289: Add new inventory management logic --- ...sInventoryManagementFeatureFlagChecker.php | 25 ++ .../Checker/FeatureFlagCheckerInterface.php | 17 + .../Checker/OrderItemAvailabilityChecker.php | 41 ++ ...ductBundleOrderItemAvailabilityChecker.php | 41 ++ ...eOrderItemAvailabilityCheckerInterface.php | 20 + .../Operator/OrderInventoryOperator.php | 124 ++++++ .../ProductBundleOrderInventoryOperator.php | 216 ++++++++++ ...tBundleOrderInventoryOperatorInterface.php | 26 ++ src/Resources/config/services.xml | 2 + src/Resources/config/services/checker.xml | 25 ++ src/Resources/config/services/operator.xml | 20 + tests/Application/.env | 4 + ...entoryManagementFeatureFlagCheckerTest.php | 30 ++ .../OrderItemAvailabilityCheckerTest.php | 117 ++++++ ...BundleOrderItemAvailabilityCheckerTest.php | 133 +++++++ .../Operator/OrderInventoryOperatorTest.php | 175 +++++++++ ...roductBundleOrderInventoryOperatorTest.php | 368 ++++++++++++++++++ 17 files changed, 1384 insertions(+) create mode 100644 src/Inventory/Checker/BundledProductsInventoryManagementFeatureFlagChecker.php create mode 100644 src/Inventory/Checker/FeatureFlagCheckerInterface.php create mode 100644 src/Inventory/Checker/OrderItemAvailabilityChecker.php create mode 100644 src/Inventory/Checker/ProductBundleOrderItemAvailabilityChecker.php create mode 100644 src/Inventory/Checker/ProductBundleOrderItemAvailabilityCheckerInterface.php create mode 100644 src/Inventory/Operator/OrderInventoryOperator.php create mode 100644 src/Inventory/Operator/ProductBundleOrderInventoryOperator.php create mode 100644 src/Inventory/Operator/ProductBundleOrderInventoryOperatorInterface.php create mode 100644 src/Resources/config/services/checker.xml create mode 100644 src/Resources/config/services/operator.xml create mode 100644 tests/Unit/Inventory/Checker/BundledProductsInventoryManagementFeatureFlagCheckerTest.php create mode 100644 tests/Unit/Inventory/Checker/OrderItemAvailabilityCheckerTest.php create mode 100644 tests/Unit/Inventory/Checker/ProductBundleOrderItemAvailabilityCheckerTest.php create mode 100644 tests/Unit/Inventory/Operator/OrderInventoryOperatorTest.php create mode 100644 tests/Unit/Inventory/Operator/ProductBundleOrderInventoryOperatorTest.php diff --git a/src/Inventory/Checker/BundledProductsInventoryManagementFeatureFlagChecker.php b/src/Inventory/Checker/BundledProductsInventoryManagementFeatureFlagChecker.php new file mode 100644 index 00000000..d962d614 --- /dev/null +++ b/src/Inventory/Checker/BundledProductsInventoryManagementFeatureFlagChecker.php @@ -0,0 +1,25 @@ +enabled; + } +} diff --git a/src/Inventory/Checker/FeatureFlagCheckerInterface.php b/src/Inventory/Checker/FeatureFlagCheckerInterface.php new file mode 100644 index 00000000..58a2f6be --- /dev/null +++ b/src/Inventory/Checker/FeatureFlagCheckerInterface.php @@ -0,0 +1,17 @@ +featureFlagChecker->isEnabled()) { + return $this->decorated->isReservedStockSufficient($orderItem); + } + + /** @var ProductInterface $product */ + $product = $orderItem->getProduct(); + if (!$product->isBundle()) { + return $this->decorated->isReservedStockSufficient($orderItem); + } + + return $this->bundleOrderItemAvailabilityChecker->areOrderedBundledProductVariantsAvailable($orderItem); + } +} diff --git a/src/Inventory/Checker/ProductBundleOrderItemAvailabilityChecker.php b/src/Inventory/Checker/ProductBundleOrderItemAvailabilityChecker.php new file mode 100644 index 00000000..ccea2370 --- /dev/null +++ b/src/Inventory/Checker/ProductBundleOrderItemAvailabilityChecker.php @@ -0,0 +1,41 @@ +getProductBundleOrderItems() as $bundleOrderItem) { + $quantity = $orderItem->getQuantity() * (int) $bundleOrderItem->getQuantity(); + /** @var ProductVariantInterface $variant */ + $variant = $bundleOrderItem->getProductVariant(); + if (!$variant->isTracked()) { + continue; + } + + if (0 > (int) $variant->getOnHold() - $quantity || 0 > (int) $variant->getOnHand() - $quantity) { + return false; + } + } + + return true; + } +} diff --git a/src/Inventory/Checker/ProductBundleOrderItemAvailabilityCheckerInterface.php b/src/Inventory/Checker/ProductBundleOrderItemAvailabilityCheckerInterface.php new file mode 100644 index 00000000..48ad4c1e --- /dev/null +++ b/src/Inventory/Checker/ProductBundleOrderItemAvailabilityCheckerInterface.php @@ -0,0 +1,20 @@ +lockOrderProductVariants($order); + + if (!$this->featureFlagChecker->isEnabled()) { + $this->decorated->cancel($order); + } + + if (in_array( + $order->getPaymentState(), + [OrderPaymentStates::STATE_PAID, OrderPaymentStates::STATE_REFUNDED], + true, + )) { + $this->productBundleOrderInventoryOperator->giveBack($order); + + return; + } + + $this->productBundleOrderInventoryOperator->release($order); + } + + public function hold(OrderInterface $order): void + { + $this->lockOrderProductVariants($order); + + if (!$this->featureFlagChecker->isEnabled()) { + $this->decorated->hold($order); + + return; + } + + $this->productBundleOrderInventoryOperator->hold($order); + } + + public function sell(OrderInterface $order): void + { + $this->lockOrderProductVariants($order); + + if (!$this->featureFlagChecker->isEnabled()) { + $this->decorated->sell($order); + + return; + } + + $this->productBundleOrderInventoryOperator->sell($order); + } + + private function lockOrderProductVariants(OrderInterface $order): void + { + /** @var OrderItemInterface $orderItem */ + foreach ($order->getItems() as $orderItem) { + $this->lockOrderItemProductVariants($orderItem); + } + } + + private function lockOrderItemProductVariants(OrderItemInterface $orderItem): void + { + /** @var ProductInterface $product */ + $product = $orderItem->getProduct(); + if ($this->featureFlagChecker->isEnabled() && $product->isBundle()) { + $this->lockBundledOrderItemProductVariants($orderItem); + } else { + $this->lockOrderItemProductVariant($orderItem); + } + } + + private function lockOrderItemProductVariant(OrderItemInterface $orderItem): void + { + $this->lockProductVariant($orderItem->getVariant()); + } + + private function lockBundledOrderItemProductVariants(OrderItemInterface $orderItem): void + { + foreach ($orderItem->getProductBundleOrderItems() as $bundleOrderItem) { + $this->lockProductVariant($bundleOrderItem->getProductVariant()); + } + } + + private function lockProductVariant(?ProductVariantInterface $variant): void + { + if (null === $variant) { + throw new \InvalidArgumentException('Variant cannot be null'); + } + + if (!$variant->isTracked()) { + return; + } + + $this->productVariantManager->lock($variant, LockMode::OPTIMISTIC, $variant->getVersion()); + } +} diff --git a/src/Inventory/Operator/ProductBundleOrderInventoryOperator.php b/src/Inventory/Operator/ProductBundleOrderInventoryOperator.php new file mode 100644 index 00000000..7515a52a --- /dev/null +++ b/src/Inventory/Operator/ProductBundleOrderInventoryOperator.php @@ -0,0 +1,216 @@ +getItems() as $orderItem) { + /** @var ProductInterface $product */ + $product = $orderItem->getProduct(); + if ($product->isBundle()) { + $this->holdBundleOrderItem($orderItem); + } else { + $this->holdRegularOrderItem($orderItem); + } + } + } + + public function sell(OrderInterface $order): void + { + /** @var OrderItemInterface $orderItem */ + foreach ($order->getItems() as $orderItem) { + /** @var ProductInterface $product */ + $product = $orderItem->getProduct(); + if ($product->isBundle()) { + $this->sellBundleOrderItem($orderItem); + } else { + $this->sellRegularOrderItem($orderItem); + } + } + } + + /** @throws \InvalidArgumentException */ + public function release(OrderInterface $order): void + { + /** @var OrderItemInterface $orderItem */ + foreach ($order->getItems() as $orderItem) { + /** @var ProductInterface $product */ + $product = $orderItem->getProduct(); + if ($product->isBundle()) { + $this->releaseBundleOrderItem($orderItem); + } else { + $this->releaseRegularOrderItem($orderItem); + } + } + } + + public function giveBack(OrderInterface $order): void + { + /** @var OrderItemInterface $orderItem */ + foreach ($order->getItems() as $orderItem) { + /** @var ProductInterface $product */ + $product = $orderItem->getProduct(); + if ($product->isBundle()) { + $this->giveBackBundleOrderItem($orderItem); + } else { + $this->giveBackRegularOrderItem($orderItem); + } + } + } + + private function holdBundleOrderItem(OrderItemInterface $orderItem): void + { + foreach ($orderItem->getProductBundleOrderItems() as $bundleOrderItem) { + $quantity = $orderItem->getQuantity() * (int) $bundleOrderItem->getQuantity(); + $variant = $bundleOrderItem->getProductVariant(); + + $this->holdProductVariant($variant, $quantity); + } + } + + private function holdRegularOrderItem(OrderItemInterface $orderItem): void + { + $quantity = $orderItem->getQuantity(); + $variant = $orderItem->getVariant(); + + $this->holdProductVariant($variant, $quantity); + } + + private function holdProductVariant(?ProductVariantInterface $variant, int $quantity): void + { + if (null === $variant) { + throw new \InvalidArgumentException('Variant cannot be null'); + } + + if (!$variant->isTracked()) { + return; + } + + $variant->setOnHold((int) $variant->getOnHold() + $quantity); + } + + private function sellBundleOrderItem(OrderItemInterface $orderItem): void + { + foreach ($orderItem->getProductBundleOrderItems() as $bundleOrderItem) { + $quantity = $orderItem->getQuantity() * (int) $bundleOrderItem->getQuantity(); + $variant = $bundleOrderItem->getProductVariant(); + + $this->sellProductVariant($variant, $quantity); + } + } + + private function sellRegularOrderItem(OrderItemInterface $orderItem): void + { + $quantity = $orderItem->getQuantity(); + $variant = $orderItem->getVariant(); + + $this->sellProductVariant($variant, $quantity); + } + + private function sellProductVariant(?ProductVariantInterface $variant, int $quantity): void + { + if (null === $variant) { + throw new \InvalidArgumentException('Variant cannot be null'); + } + + if (!$variant->isTracked()) { + return; + } + + if (((int) $variant->getOnHold() - $quantity) < 0) { + throw new NotEnoughUnitsOnHoldException((string) $variant->getName()); + } + + if (((int) $variant->getOnHand() - $quantity) < 0) { + throw new NotEnoughUnitsOnHandException((string) $variant->getName()); + } + + $variant->setOnHold((int) $variant->getOnHold() - $quantity); + $variant->setOnHand((int) $variant->getOnHand() - $quantity); + } + + private function releaseBundleOrderItem(OrderItemInterface $orderItem): void + { + foreach ($orderItem->getProductBundleOrderItems() as $bundleOrderItem) { + $quantity = $orderItem->getQuantity() * (int) $bundleOrderItem->getQuantity(); + $variant = $bundleOrderItem->getProductVariant(); + + $this->releaseProductVariant($variant, $quantity); + } + } + + private function releaseRegularOrderItem(OrderItemInterface $orderItem): void + { + $quantity = $orderItem->getQuantity(); + $variant = $orderItem->getVariant(); + + $this->releaseProductVariant($variant, $quantity); + } + + private function releaseProductVariant(?ProductVariantInterface $variant, int $quantity): void + { + if (null === $variant) { + throw new \InvalidArgumentException('Variant cannot be null'); + } + + if (!$variant->isTracked()) { + return; + } + + if (((int) $variant->getOnHold() - $quantity) < 0) { + throw new NotEnoughUnitsOnHoldException((string) $variant->getName()); + } + + $variant->setOnHold((int) $variant->getOnHold() - $quantity); + } + + private function giveBackBundleOrderItem(OrderItemInterface $orderItem): void + { + foreach ($orderItem->getProductBundleOrderItems() as $bundleOrderItem) { + $quantity = $orderItem->getQuantity() * (int) $bundleOrderItem->getQuantity(); + $variant = $bundleOrderItem->getProductVariant(); + + $this->giveBackProductVariant($variant, $quantity); + } + } + + private function giveBackRegularOrderItem(OrderItemInterface $orderItem): void + { + $quantity = $orderItem->getQuantity(); + $variant = $orderItem->getVariant(); + + $this->giveBackProductVariant($variant, $quantity); + } + + private function giveBackProductVariant(?ProductVariantInterface $variant, int $quantity): void + { + if (null === $variant) { + throw new \InvalidArgumentException('Variant cannot be null'); + } + + if (!$variant->isTracked()) { + return; + } + + $variant->setOnHand((int) $variant->getOnHand() + $quantity); + } +} diff --git a/src/Inventory/Operator/ProductBundleOrderInventoryOperatorInterface.php b/src/Inventory/Operator/ProductBundleOrderInventoryOperatorInterface.php new file mode 100644 index 00000000..b196474c --- /dev/null +++ b/src/Inventory/Operator/ProductBundleOrderInventoryOperatorInterface.php @@ -0,0 +1,26 @@ + + @@ -10,6 +11,7 @@ + diff --git a/src/Resources/config/services/checker.xml b/src/Resources/config/services/checker.xml new file mode 100644 index 00000000..061b8e8f --- /dev/null +++ b/src/Resources/config/services/checker.xml @@ -0,0 +1,25 @@ + + + + + "false" + + + + + %env(bool:BUNDLED_PRODUCTS_INVENTORY_MANAGEMENT_FEATURE)% + + + + + + + + + + diff --git a/src/Resources/config/services/operator.xml b/src/Resources/config/services/operator.xml new file mode 100644 index 00000000..c353aa9e --- /dev/null +++ b/src/Resources/config/services/operator.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/tests/Application/.env b/tests/Application/.env index 8ca196db..57445681 100644 --- a/tests/Application/.env +++ b/tests/Application/.env @@ -42,3 +42,7 @@ JWT_PASSPHRASE=acme_plugin_development # Delivery is disabled by default via "null://localhost" MAILER_URL=smtp://localhost ###< symfony/swiftmailer-bundle ### + +###> bitbag/product-bundle-plugin ### +BUNDLED_PRODUCTS_INVENTORY_MANAGEMENT_FEATURE=true +###< bitbag/product-bundle-plugin ### diff --git a/tests/Unit/Inventory/Checker/BundledProductsInventoryManagementFeatureFlagCheckerTest.php b/tests/Unit/Inventory/Checker/BundledProductsInventoryManagementFeatureFlagCheckerTest.php new file mode 100644 index 00000000..2478a637 --- /dev/null +++ b/tests/Unit/Inventory/Checker/BundledProductsInventoryManagementFeatureFlagCheckerTest.php @@ -0,0 +1,30 @@ +isEnabled()); + } + + public function testIsDisabled(): void + { + $checker = new BundledProductsInventoryManagementFeatureFlagChecker(false); + self::assertFalse($checker->isEnabled()); + } +} diff --git a/tests/Unit/Inventory/Checker/OrderItemAvailabilityCheckerTest.php b/tests/Unit/Inventory/Checker/OrderItemAvailabilityCheckerTest.php new file mode 100644 index 00000000..54d64fe2 --- /dev/null +++ b/tests/Unit/Inventory/Checker/OrderItemAvailabilityCheckerTest.php @@ -0,0 +1,117 @@ +decorated = $this->createMock(OrderItemAvailabilityCheckerInterface::class); + $this->featureFlagChecker = $this->createMock(FeatureFlagCheckerInterface::class); + $this->bundleOrderItemAvailabilityChecker = $this->createMock(ProductBundleOrderItemAvailabilityCheckerInterface::class); + + $this->checker = new OrderItemAvailabilityChecker( + $this->decorated, + $this->featureFlagChecker, + $this->bundleOrderItemAvailabilityChecker, + ); + } + + public function testItCallsDecoratedIfFeatureDisabled(): void + { + $this->featureFlagChecker + ->expects(self::once()) + ->method('isEnabled') + ->willReturn(false); + + $this->decorated + ->expects(self::once()) + ->method('isReservedStockSufficient'); + + $this->bundleOrderItemAvailabilityChecker->expects(self::never())->method(self::anything()); + + $this->checker->isReservedStockSufficient($this->createMock(OrderItemInterface::class)); + } + + public function testItCallsDecoratedIfProductIsNotBundle(): void + { + $this->featureFlagChecker + ->expects(self::once()) + ->method('isEnabled') + ->willReturn(true); + $this->decorated + ->expects(self::once()) + ->method('isReservedStockSufficient'); + + $this->bundleOrderItemAvailabilityChecker->expects(self::never())->method(self::anything()); + + $product = $this->createMock(ProductInterface::class); + $product + ->expects(self::once()) + ->method('isBundle') + ->willReturn(false); + + $orderItem = $this->createMock(OrderItemInterface::class); + $orderItem + ->expects(self::once()) + ->method('getProduct') + ->willReturn($product); + + $this->checker->isReservedStockSufficient($orderItem); + } + + public function testItCallsProductBundleCheckerIfProductIsBundle(): void + { + $this->featureFlagChecker + ->expects(self::once()) + ->method('isEnabled') + ->willReturn(true); + + $this->decorated->expects(self::never())->method(self::anything()); + + $product = $this->createMock(ProductInterface::class); + $product + ->expects(self::once()) + ->method('isBundle') + ->willReturn(true); + + $orderItem = $this->createMock(OrderItemInterface::class); + $orderItem + ->expects(self::once()) + ->method('getProduct') + ->willReturn($product); + + $this->bundleOrderItemAvailabilityChecker + ->expects(self::once()) + ->method('areOrderedBundledProductVariantsAvailable') + ->with($orderItem); + + $this->checker->isReservedStockSufficient($orderItem); + } +} diff --git a/tests/Unit/Inventory/Checker/ProductBundleOrderItemAvailabilityCheckerTest.php b/tests/Unit/Inventory/Checker/ProductBundleOrderItemAvailabilityCheckerTest.php new file mode 100644 index 00000000..91402120 --- /dev/null +++ b/tests/Unit/Inventory/Checker/ProductBundleOrderItemAvailabilityCheckerTest.php @@ -0,0 +1,133 @@ +areOrderedBundledProductVariantsAvailable($orderItem)); + } + + public function provideAreOrderedBundledProductVariantsAvailable(): array + { + $unTrackedVariant = $this->mockProductVariant(false, 0, 0); + $unTrackedBundleOrderItem = $this->mockBundleOrderItem($unTrackedVariant, 2); + $orderItem1 = $this->mockOrderItem([$unTrackedBundleOrderItem], 2); + + $trackedVariantOutOfStock = $this->mockProductVariant(true, 0, 0); + $trackedOutOfStockBundleOrderItem = $this->mockBundleOrderItem($trackedVariantOutOfStock, 2); + $orderItem2 = $this->mockOrderItem([$trackedOutOfStockBundleOrderItem], 2); + + $trackedVariantInStock = $this->mockProductVariant(true, 10, 20); + $trackedInStockBundleOrderItem = $this->mockBundleOrderItem($trackedVariantInStock, 2); + $orderItem3 = $this->mockOrderItem([$trackedOutOfStockBundleOrderItem, $trackedInStockBundleOrderItem], 2); + + $orderItem4 = $this->mockOrderItem([$trackedInStockBundleOrderItem], 5); + + $trackedVariantInStock2 = $this->mockProductVariant(true, 20, 10); + $trackedInStockBundleOrderItem2 = $this->mockBundleOrderItem($trackedVariantInStock2, 2); + $orderItem5 = $this->mockOrderItem([$trackedInStockBundleOrderItem2], 5); + + $trackedVariantInStock3 = $this->mockProductVariant(true, 10, 9); + $trackedInStockBundleOrderItem3 = $this->mockBundleOrderItem($trackedVariantInStock3, 2); + $orderItem6 = $this->mockOrderItem([$trackedInStockBundleOrderItem3], 5); + + $trackedVariantInStock4 = $this->mockProductVariant(true, 9, 10); + $trackedInStockBundleOrderItem4 = $this->mockBundleOrderItem($trackedVariantInStock4, 2); + $orderItem7 = $this->mockOrderItem([$trackedInStockBundleOrderItem4], 5); + + return [ + 'untracked variant' => [ + $orderItem1, + true, + ], + 'variant out of stock' => [ + $orderItem2, + false, + ], + 'one variant out of stock, one in stock' => [ + $orderItem3, + false, + ], + 'on-hold edge case' => [ + $orderItem4, + true, + ], + 'on-hand edge case' => [ + $orderItem5, + true, + ], + 'on-hold insufficient' => [ + $orderItem6, + false, + ], + 'on-hand insufficient' => [ + $orderItem6, + false, + ], + ]; + } + + private function mockProductVariant(bool $isTracked, int $onHold, int $onHand): ProductVariantInterface + { + $variant = $this->createMock(ProductVariantInterface::class); + $variant + ->method('isTracked') + ->willReturn($isTracked); + $variant + ->method('getOnHold') + ->willReturn($onHold); + $variant + ->method('getOnHand') + ->willReturn($onHand); + + return $variant; + } + + private function mockBundleOrderItem(ProductVariantInterface $variant, int $quantity): ProductBundleOrderItemInterface + { + $item = $this->createMock(ProductBundleOrderItemInterface::class); + $item + ->method('getProductVariant') + ->willReturn($variant); + $item + ->method('getQuantity') + ->willReturn($quantity); + + return $item; + } + + /** @param ProductBundleOrderItemInterface[] $bundleOrderItems */ + private function mockOrderItem(array $bundleOrderItems, int $quantity): OrderItemInterface + { + $orderItem = $this->createMock(OrderItemInterface::class); + $orderItem + ->method('getProductBundleOrderItems') + ->willReturn($bundleOrderItems); + $orderItem + ->method('getQuantity') + ->willReturn($quantity); + + return $orderItem; + } +} diff --git a/tests/Unit/Inventory/Operator/OrderInventoryOperatorTest.php b/tests/Unit/Inventory/Operator/OrderInventoryOperatorTest.php new file mode 100644 index 00000000..a19b01be --- /dev/null +++ b/tests/Unit/Inventory/Operator/OrderInventoryOperatorTest.php @@ -0,0 +1,175 @@ +decorated = $this->createMock(SyliusOrderInventoryOperatorInterface::class); + $this->productVariantManager = $this->createMock(EntityManagerInterface::class); + $this->featureFlagChecker = $this->createMock(FeatureFlagCheckerInterface::class); + $this->productBundleOrderInventoryOperator = $this->createMock(ProductBundleOrderInventoryOperatorInterface::class); + + $this->orderInventoryOperator = new OrderInventoryOperator( + $this->decorated, + $this->productVariantManager, + $this->featureFlagChecker, + $this->productBundleOrderInventoryOperator, + ); + } + + public function testCancelCallsDecoratedIfFeatureFlagDisabled(): void + { + $order = $this->createMock(OrderInterface::class); + + $this->featureFlagChecker + ->expects(self::once()) + ->method('isEnabled') + ->willReturn(false); + + $this->decorated + ->expects(self::once()) + ->method('cancel') + ->with($order); + + $this->orderInventoryOperator->cancel($order); + } + + public function testCancelGivesBackInventoryIfOrderPaidOrRefunded(): void + { + $order = $this->createMock(OrderInterface::class); + + $order->method('getPaymentState') + ->willReturn(OrderPaymentStates::STATE_PAID); + + $this->featureFlagChecker + ->method('isEnabled') + ->willReturn(true); + + $this->productBundleOrderInventoryOperator + ->expects(self::once()) + ->method('giveBack') + ->with($order); + + $this->orderInventoryOperator->cancel($order); + } + + public function testCancelReleasesInventoryIfNotPaidOrRefunded(): void + { + $order = $this->createMock(OrderInterface::class); + + $order->method('getPaymentState') + ->willReturn(OrderPaymentStates::STATE_CART); + + $this->featureFlagChecker + ->method('isEnabled') + ->willReturn(true); + + $this->productBundleOrderInventoryOperator + ->expects(self::once()) + ->method('release') + ->with($order); + + $this->orderInventoryOperator->cancel($order); + } + + public function testHoldCallsDecoratedIfFeatureFlagDisabled(): void + { + $order = $this->createMock(OrderInterface::class); + + $this->featureFlagChecker + ->expects(self::once()) + ->method('isEnabled') + ->willReturn(false); + + $this->decorated + ->expects(self::once()) + ->method('hold') + ->with($order); + + $this->orderInventoryOperator->hold($order); + } + + public function testHoldHandlesBundleIfFeatureFlagEnabled(): void + { + $order = $this->createMock(OrderInterface::class); + + $this->decorated->expects(self::never())->method(self::anything()); + + $this->featureFlagChecker + ->method('isEnabled') + ->willReturn(true); + + $this->productBundleOrderInventoryOperator + ->expects(self::once()) + ->method('hold') + ->with($order); + + $this->orderInventoryOperator->hold($order); + } + + public function testSellCallsDecoratedIfFeatureFlagDisabled(): void + { + $order = $this->createMock(OrderInterface::class); + + $this->featureFlagChecker + ->expects(self::once()) + ->method('isEnabled') + ->willReturn(false); + + $this->decorated + ->expects(self::once()) + ->method('sell') + ->with($order); + + $this->orderInventoryOperator->sell($order); + } + + public function testSellHandlesBundleIfFeatureFlagEnabled(): void + { + $order = $this->createMock(OrderInterface::class); + + $this->decorated->expects(self::never())->method(self::anything()); + + $this->featureFlagChecker + ->method('isEnabled') + ->willReturn(true); + + $this->productBundleOrderInventoryOperator + ->expects(self::once()) + ->method('sell') + ->with($order); + + $this->orderInventoryOperator->sell($order); + } +} diff --git a/tests/Unit/Inventory/Operator/ProductBundleOrderInventoryOperatorTest.php b/tests/Unit/Inventory/Operator/ProductBundleOrderInventoryOperatorTest.php new file mode 100644 index 00000000..e6e04a54 --- /dev/null +++ b/tests/Unit/Inventory/Operator/ProductBundleOrderInventoryOperatorTest.php @@ -0,0 +1,368 @@ +bundle = $this->createMock(ProductInterface::class); + $this->bundle + ->method('isBundle') + ->willReturn(true); + + $this->regularProduct = $this->createMock(ProductInterface::class); + $this->regularProduct + ->method('isBundle') + ->willReturn(false); + + $this->operator = new ProductBundleOrderInventoryOperator(); + } + + public function testItHolds(): void + { + $bundleVariant1 = $this->createMock(ProductVariantInterface::class); + $bundleVariant1 + ->method('isTracked') + ->willReturn(true); + $bundleVariant1 + ->method('getOnHold') + ->willReturn(10); + $bundleVariant1 + ->method('setOnHold') + ->with(14); + + $bundleVariant2 = $this->createMock(ProductVariantInterface::class); + $bundleVariant2 + ->method('isTracked') + ->willReturn(true); + $bundleVariant2 + ->method('getOnHold') + ->willReturn(20); + $bundleVariant2 + ->method('setOnHold') + ->with(26); + + $bundleOrderItem = $this->createMock(OrderItemInterface::class); + $bundleOrderItem + ->expects(self::once()) + ->method('getProduct') + ->willReturn($this->bundle); + + $bundleOrderItem + ->expects(self::exactly(2)) + ->method('getQuantity') + ->willReturn(2); + + $bundleOrderItem + ->expects(self::once()) + ->method('getProductBundleOrderItems') + ->willReturn([ + $this->mockProductBundleOrderItem($bundleVariant1, 2), + $this->mockProductBundleOrderItem($bundleVariant2, 3), + ]); + $bundleOrderItem->expects(self::never())->method('getVariant'); + + $regularOrderItem = $this->createMock(OrderItemInterface::class); + $regularOrderItem + ->expects(self::once()) + ->method('getProduct') + ->willReturn($this->regularProduct); + $regularOrderItem + ->expects(self::once()) + ->method('getQuantity') + ->willReturn(3); + + $variant = $this->createMock(ProductVariantInterface::class); + $variant + ->method('isTracked') + ->willReturn(true); + $variant + ->method('getOnHold') + ->willReturn(100); + $variant + ->method('setOnHold') + ->with(103); + + $regularOrderItem + ->expects(self::once()) + ->method('getVariant') + ->willReturn($variant); + + $regularOrderItem->expects(self::never())->method('getProductBundleOrderItems'); + + $order = $this->mockOrder( + $regularOrderItem, + $bundleOrderItem, + ); + + $this->operator->hold($order); + } + + public function testItSells(): void + { + $bundleVariant1 = $this->createMock(ProductVariantInterface::class); + $bundleVariant1 + ->method('isTracked') + ->willReturn(true); + $bundleVariant1 + ->method('getOnHold') + ->willReturn(10); + $bundleVariant1 + ->method('getOnHand') + ->willReturn(20); + $bundleVariant1 + ->method('setOnHold') + ->with(6); + $bundleVariant1 + ->method('setOnHand') + ->with(16); + + $bundleOrderItem = $this->createMock(OrderItemInterface::class); + $bundleOrderItem + ->expects(self::once()) + ->method('getProduct') + ->willReturn($this->bundle); + + $bundleOrderItem + ->expects(self::once()) + ->method('getQuantity') + ->willReturn(2); + + $bundleOrderItem + ->expects(self::once()) + ->method('getProductBundleOrderItems') + ->willReturn([ + $this->mockProductBundleOrderItem($bundleVariant1, 2), + ]); + $bundleOrderItem->expects(self::never())->method('getVariant'); + + $regularOrderItem = $this->createMock(OrderItemInterface::class); + $regularOrderItem + ->expects(self::once()) + ->method('getProduct') + ->willReturn($this->regularProduct); + $regularOrderItem + ->expects(self::once()) + ->method('getQuantity') + ->willReturn(3); + + $variant = $this->createMock(ProductVariantInterface::class); + $variant + ->method('isTracked') + ->willReturn(true); + $variant + ->method('getOnHold') + ->willReturn(100); + $variant + ->method('setOnHold') + ->with(97); + $variant + ->method('getOnHand') + ->willReturn(200); + $variant + ->method('setOnHand') + ->with(197); + + $regularOrderItem + ->expects(self::once()) + ->method('getVariant') + ->willReturn($variant); + + $regularOrderItem->expects(self::never())->method('getProductBundleOrderItems'); + + $order = $this->mockOrder( + $regularOrderItem, + $bundleOrderItem, + ); + + $this->operator->sell($order); + } + + public function testItReleases(): void + { + $bundleVariant1 = $this->createMock(ProductVariantInterface::class); + $bundleVariant1 + ->method('isTracked') + ->willReturn(true); + $bundleVariant1 + ->method('getOnHold') + ->willReturn(10); + $bundleVariant1 + ->method('setOnHold') + ->with(6); + + $bundleOrderItem = $this->createMock(OrderItemInterface::class); + $bundleOrderItem + ->expects(self::once()) + ->method('getProduct') + ->willReturn($this->bundle); + + $bundleOrderItem + ->expects(self::once()) + ->method('getQuantity') + ->willReturn(2); + + $bundleOrderItem + ->expects(self::once()) + ->method('getProductBundleOrderItems') + ->willReturn([ + $this->mockProductBundleOrderItem($bundleVariant1, 2), + ]); + $bundleOrderItem->expects(self::never())->method('getVariant'); + + $regularOrderItem = $this->createMock(OrderItemInterface::class); + $regularOrderItem + ->expects(self::once()) + ->method('getProduct') + ->willReturn($this->regularProduct); + $regularOrderItem + ->expects(self::once()) + ->method('getQuantity') + ->willReturn(3); + + $variant = $this->createMock(ProductVariantInterface::class); + $variant + ->method('isTracked') + ->willReturn(true); + $variant + ->method('getOnHold') + ->willReturn(100); + $variant + ->method('setOnHold') + ->with(97); + + $regularOrderItem + ->expects(self::once()) + ->method('getVariant') + ->willReturn($variant); + + $regularOrderItem->expects(self::never())->method('getProductBundleOrderItems'); + + $order = $this->mockOrder( + $regularOrderItem, + $bundleOrderItem, + ); + + $this->operator->release($order); + } + + public function testItGivesBack(): void + { + $bundleVariant1 = $this->createMock(ProductVariantInterface::class); + $bundleVariant1 + ->method('isTracked') + ->willReturn(true); + $bundleVariant1 + ->method('getOnHand') + ->willReturn(10); + $bundleVariant1 + ->method('setOnHand') + ->with(14); + + $bundleOrderItem = $this->createMock(OrderItemInterface::class); + $bundleOrderItem + ->expects(self::once()) + ->method('getProduct') + ->willReturn($this->bundle); + + $bundleOrderItem + ->expects(self::once()) + ->method('getQuantity') + ->willReturn(2); + + $bundleOrderItem + ->expects(self::once()) + ->method('getProductBundleOrderItems') + ->willReturn([ + $this->mockProductBundleOrderItem($bundleVariant1, 2), + ]); + $bundleOrderItem->expects(self::never())->method('getVariant'); + + $regularOrderItem = $this->createMock(OrderItemInterface::class); + $regularOrderItem + ->expects(self::once()) + ->method('getProduct') + ->willReturn($this->regularProduct); + $regularOrderItem + ->expects(self::once()) + ->method('getQuantity') + ->willReturn(3); + + $variant = $this->createMock(ProductVariantInterface::class); + $variant + ->method('isTracked') + ->willReturn(true); + $variant + ->method('getOnHand') + ->willReturn(100); + $variant + ->method('setOnHand') + ->with(103); + + $regularOrderItem + ->expects(self::once()) + ->method('getVariant') + ->willReturn($variant); + + $regularOrderItem->expects(self::never())->method('getProductBundleOrderItems'); + + $order = $this->mockOrder( + $regularOrderItem, + $bundleOrderItem, + ); + + $this->operator->giveBack($order); + } + + private function mockProductBundleOrderItem( + ProductVariantInterface $variant, + int $quantity, + ): ProductBundleOrderItemInterface { + $bundleOrderItem = $this->createMock(ProductBundleOrderItemInterface::class); + $bundleOrderItem + ->method('getProductVariant') + ->willReturn($variant); + $bundleOrderItem + ->method('getQuantity') + ->willReturn($quantity); + + return $bundleOrderItem; + } + + private function mockOrder(OrderItemInterface ...$items): OrderInterface + { + $order = $this->createMock(OrderInterface::class); + $order + ->method('getItems') + ->willReturn(new ArrayCollection($items)); + + return $order; + } +} From 67b20532dee589bbb947577856f9a2126c42be42 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Mon, 16 Sep 2024 14:22:55 +0200 Subject: [PATCH 38/40] OP-289: Bump min required patches, fix version selection in builds, fix 1.12 compatibility --- .docker/Dockerfile | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/coding_standard.yml | 2 +- composer.json | 2 +- features/having_bundled_product_in_store.feature | 12 ++++++------ .../AddProductBundleItemToCartCommandProvider.php | 6 ++---- .../views/Shop/Product/_variantSelection.html.twig | 2 +- tests/Behat/Context/Api/ProductBundleContext.php | 12 ++++++++++-- tests/Behat/Resources/services.yml | 1 + tests/Behat/Resources/suites.yml | 2 -- 10 files changed, 24 insertions(+), 19 deletions(-) diff --git a/.docker/Dockerfile b/.docker/Dockerfile index 834732c5..f262b23d 100644 --- a/.docker/Dockerfile +++ b/.docker/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 ARG DEBIAN_FRONTEND=noninteractive -ARG PHP_VERSION=8.1 +ARG PHP_VERSION=8.2 ENV LC_ALL=C.UTF-8 # Install basic tools diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0d6ee0f5..2501b39b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,7 @@ jobs: matrix: php: [ "8.1", "8.2", "8.3" ] symfony: [ "^5.4", "^6.4" ] - sylius: [ "^1.12", "^1.13" ] + sylius: [ "~1.12.17", "~1.13.2" ] node: [ "18.x", "20.x" ] mysql: [ "8.0" ] diff --git a/.github/workflows/coding_standard.yml b/.github/workflows/coding_standard.yml index 2e7d9fe8..4a84c646 100644 --- a/.github/workflows/coding_standard.yml +++ b/.github/workflows/coding_standard.yml @@ -20,7 +20,7 @@ jobs: matrix: php: [ "8.1", "8.2", "8.3" ] symfony: [ "^5.4", "^6.4" ] - sylius: [ "^1.12", "^1.13" ] + sylius: [ "~1.12.17", "~1.13.2" ] node: [ "18.x", "20.x" ] steps: diff --git a/composer.json b/composer.json index b7cd948b..6e5712a6 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "license": "MIT", "require": { "php": "^8.1", - "sylius/sylius": "~1.12.0 || ~1.13.0" + "sylius/sylius": "~1.12.17 || ~1.13.2" }, "require-dev": { "ext-json": "*", diff --git a/features/having_bundled_product_in_store.feature b/features/having_bundled_product_in_store.feature index 20ea435a..cfe023bb 100644 --- a/features/having_bundled_product_in_store.feature +++ b/features/having_bundled_product_in_store.feature @@ -71,8 +71,8 @@ Feature: Having a product in store which is a bundle of other products When I pick up my cart And I add bundle "Jim Beam&Coke" with quantity 5 to my cart and overwrite "JIM_BEAM" with "JIM_BEAM_1L" Then I should have bundle "Jim Beam&Coke" with quantity 5 in my cart - And I should have product variant "Jim Beam 1L" in bundled items - And I should not have product variant "Jim Beam" in bundled items + And I should have product variant "JIM_BEAM_1L" in bundled items + And I should not have product variant "JIM_BEAM" in bundled items And I should have product "Coca-Cola" in bundled items @api @@ -80,8 +80,8 @@ Feature: Having a product in store which is a bundle of other products When I pick up my cart And I add bundle "Jim Beam&Coke" with quantity 5 to my cart and overwrite "COCA_COLA" with "JIM_BEAM_1L" Then I should have bundle "Jim Beam&Coke" with quantity 5 in my cart - And I should not have product variant "Jim Beam 1L" in bundled items - And I should have product variant "Jim Beam" in bundled items + And I should not have product variant "JIM_BEAM_1L" in bundled items + And I should have product variant "JIM_BEAM" in bundled items And I should have product "Coca-Cola" in bundled items @api @@ -90,6 +90,6 @@ Feature: Having a product in store which is a bundle of other products When I pick up my cart And I add bundle "Jim Beam&Coke" with quantity 5 to my cart and overwrite "JIM_BEAM" with "JIM_BEAM_1L" Then I should have bundle "Jim Beam&Coke" with quantity 5 in my cart - And I should not have product variant "Jim Beam 1L" in bundled items - And I should have product variant "Jim Beam" in bundled items + And I should not have product variant "JIM_BEAM_1L" in bundled items + And I should have product variant "JIM_BEAM" in bundled items And I should have product "Coca-Cola" in bundled items diff --git a/src/Provider/AddProductBundleItemToCartCommandProvider.php b/src/Provider/AddProductBundleItemToCartCommandProvider.php index c6632f54..5d9137ba 100644 --- a/src/Provider/AddProductBundleItemToCartCommandProvider.php +++ b/src/Provider/AddProductBundleItemToCartCommandProvider.php @@ -77,14 +77,12 @@ private function overwriteVariant( private function shouldOverwriteVariant(string $oldVariantCode, string $newVariantCode): bool { - /** @var ?ProductVariantInterface $oldVariant */ $oldVariant = $this->productVariantRepository->findOneBy(['code' => $oldVariantCode]); - /** @var ?ProductVariantInterface $oldVariant */ $newVariant = $this->productVariantRepository->findOneBy(['code' => $newVariantCode]); return - null !== $oldVariant && - null !== $newVariant && + $oldVariant instanceof ProductVariantInterface && + $newVariant instanceof ProductVariantInterface && $oldVariant->getProduct() === $newVariant->getProduct(); } } diff --git a/src/Resources/views/Shop/Product/_variantSelection.html.twig b/src/Resources/views/Shop/Product/_variantSelection.html.twig index 4f0c160d..30f96ac7 100644 --- a/src/Resources/views/Shop/Product/_variantSelection.html.twig +++ b/src/Resources/views/Shop/Product/_variantSelection.html.twig @@ -1,5 +1,5 @@ {% if product.isConfigurable() and product.getVariantSelectionMethod() == 'match' and not product.enabledVariants.empty() %} - {% include '@SyliusShop/Product/Show/_variantsPricing.html.twig' with {'pricing': sylius_product_variants_map(product, {'channel': sylius.channel}), 'variants': product.enabledVariants} %} + {% include '@SyliusShop/Product/Show/_variantsPricing.html.twig' with {'pricing': sylius_product_variant_prices(product, sylius.channel), 'variants': product.enabledVariants} %} {% endif %} {% include '@BitBagSyliusProductBundlePlugin/Shop/Product/Show/_inventory.html.twig' %} diff --git a/tests/Behat/Context/Api/ProductBundleContext.php b/tests/Behat/Context/Api/ProductBundleContext.php index e08ee862..d408136c 100644 --- a/tests/Behat/Context/Api/ProductBundleContext.php +++ b/tests/Behat/Context/Api/ProductBundleContext.php @@ -20,6 +20,7 @@ use Sylius\Behat\Context\Api\Resources; use Sylius\Behat\Service\SharedStorageInterface; use Sylius\Component\Core\Model\ProductVariantInterface; +use Sylius\Component\Core\Repository\ProductVariantRepositoryInterface; use Symfony\Component\HttpFoundation\Request as HttpRequest; use Webmozart\Assert\Assert; @@ -30,6 +31,7 @@ public function __construct( private readonly ApiClientInterface $client, private readonly RequestFactoryInterface $requestFactory, private readonly ResponseCheckerInterface $responseChecker, + private readonly ProductVariantRepositoryInterface $productVariantRepository, ) { } @@ -116,8 +118,11 @@ public function iShouldHaveProductInBundledItems(ProductInterface $product): voi /** * @When I should have product variant :productVariant in bundled items */ - public function iShouldHaveProductVariantInBundledItems(ProductVariantInterface $productVariant): void + public function iShouldHaveProductVariantInBundledItems(string $productVariant): void { + $productVariant = $this->productVariantRepository->findOneBy(['code' => $productVariant]); + Assert::isInstanceOf($productVariant, ProductVariantInterface::class); + $response = $this->client->show(Resources::ORDERS, $this->sharedStorage->get('cart_token')); $productBundleOrderItems = $this->responseChecker->getValue($response, 'items')[0]['productBundleOrderItems']; @@ -133,8 +138,11 @@ public function iShouldHaveProductVariantInBundledItems(ProductVariantInterface /** * @When I should not have product variant :productVariant in bundled items */ - public function iShouldNotHaveProductVariantInBundledItems(ProductVariantInterface $productVariant): void + public function iShouldNotHaveProductVariantInBundledItems(string $productVariant): void { + $productVariant = $this->productVariantRepository->findOneBy(['code' => $productVariant]); + Assert::isInstanceOf($productVariant, ProductVariantInterface::class); + $response = $this->client->show(Resources::ORDERS, $this->sharedStorage->get('cart_token')); $productBundleOrderItems = $this->responseChecker->getValue($response, 'items')[0]['productBundleOrderItems']; diff --git a/tests/Behat/Resources/services.yml b/tests/Behat/Resources/services.yml index cf7ea5a4..25ae9378 100644 --- a/tests/Behat/Resources/services.yml +++ b/tests/Behat/Resources/services.yml @@ -53,3 +53,4 @@ services: - '@sylius.behat.api_platform_client.shop' - '@sylius.behat.request_factory' - '@Sylius\Behat\Client\ResponseCheckerInterface' + - '@sylius.repository.product_variant' diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index 719e3a2f..56af2b95 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -93,7 +93,6 @@ default: - sylius.behat.context.transform.locale - sylius.behat.context.transform.payment - sylius.behat.context.transform.product - - sylius.behat.context.transform.product_variant - sylius.behat.context.transform.promotion - sylius.behat.context.transform.shared_storage - sylius.behat.context.transform.shipping_method @@ -102,7 +101,6 @@ default: - sylius.behat.context.api.shop.cart - sylius.behat.context.api.shop.checkout - - sylius.behat.context.api.shop.checkout.complete - bitbag_sylius_product_bundle_plugin.behat.context.api.product_bundle - bitbag_sylius_product_bundle_plugin.behat.context.setup.product_bundle From 04f657e0c6121ab7a3e4a987e5e4a87660511ae1 Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Thu, 19 Sep 2024 08:49:56 +0200 Subject: [PATCH 39/40] OP-289: Add info on enabling bundled products stock management to readme --- README.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8f734986..efd112e4 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ This **open-source plugin was developed to help the Sylius community**. If you h vendor/bin/rector process src --config=vendor/bitbag/product-bundle-plugin/rector.php ``` -6. (applied if not using Rector) Extend `Product`(including Doctrine mapping): +6. (applied if not using Rector) Extend `Product` (including Doctrine mapping): ```php ``` -10. Add configuration for extended product and order item: +10. (optional) If you want to manage stock for bundled products instead of managing bundle stock, add this to your .env: + + ```dotenv + ###> bitbag/product-bundle-plugin ### + BUNDLED_PRODUCTS_INVENTORY_MANAGEMENT_FEATURE=true + ###< bitbag/product-bundle-plugin ### + ``` + +11. Add configuration for extended product and order item: ```yaml # config/packages/_sylius.yaml @@ -189,7 +197,7 @@ This **open-source plugin was developed to help the Sylius community**. If you h ``` -11. If you have full configuration in xml override doctrine config: +12. If you have full configuration in xml override doctrine config: ```yaml # config/packages/doctrine.yaml @@ -207,7 +215,7 @@ doctrine: ``` -12. Add plugin templates: +13. Add plugin templates: - Inject blocks: ```yaml @@ -253,18 +261,18 @@ sylius_ui: cp -R vendor/bitbag/product-bundle-plugin/src/Resources/views/Shop/Common templates/bundles/SyliusShopBundle ``` -13. Please clear application cache by running command below: +14. Please clear application cache by running command below: ```bash bin/console cache:clear ``` -14. Finish the installation by updating the database schema and installing assets: +15. Finish the installation by updating the database schema and installing assets: ```bash bin/console doctrine:migrations:migrate ``` -15. Add plugin assets to your project: +16. Add plugin assets to your project: [Import webpack config](./README_webpack-config.md)* ## Testing From ccee940c45b5989aa374ef954108f1b3cb95fadd Mon Sep 17 00:00:00 2001 From: Hubert Filar Date: Thu, 19 Sep 2024 12:14:33 +0200 Subject: [PATCH 40/40] OP-289: Add products in bundle section on admin product show page --- src/Resources/config/services/twig.xml | 1 + .../views/Admin/Product/show.html.twig | 30 ++++++++++++++++++- .../Extension/ProductBundlesExtension.php | 10 +++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/Resources/config/services/twig.xml b/src/Resources/config/services/twig.xml index e91dea65..8c63e5fc 100644 --- a/src/Resources/config/services/twig.xml +++ b/src/Resources/config/services/twig.xml @@ -12,6 +12,7 @@ + diff --git a/src/Resources/views/Admin/Product/show.html.twig b/src/Resources/views/Admin/Product/show.html.twig index 2c19840a..aeb97dc3 100644 --- a/src/Resources/views/Admin/Product/show.html.twig +++ b/src/Resources/views/Admin/Product/show.html.twig @@ -16,11 +16,39 @@

{{ 'bitbag_sylius_product_bundle.ui.bundles'|trans }}

{% for bundle in bundles %} {% endfor %}
{% endif %} + + {% if product.isBundle %} + {% set bundledProducts = bitbag_get_products_from_bundle(product) %} + +

{{ 'bitbag_sylius_product_bundle.ui.products_in_bundle'|trans }}

+
+ {% for bundledProduct in bundledProducts %} + {% set variant = bundledProduct.productVariant %} + {% set product = variant.product %} +
+ {% include '@SyliusAdmin/Product/_mainImage.html.twig' with {'product': product, 'filter': 'sylius_admin_product_thumbnail'} %} +
+ + + {{ variant.code }} + + + {{ 'sylius.ui.qty'|trans }}: {{ bundledProduct.quantity }} + +
+
+ {% endfor %} +
+ {% endif %} {% endblock %} diff --git a/src/Twig/Extension/ProductBundlesExtension.php b/src/Twig/Extension/ProductBundlesExtension.php index caf346cf..39556207 100644 --- a/src/Twig/Extension/ProductBundlesExtension.php +++ b/src/Twig/Extension/ProductBundlesExtension.php @@ -12,7 +12,9 @@ namespace BitBag\SyliusProductBundlePlugin\Twig\Extension; use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleInterface; +use BitBag\SyliusProductBundlePlugin\Entity\ProductBundleItemInterface; use BitBag\SyliusProductBundlePlugin\Entity\ProductInterface; +use BitBag\SyliusProductBundlePlugin\Repository\ProductBundleItemRepositoryInterface; use BitBag\SyliusProductBundlePlugin\Repository\ProductBundleRepositoryInterface; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; @@ -21,6 +23,7 @@ final class ProductBundlesExtension extends AbstractExtension { public function __construct( private readonly ProductBundleRepositoryInterface $productBundleRepository, + private readonly ProductBundleItemRepositoryInterface $productBundleItemRepository, ) { } @@ -28,6 +31,7 @@ public function getFunctions(): array { return [ new TwigFunction('bitbag_get_bundles_containing_product', [$this, 'getBundlesForProduct'], ['is_safe' => ['html']]), + new TwigFunction('bitbag_get_products_from_bundle', [$this, 'getProductsFromBundle'], ['is_safe' => ['html']]), ]; } @@ -36,4 +40,10 @@ public function getBundlesForProduct(ProductInterface $product): array { return $this->productBundleRepository->findBundlesByVariants($product->getVariants()); } + + /** @return ProductBundleItemInterface[] */ + public function getProductsFromBundle(ProductInterface $product): array + { + return $this->productBundleItemRepository->findByBundleCode((string) $product->getCode()); + } }