Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 68 additions & 4 deletions src/ApiPlatform/Resources/TaxRule/TaxRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,17 @@
use PrestaShop\PrestaShop\Core\Domain\TaxRulesGroup\Exception\TaxRulesGroupNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\TaxRulesGroup\TaxRule\Command\AddTaxRuleCommand;
use PrestaShop\PrestaShop\Core\Domain\TaxRulesGroup\TaxRule\Command\DeleteTaxRuleCommand;
use PrestaShop\PrestaShop\Core\Domain\TaxRulesGroup\TaxRule\Command\EditTaxRuleCommand;
use PrestaShop\PrestaShop\Core\Domain\TaxRulesGroup\TaxRule\Exception\CannotAddTaxRuleException;
use PrestaShop\PrestaShop\Core\Domain\TaxRulesGroup\TaxRule\Exception\CannotDeleteTaxRuleException;
use PrestaShop\PrestaShop\Core\Domain\TaxRulesGroup\TaxRule\Exception\CannotUpdateTaxRuleException;
use PrestaShop\PrestaShop\Core\Domain\TaxRulesGroup\TaxRule\Exception\TaxRuleConstraintException;
use PrestaShop\PrestaShop\Core\Domain\TaxRulesGroup\TaxRule\Exception\TaxRuleNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\TaxRulesGroup\TaxRule\Query\GetTaxRuleForEditing;
use PrestaShopBundle\ApiPlatform\Metadata\CQRSCreate;
use PrestaShopBundle\ApiPlatform\Metadata\CQRSDelete;
use PrestaShopBundle\ApiPlatform\Metadata\CQRSGet;
use PrestaShopBundle\ApiPlatform\Metadata\CQRSPartialUpdate;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\Constraints as Assert;

Expand All @@ -46,6 +51,25 @@
CQRSCommandMapping: self::COMMAND_MAPPING,
experimentalOperation: true,
),
new CQRSGet(
uriTemplate: '/tax-rules/{taxRuleId}',
requirements: ['taxRuleId' => '\d+'],
CQRSQuery: GetTaxRuleForEditing::class,
scopes: ['tax_rule_read'],
CQRSQueryMapping: self::QUERY_MAPPING,
experimentalOperation: true,
),
new CQRSPartialUpdate(
uriTemplate: '/tax-rules/{taxRuleId}',
requirements: ['taxRuleId' => '\d+'],
validationContext: ['groups' => ['Default', 'Update']],
CQRSCommand: EditTaxRuleCommand::class,
CQRSQuery: GetTaxRuleForEditing::class,
scopes: ['tax_rule_write'],
CQRSQueryMapping: self::QUERY_MAPPING,
CQRSCommandMapping: self::UPDATE_COMMAND_MAPPING,
experimentalOperation: true,
),
new CQRSDelete(
uriTemplate: '/tax-rules/{taxRuleId}',
requirements: ['taxRuleId' => '\d+'],
Expand All @@ -61,6 +85,7 @@
TaxRuleConstraintException::class => Response::HTTP_UNPROCESSABLE_ENTITY,
CannotAddTaxRuleException::class => Response::HTTP_UNPROCESSABLE_ENTITY,
CannotDeleteTaxRuleException::class => Response::HTTP_UNPROCESSABLE_ENTITY,
CannotUpdateTaxRuleException::class => Response::HTTP_UNPROCESSABLE_ENTITY,
],
)]
class TaxRule
Expand All @@ -74,24 +99,63 @@ class TaxRule

// The Core command also accepts 0 to mean "all active countries", but this endpoint always creates
// exactly one tax rule, so fan-out is rejected here rather than exposing a variable-size response
#[Assert\GreaterThan(0, groups: ['Create'])]
#[Assert\GreaterThan(0, groups: ['Create', 'Update'])]
public int $countryId;

#[Assert\Count(max: 1, groups: ['Create'])]
#[Assert\Count(max: 1, groups: ['Create', 'Update'])]
public array $stateIds = [0];

// 0 means no tax is associated to this rule (see testListTaxRuleWithoutTax)
#[Assert\GreaterThanOrEqual(0, groups: ['Create'])]
#[Assert\GreaterThanOrEqual(0, groups: ['Create', 'Update'])]
public int $taxId;

#[Assert\Range(min: 0, max: 2, groups: ['Create'])]
#[Assert\Range(min: 0, max: 2, groups: ['Create', 'Update'])]
public int $behavior = 0;

// A single zip code or a "from-to" range, '' when the rule applies to every zip code
public string $zipCode = '';

public string $description = '';

private string $zipCodeFrom = '0';

private string $zipCodeTo = '0';

public const COMMAND_MAPPING = [
'[taxRuleIds][0]' => '[taxRuleId]',
];

public const QUERY_MAPPING = [
'[stateId]' => '[stateIds][0]',
];

public const UPDATE_COMMAND_MAPPING = [
'[stateIds][0]' => '[stateId]',
];

// The query returns the zip code range as two fields while the commands take the "from-to" string, these setters
// rebuild that string so that the read and write formats match
public function setZipcodeFrom(string $zipCodeFrom): void
{
$this->zipCodeFrom = $zipCodeFrom;
$this->zipCode = $this->formatZipCode();
}

public function setZipcodeTo(string $zipCodeTo): void
{
$this->zipCodeTo = $zipCodeTo;
$this->zipCode = $this->formatZipCode();
}

private function formatZipCode(): string
{
if ('0' === $this->zipCodeFrom || '' === $this->zipCodeFrom) {
return '';
}
if ('0' === $this->zipCodeTo || '' === $this->zipCodeTo) {
return $this->zipCodeFrom;
}

return $this->zipCodeFrom . '-' . $this->zipCodeTo;
}
}
110 changes: 108 additions & 2 deletions tests/Integration/ApiPlatform/TaxRuleEndpointTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ public static function getProtectedEndpoints(): iterable
'/tax-rules',
];

yield 'get endpoint' => [
'GET',
'/tax-rules/1',
];

yield 'partial update endpoint' => [
'PATCH',
'/tax-rules/1',
];

yield 'delete endpoint' => [
'DELETE',
'/tax-rules/1',
Expand Down Expand Up @@ -236,7 +246,7 @@ public function testSortTaxRules(array $fixtures): void
}

/**
* @return array{taxRuleId: int, taxRulesGroupId: int}
* @return array{taxRuleId: int, taxRulesGroupId: int, taxId: int, countryId: int}
*/
public function testAddTaxRule(): array
{
Expand Down Expand Up @@ -289,12 +299,108 @@ public function testAddTaxRule(): array
$taxRules['items'][0]
);

return ['taxRuleId' => $taxRuleId, 'taxRulesGroupId' => $taxRulesGroupId];
return ['taxRuleId' => $taxRuleId, 'taxRulesGroupId' => $taxRulesGroupId, 'taxId' => $taxId, 'countryId' => $countryId];
}

/**
* @depends testAddTaxRule
*
* @param array{taxRuleId: int, taxRulesGroupId: int, taxId: int, countryId: int} $fixtures
*
* @return array{taxRuleId: int, taxRulesGroupId: int, taxId: int, countryId: int}
*/
public function testGetTaxRule(array $fixtures): array
{
$this->assertEquals(
[
'taxRuleId' => $fixtures['taxRuleId'],
'taxRulesGroupId' => $fixtures['taxRulesGroupId'],
'countryId' => $fixtures['countryId'],
'stateIds' => [0],
'taxId' => $fixtures['taxId'],
'behavior' => 1,
'zipCode' => '',
'description' => 'API test add tax rule',
],
$this->getItem('/tax-rules/' . $fixtures['taxRuleId'], ['tax_rule_read'])
);

return $fixtures;
}

/**
* @depends testGetTaxRule
*
* @param array{taxRuleId: int, taxRulesGroupId: int, taxId: int, countryId: int} $fixtures
*
* @return array{taxRuleId: int, taxRulesGroupId: int, taxId: int, countryId: int}
*/
public function testPartialUpdateTaxRule(array $fixtures): array
{
$expectedTaxRule = [
'taxRuleId' => $fixtures['taxRuleId'],
'taxRulesGroupId' => $fixtures['taxRulesGroupId'],
'countryId' => $fixtures['countryId'],
'stateIds' => [0],
'taxId' => $fixtures['taxId'],
'behavior' => 2,
'zipCode' => '28000-28999',
'description' => 'API test updated tax rule',
];

$this->assertEquals($expectedTaxRule, $this->partialUpdateItem('/tax-rules/' . $fixtures['taxRuleId'], [
'behavior' => 2,
'zipCode' => '28000-28999',
'description' => 'API test updated tax rule',
], ['tax_rule_write']));
$this->assertEquals($expectedTaxRule, $this->getItem('/tax-rules/' . $fixtures['taxRuleId'], ['tax_rule_read']));

// A single zip code is read back as is
$this->assertEquals(
['zipCode' => '28001'] + $expectedTaxRule,
$this->partialUpdateItem('/tax-rules/' . $fixtures['taxRuleId'], ['zipCode' => '28001'], ['tax_rule_write'])
);

return $fixtures;
}

public function testGetUnknownTaxRule(): void
{
$this->getItem('/tax-rules/999999', ['tax_rule_read'], Response::HTTP_NOT_FOUND);
$this->partialUpdateItem('/tax-rules/999999', ['behavior' => 1], ['tax_rule_write'], Response::HTTP_NOT_FOUND);
}

/**
* @depends testPartialUpdateTaxRule
*
* @param array{taxRuleId: int, taxRulesGroupId: int, taxId: int, countryId: int} $fixtures
*
* @return array{taxRuleId: int, taxRulesGroupId: int, taxId: int, countryId: int}
*/
public function testInvalidPartialUpdateTaxRule(array $fixtures): array
{
$validationErrorsResponse = $this->partialUpdateItem('/tax-rules/' . $fixtures['taxRuleId'], [
'countryId' => 0,
'behavior' => 5,
], ['tax_rule_write'], Response::HTTP_UNPROCESSABLE_ENTITY);
$this->assertIsArray($validationErrorsResponse);
$this->assertValidationErrors([
[
'propertyPath' => 'countryId',
'message' => 'This value should be greater than 0.',
],
[
'propertyPath' => 'behavior',
'message' => 'This value should be between 0 and 2.',
],
], $validationErrorsResponse);

return $fixtures;
}

/**
* @depends testInvalidPartialUpdateTaxRule
*
* @param array{taxRuleId: int, taxRulesGroupId: int} $fixtures
*/
public function testDeleteTaxRule(array $fixtures): void
Expand Down