ldc-boutique-cnc-sable/app/Directories/EloquentOrderDirectory.php
Jérémy Munsch 3b3997d547
feat(employees): automated sync with Arcadia API, BO settings, Shopper value objects, and pagination migration
- Implement automated synchronization of employees via Arcadia API (EmployeeSyncService, SynchronizeEmployeesWithArcadiaApi, FetchAndUpsertArcadiaEmployees)
- Add Back-Office application settings for employee sites (Setting singleton, GetSettingsQuery, UpdateSettings, SettingsController, EmployeeSitesInputComponent.vue)
- Strongly type Employee attributes using Domain Value Objects (ShopperEstablishmentCode, ShopperSiteCode, ShopperServiceCode) and Eloquent Casts
- Migrate pagination from Domain to Shared\Pagination namespace across queries and inputs
- Remove legacy and dead CSV employee import/export code
- Restore Domain\Ports\Services\CsvGenerator interface for Regate order generation
- Add comprehensive Pest unit, integration, and feature test coverage
2026-10-27 09:58:44 +01:00

185 lines
6.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Directories;
use App\Contracts\Directories\OrderDirectory;
use App\Models\Customer;
use App\Models\Order;
use App\Models\OrderLine;
use Carbon\CarbonInterface;
use Domain\Enums\OrderStatus;
use Illuminate\Contracts\Pagination\LengthAwarePaginator as PaginationLengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Date;
use Money\Currency;
use Money\Money;
use Ramsey\Uuid\UuidInterface;
use Shared\Pagination\Pagination;
use SortDirection;
class EloquentOrderDirectory implements OrderDirectory
{
/** @return \Illuminate\Pagination\LengthAwarePaginator<int,\App\Models\Order> */
public function getPaginatedOrdersOf(
Customer $customer,
Pagination $pagination,
bool $orderByDate = false
): PaginationLengthAwarePaginator {
$query = $this->orderByQueryFor($customer);
if ($query === null) {
return new LengthAwarePaginator(
items: [],
total: 0,
perPage: $pagination->pageSize()->value(),
currentPage: $pagination->pageNumber()->value(),
);
}
if ($orderByDate) {
$query->orderBy('id', SortDirection::Descending);
}
return $query->paginate(
perPage: $pagination->pageSize()->value(),
page: $pagination->pageNumber()->value(),
);
}
public function find(UuidInterface $id): ?Order
{
return Order::query()
->where('id', $id->toString())
->first();
}
public function findOf(Customer $customer, UuidInterface $id): ?Order
{
$query = $this->orderByQueryFor($customer);
if ($query === null) {
return null;
}
return $query->where('id', $id->toString())->first();
}
/** @return \Illuminate\Database\Eloquent\Builder<\App\Models\Order>|null */
private function orderByQueryFor(Customer $customer): ?Builder
{
/** @var \Domain\Models\ValueObjects\Matricule | null */
$employeeMatricule = $customer->employee()->value('matricule');
if ($employeeMatricule === null) {
return null;
}
return Order::query()
->where('employee_matricule', $employeeMatricule->value);
}
/**
* @return Collection<int,Order>
*/
public function getValidatedOrders(CarbonInterface $start, CarbonInterface $end): Collection
{
return Order::query()
->whereIn('status', [OrderStatus::VALIDATED->value])
->whereCreatedAtBetween($start, $end)
->get();
}
/**
* @return Collection<int,Order>
*/
public function getOrdersByStatus(OrderStatus $status, CarbonInterface $start, CarbonInterface $end): Collection
{
return Order::query()
->where('status', $status->value)
->whereCreatedAtBetween($start, $end)
->get();
}
public function getNbActiveClients(CarbonInterface $start, CarbonInterface $end): int
{
$matricules = Order::query()
->whereCreatedAtBetween($start, $end)
->get(['employee'])
->map(fn (Order $order) => $order->employee->matricule->value)
->filter()
->unique();
if ($matricules->isEmpty()) {
return 0;
}
return Customer::query()
->whereHas('employee', function (Builder $query) use ($matricules): void {
$query->whereIn('matricule', $matricules);
})->count();
}
public function getNbCmdWith7DayDelay(CarbonInterface $start, CarbonInterface $end): int
{
return Order::query()
->whereIn('status', [OrderStatus::VALIDATED->value])
->whereCreatedAtBetween($start, $end)
->get()
->filter(function (Order $order) {
if (is_null($order->pickup_day) || is_null($order->created_at)) {
return false;
}
return $order->pickup_day->lessThanOrEqualTo($order->created_at->modify('+7 day'));
})->count();
}
public function getNbCmdWith14DayDelay(CarbonInterface $start, CarbonInterface $end): int
{
return Order::query()
->whereIn('status', [OrderStatus::VALIDATED->value])
->whereCreatedAtBetween($start, $end)
->get()
->filter(function (Order $order) {
if (is_null($order->pickup_day) || is_null($order->created_at)) {
return false;
}
return Date::create($order->pickup_day)->betweenIncluded(
Date::create($order->created_at->modify('+8 day')),
Date::create($order->created_at->modify('+14 day'))
);
})->count();
}
public function getCaHt(CarbonInterface $start, CarbonInterface $end): float
{
$totalCaHt = OrderLine::query()->whereHas('order', function (Builder $query) use ($start, $end): void {
$query->whereIn('status', [OrderStatus::VALIDATED->value])
->whereCreatedAtBetween($start, $end);
})->sum('total_ht');
return ((float) (new Money((int) $totalCaHt, resolve(Currency::class)))->getAmount()) / 100;
}
public function getCaTtc(CarbonInterface $start, CarbonInterface $end): float
{
$totalCaTtc = OrderLine::query()->whereHas('order', function (Builder $query) use ($start, $end): void {
$query->whereIn('status', [OrderStatus::VALIDATED->value])
->whereCreatedAtBetween($start, $end);
})->sum('total_ttc');
return ((float) (new Money((int) $totalCaTtc, resolve(Currency::class)))->getAmount()) / 100;
}
public function getTotalQuantity(CarbonInterface $start, CarbonInterface $end): int
{
return (int) OrderLine::query()->whereHas('order', function (Builder $query) use ($start, $end): void {
$query->where('status', OrderStatus::VALIDATED->value)
->whereCreatedAtBetween($start, $end);
})->sum('quantity');
}
}