mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-05 20:57:12 +00:00
[ApiBundle] Implement Sales Statistics feature
This commit is contained in:
parent
a8b9c54e2c
commit
37ad43e0de
12 changed files with 358 additions and 31 deletions
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* 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\Controller;
|
||||
|
||||
use Sylius\Bundle\ApiBundle\Query\Admin\GetSalesStatistics;
|
||||
use Sylius\Component\Channel\Context\ChannelContextInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Messenger\Exception\HandlerFailedException;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
use Symfony\Component\Messenger\Stamp\HandledStamp;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
final class GetSalesStatisticsAction
|
||||
{
|
||||
public function __construct(
|
||||
private MessageBusInterface $queryBus,
|
||||
private ChannelContextInterface $channelContext,
|
||||
private SerializerInterface $serializer,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(Request $request): Response
|
||||
{
|
||||
$channelCode = $request->query->get('channelCode') ?? $this->channelContext->getChannel()->getCode();
|
||||
|
||||
try {
|
||||
$envelope = $this->queryBus->dispatch(
|
||||
new GetSalesStatistics(channelCode: $channelCode),
|
||||
);
|
||||
} catch (HandlerFailedException $exception) {
|
||||
return new JsonResponse(['error' => $exception->getPrevious()->getMessage()], Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
/** @var HandledStamp $handledStamp */
|
||||
$handledStamp = $envelope->last(HandledStamp::class);
|
||||
Assert::notNull($handledStamp);
|
||||
|
||||
return new JsonResponse($this->serializer->serialize($handledStamp->getResult(), 'json'), Response::HTTP_OK, [], true);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* 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\Query\Admin;
|
||||
|
||||
use Sylius\Bundle\ApiBundle\Command\ChannelCodeAwareInterface;
|
||||
use Sylius\Bundle\ApiBundle\Command\LocaleCodeAwareInterface;
|
||||
|
||||
/** @experimental */
|
||||
class GetSalesStatistics implements ChannelCodeAwareInterface, LocaleCodeAwareInterface
|
||||
{
|
||||
public ?\DateTimeInterface $startDate = null;
|
||||
|
||||
public ?\DateTimeInterface $endDate = null;
|
||||
|
||||
public function __construct(private ?string $localeCode = null, private ?string $channelCode = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function getChannelCode(): ?string
|
||||
{
|
||||
return $this->channelCode;
|
||||
}
|
||||
|
||||
public function setChannelCode(?string $channelCode): void
|
||||
{
|
||||
$this->channelCode = $channelCode;
|
||||
}
|
||||
|
||||
public function getLocaleCode(): ?string
|
||||
{
|
||||
return $this->localeCode;
|
||||
}
|
||||
|
||||
public function setLocaleCode(?string $localeCode): void
|
||||
{
|
||||
$this->localeCode = $localeCode;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* 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\QueryHandler\Admin;
|
||||
|
||||
use Sylius\Bundle\ApiBundle\Query\Admin\GetSalesStatistics;
|
||||
use Sylius\Bundle\CoreBundle\Provider\SalesStatisticsProviderInterface;
|
||||
use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
|
||||
use Sylius\Component\Core\Dashboard\Interval;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\ValueObject\SalesStatistics;
|
||||
|
||||
/** @experimental */
|
||||
final class GetSalesStatisticsHandler
|
||||
{
|
||||
/** @param ChannelRepositoryInterface<ChannelInterface> $channelRepository */
|
||||
public function __construct(
|
||||
private SalesStatisticsProviderInterface $salesStatisticsProvider,
|
||||
private ChannelRepositoryInterface $channelRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(GetSalesStatistics $query): SalesStatistics
|
||||
{
|
||||
$startDate = new \DateTimeImmutable('first day of january this year');
|
||||
$endDate = new \DateTimeImmutable('first day of january next year');
|
||||
|
||||
/** @var ChannelInterface|null $channel */
|
||||
$channel = $this->channelRepository->findOneByCode($query->getChannelCode());
|
||||
|
||||
if ($channel === null) {
|
||||
throw new \InvalidArgumentException(sprintf('Channel with code "%s" does not exist.', $query->getChannelCode()));
|
||||
}
|
||||
|
||||
return $this->salesStatisticsProvider->provide(
|
||||
$channel,
|
||||
$startDate,
|
||||
$endDate,
|
||||
Interval::month(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,12 @@ sylius_api_admin_authentication_token:
|
|||
path: /admin/authentication-token
|
||||
methods: ['POST']
|
||||
|
||||
sylius_api_admin_sales_statistics:
|
||||
path: /admin/sales-statistics
|
||||
methods: ['GET']
|
||||
defaults:
|
||||
_controller: Sylius\Bundle\ApiBundle\Controller\GetSalesStatisticsAction
|
||||
|
||||
sylius_api_shop_authentication_token:
|
||||
path: /shop/authentication-token
|
||||
methods: ['POST']
|
||||
|
|
|
|||
|
|
@ -161,5 +161,11 @@
|
|||
<service id="Sylius\Bundle\ApiBundle\Validator\ResourceInputDataPropertiesValidatorInterface" class="Sylius\Bundle\ApiBundle\Validator\ResourceApiInputDataPropertiesValidator">
|
||||
<argument type="service" id="validator" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\ApiBundle\Controller\GetSalesStatisticsAction" public="true">
|
||||
<argument type="service" id="sylius.query_bus" />
|
||||
<argument type="service" id="sylius.context.channel" />
|
||||
<argument type="service" id="serializer" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"
|
||||
>
|
||||
<services>
|
||||
<defaults>
|
||||
<defaults public="true">
|
||||
<tag name="messenger.message_handler" bus="sylius.query_bus" />
|
||||
</defaults>
|
||||
|
||||
|
|
@ -28,5 +28,11 @@
|
|||
<service id="Sylius\Bundle\ApiBundle\QueryHandler\GetAddressLogEntryCollectionHandler">
|
||||
<argument type="service" id="sylius.repository.address_log_entry" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\ApiBundle\QueryHandler\Admin\GetSalesStatisticsHandler">
|
||||
<argument type="service" id="Sylius\Bundle\CoreBundle\Provider\SalesStatisticsProviderInterface" />
|
||||
<argument type="service" id="sylius.repository.channel" />
|
||||
<tag name="messenger.message_handler" bus="sylius.query_bus" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* 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\CoreBundle\Provider;
|
||||
|
||||
use Sylius\Component\Core\Dashboard\DashboardStatisticsProviderInterface;
|
||||
use Sylius\Component\Core\Dashboard\Interval;
|
||||
use Sylius\Component\Core\Dashboard\SalesDataProviderInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\ValueObject\SalesStatistics;
|
||||
|
||||
class SalesStatisticsProvider implements SalesStatisticsProviderInterface
|
||||
{
|
||||
public function __construct(
|
||||
private DashboardStatisticsProviderInterface $statisticsProvider,
|
||||
private SalesDataProviderInterface $salesDataProvider,
|
||||
) {
|
||||
}
|
||||
|
||||
public function provide(
|
||||
ChannelInterface $channel,
|
||||
\DateTimeInterface $startDate,
|
||||
\DateTimeInterface $endDate,
|
||||
Interval $interval,
|
||||
): SalesStatistics {
|
||||
$statistics = $this->statisticsProvider->getStatisticsForChannelInPeriod($channel, $startDate, $endDate);
|
||||
|
||||
$salesSummary = $this->salesDataProvider->getSalesSummary(
|
||||
$channel,
|
||||
$startDate,
|
||||
$endDate,
|
||||
$interval,
|
||||
true,
|
||||
);
|
||||
|
||||
return new SalesStatistics(
|
||||
$salesSummary,
|
||||
$interval,
|
||||
$statistics->getNumberOfNewCustomers(),
|
||||
$statistics->getNumberOfNewOrders(),
|
||||
$statistics->getTotalSales(),
|
||||
$statistics->getAverageOrderValue(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* 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\CoreBundle\Provider;
|
||||
|
||||
use Sylius\Component\Core\Dashboard\Interval;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\ValueObject\SalesStatistics;
|
||||
|
||||
interface SalesStatisticsProviderInterface
|
||||
{
|
||||
public function provide(
|
||||
ChannelInterface $channel,
|
||||
\DateTimeInterface $startDate,
|
||||
\DateTimeInterface $endDate,
|
||||
Interval $interval,
|
||||
): SalesStatistics;
|
||||
}
|
||||
|
|
@ -35,5 +35,10 @@
|
|||
<argument type="service" id="sylius.repository.locale" />
|
||||
<argument type="string">%locale%</argument>
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\CoreBundle\Provider\SalesStatisticsProviderInterface" class="Sylius\Bundle\CoreBundle\Provider\SalesStatisticsProvider" >
|
||||
<argument type="service" id="Sylius\Component\Core\Dashboard\DashboardStatisticsProviderInterface"/>
|
||||
<argument type="service" id="Sylius\Component\Core\Dashboard\SalesDataProviderInterface"/>
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
|
|||
33
src/Sylius/Component/Core/ValueObject/SalesInPeriod.php
Normal file
33
src/Sylius/Component/Core/ValueObject/SalesInPeriod.php
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* 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\Component\Core\ValueObject;
|
||||
|
||||
class SalesInPeriod
|
||||
{
|
||||
public function __construct(
|
||||
private int $total,
|
||||
private \DateTimeInterface $period,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getPeriod(): \DateTimeInterface
|
||||
{
|
||||
return $this->period;
|
||||
}
|
||||
|
||||
public function getTotal(): int
|
||||
{
|
||||
return $this->total;
|
||||
}
|
||||
}
|
||||
63
src/Sylius/Component/Core/ValueObject/SalesStatistics.php
Normal file
63
src/Sylius/Component/Core/ValueObject/SalesStatistics.php
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* 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\Component\Core\ValueObject;
|
||||
|
||||
use Sylius\Component\Core\Dashboard\Interval;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
class SalesStatistics
|
||||
{
|
||||
/** @param SalesInPeriod[] $salesInPeriod */
|
||||
public function __construct(
|
||||
private array $salesInPeriod,
|
||||
private Interval $intervalType,
|
||||
private int $newCustomersCount,
|
||||
private int $newOrdersCount,
|
||||
private int $totalSales,
|
||||
private int $averageOrderValue,
|
||||
) {
|
||||
Assert::allIsInstanceOf($salesInPeriod, SalesInPeriod::class);
|
||||
}
|
||||
|
||||
/** @return SalesInPeriod[] */
|
||||
public function getSalesInPeriod(): array
|
||||
{
|
||||
return $this->salesInPeriod;
|
||||
}
|
||||
|
||||
public function getTotalSales(): int
|
||||
{
|
||||
return $this->totalSales;
|
||||
}
|
||||
|
||||
public function getNewCustomersCount(): int
|
||||
{
|
||||
return $this->newCustomersCount;
|
||||
}
|
||||
|
||||
public function getNewOrdersCount(): int
|
||||
{
|
||||
return $this->newOrdersCount;
|
||||
}
|
||||
|
||||
public function getAverageOrderValue(): int
|
||||
{
|
||||
return $this->averageOrderValue;
|
||||
}
|
||||
|
||||
public function getIntervalType(): string
|
||||
{
|
||||
return $this->intervalType->asString();
|
||||
}
|
||||
}
|
||||
30
symfony.lock
30
symfony.lock
|
|
@ -5,12 +5,6 @@
|
|||
"alcohol/iso4217": {
|
||||
"version": "4.0.0"
|
||||
},
|
||||
"amphp/amp": {
|
||||
"version": "v2.5.0"
|
||||
},
|
||||
"amphp/byte-stream": {
|
||||
"version": "v1.8.0"
|
||||
},
|
||||
"api-platform/core": {
|
||||
"version": "2.5",
|
||||
"recipe": {
|
||||
|
|
@ -49,15 +43,9 @@
|
|||
"coduo/php-to-string": {
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"composer/pcre": {
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"composer/semver": {
|
||||
"version": "1.5.1"
|
||||
},
|
||||
"composer/xdebug-handler": {
|
||||
"version": "1.4.3"
|
||||
},
|
||||
"dealerdirect/phpcodesniffer-composer-installer": {
|
||||
"version": "v0.7.0"
|
||||
},
|
||||
|
|
@ -67,9 +55,6 @@
|
|||
"dmore/chrome-mink-driver": {
|
||||
"version": "2.7.0"
|
||||
},
|
||||
"dnoegel/php-xdg-base-dir": {
|
||||
"version": "v0.1.1"
|
||||
},
|
||||
"doctrine/annotations": {
|
||||
"version": "1.0",
|
||||
"recipe": {
|
||||
|
|
@ -161,12 +146,6 @@
|
|||
"fakerphp/faker": {
|
||||
"version": "v1.12.0"
|
||||
},
|
||||
"felixfbecker/advanced-json-rpc": {
|
||||
"version": "v3.1.1"
|
||||
},
|
||||
"felixfbecker/language-server-protocol": {
|
||||
"version": "v1.4.0"
|
||||
},
|
||||
"friends-of-behat/mink": {
|
||||
"version": "v1.8.0"
|
||||
},
|
||||
|
|
@ -358,9 +337,6 @@
|
|||
"config/packages/test/nelmio_alice.yaml"
|
||||
]
|
||||
},
|
||||
"netresearch/jsonmapper": {
|
||||
"version": "v2.1.0"
|
||||
},
|
||||
"nikic/php-parser": {
|
||||
"version": "v4.6.0"
|
||||
},
|
||||
|
|
@ -466,9 +442,6 @@
|
|||
"polishsymfonycommunity/symfony-mocker-container": {
|
||||
"version": "v1.0.2"
|
||||
},
|
||||
"psalm/plugin-mockery": {
|
||||
"version": "0.3.0"
|
||||
},
|
||||
"psr/cache": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
|
|
@ -985,9 +958,6 @@
|
|||
"twig/twig": {
|
||||
"version": "v2.13.1"
|
||||
},
|
||||
"vimeo/psalm": {
|
||||
"version": "3.11.6"
|
||||
},
|
||||
"webmozart/assert": {
|
||||
"version": "1.9.1"
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue