<?php
namespace App\Service;
use App\Entity\Delivery;
use App\Entity\Variables\PriceComplementary;
use App\Entity\Variables\PricePerKilometer;
use App\Entity\Variables\Tax;
use App\Entity\VehicleType;
use App\Enum\MercureToEnum;
use App\Enum\MercureTypeEnum;
use App\Enum\PriceComplementaryEnum;
use App\Enum\TaxEnum;
use App\Repository\Variables\PriceComplementaryRepository;
use App\Repository\Variables\PricePerKilometerRepository;
use App\Repository\Variables\TaxRepository;
use Doctrine\Persistence\ManagerRegistry;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
class DeliveryService
{
public function __construct(
private RequestStack $requestStack,
private ManagerRegistry $doctrine,
private HubInterface $hub,
private LoggerInterface $logger
) {
}
/**
* Get SousTotal HT.
*
* distance en mètre
*/
public function getSousTotalHT(int $distance, float $fraisMeteo): float
{
/**
* @var PricePerKilometerRepository $pricePerKilometerRepository
*/
$pricePerKilometerRepository = $this->doctrine->getRepository(PricePerKilometer::class);
/**
* @var PricePerKilometer[] $prices
*/
$prices = $pricePerKilometerRepository->getCurrents();
$tarifMetrique = [];
foreach ($prices as $price) {
$tarifMetrique[$price->getUpperLanding()] = $price->getRateMetricTTC();
}
/**
* @var PriceComplementaryRepository $priceComplementaryRepository
*/
$priceComplementaryRepository = $this->doctrine->getRepository(PriceComplementary::class);
$price = $priceComplementaryRepository->getCurrentPrice(PriceComplementaryEnum::MINIMUM->value);
if (!$price || !($price instanceof PriceComplementary)) {
throw new \Exception('Price not found');
}
$minimum = $price->getRateTTC();
$sousTotal = $fraisMeteo;
$remainingDistance = $distance;
if ($distance <= 1000) {
$tarifMetriqueHT = $tarifMetrique[1.00];
$sousTotal = ($tarifMetriqueHT * $distance / 2) + $minimum;
} else {
do {
$ceil = ceil($remainingDistance / 1000);
$floor = floor($remainingDistance / 1000);
$multiple = 1000;
$rest = $remainingDistance - $floor * 1000;
if ($rest) {
$multiple = $rest;
}
$tarifMetriqueHT = $tarifMetrique[$ceil];
$sousTotal = $sousTotal + ($tarifMetriqueHT * $multiple);
$remainingDistance = $remainingDistance - $multiple;
} while ($remainingDistance > 0);
}
return $sousTotal;
}
/**
* Get options.
*
* @return array<string, mixed>
*/
public function getOptions(Delivery $delivery, float $sousTotal): array
{
/**
* @var PriceComplementaryRepository $priceComplementaryRepository
*/
$priceComplementaryRepository = $this->doctrine->getRepository(PriceComplementary::class);
/**
* @var array<int, PriceComplementary> $prices
*/
$prices = $priceComplementaryRepository->getCurrents();
$options = [];
foreach (PriceComplementaryEnum::cases() as $case) {
foreach ($prices as $price) {
if ($case->value == $price->getOptionRate()) {
$options[$case->value] = $price->getRateTTC();
}
}
}
$articles = $delivery->getDeliveryArticles();
$roundTrip = false;
$brittle = $floor = $insurance = $adValorem = 0;
foreach ($articles as $article) {
if ($article->isRoundTrip()) {
$roundTrip = $sousTotal / 2;
}
if ($article->isBrittle()) {
$brittle += $options[PriceComplementaryEnum::BRITTLE->value];
}
if ($article->isInsurance()) {
$insurance += $options[PriceComplementaryEnum::INSURANCE->value];
}
if ($article->isAdValorem()) {
$adValorem += $sousTotal * $options[PriceComplementaryEnum::ADVALOREM->value] / 100;
}
}
$sender = $delivery->getSender();
if (!$sender) {
throw new \Exception('Sender not found', Response::HTTP_NOT_FOUND);
}
$floorSender = $sender->getFloor();
if ($floorSender && $floorSender > 0) {
$floor += $options[PriceComplementaryEnum::FLOOR->value];
}
$recipient = $delivery->getRecipient();
if (!$recipient) {
throw new \Exception('Recipient not found', Response::HTTP_NOT_FOUND);
}
$floorRecipient = $recipient->getFloor();
if ($floorRecipient && $floorRecipient > 0) {
$floor += $options[PriceComplementaryEnum::FLOOR->value];
}
$additionalArticle = count($articles) > 1 ? $options[PriceComplementaryEnum::ADDITIONAL_ARTICLE->value] * count($articles) : 0;
return [
'roundTrip' => $roundTrip,
'brittle' => $brittle,
'floor' => $floor,
'insurance' => $insurance,
'adValorem' => $adValorem,
'additionalArticle' => $additionalArticle,
'total' => $roundTrip + $brittle + $floor + $insurance + $adValorem + $additionalArticle,
];
}
/**
* Check if is electric.
*/
public function isElectric(Delivery $delivery): bool
{
$vehicleType = $delivery->getVehicleType();
$electrics = [VehicleType::ELECTRIC_BYCICLE, VehicleType::ELECTRIC_TWO_WHEELER];
// if (in_array($vehicleType, $electrics)) {
if ($vehicleType->getEnergy()->getName() == "électrique")
return true;
return false;
}
public function getFuelSurcharge(): float
{
/** TODO index - 2 */
$index2 = 102;
/** TODO index */
$index = 100;
/** TODO ponderation */
$ponderation = 0.1;
return ((($index2 - $index) / $index) * 100) * $ponderation;
}
public function setDeliverySessionData(bool $isPro, bool $send, ?int $deliveryId): void
{
if (!$deliveryId) {
throw new \Exception('DeliveryId not found', Response::HTTP_NOT_FOUND);
}
$session = $this->requestStack->getSession();
$session->set('isPro', $isPro);
$session->set('send', $send);
$session->set('deliveryId', $deliveryId);
}
/**
* @return array<string, mixed>
*/
public function getDeliverySessionData(): array
{
$session = $this->requestStack->getSession();
return [
'isPro' => $session->get('isPro', 0),
'send' => $session->get('send', 0),
'deliveryId' => $session->get('deliveryId', 0),
];
}
public function getThermicCost(Delivery $delivery): float
{
return $this->isElectric($delivery) ? 0 : $this->getFuelSurcharge();
}
/**
* TODO getDiscount.
*/
public function getDiscount(): int
{
return 0;
}
public function getTotalHT(Delivery $delivery, int $distance, float $fraisMeteo, float $electricFee): float
{
$sousTotalHT = $this->getSousTotalHT($distance, $fraisMeteo);
$options = $this->getOptions($delivery, $sousTotalHT);
$thermic = $this->getThermicCost($delivery);
$electric = $this->isElectric($delivery) ? $electricFee : 0;
$discount = $this->getDiscount();
return $sousTotalHT + $options['total'] - $discount + $thermic + $electric;
}
public function getTotalTTC(Delivery $delivery, int $distance, float $fraisMeteo, float $electricFee, float $tva): float
{
$sousTotalTTC = $this->getTotalHT($delivery, $distance, $fraisMeteo, $electricFee);
return round($sousTotalTTC + ($tva * $sousTotalTTC), 2);
}
/**
* @return ?array{totalTTC: float}
*/
public function getTotals(Delivery $delivery)
{
return $delivery->getDeliveryTotals();
}
/**
* Get TVA.
*
* @throws \Exception
*/
public function getTVA(): float
{
/**
* @var TaxRepository $taxRepository
*/
$taxRepository = $this->doctrine->getRepository(Tax::class);
/**
* @var ?Tax $tva
*/
$tva = $taxRepository->getCurrent(TaxEnum::NORMAL->value);
if (!$tva) {
throw new \Exception('TVA not found');
}
return $tva->getTauxPercent();
}
/**
* get Price.
*
* @throws \Exception
*/
public function getPrice(PriceComplementaryEnum $price): float
{
/**
* @var PriceComplementaryRepository $priceComplementaryRepository
*/
$priceComplementaryRepository = $this->doctrine->getRepository(PriceComplementary::class);
$price = $priceComplementaryRepository->getCurrentPrice($price->value);
if (null === $price || !($price instanceof PriceComplementary)) {
throw new \Exception('Price not found');
}
return $price->getRateTTC();
}
/**
* @throws \Exception
*/
public function publishMessage(
?int $id, MercureToEnum $to, MercureTypeEnum $type, mixed $message, string $nextPost = null, string $nextGet = null
): void {
if (!$id) {
throw new \Exception('Id not found', Response::HTTP_NOT_FOUND);
}
$data = [
'to' => $to->value,
'type' => $type,
'message' => $message,
];
if ($nextPost) {
$data['nextPost'] = $nextPost;
}
if ($nextGet) {
$data['nextGet'] = $nextGet;
}
$data['topic'] = sprintf('http://nibuco/%s/%s', $to->value, $id);
$json = json_encode($data);
if (!$json) {
throw new \Exception('Json error', Response::HTTP_BAD_REQUEST);
}
$update = new Update(
$data['topic'],
$json
);
$this->hub->publish($update);
$this->logger->info(\sprintf('MERCURE : Publish message to %s for type %s (id: %d)', $to->value, $type->value, $id));
}
}