- 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
185 lines
6.2 KiB
PHP
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');
|
|
}
|
|
}
|