feature #14811 Cover managing the tax rates in API (hatem20, jakubtobiasz)

This PR was merged into the 1.13 branch.

Discussion
----------

| Q               | A                                                            |
|-----------------|--------------------------------------------------------------|
| Branch?         | 1.13 <!-- see the comment below -->                  |
| Bug fix?        | no                                                       |
| New feature?    | yes                                                       |
| BC breaks?      | no                                                       |
| Deprecations?   | no <!-- don't forget to update the UPGRADE-*.md file --> |
| Related tickets | successor of #11884                      |
| License         | MIT                                                          |


Commits
-------

03c34387a7 [Tax rate][Behat] Add include tax rate step in add tax feature
f84fce7dc9 [API][Tax rate] Implement adding_tax_rate feature
c8fe3b325f [API][Tax rate] Implement deleting_tax_rate feature
e147639d37 [API][Tax rate] Implement deleting_multiple_tax_rates feature
bacef86376 [API][Tax rate] Implement tax_rate_unique_code_validation feature
161d25d6dd [API][Tax rate] Implement tax_rate_validation feature
7cdf3e7983 [API][Tax rate] Implement editing_tax_rate feature
cff62a6337 Adjust Tax Rates API related Behat scenarios to the new standard
This commit is contained in:
Grzegorz Sadowski 2023-02-23 12:50:59 +01:00 committed by GitHub
commit aef3367b2c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 1079 additions and 22 deletions

View file

@ -9,7 +9,7 @@ Feature: Adding a new tax rate
And the store has a tax category "Food and Beverage"
And I am logged in as an administrator
@ui
@api @ui
Scenario: Adding a new tax rate
When I want to create a new tax rate
And I specify its code as "US_SALES_TAX"
@ -22,7 +22,7 @@ Feature: Adding a new tax rate
Then I should be notified that it has been successfully created
And the tax rate "United States Sales Tax" should appear in the registry
@ui
@api @ui
Scenario: Adding a zero tax rate
When I want to create a new tax rate
And I specify its code as "US_SALES_TAX"
@ -35,7 +35,7 @@ Feature: Adding a new tax rate
Then I should be notified that it has been successfully created
And the tax rate "United States Sales Tax" should appear in the registry
@ui
@api @ui
Scenario: Adding a new tax rate with start and end date
When I want to create a new tax rate
And I specify its code as "US_SALES_TAX"
@ -49,7 +49,7 @@ Feature: Adding a new tax rate
Then I should be notified that it has been successfully created
And the tax rate "United States Sales Tax" should appear in the registry
@ui
@api @ui
Scenario: Adding a new tax rate with start date only
When I want to create a new tax rate
And I specify its code as "US_SALES_TAX"
@ -63,7 +63,7 @@ Feature: Adding a new tax rate
Then I should be notified that it has been successfully created
And the tax rate "United States Sales Tax" should appear in the registry
@ui
@api @ui
Scenario: Adding a new tax rate with end date only
When I want to create a new tax rate
And I specify its code as "US_SALES_TAX"
@ -77,7 +77,7 @@ Feature: Adding a new tax rate
Then I should be notified that it has been successfully created
And the tax rate "United States Sales Tax" should appear in the registry
@ui @javascript
@api @ui @javascript
Scenario: Adding a new tax rate which will be included in product price
When I want to create a new tax rate
And I specify its code as "US_SALES_TAX"
@ -90,3 +90,4 @@ Feature: Adding a new tax rate
And I add it
Then I should be notified that it has been successfully created
And the tax rate "United States Sales Tax" should appear in the registry
And the tax rate "United States Sales Tax" should be included in price

View file

@ -11,7 +11,7 @@ Feature: Deleting multiple tax rates
And the store has "High VAT" tax rate of 40% for "Food" for the rest of the world
And I am logged in as an administrator
@ui @javascript
@ui @javascript @api
Scenario: Deleting multiple tax rates at once
When I browse tax rates
And I check the "Low VAT" tax rate

View file

@ -10,7 +10,7 @@ Feature: Deleting a tax rate
And the store has "United States Sales Tax" tax rate of 20% for "Sports gear" within the "US" zone
And I am logged in as an administrator
@ui
@ui @api
Scenario: Deleted tax rate should disappear from the registry
When I delete tax rate "United States Sales Tax"
Then I should be notified that it has been successfully deleted

View file

@ -18,12 +18,12 @@ Feature: Editing tax rate
Then I should be notified that code cannot be changed
And tax rate "United States Sales Tax" should still have code "united_states_sales_tax"
@ui
@ui @api
Scenario: Seeing disabled code field when editing tax rate
When I want to modify a tax rate "United States Sales Tax"
Then the code field should be disabled
@ui
@ui @api
Scenario: Renaming the tax rate
When I want to modify a tax rate "United States Sales Tax"
And I rename it to "US VAT"
@ -31,7 +31,7 @@ Feature: Editing tax rate
Then I should be notified that it has been successfully edited
And this tax rate name should be "US VAT"
@ui
@ui @api
Scenario: Changing the tax rate amount
When I want to modify a tax rate "United States Sales Tax"
And I specify its amount as 16%
@ -39,7 +39,7 @@ Feature: Editing tax rate
Then I should be notified that it has been successfully edited
And this tax rate amount should be 16%
@ui
@ui @api
Scenario: Changing related tax category
Given the store has a tax category "Food and Beverage" also
When I want to modify a tax rate "United States Sales Tax"
@ -48,7 +48,7 @@ Feature: Editing tax rate
Then I should be notified that it has been successfully edited
And this tax rate should be applicable for the "Food and Beverage" tax category
@ui
@ui @api
Scenario: Changing related zone
Given there is a zone "The Rest of the World" containing all other countries
When I want to modify a tax rate "United States Sales Tax"

View file

@ -10,7 +10,7 @@ Feature: Tax rate unique code validation
And the store has "United States Sales Tax" tax rate of 20% for "Sports gear" within the "US" zone identified by the "UNITED_STATES_SALES_TAX" code
And I am logged in as an administrator
@ui
@ui @api
Scenario: Trying to add tax rate with taken code
When I want to create a new tax rate
And I specify its code as "UNITED_STATES_SALES_TAX"

View file

@ -9,7 +9,7 @@ Feature: Tax rate validation
And the store has a tax category "Food and Beverage"
And I am logged in as an administrator
@ui
@ui @api
Scenario: Trying to add a new tax rate without specifying its code
When I want to create a new tax rate
And I name it "Food and Beverage Tax Rates"
@ -27,7 +27,7 @@ Feature: Tax rate validation
Then I should be notified that amount is required
And tax rate with name "Food and Beverage Tax Rates" should not be added
@ui
@ui @api
Scenario: Trying to add a new tax rate without specifying its name
When I want to create a new tax rate
And I specify its code as "UNITED_STATES_SALES_TAX"
@ -36,7 +36,7 @@ Feature: Tax rate validation
Then I should be notified that name is required
And tax rate with code "UNITED_STATES_SALES_TAX" should not be added
@ui
@ui @api
Scenario: Trying to add a new tax rate without specifying its zone
Given the store does not have any zones defined
When I want to create a new tax rate
@ -46,7 +46,7 @@ Feature: Tax rate validation
Then I should be notified that zone has to be selected
And tax rate with name "Food and Beverage Tax Rates" should not be added
@ui
@ui @api
Scenario: Trying to add a new tax rate without specifying its category
Given the store does not have any categories defined
When I want to create a new tax rate
@ -65,7 +65,7 @@ Feature: Tax rate validation
Then I should be notified that amount is required
And this tax rate amount should still be 20%
@ui
@ui @api
Scenario: Trying to remove name from existing tax rate
Given the store has "United States Sales Tax" tax rate of 20% for "Sports gear" within the "US" zone
When I want to modify this tax rate

View file

@ -4,7 +4,7 @@ Feature: Filtering tax rates by end date
As an Administrator
I want to be able to filter tax rates on the list
Background:
Background:
Given the store operates on a single channel in "United States"
And the store has "2022 tax rate" tax rate of 50% for "Clothes" within the "US" zone with dates between "2022-01-01" and "2022-12-31"
And the store has "2023 tax rate" tax rate of 15% for "Clothes" within the "US" zone with dates between "2023-01-01" and "2023-12-31"
@ -20,7 +20,7 @@ Feature: Filtering tax rates by end date
And I should see the tax rate "3 weeks tax rate" in the list
@ui @api
Scenario: Filtering catalog promotions up to end date
Scenario: Filtering tax rates up to end date
When I browse tax rates
And I filter tax rates by end date up to "2022-12-31"
Then I should not see a tax rate with name "2023 tax rate"
@ -28,7 +28,7 @@ Feature: Filtering tax rates by end date
But I should see the tax rate "2022 tax rate" in the list
@ui @api
Scenario: Filtering catalog promotions in a end date range
Scenario: Filtering tax rates in a end date range
When I browse tax rates
And I filter tax rates by end date from "2023-01-02" up to "2023-01-31"
Then I should not see a tax rate with name "2023 tax rate"

View file

@ -0,0 +1,521 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Context\Api\Admin;
use ApiPlatform\Core\Api\IriConverterInterface;
use Behat\Behat\Context\Context;
use Sylius\Behat\Client\ApiClientInterface;
use Sylius\Behat\Client\ResponseCheckerInterface;
use Sylius\Behat\Context\Api\Resources;
use Sylius\Behat\Service\SharedStorageInterface;
use Sylius\Component\Addressing\Model\ZoneInterface;
use Sylius\Component\Core\Model\TaxRateInterface;
use Sylius\Component\Taxation\Model\TaxCategoryInterface;
use Webmozart\Assert\Assert;
class ManagingTaxRatesContext implements Context
{
public function __construct(
private ApiClientInterface $client,
private ResponseCheckerInterface $responseChecker,
private IriConverterInterface $iriConverter,
private SharedStorageInterface $sharedStorage
) {
}
/**
* @When I want to create a new tax rate
*/
public function iWantToCreateANewTaxRate(): void
{
$this->client->buildCreateRequest(Resources::TAX_RATES);
}
/**
* @When I specify its code as :code
* @When I do not specify its code
*/
public function iSpecifyItsCodeAs(string $code = ''): void
{
$this->client->addRequestData('code', $code);
}
/**
* @When I name it :name
* @When I do not name it
* @When I rename it to :name
*/
public function iNameIt(string $name = ''): void
{
$this->client->addRequestData('name', $name);
}
/**
* @When I define it for the :zone zone
* @When I do not specify its zone
* @When I change its zone to :zone
*/
public function iDefineItForTheZone(?ZoneInterface $zone = null): void
{
if (null === $zone) {
return;
}
$this->client->addRequestData('zone', $this->iriConverter->getIriFromItem($zone));
}
/**
* @When I make it applicable for the :taxCategory tax category
* @When I change it to be applicable for the :taxCategory tax category
* @When I do not specify related tax category
*/
public function iMakeItApplicableForTheTaxCategory(?TaxCategoryInterface $taxCategory = null): void
{
if (null === $taxCategory) {
return;
}
$this->client->addRequestData('category', $this->iriConverter->getIriFromItem($taxCategory));
}
/**
* @When I specify its amount as :amount%
* @When I do not specify its amount
*/
public function iSpecifyItsAmountAs(?string $amount = null): void
{
if (null === $amount) {
return;
}
$this->client->addRequestData('amount', $amount);
}
/**
* @When I choose the default tax calculator
*/
public function iChooseTheDefaultTaxCalculator(): void
{
$this->client->addRequestData('calculator', 'default');
}
/**
* @When I make it start at :startDate and end at :endDate
*/
public function iMakeItStartAtAndEndAt(string $startDate, string $endDate): void
{
$this->client->addRequestData('startDate', $startDate);
$this->client->addRequestData('endDate', $endDate);
}
/**
* @When I set the start date to :startDate
*/
public function iSetTheStartDateTo(string $startDate): void
{
$this->client->addRequestData('startDate', $startDate);
}
/**
* @When I set the end date to :endDate
*/
public function iSetTheEndDateTo(string $endDate): void
{
$this->client->addRequestData('endDate', $endDate);
}
/**
* @When I add it
* @When I try to add it
*/
public function iAddIt(): void
{
$this->client->create();
}
/**
* @When I choose "Included in price" option
*/
public function iChooseOption()
{
$this->client->addRequestData('includedInPrice', true);
}
/**
* @When /^I want to modify (this tax rate)$/
* @When I want to modify a tax rate :taxRate
*/
public function iWantToModifyThisTaxRate(TaxRateInterface $taxRate): void
{
$this->client->buildUpdateRequest(Resources::TAX_RATES, (string) $taxRate->getCode());
$this->client->addRequestData('amount', (string) $taxRate->getAmount());
}
/**
* @When I remove its amount
*/
public function iRemoveItsAmount(): void
{
$this->client->addRequestData('amount', '');
}
/**
* @When I save my changes
* @When I try to save my changes
*/
public function iSaveMyChanges(): void
{
$this->client->update();
}
/**
* @When I browse tax rates
*/
public function iBrowseTaxRates(): void
{
$this->client->index(Resources::TAX_RATES);
}
/**
* @When I check the :taxRate tax rate
* @When I check also the :taxRate tax rate
*/
public function iCheckTheTaxRate(TaxRateInterface $taxRate): void
{
$taxRatesToDelete = [];
if ($this->sharedStorage->has('tax_rates_to_delete')) {
$taxRatesToDelete = $this->sharedStorage->get('tax_rates_to_delete');
}
$taxRatesToDelete[] = $taxRate->getCode();
$this->sharedStorage->set('tax_rates_to_delete', $taxRatesToDelete);
}
/**
* @When I delete them
*/
public function iDeleteThem(): void
{
foreach ($this->sharedStorage->get('tax_rates_to_delete') as $id) {
$this->client->delete(Resources::TAX_RATES, (string) $id)->getContent();
}
}
/**
* @When I remove its name
*/
public function iRemoveItsName(): void
{
$this->client->addRequestData('name', '');
}
/**
* @When I filter tax rates by start date from :startDate
*/
public function iFilterTaxRatesByStartDateFrom(string $startDate): void
{
$this->client->addFilter('startDate[after]', $startDate);
$this->client->filter();
}
/**
* @When I filter tax rates by start date up to :startDate
*/
public function iFilterTaxRatesByStartDateUpTo(string $startDate): void
{
$this->client->addFilter('startDate[before]', $startDate);
$this->client->filter();
}
/**
* @When I filter tax rates by start date from :startDate up to :endDate
*/
public function iFilterTaxRatesByStartDateFromUpTo(string $startDate, string $endDate): void
{
$this->client->addFilter('startDate[after]', $startDate);
$this->client->addFilter('startDate[before]', $endDate);
$this->client->filter();
}
/**
* @When I filter tax rates by end date from :endDate
*/
public function iFilterTaxRatesByEndDateFrom(string $endDate): void
{
$this->client->addFilter('endDate[after]', $endDate);
$this->client->filter();
}
/**
* @When I filter tax rates by end date up to :endDate
*/
public function iFilterTaxRatesByEndDateUpTo(string $endDate): void
{
$this->client->addFilter('endDate[before]', $endDate);
$this->client->filter();
}
/**
* @When I filter tax rates by end date from :startDate up to :endDate
*/
public function iFilterTaxRatesByEndDateFromUpTo(string $startDate, string $endDate): void
{
$this->client->addFilter('endDate[after]', $startDate);
$this->client->addFilter('endDate[before]', $endDate);
$this->client->filter();
}
/**
* @When I delete tax rate :taxRate
*/
public function iDeleteTaxRate(TaxRateInterface $taxRate): void
{
$this->client->delete(Resources::TAX_RATES, (string) $taxRate->getCode());
}
/**
* @Then I should be notified that it has been successfully created
*/
public function iShouldBeNotifiedThatItHasBeenSuccessfullyCreated(): void
{
Assert::true(
$this->responseChecker->isCreationSuccessful($this->client->getLastResponse()),
'Tax tax rate could not be created'
);
}
/**
* @Then the tax rate :taxRate should appear in the registry
* @Then I should see the tax rate :taxRate in the list
*/
public function theTaxRateShouldAppearInTheRegistry(TaxRateInterface $taxRate): void
{
$name = $taxRate->getName();
Assert::true(
$this->responseChecker->hasItemWithValue($this->client->index(Resources::TAX_RATES), 'name', $name),
sprintf('Tax rate with name %s does not exist', $name)
);
}
/**
* @Then the tax rate :taxRate should be included in price
*/
public function theTaxRateShouldIncludePrice(TaxRateInterface $taxRate): void
{
Assert::true(
$taxRate->isIncludedInPrice(),
sprintf('Tax rate is not included in price')
);
}
/**
* @Then I should be notified that it has been successfully deleted
*/
public function iShouldBeNotifiedThatItHasBeenSuccessfullyDeleted(): void
{
Assert::true(
$this->responseChecker->isDeletionSuccessful($this->client->getLastResponse()),
'Tax rate could not be deleted'
);
}
/**
* @Then /^(this tax rate) should no longer exist in the registry$/
*/
public function thisTaxRateShouldNoLongerExistInTheRegistry(TaxRateInterface $taxRate): void
{
$name = $taxRate->getName();
Assert::false(
$this->responseChecker->hasItemWithValue($this->client->index(Resources::TAX_RATES), 'name', $name),
sprintf('Tax rate with name %s exists', $name)
);
}
/**
* @Then I should be notified that they have been successfully deleted
*/
public function iShouldBeNotifiedThatTheyHaveBeenSuccessfullyDeleted(): void
{
Assert::true(
$this->responseChecker->isDeletionSuccessful($this->client->getLastResponse()),
'Tax rate could not be deleted'
);
}
/**
* @Then I should see a single tax rate in the list
*/
public function iShouldSeeASingleTaxRateInTheList(): void
{
Assert::same($this->responseChecker->countCollectionItems($this->client->index(Resources::TAX_RATES)), 1);
}
/**
* @Then I should be notified that tax rate with this code already exists
*/
public function iShouldBeNotifiedThatTaxRateWithThisCodeAlreadyExists(): void
{
$response = $this->client->getLastResponse();
Assert::false(
$this->responseChecker->isCreationSuccessful($response),
'Tax rate has been created successfully, but it should not'
);
Assert::same(
$this->responseChecker->getError($response),
'code: The tax rate with given code already exists.'
);
}
/**
* @Then there should still be only one tax rate with code :code
*/
public function thereShouldStillBeOnlyOneTaxRateWithCode(string $code): void
{
Assert::count(
$this->responseChecker->getCollectionItemsWithValue($this->client->index(Resources::TAX_RATES), 'code', $code),
1,
sprintf('There is more than one tax rate with code %s', $code)
);
}
/**
* @Then I should be notified that :element is required
*/
public function iShouldBeNotifiedThatCodeIsRequired(string $element): void
{
Assert::contains(
$this->responseChecker->getError($this->client->getLastResponse()),
sprintf('%s: Please enter tax rate %s.', $element, $element)
);
}
/**
* @Then tax rate with :element :code should not be added
*/
public function taxRateWithCodeShouldNotBeAdded(string $element, string $code): void
{
Assert::false(
$this->responseChecker->hasItemWithValue($this->client->index(Resources::TAX_RATES), $element, $code),
sprintf('Tax rate with %s %s exist', $element, $code)
);
}
/**
* @Then I should be notified that zone has to be selected
*/
public function iShouldBeNotifiedThatZoneHasToBeSelected(): void
{
Assert::contains(
$this->responseChecker->getError($this->client->getLastResponse()),
'zone: Please select tax zone.'
);
}
/**
* @Then I should be notified that category has to be selected
*/
public function iShouldBeNotifiedThatCategoryHasToBeSelected(): void
{
Assert::contains(
$this->responseChecker->getError($this->client->getLastResponse()),
'category: Please select tax category.'
);
}
/**
* @Then I should not see a tax rate with name :name
*/
public function iShouldNotSeeATaxRateWithName(string $name): void
{
Assert::false(
$this->responseChecker->hasItemWithValue($this->client->getLastResponse(), 'name', $name),
sprintf('Tax rate with name %s exists', $name)
);
}
/**
* @Then /^(this tax rate) amount should still be ([^"]+)%$/
*/
public function thisTaxRateAmountShouldStillBe(TaxRateInterface $taxRate, string $taxRateAmount): void
{
Assert::true($taxRate->getAmount(), $taxRateAmount);
}
/**
* @Then /^(this tax rate) should still be named "([^"]+)"$/
* @Then /^(this tax rate) name should be "([^"]*)"$/
*/
public function thisTaxRateShouldStillBeNamed(TaxRateInterface $taxRate, string $taxRateName): void
{
Assert::true(
$this->responseChecker->hasValue($this->client->show(Resources::TAX_RATES, (string) $taxRate->getCode()), 'name', $taxRateName),
sprintf('Tax rate name is not %s', $taxRateName)
);
}
/**
* @Then the code field should be disabled
*/
public function theCodeFieldShouldBeDisabled(): void
{
$this->client->updateRequestData(['code' => 'NEW_CODE']);
Assert::false($this->responseChecker->hasValue($this->client->update(), 'code', 'NEW_CODE'));
}
/**
* @Then I should be notified that it has been successfully edited
*/
public function iShouldBeNotifiedThatItHasBeenSuccessfullyEdited(): void
{
Assert::true(
$this->responseChecker->isUpdateSuccessful($this->client->getLastResponse()),
'Tax rate could not be edited'
);
}
/**
* @Then /^(this tax rate) amount should be ([^"]+)%$/
*/
public function thisTaxRateAmountShouldBe(TaxRateInterface $taxRate, int $taxRateAmount): void
{
Assert::true(
$this->responseChecker->hasValue($this->client->show(Resources::TAX_RATES, (string) $taxRate->getCode()), 'amount', $taxRateAmount),
sprintf('Tax rate amount is not %s', $taxRateAmount)
);
}
/**
* @Then /^(this tax rate) should be applicable for the ("[^"]+" tax category)$/
*/
public function thisTaxRateShouldBeApplicableForTheTaxCategory(TaxRateInterface $taxRate, TaxCategoryInterface $taxCategory): void
{
Assert::true(
$this->responseChecker->hasValue($this->client->show(Resources::TAX_RATES, (string) $taxRate->getCode()), 'category', $this->iriConverter->getIriFromItem($taxCategory)),
sprintf('Tax rate is not applicable for %s tax category', $taxCategory)
);
}
/**
* @Then /^(this tax rate) should be applicable in ("[^"]+" zone)$/
*/
public function thisTaxRateShouldBeApplicableInZone(TaxRateInterface $taxRate, ZoneInterface $zone): void
{
Assert::true(
$this->responseChecker->hasValue($this->client->show(Resources::TAX_RATES, (string) $taxRate->getCode()), 'zone', $this->iriConverter->getIriFromItem($zone)),
sprintf('Tax rate is not applicable for %s zone', $zone)
);
}
}

View file

@ -73,6 +73,8 @@ final class Resources
public const TAX_CATEGORIES = 'tax-categories';
PUBLIC CONST TAX_RATES = 'tax-rates';
public const ZONES = 'zones';
private function __construct()

View file

@ -26,6 +26,11 @@ class UpdatePage extends BaseUpdatePage implements UpdatePageInterface
$this->getDocument()->selectFieldOption('Zone', 'Select');
}
public function isIncludedInPrice(): bool
{
return (bool) $this->getElement('included_in_price')->getValue();
}
protected function getCodeElement(): NodeElement
{
return $this->getElement('code');
@ -40,6 +45,7 @@ class UpdatePage extends BaseUpdatePage implements UpdatePageInterface
'code' => '#sylius_tax_rate_code',
'name' => '#sylius_tax_rate_name',
'zone' => '#sylius_tax_rate_zone',
'included_in_price' => '#sylius_tax_rate_includedInPrice'
]);
}
}

View file

@ -181,6 +181,13 @@
<argument type="service" id="sylius.behat.shared_storage" />
</service>
<service id="sylius.behat.context.api.admin.managing_tax_rates" class="Sylius\Behat\Context\Api\Admin\ManagingTaxRatesContext">
<argument type="service" id="sylius.behat.api_platform_client.admin" />
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />
<argument type="service" id="api_platform.iri_converter" />
<argument type="service" id="sylius.behat.shared_storage" />
</service>
<service id="sylius.behat.context.api.admin.resetting_password" class="Sylius\Behat\Context\Api\Admin\ResettingPasswordContext">
<argument type="service" id="sylius.behat.api_platform_client.shop" />
<argument type="service" id="sylius.behat.request_factory" />

View file

@ -53,6 +53,7 @@ imports:
- suites/api/shipping/managing_shipping_methods.yml
- suites/api/taxation/applying_taxes.yml
- suites/api/taxation/managing_tax_categories.yml
- suites/api/taxation/managing_tax_rates.yml
- suites/api/taxon/managing_taxons.yml
- suites/api/user/managing_administrators.yml
- suites/api/user/managing_customer_groups.yml

View file

@ -0,0 +1,23 @@
# This file is part of the Sylius package.
# (c) Paweł Jędrzejewski
default:
suites:
api_managing_tax_rates:
contexts:
- sylius.behat.context.hook.doctrine_orm
- sylius.behat.context.transform.shared_storage
- sylius.behat.context.transform.tax_category
- sylius.behat.context.transform.tax_rate
- sylius.behat.context.transform.zone
- sylius.behat.context.setup.channel
- sylius.behat.context.setup.admin_api_security
- sylius.behat.context.setup.taxation
- sylius.behat.context.setup.zone
- sylius.behat.context.api.admin.managing_tax_rates
filters:
tags: "@managing_tax_rates&&@api"

View file

@ -0,0 +1,82 @@
<?xml version="1.0" ?>
<!--
This file is part of the Sylius package.
(c) Paweł Jędrzejewski
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
-->
<resources xmlns="https://api-platform.com/schema/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata https://api-platform.com/schema/metadata/metadata-2.0.xsd"
>
<resource class="%sylius.model.tax_rate.class%" shortName="TaxRate">
<attribute name="route_prefix">admin</attribute>
<attribute name="validation_groups">sylius</attribute>
<itemOperations>
<itemOperation name="admin_get">
<attribute name="method">GET</attribute>
<attribute name="normalization_context">
<attribute name="groups">
<attribute>admin:tax_rate:read</attribute>
</attribute>
</attribute>
</itemOperation>
<itemOperation name="admin_put">
<attribute name="method">PUT</attribute>
<attribute name="denormalization_context">
<attribute name="groups">
<attribute>admin:tax_rate:update</attribute>
</attribute>
</attribute>
<attribute name="normalization_context">
<attribute name="groups">
<attribute>admin:tax_rate:read</attribute>
</attribute>
</attribute>
</itemOperation>
<itemOperation name="admin_delete">
<attribute name="method">DELETE</attribute>
</itemOperation>
</itemOperations>
<collectionOperations>
<collectionOperation name="admin_get">
<attribute name="method">GET</attribute>
<attribute name="filters">
<attribute>sylius.api.tax_rate.date_filter</attribute>
</attribute>
<attribute name="normalization_context">
<attribute name="groups">
<attribute>admin:tax_rate:read</attribute>
</attribute>
</attribute>
</collectionOperation>
<collectionOperation name="admin_post">
<attribute name="method">POST</attribute>
<attribute name="denormalization_context">
<attribute name="groups">
<attribute>admin:tax_rate:create</attribute>
</attribute>
</attribute>
<attribute name="normalization_context">
<attribute name="groups">
<attribute>admin:tax_rate:read</attribute>
</attribute>
</attribute>
</collectionOperation>
</collectionOperations>
<property name="id" identifier="false" writable="false" />
<property name="code" identifier="true" required="true" />
</resource>
</resources>

View file

@ -0,0 +1,73 @@
<?xml version="1.0" ?>
<!--
This file is part of the Sylius package.
(c) Paweł Jędrzejewski
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
-->
<serializer xmlns="http://symfony.com/schema/dic/serializer-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/serializer-mapping https://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd"
>
<class name="Sylius\Component\Taxation\Model\TaxRate">
<attribute name="id">
<group>admin:tax_rate:read</group>
</attribute>
<attribute name="createdAt">
<group>admin:tax_rate:read</group>
</attribute>
<attribute name="updatedAt">
<group>admin:tax_rate:read</group>
</attribute>
<attribute name="code">
<group>admin:tax_rate:read</group>
<group>admin:tax_rate:create</group>
</attribute>
<attribute name="zone">
<group>admin:tax_rate:read</group>
<group>admin:tax_rate:create</group>
<group>admin:tax_rate:update</group>
</attribute>
<attribute name="name">
<group>admin:tax_rate:read</group>
<group>admin:tax_rate:create</group>
<group>admin:tax_rate:update</group>
</attribute>
<attribute name="category">
<group>admin:tax_rate:read</group>
<group>admin:tax_rate:create</group>
<group>admin:tax_rate:update</group>
</attribute>
<attribute name="calculator">
<group>admin:tax_rate:read</group>
<group>admin:tax_rate:create</group>
<group>admin:tax_rate:update</group>
</attribute>
<attribute name="amount">
<group>admin:tax_rate:read</group>
<group>admin:tax_rate:create</group>
<group>admin:tax_rate:update</group>
</attribute>
<attribute name="includedInPrice">
<group>admin:tax_rate:read</group>
<group>admin:tax_rate:create</group>
<group>admin:tax_rate:update</group>
</attribute>
<attribute name="startDate">
<group>admin:tax_rate:read</group>
<group>admin:tax_rate:create</group>
<group>admin:tax_rate:update</group>
</attribute>
<attribute name="endDate">
<group>admin:tax_rate:read</group>
<group>admin:tax_rate:create</group>
<group>admin:tax_rate:update</group>
</attribute>
</class>
</serializer>

View file

@ -169,10 +169,32 @@
<tag name="api_platform.filter" />
</service>
<service id="sylius.api.tax_rates_start_date_filter" parent="api_platform.doctrine.orm.date_filter" public="true">
<argument type="collection">
<argument key="startDate">exclude_null</argument>
</argument>
<tag name="api_platform.filter" />
</service>
<service id="sylius.api.tax_rates_end_date_filter" parent="api_platform.doctrine.orm.date_filter" public="true">
<argument type="collection">
<argument key="endDate">exclude_null</argument>
</argument>
<tag name="api_platform.filter" />
</service>
<service id="Sylius\Bundle\ApiBundle\Filter\Doctrine\CatalogPromotionChannelFilter" public="true">
<argument type="service" id="api_platform.iri_converter" />
<argument type="service" id="doctrine" />
<tag name="api_platform.filter" />
</service>
<service id="sylius.api.tax_rate.date_filter" parent="ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\DateFilter" public="true">
<argument type="collection">
<argument key="startDate"/>
<argument key="endDate"/>
</argument>
<tag name="api_platform.filter" />
</service>
</services>
</container>

View file

@ -0,0 +1,123 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Tests\Api\Admin;
use Sylius\Component\Core\Model\TaxRateInterface;
use Sylius\Tests\Api\JsonApiTestCase;
use Sylius\Tests\Api\Utils\AdminUserLoginTrait;
use Symfony\Component\HttpFoundation\Response;
final class TaxRatesTest extends JsonApiTestCase
{
use AdminUserLoginTrait;
/** @test */
public function it_gets_a_tax_rate(): void
{
$fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'tax_rates.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
/** @var TaxRateInterface $taxRate */
$taxRate = $fixtures['regular_tax'];
$this->client->request(
method: 'GET',
uri: sprintf('/api/v2/admin/tax-rates/%s', $taxRate->getCode()),
server: $header,
);
$this->assertResponse(
$this->client->getResponse(),
'admin/tax_rate/get_tax_rate_response',
Response::HTTP_OK,
);
}
/** @test */
public function it_gets_tax_rates(): void
{
$this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'tax_rates.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
$this->client->request(
method: 'GET',
uri: '/api/v2/admin/tax-rates',
server: $header,
);
$this->assertResponse(
$this->client->getResponse(),
'admin/tax_rate/get_tax_rates_response',
Response::HTTP_OK,
);
}
/** @test */
public function it_creates_a_new_tax_rate(): void
{
$this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'tax_rates.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
$this->client->request(
method: 'POST',
uri: '/api/v2/admin/tax-rates',
server: $header,
content: json_encode([
'code' => 'unregular_tax',
'zone' => '/api/v2/admin/zones/EU',
'category' => '/api/v2/admin/tax-categories/TC1',
'name' => 'Unregular Tax 90%',
'amount' => '0.9',
'includedInPrice' => true,
'calculator' => 'default',
], JSON_THROW_ON_ERROR),
);
$this->assertResponse(
$this->client->getResponse(),
'admin/tax_rate/post_tax_rate_response',
Response::HTTP_CREATED,
);
}
/** @test */
public function it_updates_an_existing_tax_rate(): void
{
$fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'tax_rates.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
/** @var TaxRateInterface $taxRate */
$taxRate = $fixtures['regular_tax'];
$this->client->request(
method: 'PUT',
uri: '/api/v2/admin/tax-rates/' . $taxRate->getCode(),
server: $header,
content: json_encode([
'zone' => '/api/v2/admin/zones/EU',
'category' => '/api/v2/admin/tax-categories/TC2',
'name' => 'Regular Tax 30%',
'amount' => '0.3',
'includedInPrice' => true,
'calculator' => 'default',
], JSON_THROW_ON_ERROR),
);
$this->assertResponse(
$this->client->getResponse(),
'admin/tax_rate/put_tax_rate_response',
Response::HTTP_OK,
);
}
}

View file

@ -0,0 +1,33 @@
Sylius\Component\Addressing\Model\ZoneMember:
member_{NL, BE}:
code: <current()>
Sylius\Component\Addressing\Model\Zone:
zone_eu:
code: EU
name: European Union
type: country
members: ["@member_NL", "@member_BE"]
Sylius\Component\Taxation\Model\TaxCategory:
tax_category_{1..3}:
code: TC<current()>
name: Tax Category <current()>
description: <sentence(6)>
Sylius\Component\Core\Model\TaxRate:
sales_tax:
code: sales_tax
name: "Sales Tax 20\\%"
zone: "@zone_eu"
calculator: "default"
category: "@tax_category_1"
amount: 0.2
regular_tax:
code: regular_tax
name: "Regular Tax 20\\%"
zone: "@zone_eu"
calculator: "default"
category: "@tax_category_2"
amount: 0.2

View file

@ -0,0 +1,17 @@
{
"@context": "\/api\/v2\/contexts\/TaxRate",
"@id": "\/api\/v2\/admin\/tax-rates\/regular_tax",
"@type": "TaxRate",
"zone": "\/api\/v2\/admin\/zones\/EU",
"id": @integer@,
"code": "regular_tax",
"category": "\/api\/v2\/admin\/tax-categories\/TC2",
"name": "Regular Tax 20%",
"amount": 0.2,
"includedInPrice": false,
"calculator": "default",
"startDate": null,
"endDate": null,
"createdAt": @datetime@,
"updatedAt": @datetime@
}

View file

@ -0,0 +1,95 @@
{
"@context": "\/api\/v2\/contexts\/TaxRate",
"@id": "\/api\/v2\/admin\/tax-rates",
"@type": "hydra:Collection",
"hydra:member": [
{
"@id": "\/api\/v2\/admin\/tax-rates\/sales_tax",
"@type": "TaxRate",
"zone": "\/api\/v2\/admin\/zones\/EU",
"id": @integer@,
"code": "sales_tax",
"category": "\/api\/v2\/admin\/tax-categories\/TC1",
"name": "Sales Tax 20%",
"amount": 0.2,
"includedInPrice": false,
"calculator": "default",
"startDate": null,
"endDate": null,
"createdAt": @datetime@,
"updatedAt": @datetime@
},
{
"@id": "\/api\/v2\/admin\/tax-rates\/regular_tax",
"@type": "TaxRate",
"zone": "\/api\/v2\/admin\/zones\/EU",
"id": @integer@,
"code": "regular_tax",
"category": "\/api\/v2\/admin\/tax-categories\/TC2",
"name": "Regular Tax 20%",
"amount": 0.2,
"includedInPrice": false,
"calculator": "default",
"startDate": null,
"endDate": null,
"createdAt": @datetime@,
"updatedAt": @datetime@
}
],
"hydra:totalItems": 2,
"hydra:search": {
"@type": "hydra:IriTemplate",
"hydra:template": "\/api\/v2\/admin\/tax-rates{?startDate[before],startDate[strictly_before],startDate[after],startDate[strictly_after],endDate[before],endDate[strictly_before],endDate[after],endDate[strictly_after]}",
"hydra:variableRepresentation": "BasicRepresentation",
"hydra:mapping": [
{
"@type": "IriTemplateMapping",
"variable": "startDate[before]",
"property": "startDate",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "startDate[strictly_before]",
"property": "startDate",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "startDate[after]",
"property": "startDate",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "startDate[strictly_after]",
"property": "startDate",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "endDate[before]",
"property": "endDate",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "endDate[strictly_before]",
"property": "endDate",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "endDate[after]",
"property": "endDate",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "endDate[strictly_after]",
"property": "endDate",
"required": false
}
]
}
}

View file

@ -0,0 +1,17 @@
{
"@context": "\/api\/v2\/contexts\/TaxRate",
"@id": "\/api\/v2\/admin\/tax-rates\/regular_tax",
"@type": "TaxRate",
"zone": "\/api\/v2\/admin\/zones\/EU",
"id": @integer@,
"code": "regular_tax",
"category": "\/api\/v2\/admin\/tax-categories\/TC2",
"name": "Regular Tax 40%",
"amount": 0.4,
"includedInPrice": true,
"calculator": "default",
"startDate": null,
"endDate": null,
"createdAt": @datetime@,
"updatedAt": @datetime@
}

View file

@ -0,0 +1,17 @@
{
"@context": "\/api\/v2\/contexts\/TaxRate",
"@id": "\/api\/v2\/admin\/tax-rates\/unregular_tax",
"@type": "TaxRate",
"zone": "\/api\/v2\/admin\/zones\/EU",
"id": @integer@,
"code": "unregular_tax",
"category": "\/api\/v2\/admin\/tax-categories\/TC1",
"name": "Unregular Tax 90%",
"amount": 0.9,
"includedInPrice": true,
"calculator": "default",
"startDate": null,
"endDate": null,
"createdAt": @datetime@,
"updatedAt": @datetime@
}

View file

@ -0,0 +1,17 @@
{
"@context": "\/api\/v2\/contexts\/TaxRate",
"@id": "\/api\/v2\/admin\/tax-rates\/regular_tax",
"@type": "TaxRate",
"zone": "\/api\/v2\/admin\/zones\/EU",
"id": @integer@,
"code": "regular_tax",
"category": "\/api\/v2\/admin\/tax-categories\/TC2",
"name": "Regular Tax 30%",
"amount": 0.3,
"includedInPrice": true,
"calculator": "default",
"startDate": null,
"endDate": null,
"createdAt": @datetime@,
"updatedAt": @datetime@
}