diff --git a/features/product/managing_products/modifying_taxons_assigned_to_an_existing_product.feature b/features/product/managing_products/modifying_taxons_assigned_to_an_existing_product.feature index 8ec96e793de..cf0f4fc3a45 100644 --- a/features/product/managing_products/modifying_taxons_assigned_to_an_existing_product.feature +++ b/features/product/managing_products/modifying_taxons_assigned_to_an_existing_product.feature @@ -14,7 +14,8 @@ Feature: Modifying taxons assigned to an existing product @ui @api Scenario: Modifying taxons assigned to a product - When I change that the "T-Shirt" product belongs to the "T-Shirts" taxon + When I change that the "T-Shirt" product does not belong to the "Clothes" taxon + And I add "T-Shirts" taxon to the "T-Shirt" product Then the product "T-Shirt" should have the "T-Shirts" taxon @ui @api diff --git a/features/promotion/applying_catalog_promotions/not_reapplying_catalog_promotions_on_variants_once_products_taxon_changes.feature b/features/promotion/applying_catalog_promotions/not_reapplying_catalog_promotions_on_variants_once_products_taxon_changes.feature index 5088aad71df..c9cd8bf9662 100644 --- a/features/promotion/applying_catalog_promotions/not_reapplying_catalog_promotions_on_variants_once_products_taxon_changes.feature +++ b/features/promotion/applying_catalog_promotions/not_reapplying_catalog_promotions_on_variants_once_products_taxon_changes.feature @@ -16,16 +16,16 @@ Feature: Not reapplying catalog promotions on variants once the product’s taxo And there is disabled catalog promotion "Surprise sale" between "2021-07-01" and "2022-05-04" available in "Web-US" channel that reduces price by "90%" and applies on "Dishes" taxon And I am logged in as an administrator - @ui + @api @ui Scenario: Changing products taxon to taxon with scheduled catalog promotion When I change that the "T-Shirt" product does not belong to the "Clothes" taxon - And I change that the "T-Shirt" product belongs to the "Shirts" taxon + And I add "Shirts" taxon to the "T-Shirt" product Then the visitor should see that the "PHP T-Shirt" variant is not discounted And the visitor should still see "$100.00" as the price of the "T-Shirt" product in the "Web-US" channel - @ui + @api @ui Scenario: Changing products taxon to taxon with disabled catalog promotion When I change that the "T-Shirt" product does not belong to the "Clothes" taxon - And I change that the "T-Shirt" product belongs to the "Dishes" taxon + And I add "Dishes" taxon to the "T-Shirt" product Then the visitor should see that the "PHP T-Shirt" variant is not discounted And the visitor should still see "$100.00" as the price of the "T-Shirt" product in the "Web-US" channel diff --git a/features/promotion/applying_catalog_promotions/reapplying_catalog_promotions_on_variants_once_products_taxon_changes.feature b/features/promotion/applying_catalog_promotions/reapplying_catalog_promotions_on_variants_once_products_taxon_changes.feature index f87d347bbfd..b9b3f3f1e3e 100644 --- a/features/promotion/applying_catalog_promotions/reapplying_catalog_promotions_on_variants_once_products_taxon_changes.feature +++ b/features/promotion/applying_catalog_promotions/reapplying_catalog_promotions_on_variants_once_products_taxon_changes.feature @@ -16,12 +16,12 @@ Feature: Reapplying catalog promotions on variants once the product’s taxon ch And there is another catalog promotion "Summer sale" that reduces price by "50%" and applies on "Dishes" taxon And I am logged in as an administrator - @ui + @api @ui Scenario: Removing a taxon from a product When I change that the "T-Shirt" product does not belong to the "Clothes" taxon Then the visitor should see that the "PHP T-Shirt" variant is not discounted - @ui @api + @api @ui Scenario: Adding a taxon to a product When I assign the "Dishes" taxon to the "Mug" product Then the visitor should see that the "PHP Mug" variant is discounted from "$10.00" to "$5.00" with "Summer sale" promotion diff --git a/features/promotion/managing_catalog_promotions/sorting_catalog_promotions.feature b/features/promotion/managing_catalog_promotions/sorting_catalog_promotions.feature index 8a2c6d5e219..1d37a02957a 100644 --- a/features/promotion/managing_catalog_promotions/sorting_catalog_promotions.feature +++ b/features/promotion/managing_catalog_promotions/sorting_catalog_promotions.feature @@ -16,69 +16,69 @@ Feature: Sorting listed catalog promotion And its priority is 2 And I am logged in as an administrator - @ui + @api @ui Scenario: Catalog promotions are sorted by ascending code by default When I browse catalog promotions Then I should see 3 catalog promotions on the list And the first catalog promotion should have code "a" - @ui + @api @ui Scenario: Changing the code sorting order to descending When I browse catalog promotions And I sort catalog promotions by descending code Then I should see 3 catalog promotions on the list And the first catalog promotion should have code "not-b" - @ui + @api @ui Scenario: Sorting catalog promotions by name in ascending order When I browse catalog promotions And I sort catalog promotions by ascending name Then I should see 3 catalog promotions on the list And the first catalog promotion should have code "a" - @ui + @api @ui Scenario: Sorting catalog promotion by name in descending order When I browse catalog promotions And I sort catalog promotions by descending name Then I should see 3 catalog promotions on the list And the first catalog promotion should have code "c" - @ui @no-postgres + @api @ui @no-postgres Scenario: Sorting catalog promotion by start date in ascending order When I browse catalog promotions And I sort catalog promotions by ascending "start date" Then I should see 3 catalog promotions on the list And the first catalog promotion should have code "not-b" - @ui @no-postgres + @api @ui @no-postgres Scenario: Sorting catalog promotion by start date in descending order When I browse catalog promotions And I sort catalog promotions by descending "start date" Then I should see 3 catalog promotions on the list And the first catalog promotion should have code "a" - @ui @no-postgres + @api @ui @no-postgres Scenario: Sorting catalog promotion by end date in ascending order When I browse catalog promotions And I sort catalog promotions by ascending "end date" Then I should see 3 catalog promotions on the list And the first catalog promotion should have code "a" - @ui @no-postgres + @api @ui @no-postgres Scenario: Sorting catalog promotion by end date in descending order When I browse catalog promotions And I sort catalog promotions by descending "end date" Then I should see 3 catalog promotions on the list And the first catalog promotion should have code "c" - @ui + @api @ui Scenario: Sorting catalog promotion by priority in ascending order When I browse catalog promotions And I sort catalog promotions by ascending priority Then I should see 3 catalog promotions on the list And the first catalog promotion should have code "not-b" - @ui + @api @ui Scenario: Sorting catalog promotion by priority in descending order When I browse catalog promotions And I sort catalog promotions by descending priority diff --git a/features/promotion/tracking_usage/checking_promotion_usage.feature b/features/promotion/tracking_usage/checking_promotion_usage.feature index d8fd2c99709..3cfae047d5d 100644 --- a/features/promotion/tracking_usage/checking_promotion_usage.feature +++ b/features/promotion/tracking_usage/checking_promotion_usage.feature @@ -16,7 +16,7 @@ Feature: Checking a promotion usage after placing an order And there is a promotion "Christmas promotion" And I am logged in as an administrator - @ui + @api @ui Scenario: Seeing item fixed discount promotion usage unchanged after order placement Given the promotion gives "$10.00" off on every product with minimum price at "$50.00" And the promotion gives another "$5.00" off on every product classified as "T-Shirts" @@ -26,7 +26,7 @@ Feature: Checking a promotion usage after placing an order When I browse promotions Then the promotion "Christmas promotion" should not be used - @ui + @api @ui Scenario: Seeing item fixed discount promotion usage increased after order placement Given the promotion gives "$10.00" off on every product with minimum price at "$50.00" And the promotion gives another "$5.00" off on every product classified as "Mugs" @@ -36,7 +36,7 @@ Feature: Checking a promotion usage after placing an order When I browse promotions Then the promotion "Christmas promotion" should be used 1 time - @ui + @api @ui Scenario: Seeing shipping percentage discount promotion usage unchanged after order placement Given the promotion gives "100%" discount on shipping to every order And there is a customer "john.doe@gmail.com" that placed an order "#00000022" diff --git a/features/promotion/tracking_usage/decreasing_promotion_coupon_usage_after_cancelling_order.feature b/features/promotion/tracking_usage/decreasing_promotion_coupon_usage_after_cancelling_order.feature index 5a8204d9c46..e20c381ff99 100644 --- a/features/promotion/tracking_usage/decreasing_promotion_coupon_usage_after_cancelling_order.feature +++ b/features/promotion/tracking_usage/decreasing_promotion_coupon_usage_after_cancelling_order.feature @@ -12,7 +12,7 @@ Feature: Decreasing a promotion coupon usage after cancelling an order And the store has promotion "Christmas sale" with coupon "SANTA2016" And I am logged in as an administrator - @ui + @api @ui Scenario: Seeing promotion coupon usage decreased after order cancellation Given there is a customer "john.doe@gmail.com" that placed an order "#00000022" And the customer bought a single "PHP T-Shirt" using "SANTA2016" coupon @@ -21,7 +21,7 @@ Feature: Decreasing a promotion coupon usage after cancelling an order When I browse all coupons of "Christmas sale" promotion Then "SANTA2016" coupon should be used 0 times - @ui + @api @ui Scenario: Seeing promotion coupon usage decreased to 1 after second order cancellation Given there is a customer "john.doe@gmail.com" that placed an order "#00000022" And the customer bought a single "PHP T-Shirt" using "SANTA2016" coupon diff --git a/features/promotion/tracking_usage/decreasing_promotion_usage_after_cancelling_order.feature b/features/promotion/tracking_usage/decreasing_promotion_usage_after_cancelling_order.feature index 70f70f7f534..5b739c684c5 100644 --- a/features/promotion/tracking_usage/decreasing_promotion_usage_after_cancelling_order.feature +++ b/features/promotion/tracking_usage/decreasing_promotion_usage_after_cancelling_order.feature @@ -12,7 +12,7 @@ Feature: Decreasing a promotion usage after cancelling an order And there is a promotion "Limited promotion" limited to 5 usages And I am logged in as an administrator - @ui + @api @ui Scenario: Seeing promotion usage decreased after order cancellation Given there is a customer "john.doe@gmail.com" that placed an order "#00000022" And the customer bought a single "PHP T-Shirt" diff --git a/features/promotion/tracking_usage/increasing_promotion_coupon_usage_after_placing_order.feature b/features/promotion/tracking_usage/increasing_promotion_coupon_usage_after_placing_order.feature index 06cc8c901bd..bea00a7c3f9 100644 --- a/features/promotion/tracking_usage/increasing_promotion_coupon_usage_after_placing_order.feature +++ b/features/promotion/tracking_usage/increasing_promotion_coupon_usage_after_placing_order.feature @@ -12,7 +12,7 @@ Feature: Increasing a promotion coupon usage after placing an order And the store has promotion "Christmas sale" with coupon "SANTA2016" And I am logged in as an administrator - @ui + @api @ui Scenario: Seeing promotion coupon usage increased after order placement Given there is a customer "john.doe@gmail.com" that placed an order "#00000022" And the customer bought a single "PHP T-Shirt" using "SANTA2016" coupon @@ -20,7 +20,7 @@ Feature: Increasing a promotion coupon usage after placing an order When I browse all coupons of "Christmas sale" promotion Then "SANTA2016" coupon should be used 1 time - @ui + @api @ui Scenario: Seeing promotion coupon usage increased correctly after few orders placement Given there is a customer "john.doe@gmail.com" that placed an order "#00000022" And the customer bought a single "PHP T-Shirt" using "SANTA2016" coupon diff --git a/features/promotion/tracking_usage/increasing_promotion_usage_after_placing_order.feature b/features/promotion/tracking_usage/increasing_promotion_usage_after_placing_order.feature index be78161f541..412a1c5e743 100644 --- a/features/promotion/tracking_usage/increasing_promotion_usage_after_placing_order.feature +++ b/features/promotion/tracking_usage/increasing_promotion_usage_after_placing_order.feature @@ -13,7 +13,7 @@ Feature: Increasing a promotion usage after placing an order And it gives "$10.00" discount to every order And I am logged in as an administrator - @ui + @api @ui Scenario: Seeing promotion usage increased after order placement Given there is a customer "john.doe@gmail.com" that placed an order "#00000022" And the customer bought a single "PHP T-Shirt" diff --git a/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php b/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php index 3c0f48402b8..3a24e3240f8 100644 --- a/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php +++ b/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php @@ -888,6 +888,18 @@ public function iFilterByDateFromDateToDate(string $dateType, string $fromDate, $this->client->filter(); } + /** + * @When I sort catalog promotions by :order :field + */ + public function iSortCatalogPromotionByOrderField(string $order, string $field): void + { + $this->client->addFilter( + sprintf('order[%s]', lcfirst(str_replace(' ', '', ucwords($field)))), + $order === 'descending' ? 'desc' : 'asc', + ); + $this->client->filter(); + } + /** * @When I request the removal of :catalogPromotion catalog promotion */ @@ -1554,6 +1566,24 @@ public function iShouldNotSeeACatalogPromotionWithName(string $name): void ); } + /** + * @Then I should see :count catalog promotions on the list + */ + public function iShouldSeeCountCatalogPromotionsOnTheList(int $count): void + { + Assert::count($this->responseChecker->getCollection($this->client->getLastResponse()), $count); + } + + /** + * @Then the first catalog promotion should have code :code + */ + public function theFirstCatalogPromotionShouldHaveCode(string $code): void + { + $catalogPromotions = $this->responseChecker->getCollection($this->client->getLastResponse()); + + Assert::same(reset($catalogPromotions)['code'], $code); + } + private function catalogPromotionAppliesOnVariants(ProductVariantInterface ...$productVariants): bool { $response = $this->responseChecker->getResponseContent($this->client->getLastResponse()); diff --git a/src/Sylius/Behat/Context/Api/Admin/ManagingProductTaxonsContext.php b/src/Sylius/Behat/Context/Api/Admin/ManagingProductTaxonsContext.php index f9ebff9202c..709682c7bb0 100644 --- a/src/Sylius/Behat/Context/Api/Admin/ManagingProductTaxonsContext.php +++ b/src/Sylius/Behat/Context/Api/Admin/ManagingProductTaxonsContext.php @@ -28,21 +28,11 @@ public function __construct(private ApiClientInterface $client, private IriConve { } - /** - * @When I change that the :product product belongs to the :taxon taxon - */ - public function iChangeThatTheProductBelongsToTheTaxon(ProductInterface $product, TaxonInterface $taxon): void - { - $this->client->buildUpdateRequest(Resources::PRODUCT_TAXONS, (string) $product->getProductTaxons()->current()->getId()); - $this->client->updateRequestData(['taxon' => $this->iriConverter->getIriFromResource($taxon)]); - $this->client->update(); - } - /** * @When I assign the :taxon taxon to the :product product * @When I (try to) add :taxon taxon to the :product product */ - public function iAddTaxonToTheProduct(TaxonInterface $taxon, ProductInterface $product): void + public function iAddTaxonToTheProduct(ProductInterface $product, TaxonInterface $taxon): void { $this->client->buildCreateRequest(Resources::PRODUCT_TAXONS); $this->client->addRequestData('taxon', $this->iriConverter->getIriFromResource($taxon)); @@ -76,15 +66,22 @@ public function iTryToAssignAnEmptyProductToTheTaxon(TaxonInterface $taxon): voi public function iTryToAssignTheProductTaxonOfProductAndTaxonToTheProduct( ProductInterface $productTaxonProduct, TaxonInterface $productTaxonTaxon, + ): void { + $this->iAddTaxonToTheProduct($productTaxonProduct, $productTaxonTaxon); + } + + /** + * @When I change that the :product product does not belong to the :taxon taxon + */ + public function iChangeThatTheProductDoesNotBelongToTheTaxon( ProductInterface $product, + TaxonInterface $taxon, ): void { - $productTaxon = $productTaxonProduct->getProductTaxons()->filter( - fn (ProductTaxonInterface $productTaxon) => $productTaxonTaxon === $productTaxon->getTaxon(), + $productTaxon = $product->getProductTaxons()->filter( + fn (ProductTaxonInterface $productTaxon) => $taxon === $productTaxon->getTaxon(), )->first(); - $this->client->buildUpdateRequest(Resources::PRODUCTS, $product->getCode()); - $this->client->addRequestData('productTaxons', [$this->iriConverter->getIriFromResource($productTaxon)]); - $this->client->update(); + $this->client->delete(Resources::PRODUCT_TAXONS, (string) $productTaxon->getId()); } /** diff --git a/src/Sylius/Behat/Context/Api/Admin/ManagingProductsContext.php b/src/Sylius/Behat/Context/Api/Admin/ManagingProductsContext.php index fce2dbaf3b5..ece2895a51c 100644 --- a/src/Sylius/Behat/Context/Api/Admin/ManagingProductsContext.php +++ b/src/Sylius/Behat/Context/Api/Admin/ManagingProductsContext.php @@ -557,13 +557,13 @@ public function thisProductMainTaxonShouldBe(ProductInterface $product, TaxonInt */ public function thisProductTaxonShouldBe(ProductInterface $product, TaxonInterface $taxon): void { - $productTaxonId = $this->getProductTaxonId($product); - - $response = $this->client->show(Resources::PRODUCT_TAXONS, (string) $productTaxonId); - $productTaxonIri = $this->responseChecker->getValue($response, 'taxon'); - $productTaxonCodes = explode('/', $productTaxonIri); - - Assert::same(array_pop($productTaxonCodes), $taxon->getCode()); + $this->client->index(Resources::PRODUCT_TAXONS); + Assert::true( + $this->responseChecker->hasItemWithValues($this->client->getLastResponse(), [ + 'product' => $this->sectionAwareIriConverter->getIriFromResourceInSection($product, 'admin'), + 'taxon' => $this->sectionAwareIriConverter->getIriFromResourceInSection($taxon, 'admin'), + ]) + ); } /** @@ -871,14 +871,6 @@ private function getLastResponse(): Response return $this->sharedStorage->has('response') ? $this->sharedStorage->get('response') : $this->client->getLastResponse(); } - private function getProductTaxonId(ProductInterface $product): string - { - $productResponse = $this->client->show(Resources::PRODUCTS, (string) $product->getCode()); - $productTaxonUrl = explode('/', $this->responseChecker->getValue($productResponse, 'productTaxons')[0]); - - return array_pop($productTaxonUrl); - } - private function getAttributeValueInProperType( ProductAttributeInterface $productAttribute, string $value, diff --git a/src/Sylius/Behat/Context/Api/Admin/ManagingPromotionCouponsContext.php b/src/Sylius/Behat/Context/Api/Admin/ManagingPromotionCouponsContext.php index 359f4c31d3d..1f82672be49 100644 --- a/src/Sylius/Behat/Context/Api/Admin/ManagingPromotionCouponsContext.php +++ b/src/Sylius/Behat/Context/Api/Admin/ManagingPromotionCouponsContext.php @@ -40,6 +40,7 @@ public function __construct( /** * @Given /^I am browsing coupons of (this promotion)$/ * @When /^I want to view all coupons of (this promotion)$/ + * @When /^I browse all coupons of ("[^"]+" promotion)$/ */ public function iWantToViewAllCouponsOfThisPromotion(PromotionInterface $promotion): void { @@ -610,6 +611,24 @@ public function iShouldNotBeAbleToEditItsCode(): void Assert::false($this->responseChecker->hasValue($this->client->update(), 'code', 'NEW_CODE')); } + /** + * @Then /^("[^"]+" coupon) should be used (\d+) time(?:|s)$/ + */ + public function couponShouldHaveUsageLimit(PromotionCouponInterface $promotionCoupon, int $used): void + { + $returnedPromotionCoupon = current($this->responseChecker->getCollectionItemsWithValue( + $this->client->getLastResponse(), + 'code', + $promotionCoupon->getCode(), + )); + + Assert::same( + $returnedPromotionCoupon['used'], + $used, + sprintf('The promotion coupon %s has been used %s times', $promotionCoupon->getCode(), $returnedPromotionCoupon['used']), + ); + } + private function sortBy(string $order, string $field): void { $this->client->sort([$field => str_starts_with($order, 'de') ? 'desc' : 'asc']); diff --git a/src/Sylius/Behat/Context/Api/Admin/ManagingPromotionsContext.php b/src/Sylius/Behat/Context/Api/Admin/ManagingPromotionsContext.php index be6b1735a95..943ae54483b 100644 --- a/src/Sylius/Behat/Context/Api/Admin/ManagingPromotionsContext.php +++ b/src/Sylius/Behat/Context/Api/Admin/ManagingPromotionsContext.php @@ -827,6 +827,25 @@ public function theFirstPromotionOnTheListShouldHave(string $togglePosition, str Assert::same($item[$field], $value); } + /** + * @Then the promotion :promotion should be used :usage time(s) + * @Then the promotion :promotion should not be used + */ + public function thePromotionShouldBeUsedTime(PromotionInterface $promotion, int $usage = 0): void + { + $returnedPromotion = current($this->responseChecker->getCollectionItemsWithValue( + $this->client->getLastResponse(), + 'code', + $promotion->getCode() + )); + + Assert::same( + $returnedPromotion['used'], + $usage, + sprintf('The promotion %s has been used %s times', $promotion->getName(), $returnedPromotion['used']), + ); + } + private function addToRequestAction(string $type, array $configuration): void { $data['actions'][] = [ diff --git a/src/Sylius/Behat/Context/Ui/Admin/ManagingProductTaxonsContext.php b/src/Sylius/Behat/Context/Ui/Admin/ManagingProductTaxonsContext.php index c323150fd15..2628a21596e 100644 --- a/src/Sylius/Behat/Context/Ui/Admin/ManagingProductTaxonsContext.php +++ b/src/Sylius/Behat/Context/Ui/Admin/ManagingProductTaxonsContext.php @@ -25,11 +25,10 @@ public function __construct(private UpdateSimpleProductPageInterface $updateSimp } /** - * @When I change that the :product product belongs to the :taxon taxon * @When I add :taxon taxon to the :product product * @When I assign the :taxon taxon to the :product product */ - public function iChangeThatTheProductBelongsToTheTaxon(ProductInterface $product, TaxonInterface $taxon): void + public function iAddTaxonToTheProduct(ProductInterface $product, TaxonInterface $taxon): void { $this->updateSimpleProductPage->open(['id' => $product->getId()]); $this->updateSimpleProductPage->selectProductTaxon($taxon); diff --git a/src/Sylius/Behat/Resources/config/suites/api/promotion/managing_promotions.yml b/src/Sylius/Behat/Resources/config/suites/api/promotion/managing_promotions.yml index 0bb033a8949..c530c0b5e52 100644 --- a/src/Sylius/Behat/Resources/config/suites/api/promotion/managing_promotions.yml +++ b/src/Sylius/Behat/Resources/config/suites/api/promotion/managing_promotions.yml @@ -27,6 +27,7 @@ default: - sylius.behat.context.setup.order - sylius.behat.context.setup.payment - sylius.behat.context.setup.product + - sylius.behat.context.setup.product_taxon - sylius.behat.context.setup.promotion - sylius.behat.context.setup.shipping - sylius.behat.context.setup.taxonomy diff --git a/src/Sylius/Bundle/ApiBundle/DataPersister/ProductTaxonDataPersister.php b/src/Sylius/Bundle/ApiBundle/DataPersister/ProductTaxonDataPersister.php index 9df5959872e..1d9a3a25b2e 100644 --- a/src/Sylius/Bundle/ApiBundle/DataPersister/ProductTaxonDataPersister.php +++ b/src/Sylius/Bundle/ApiBundle/DataPersister/ProductTaxonDataPersister.php @@ -44,8 +44,11 @@ public function persist($data, array $context = []) $this->eventBus->dispatch(new ProductUpdated($product->getCode())); } + /** @param ProductTaxonInterface $data */ public function remove($data, array $context = []) { - return $this->decoratedDataPersister->remove($data, $context); + $this->decoratedDataPersister->remove($data, $context); + + $this->eventBus->dispatch(new ProductUpdated($data->getProduct()->getCode())); } } diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/CatalogPromotion.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/CatalogPromotion.xml index 7b2f99a93ad..6ad03995623 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/CatalogPromotion.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/CatalogPromotion.xml @@ -29,6 +29,11 @@ sylius.api.catalog_promotion_start_date_filter sylius.api.catalog_promotion_end_date_filter Sylius\Bundle\ApiBundle\Filter\Doctrine\CatalogPromotionChannelFilter + sylius.api.order_filter.code + sylius.api.order_filter.name + sylius.api.order_filter.start_date + sylius.api.order_filter.end_date + sylius.api.order_filter.priority admin:catalog_promotion:read diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/ProductTaxon.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/ProductTaxon.xml index 9c2ab6dfe9f..c0bc31adad8 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/ProductTaxon.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources/ProductTaxon.xml @@ -14,6 +14,10 @@ admin:product_taxon:read + + sylius.api.search_filter.product.code + sylius.api.search_filter.taxon.code + POST @@ -46,6 +50,11 @@ shop:product_taxon:read + + + DELETE + /admin/product-taxons/{id} + diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/serialization/Product.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/serialization/Product.xml index f19d2640313..a95a3d961fb 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/serialization/Product.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/serialization/Product.xml @@ -59,8 +59,6 @@ admin:product:read - admin:product:create - admin:product:update shop:product:read diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/serialization/ProductTaxon.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/serialization/ProductTaxon.xml index d394803bd36..61604865b56 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/serialization/ProductTaxon.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/serialization/ProductTaxon.xml @@ -32,5 +32,11 @@ admin:product_taxon:create shop:product_taxon:read + + admin:product_taxon:read + admin:product_taxon:update + admin:product_taxon:create + shop:product_taxon:read + diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/services/filters.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/services/filters.xml index 5ee5e433acd..30b875f7421 100644 --- a/src/Sylius/Bundle/ApiBundle/Resources/config/services/filters.xml +++ b/src/Sylius/Bundle/ApiBundle/Resources/config/services/filters.xml @@ -339,5 +339,55 @@ + + + + exact + + + + + + + exact + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sylius/Bundle/ApiBundle/spec/DataPersister/ProductTaxonDataPersisterSpec.php b/src/Sylius/Bundle/ApiBundle/spec/DataPersister/ProductTaxonDataPersisterSpec.php index 4d0c883c28f..1b42b121e1f 100644 --- a/src/Sylius/Bundle/ApiBundle/spec/DataPersister/ProductTaxonDataPersisterSpec.php +++ b/src/Sylius/Bundle/ApiBundle/spec/DataPersister/ProductTaxonDataPersisterSpec.php @@ -39,8 +39,15 @@ function it_supports_only_product_taxon_entity(ProductTaxonInterface $productTax function it_uses_decorated_data_persister_to_remove_product_taxon( ContextAwareDataPersisterInterface $decoratedDataPersister, ProductTaxonInterface $productTaxon, + ProductInterface $product, + MessageBusInterface $eventBus, ): void { + $productTaxon->getProduct()->willReturn($product); + $product->getCode()->willReturn('t_shirt'); + $message = new ProductUpdated('t_shirt'); + $decoratedDataPersister->remove($productTaxon, [])->shouldBeCalled(); + $eventBus->dispatch($message)->willReturn(new Envelope($message))->shouldBeCalled(); $this->remove($productTaxon, []); } diff --git a/tests/Api/Admin/ProductTaxonsTest.php b/tests/Api/Admin/ProductTaxonsTest.php new file mode 100644 index 00000000000..8ea20505cbd --- /dev/null +++ b/tests/Api/Admin/ProductTaxonsTest.php @@ -0,0 +1,135 @@ +loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'product/product_taxon.yaml']); + $header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER); + + /** @var ProductTaxonInterface $productTaxon */ + $productTaxon = $fixtures['product_mug_taxon_mugs']; + + $this->client->request( + method: 'GET', + uri: sprintf('/api/v2/admin/product-taxons/%s', $productTaxon->getId()), + server: $header, + ); + + $this->assertResponse( + $this->client->getResponse(), + 'admin/product_taxon/get_product_taxon_response', + Response::HTTP_OK, + ); + } + + /** @test */ + public function it_gets_product_taxons(): void + { + $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'product/product_taxon.yaml']); + $header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER); + + $this->client->request(method: 'GET', uri: '/api/v2/admin/product-taxons', server: $header); + + $this->assertResponse( + $this->client->getResponse(), + 'admin/product_taxon/get_product_taxons_response', + Response::HTTP_OK, + ); + } + + /** @test */ + public function it_creates_a_product_taxon(): void + { + $fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'product/product_taxon.yaml']); + $header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER); + + /** @var ProductInterface $product */ + $product = $fixtures['product_mug']; + /** @var TaxonInterface $taxon */ + $taxon = $fixtures['taxon_caps']; + + $this->client->request( + method: 'POST', + uri: '/api/v2/admin/product-taxons', + server: $header, + content: json_encode([ + 'product' => sprintf('/api/v2/admin/products/%s', $product->getCode()), + 'taxon' => sprintf('/api/v2/admin/taxons/%s', $taxon->getCode()), + 'position' => 10, + ], \JSON_THROW_ON_ERROR), + ); + + $this->assertResponse( + $this->client->getResponse(), + 'admin/product_taxon/post_product_taxon_response', + Response::HTTP_CREATED, + ); + } + + /** @test */ + public function it_updates_a_product_taxon(): void + { + $fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'product/product_taxon.yaml']); + $header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER); + + /** @var ProductTaxonInterface $productTaxon */ + $productTaxon = $fixtures['product_cap_taxon_caps']; + + $this->client->request( + method: 'PUT', + uri: sprintf('/api/v2/admin/product-taxons/%s', $productTaxon->getId()), + server: $header, + content: json_encode([ + 'position' => 1, + ], \JSON_THROW_ON_ERROR), + ); + + $this->assertResponse( + $this->client->getResponse(), + 'admin/product_taxon/put_product_taxon_response', + Response::HTTP_OK, + ); + } + + /** @test */ + public function it_deletes_a_product_taxon(): void + { + $fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'product/product_taxon.yaml']); + $header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER); + + /** @var ProductTaxonInterface $productTaxon */ + $productTaxon = $fixtures['product_cap_taxon_caps']; + + $this->client->request( + method: 'DELETE', + uri: sprintf('/api/v2/admin/product-taxons/%s', $productTaxon->getId()), + server: $header, + ); + + $this->assertResponseCode($this->client->getResponse(), Response::HTTP_NO_CONTENT); + } +} diff --git a/tests/Api/DataFixtures/ORM/product/product_taxon.yaml b/tests/Api/DataFixtures/ORM/product/product_taxon.yaml new file mode 100644 index 00000000000..252bdd3e2cc --- /dev/null +++ b/tests/Api/DataFixtures/ORM/product/product_taxon.yaml @@ -0,0 +1,25 @@ +Sylius\Component\Core\Model\Product: + product_mug: + code: 'MUG' + channels: ['@channel_web'] + currentLocale: 'en_US' + product_cap: + code: 'CAP' + channels: ['@channel_web'] + currentLocale: 'en_US' + +Sylius\Component\Core\Model\Taxon: + taxon_mugs: + code: 'MUGS' + taxon_caps: + code: 'CAPS' + +Sylius\Component\Core\Model\ProductTaxon: + product_mug_taxon_mugs: + product: '@product_mug' + taxon: '@taxon_mugs' + position: 1 + product_cap_taxon_caps: + product: '@product_cap' + taxon: '@taxon_caps' + position: 2 diff --git a/tests/Api/Responses/Expected/admin/channel/get_channel_response.json b/tests/Api/Responses/Expected/admin/channel/get_channel_response.json index beca1a59d74..37eb069237a 100644 --- a/tests/Api/Responses/Expected/admin/channel/get_channel_response.json +++ b/tests/Api/Responses/Expected/admin/channel/get_channel_response.json @@ -13,10 +13,7 @@ "defaultTaxZone": null, "taxCalculationStrategy": "order_items_based", "currencies": [], - "locales": [ - "\/api\/v2\/admin\/locales\/en_US", - "\/api\/v2\/admin\/locales\/pl_PL" - ], + "locales": "@array@.inArray(\"/api\/v2\/admin\/locales\/en_US\").inArray(\"\/api\/v2\/admin\/locales\/pl_PL\")", "countries": [], "themeName": null, "contactEmail": "web@sylius.com", diff --git a/tests/Api/Responses/Expected/admin/product_taxon/get_product_taxon_response.json b/tests/Api/Responses/Expected/admin/product_taxon/get_product_taxon_response.json new file mode 100644 index 00000000000..f4ea1e84dae --- /dev/null +++ b/tests/Api/Responses/Expected/admin/product_taxon/get_product_taxon_response.json @@ -0,0 +1,9 @@ +{ + "@context": "\/api\/v2\/contexts\/ProductTaxon", + "@id": "\/api\/v2\/admin\/product-taxons\/@integer@", + "@type": "ProductTaxon", + "id": @integer@, + "product": "\/api\/v2\/admin\/products\/MUG", + "taxon": "\/api\/v2\/admin\/taxons\/MUGS", + "position": 1 +} diff --git a/tests/Api/Responses/Expected/admin/product_taxon/get_product_taxons_response.json b/tests/Api/Responses/Expected/admin/product_taxon/get_product_taxons_response.json new file mode 100644 index 00000000000..5a483aa7e09 --- /dev/null +++ b/tests/Api/Responses/Expected/admin/product_taxon/get_product_taxons_response.json @@ -0,0 +1,55 @@ +{ + "@context": "\/api\/v2\/contexts\/ProductTaxon", + "@id": "\/api\/v2\/admin\/product-taxons", + "@type": "hydra:Collection", + "hydra:member": [ + { + "@id": "\/api\/v2\/admin\/product-taxons\/@integer@", + "@type": "ProductTaxon", + "id": @integer@, + "product": "\/api\/v2\/admin\/products\/MUG", + "taxon": "\/api\/v2\/admin\/taxons\/MUGS", + "position": 1 + }, + { + "@id": "\/api\/v2\/admin\/product-taxons\/@integer@", + "@type": "ProductTaxon", + "id": @integer@, + "product": "\/api\/v2\/admin\/products\/CAP", + "taxon": "\/api\/v2\/admin\/taxons\/CAPS", + "position": 2 + } + ], + "hydra:totalItems": 2, + "hydra:search": { + "@type": "hydra:IriTemplate", + "hydra:template": "\/api\/v2\/admin\/product-taxons{?product.code,product.code[],taxon.code,taxon.code[]}", + "hydra:variableRepresentation": "BasicRepresentation", + "hydra:mapping": [ + { + "@type": "IriTemplateMapping", + "variable": "product.code", + "property": "product.code", + "required": false + }, + { + "@type": "IriTemplateMapping", + "variable": "product.code[]", + "property": "product.code", + "required": false + }, + { + "@type": "IriTemplateMapping", + "variable": "taxon.code", + "property": "taxon.code", + "required": false + }, + { + "@type": "IriTemplateMapping", + "variable": "taxon.code[]", + "property": "taxon.code", + "required": false + } + ] + } +} diff --git a/tests/Api/Responses/Expected/admin/product_taxon/post_product_taxon_response.json b/tests/Api/Responses/Expected/admin/product_taxon/post_product_taxon_response.json new file mode 100644 index 00000000000..632c9024586 --- /dev/null +++ b/tests/Api/Responses/Expected/admin/product_taxon/post_product_taxon_response.json @@ -0,0 +1,9 @@ +{ + "@context": "\/api\/v2\/contexts\/ProductTaxon", + "@id": "\/api\/v2\/admin\/product-taxons\/@integer@", + "@type": "ProductTaxon", + "id": @integer@, + "product": "\/api\/v2\/admin\/products\/MUG", + "taxon": "\/api\/v2\/admin\/taxons\/CAPS", + "position": 10 +} diff --git a/tests/Api/Responses/Expected/admin/product_taxon/put_product_taxon_response.json b/tests/Api/Responses/Expected/admin/product_taxon/put_product_taxon_response.json new file mode 100644 index 00000000000..1f2ddd40301 --- /dev/null +++ b/tests/Api/Responses/Expected/admin/product_taxon/put_product_taxon_response.json @@ -0,0 +1,9 @@ +{ + "@context": "\/api\/v2\/contexts\/ProductTaxon", + "@id": "\/api\/v2\/admin\/product-taxons\/@integer@", + "@type": "ProductTaxon", + "id": @integer@, + "product": "\/api\/v2\/admin\/products\/CAP", + "taxon": "\/api\/v2\/admin\/taxons\/CAPS", + "position": 1 +}