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
+}