ldc-boutique-cnc-sable/app/Services/StatsService.php
Jérémy Munsch 205865f524
style(stats): disable cyclomatic complexity check for getStats
Disable cyclomatic complexity check for the getStats method to improve code readability.
2026-10-27 09:58:23 +01:00

329 lines
12 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services;
use App\Contracts\Directories\EmployeeDirectory;
use App\Contracts\Directories\OrderDirectory;
use Carbon\CarbonInterface;
use Carbon\CarbonPeriod;
use Domain\Enums\OrderStatus;
use Domain\Enums\StatsCustomerType;
use Domain\Enums\StatsShopType;
use Illuminate\Support\Collection;
class StatsService
{
public function __construct(
private readonly EmployeeDirectory $employeeDirectory,
private readonly OrderDirectory $orderDirectory
) {
}
/**
* getStats
*
* @param array<int, \Domain\Enums\StatsCustomerType|\Domain\Enums\StatsShopType> $statsType
* @return array<int, array{previous:int|float,current:int|float,diff:int|float}>
*
* @phpcs:disable Generic.Metrics.CyclomaticComplexity
*/
public function getStats(array $statsType, CarbonInterface $start, CarbonInterface $end): array
{
// phpcs:enable
$stats = [];
foreach ($statsType as $type) {
$previousPeriod = $this->getPreviousPeriod($start, $end);
$previousStart = $previousPeriod->getStartDate();
$previousEnd = $previousPeriod->getIncludedEndDate();
switch ($type) {
case StatsShopType::canceledRate:
$previous = $this->getCanceledRate($previousStart, $previousEnd);
$current = $this->getCanceledRate($start, $end);
break;
case StatsShopType::orderFrequency:
$previous = $this->getOrderFrequency($previousStart, $previousEnd);
$current = $this->getOrderFrequency($start, $end);
break;
case StatsShopType::purchaseFrequency:
$previous = $this->getPurchaseFrequency($previousStart, $previousEnd);
$current = $this->getPurchaseFrequency($start, $end);
break;
case StatsShopType::averageBasketHt:
$previous = $this->getAverageBasketHt($previousStart, $previousEnd);
$current = $this->getAverageBasketHt($start, $end);
break;
case StatsShopType::averageProductCountPerOrder:
$previous = $this->getAverageProductCountPerOrder($previousStart, $previousEnd);
$current = $this->getAverageProductCountPerOrder($start, $end);
break;
case StatsShopType::caHt:
$previous = $this->getCaHt($previousStart, $previousEnd);
$current = $this->getCaHt($start, $end);
break;
case StatsShopType::caTtc:
$previous = $this->getCaTtc($previousStart, $previousEnd);
$current = $this->getCaTtc($start, $end);
break;
case StatsShopType::nbCmdWith7DayDelay:
$previous = $this->getNbCmdWith7DayDelay($previousStart, $previousEnd);
$current = $this->getNbCmdWith7DayDelay($start, $end);
break;
case StatsShopType::nbCmdWith14DayDelay:
$previous = $this->getNbCmdWith14DayDelay($previousStart, $previousEnd);
$current = $this->getNbCmdWith14DayDelay($start, $end);
break;
case StatsShopType::nbActiveClients:
$previous = $this->getNbActiveClients($previousStart, $previousEnd);
$current = $this->getNbActiveClients($start, $end);
break;
case StatsShopType::nbValidateCmd:
$previous = $this->getNbValidateCmd($previousStart, $previousEnd);
$current = $this->getNbValidateCmd($start, $end);
break;
case StatsShopType::nbWaitingCmd:
$previous = $this->getNbWaitingCmd($previousStart, $previousEnd);
$current = $this->getNbWaitingCmd($start, $end);
break;
case StatsShopType::nbCanceledCmd:
$previous = $this->getNbCanceledCmd($previousStart, $previousEnd);
$current = $this->getNbCanceledCmd($start, $end);
break;
case StatsCustomerType::nbSalarie:
$previous = $this->getActiveEmployeesCount($previousStart, $previousEnd);
$current = $this->getActiveEmployeesCount($start, $end);
break;
case StatsCustomerType::nbSalarieWithAccount:
$previous = $this->getActiveEmployeesCountWithAnAccount($previousStart, $previousEnd);
$current = $this->getActiveEmployeesCountWithAnAccount($start, $end);
break;
case StatsCustomerType::membershipRate:
$previous = $this->getMembershipRate($previousStart, $previousEnd);
$current = $this->getMembershipRate($start, $end);
break;
default:
throw new \InvalidArgumentException("Unknown stats type: {$type->label()}");
}
$stats[$type->value] = [
'previous' => $previous,
'current' => $current,
'diff' => $current - $previous,
];
}
return $stats;
}
/**
* Get the percentage of active registered people / people with a generated code
*/
public function getMembershipRate(CarbonInterface $start, CarbonInterface $end): float
{
$registered = $this->getActiveEmployeesCountWithAnAccount($start, $end);
$employee = $this->getActiveEmployeesCount($start, $end);
return $employee ? round($registered / $employee * 100, 2) : 0.0;
}
/**
* Get the previous period of the selected date.
*/
public function getPreviousPeriod(CarbonInterface $start, CarbonInterface $end): CarbonPeriod
{
$diffDays = $start->copy()->diffInDays($end);
return CarbonPeriod::create(
$start->copy()->subDays($diffDays + 1),
$start->copy()->subDay()
);
}
/**
* Get the percentage of registered people / people with a generated code
*/
public function getMembershipPercentage(): float
{
$employee = $this->employeeDirectory->countActiveEmployees();
$registered = $this->employeeDirectory->countActiveRegisteredEmployees();
return $employee ? round($registered / $employee * 100, 2) : 0.0;
}
/**
* Get the number of active employees within the date range.
*/
private function getActiveEmployeesCount(CarbonInterface $start, CarbonInterface $end): int
{
return $this->employeeDirectory->countActiveEmployeesWithin($start, $end);
}
/**
* Get the number of active registered employees within the date range.
*/
private function getActiveEmployeesCountWithAnAccount(CarbonInterface $start, CarbonInterface $end): int
{
return $this->employeeDirectory->countActiveRegisteredEmployeesWithin($start, $end);
}
/**
* Get all validated orders.
*
* @return Collection<int,\App\Models\Order>
*/
private function getValidatedOrders(CarbonInterface $start, CarbonInterface $end): Collection
{
return $this->orderDirectory->getValidatedOrders($start, $end);
}
/**
* Get the number of validated command.
*/
private function getNbValidateCmd(CarbonInterface $start, CarbonInterface $end): int
{
return $this->getValidatedOrders($start, $end)->count();
}
/**
* Get the number of waiting command.
*/
private function getNbWaitingCmd(CarbonInterface $start, CarbonInterface $end): int
{
return $this->getOrdersByStatus(OrderStatus::WAITING, $start, $end)->count();
}
/**
* Get the number of canceled command.
*/
private function getNbCanceledCmd(CarbonInterface $start, CarbonInterface $end): int
{
return $this->getOrdersByStatus(OrderStatus::CANCELED, $start, $end)->count();
}
/**
* Get orders by status.
*
* @return Collection<int,\App\Models\Order>
*/
private function getOrdersByStatus(OrderStatus $statusId, CarbonInterface $start, CarbonInterface $end): Collection
{
return $this->orderDirectory->getOrdersByStatus($statusId, $start, $end);
}
/**
* Get the canceled command rate.
*/
private function getCanceledRate(CarbonInterface $start, CarbonInterface $end): float
{
$nbWaitingCmd = $this->getNbWaitingCmd($start, $end);
$nbCanceledCmd = $this->getNbCanceledCmd($start, $end);
$nbValidateCmd = $this->getNbValidateCmd($start, $end);
// Prevent division by 0.
if (($nbValidateCmd + $nbWaitingCmd + $nbCanceledCmd) !== 0) {
return round(
(($nbWaitingCmd + $nbCanceledCmd) / ($nbValidateCmd + $nbWaitingCmd + $nbCanceledCmd)) * 100,
2
);
}
return 0.0;
}
/**
* Get the order frequency.
*/
private function getOrderFrequency(CarbonInterface $start, CarbonInterface $end): float
{
$nbValidateCmd = $this->getNbValidateCmd($start, $end);
$period = new CarbonPeriod($start, $end)->count();
return round($nbValidateCmd / $period, 2);
}
/**
* Get the order frequency.
*/
private function getPurchaseFrequency(CarbonInterface $start, CarbonInterface $end): float
{
$activeClients = $this->getNbActiveClients($start, $end);
if (! $activeClients) {
return 0.0;
}
$nbValidateCmd = $this->getNbValidateCmd($start, $end);
return round($nbValidateCmd / $activeClients, 2);
}
/**
* Get the number of active clients (ordered in the period).
*/
private function getNbActiveClients(CarbonInterface $start, CarbonInterface $end): int
{
return $this->orderDirectory->getNbActiveClients($start, $end);
}
/**
* Get the number command where the collect day is 7 days after the order.
*/
private function getNbCmdWith7DayDelay(CarbonInterface $start, CarbonInterface $end): int
{
return $this->orderDirectory->getNbCmdWith7DayDelay($start, $end);
}
/**
* Get the number command where the collect day is 14 days after the order.
*/
private function getNbCmdWith14DayDelay(CarbonInterface $start, CarbonInterface $end): int
{
return $this->orderDirectory->getNbCmdWith14DayDelay($start, $end);
}
/**
* Get the CA HT.
*/
private function getCaHt(CarbonInterface $start, CarbonInterface $end): float
{
return $this->orderDirectory->getCaHt($start, $end);
}
/**
* Get the CA TTC.
*/
private function getCaTtc(CarbonInterface $start, CarbonInterface $end): float
{
return $this->orderDirectory->getCaTtc($start, $end);
}
/**
* Get the average basket HT.
*/
private function getAverageBasketHt(CarbonInterface $start, CarbonInterface $end): float
{
// Prevent division by 0.
if ($this->getNbValidateCmd($start, $end) !== 0) {
return round($this->getCaHt($start, $end) / $this->getNbValidateCmd($start, $end), 2);
}
return 0.0;
}
/**
* Get the average product count per order.
*/
private function getAverageProductCountPerOrder(CarbonInterface $start, CarbonInterface $end): float
{
$nbValidateCmd = $this->getNbValidateCmd($start, $end);
// Prevent division by 0.
if ($nbValidateCmd !== 0) {
$totalQty = $this->orderDirectory->getTotalQuantity($start, $end);
return round((float) $totalQty / $nbValidateCmd, 2);
}
return 0.0;
}
}