mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 09:02:12 +00:00
[PriceHistory][API] Add channel setup implementation
This commit is contained in:
parent
b03534491e
commit
3d2ae584f9
13 changed files with 269 additions and 3 deletions
|
|
@ -16,6 +16,7 @@ Feature: Adding a new channel
|
|||
And I specify its code as "MOBILE"
|
||||
And I name it "Mobile channel"
|
||||
And I choose "Euro" as the base currency
|
||||
And I make it available in "English (United States)"
|
||||
And I choose "English (United States)" as a default locale
|
||||
And I select the "Order items based" as tax calculation strategy
|
||||
And I add it
|
||||
|
|
@ -33,6 +34,7 @@ Feature: Adding a new channel
|
|||
And I set its contact phone number as "11331122"
|
||||
And I define its color as "blue"
|
||||
And I choose "Euro" as the base currency
|
||||
And I make it available in "English (United States)"
|
||||
And I choose "English (United States)" as a default locale
|
||||
And I choose "United States" and "Poland" as operating countries
|
||||
And I select the "Order items based" as tax calculation strategy
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ Feature: Adding a new channel with shop billing data
|
|||
And I specify its code as "MOBILE"
|
||||
And I name it "Mobile channel"
|
||||
And I choose "Euro" as the base currency
|
||||
And I make it available in "English (United States)"
|
||||
And I choose "English (United States)" as a default locale
|
||||
And I select the "Order items based" as tax calculation strategy
|
||||
And I specify company as "Ragnarok"
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ Feature: Adding a new channel with menu taxon
|
|||
And I specify its code as "MOBILE"
|
||||
And I name it "Mobile channel"
|
||||
And I choose "Euro" as the base currency
|
||||
And I make it available in "English (United States)"
|
||||
And I choose "English (United States)" as a default locale
|
||||
And I select the "Order items based" as tax calculation strategy
|
||||
And I specify menu taxon as "Clothes"
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ Feature: Choosing a required address in the checkout for a channel
|
|||
And I specify its code as "MOBILE"
|
||||
And I name it "Mobile Store"
|
||||
And I choose "USD" as the base currency
|
||||
And I make it available in "English (United States)"
|
||||
And I choose "English (United States)" as a default locale
|
||||
And I select the "Order items based" as tax calculation strategy
|
||||
And I choose shipping address as a required address in the checkout
|
||||
|
|
|
|||
|
|
@ -265,8 +265,8 @@ final class ManagingChannelsContext implements Context
|
|||
public function iShouldBeNotifiedThatItHasBeenSuccessfullyCreated(): void
|
||||
{
|
||||
Assert::true(
|
||||
$this->responseChecker->isCreationSuccessful($this->client->getLastResponse()),
|
||||
'Channel could not be created',
|
||||
$this->responseChecker->isCreationSuccessful($response = $this->client->getLastResponse()),
|
||||
'Channel could not be created: ' . $response->getContent(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
<attribute name="normalization_context">
|
||||
<attribute name="groups">admin:channel:read</attribute>
|
||||
</attribute>
|
||||
<attribute name="validation_groups">sylius</attribute>
|
||||
</collectionOperation>
|
||||
</collectionOperations>
|
||||
|
||||
|
|
@ -77,6 +78,7 @@
|
|||
<attribute name="normalization_context">
|
||||
<attribute name="groups">admin:channel:read</attribute>
|
||||
</attribute>
|
||||
<attribute name="validation_groups">sylius</attribute>
|
||||
</itemOperation>
|
||||
</itemOperations>
|
||||
|
||||
|
|
|
|||
|
|
@ -111,5 +111,20 @@
|
|||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
</attribute>
|
||||
<attribute name="lowestPriceForDiscountedProductsVisible">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="lowestPriceForDiscountedProductsCheckingPeriod">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="taxonsExcludedFromShowingLowestPrice">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
</class>
|
||||
</serializer>
|
||||
|
|
|
|||
|
|
@ -102,5 +102,10 @@
|
|||
</argument>
|
||||
<tag name="serializer.normalizer"/>
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\ApiBundle\Serializer\ChannelExcludedTaxonsDenormalizer">
|
||||
<argument type="service" id="api_platform.iri_converter" />
|
||||
<tag name="serializer.normalizer" priority="64" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
<?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\Bundle\ApiBundle\Serializer;
|
||||
|
||||
use ApiPlatform\Core\Api\IriConverterInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\TaxonInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
final class ChannelExcludedTaxonsDenormalizer implements ContextAwareDenormalizerInterface, DenormalizerAwareInterface
|
||||
{
|
||||
use DenormalizerAwareTrait;
|
||||
|
||||
private const ALREADY_CALLED = 'sylius_channel_excluded_taxons_denormalizer_already_called';
|
||||
|
||||
public function __construct(private IriConverterInterface $iriConverter)
|
||||
{
|
||||
}
|
||||
|
||||
public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool
|
||||
{
|
||||
return
|
||||
!isset($context[self::ALREADY_CALLED]) &&
|
||||
is_array($data) &&
|
||||
is_a($type, ChannelInterface::class, true)
|
||||
;
|
||||
}
|
||||
|
||||
public function denormalize(mixed $data, string $type, string $format = null, array $context = [])
|
||||
{
|
||||
$context[self::ALREADY_CALLED] = true;
|
||||
$data = (array) $data;
|
||||
|
||||
$channel = $this->denormalizer->denormalize($data, $type, $format, $context);
|
||||
Assert::isInstanceOf($channel, ChannelInterface::class);
|
||||
|
||||
$channel->clearTaxonsExcludedFromShowingLowestPrice();
|
||||
|
||||
foreach ($data['taxonsExcludedFromShowingLowestPrice'] ?? [] as $excludedTaxonIri) {
|
||||
/** @var TaxonInterface $taxon */
|
||||
$taxon = $this->iriConverter->getItemFromIri($excludedTaxonIri);
|
||||
|
||||
$channel->addTaxonExcludedFromShowingLowestPrice($taxon);
|
||||
}
|
||||
|
||||
return $channel;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
<?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 spec\Sylius\Bundle\ApiBundle\Serializer;
|
||||
|
||||
use ApiPlatform\Core\Api\IriConverterInterface;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Prophecy\Argument;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\TaxonInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
|
||||
final class ChannelExcludedTaxonsDenormalizerSpec extends ObjectBehavior
|
||||
{
|
||||
private const ALREADY_CALLED = 'sylius_channel_excluded_taxons_denormalizer_already_called';
|
||||
|
||||
function let(IriConverterInterface $iriConverter): void
|
||||
{
|
||||
$this->beConstructedWith($iriConverter);
|
||||
}
|
||||
|
||||
function it_does_not_support_denormalization_when_the_denormalizer_has_already_been_called(): void
|
||||
{
|
||||
$this->supportsDenormalization([], 'string', context: [self::ALREADY_CALLED => true])->shouldReturn(false);
|
||||
}
|
||||
|
||||
function it_does_not_support_denormalization_when_data_is_not_an_array(): void
|
||||
{
|
||||
$this->supportsDenormalization('string', 'string')->shouldReturn(false);
|
||||
}
|
||||
|
||||
function it_does_not_support_denormalization_when_type_is_not_a_channel(): void
|
||||
{
|
||||
$this->supportsDenormalization([], 'string')->shouldReturn(false);
|
||||
}
|
||||
|
||||
function it_throws_an_exception_when_denormalizing_an_object_that_is_not_a_channel(
|
||||
DenormalizerInterface $denormalizer,
|
||||
): void {
|
||||
$this->setDenormalizer($denormalizer);
|
||||
|
||||
$denormalizer->denormalize([], 'string', null, [self::ALREADY_CALLED => true])->willReturn(new \stdClass());
|
||||
|
||||
$this->shouldThrow(\InvalidArgumentException::class)->during('denormalize', [[], 'string']);
|
||||
}
|
||||
|
||||
function it_adds_excluded_taxons_from_data(
|
||||
DenormalizerInterface $denormalizer,
|
||||
IriConverterInterface $iriConverter,
|
||||
TaxonInterface $firstTaxon,
|
||||
TaxonInterface $secondTaxon,
|
||||
ChannelInterface $channel,
|
||||
): void {
|
||||
$this->setDenormalizer($denormalizer);
|
||||
|
||||
$data = ['taxonsExcludedFromShowingLowestPrice' => [
|
||||
'/api/v2/taxons/first-new-taxon',
|
||||
'/api/v2/taxons/second-new-taxon',
|
||||
]];
|
||||
|
||||
$channel->getTaxonsExcludedFromShowingLowestPrice()->willReturn(new ArrayCollection());
|
||||
|
||||
$denormalizer->denormalize($data, 'string', null, [self::ALREADY_CALLED => true])->willReturn($channel);
|
||||
|
||||
$channel->clearTaxonsExcludedFromShowingLowestPrice()->shouldBeCalled();
|
||||
|
||||
$iriConverter->getItemFromIri('/api/v2/taxons/first-new-taxon')->shouldBeCalledTimes(1)->willReturn($firstTaxon);
|
||||
$iriConverter->getItemFromIri('/api/v2/taxons/second-new-taxon')->shouldBeCalledTimes(1)->willReturn($secondTaxon);
|
||||
|
||||
$channel->addTaxonExcludedFromShowingLowestPrice($firstTaxon)->shouldBeCalledTimes(1);
|
||||
$channel->addTaxonExcludedFromShowingLowestPrice($secondTaxon)->shouldBeCalledTimes(1);
|
||||
|
||||
$this->denormalize($data, 'string')->shouldReturn($channel);
|
||||
}
|
||||
|
||||
function it_removes_excluded_taxons_when_data_has_none(
|
||||
DenormalizerInterface $denormalizer,
|
||||
IriConverterInterface $iriConverter,
|
||||
TaxonInterface $firstTaxon,
|
||||
TaxonInterface $secondTaxon,
|
||||
ChannelInterface $channel,
|
||||
): void {
|
||||
$this->setDenormalizer($denormalizer);
|
||||
|
||||
$data = [];
|
||||
|
||||
$denormalizer->denormalize($data, 'string', null, [self::ALREADY_CALLED => true])->willReturn($channel);
|
||||
|
||||
$channel->getTaxonsExcludedFromShowingLowestPrice()->willReturn(new ArrayCollection([
|
||||
$firstTaxon->getWrappedObject(),
|
||||
$secondTaxon->getWrappedObject(),
|
||||
]));
|
||||
|
||||
$channel->clearTaxonsExcludedFromShowingLowestPrice()->shouldBeCalled();
|
||||
|
||||
$iriConverter->getItemFromIri(Argument::cetera())->shouldNotBeCalled();
|
||||
|
||||
$channel->addTaxonExcludedFromShowingLowestPrice(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$this->denormalize($data, 'string')->shouldReturn($channel);
|
||||
}
|
||||
|
||||
function it_replaces_current_excluded_taxons_with_ones_from_data(
|
||||
DenormalizerInterface $denormalizer,
|
||||
IriConverterInterface $iriConverter,
|
||||
TaxonInterface $firstCurrentTaxon,
|
||||
TaxonInterface $secondCurrentTaxon,
|
||||
TaxonInterface $firstNewTaxon,
|
||||
TaxonInterface $secondNewTaxon,
|
||||
ChannelInterface $channel,
|
||||
): void {
|
||||
$this->setDenormalizer($denormalizer);
|
||||
|
||||
$data = ['taxonsExcludedFromShowingLowestPrice' => [
|
||||
'/api/v2/taxons/first-new-taxon',
|
||||
'/api/v2/taxons/second-new-taxon',
|
||||
]];
|
||||
|
||||
$denormalizer->denormalize($data, 'string', null, [self::ALREADY_CALLED => true])->willReturn($channel);
|
||||
|
||||
$channel->getTaxonsExcludedFromShowingLowestPrice()->willReturn(new ArrayCollection([
|
||||
$firstCurrentTaxon->getWrappedObject(),
|
||||
$secondCurrentTaxon->getWrappedObject(),
|
||||
]));
|
||||
|
||||
$channel->clearTaxonsExcludedFromShowingLowestPrice()->shouldBeCalled();
|
||||
|
||||
$iriConverter->getItemFromIri('/api/v2/taxons/first-new-taxon')->shouldBeCalledTimes(1)->willReturn($firstNewTaxon);
|
||||
$iriConverter->getItemFromIri('/api/v2/taxons/second-new-taxon')->shouldBeCalledTimes(1)->willReturn($secondNewTaxon);
|
||||
|
||||
$channel->addTaxonExcludedFromShowingLowestPrice($firstNewTaxon)->shouldBeCalledTimes(1);
|
||||
$channel->addTaxonExcludedFromShowingLowestPrice($secondNewTaxon)->shouldBeCalledTimes(1);
|
||||
|
||||
$this->denormalize($data, 'string')->shouldReturn($channel);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,5 +4,8 @@ sylius:
|
|||
not_blank: Please enter channel code.
|
||||
regex: Channel code can only be comprised of letters, numbers, dashes and underscores.
|
||||
unique: Channel code has to be unique.
|
||||
lowest_price_for_discounted_products_checking_period:
|
||||
greater_than: 'Value must be greater than {{ compared_value }}'
|
||||
less_than: 'Value must be less than {{ compared_value }}'
|
||||
name:
|
||||
not_blank: Please enter channel name.
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
<argument type="service" id="Sylius\Bundle\CoreBundle\PriceHistory\Processor\ProductLowestPriceBeforeDiscountProcessorInterface" />
|
||||
<argument type="service" id="sylius.repository.channel_pricing" />
|
||||
<argument>%sylius_core.price_history.batch_size%</argument>
|
||||
<tag name="sylius_price_history.entity_observer" />
|
||||
<tag name="sylius.entity_observer" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.listener.channel_pricing_change" class="Sylius\Bundle\CoreBundle\PriceHistory\EventListener\OnFlushEntityObserverListener">
|
||||
|
|
|
|||
|
|
@ -54,5 +54,32 @@
|
|||
</option>
|
||||
</constraint>
|
||||
</property>
|
||||
<getter property="lowestPriceForDiscountedProductsCheckingPeriod">
|
||||
<constraint name="NotNull">
|
||||
<option name="groups">
|
||||
<value>sylius</value>
|
||||
</option>
|
||||
</constraint>
|
||||
<constraint name="Type">
|
||||
<option name="value">int</option>
|
||||
<option name="groups">
|
||||
<value>sylius</value>
|
||||
</option>
|
||||
</constraint>
|
||||
<constraint name="LessThan">
|
||||
<option name="value">2147483647</option>
|
||||
<option name="groups">
|
||||
<value>sylius</value>
|
||||
</option>
|
||||
<option name="message">sylius.channel.lowest_price_for_discounted_products_checking_period.less_than</option>
|
||||
</constraint>
|
||||
<constraint name="GreaterThan">
|
||||
<option name="value">0</option>
|
||||
<option name="groups">
|
||||
<value>sylius</value>
|
||||
</option>
|
||||
<option name="message">sylius.channel.lowest_price_for_discounted_products_checking_period.greater_than</option>
|
||||
</constraint>
|
||||
</getter>
|
||||
</class>
|
||||
</constraint-mapping>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue