96 lines
3.0 KiB
PHP
96 lines
3.0 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Machine;
|
|
use App\Models\PricingRule;
|
|
use App\Models\Promotion;
|
|
use Carbon\Carbon;
|
|
use Carbon\CarbonInterface;
|
|
|
|
class PricingService
|
|
{
|
|
public function getActivePrice(Machine $machine, ?CarbonInterface $at = null): float
|
|
{
|
|
$at = Carbon::instance($at ?? now());
|
|
$establishment = $machine->establishment;
|
|
|
|
if ($establishment === null) {
|
|
$machine->loadMissing('establishment');
|
|
$establishment = $machine->establishment;
|
|
}
|
|
|
|
if ($establishment === null) {
|
|
throw new \RuntimeException('Établissement introuvable pour la machine.');
|
|
}
|
|
|
|
$dayType = $this->resolveDayType($at);
|
|
$time = $at->format('H:i:s');
|
|
|
|
$rules = PricingRule::query()
|
|
->where('establishment_id', $establishment->id)
|
|
->where('is_active', true)
|
|
->where(function ($query) use ($machine) {
|
|
$query->whereNull('machine_id')
|
|
->orWhere('machine_id', $machine->id);
|
|
})
|
|
->where(function ($query) use ($machine) {
|
|
$query->whereNull('machine_type')
|
|
->orWhere('machine_type', $machine->type);
|
|
})
|
|
->where(function ($query) use ($dayType) {
|
|
$query->where('day_type', 'all')
|
|
->orWhere('day_type', $dayType);
|
|
})
|
|
->where('slot_start', '<=', $time)
|
|
->where('slot_end', '>', $time)
|
|
->orderBy('priority')
|
|
->orderByRaw('CASE WHEN machine_id IS NOT NULL THEN 0 WHEN machine_type IS NOT NULL THEN 1 ELSE 2 END')
|
|
->get();
|
|
|
|
$rule = $rules->first();
|
|
|
|
if ($rule === null) {
|
|
throw new \RuntimeException('Aucune règle tarifaire active pour cette machine.');
|
|
}
|
|
|
|
$basePrice = (float) $rule->price;
|
|
|
|
return $this->applyPromotion($establishment->id, $machine->type, $at, $basePrice);
|
|
}
|
|
|
|
private function resolveDayType(CarbonInterface $at): string
|
|
{
|
|
if ($at->isWeekend()) {
|
|
return 'weekend';
|
|
}
|
|
|
|
return 'weekday';
|
|
}
|
|
|
|
private function applyPromotion(int $establishmentId, string $machineType, CarbonInterface $at, float $basePrice): float
|
|
{
|
|
$promotion = Promotion::query()
|
|
->where('establishment_id', $establishmentId)
|
|
->where('is_active', true)
|
|
->where('starts_at', '<=', $at)
|
|
->where('ends_at', '>=', $at)
|
|
->where(function ($query) use ($machineType) {
|
|
$query->where('machine_type', 'all')
|
|
->orWhere('machine_type', $machineType);
|
|
})
|
|
->orderByDesc('discount_value')
|
|
->first();
|
|
|
|
if ($promotion === null) {
|
|
return round($basePrice, 2);
|
|
}
|
|
|
|
$discount = $promotion->discount_type === 'percent'
|
|
? $basePrice * ((float) $promotion->discount_value / 100)
|
|
: (float) $promotion->discount_value;
|
|
|
|
return round(max(0, $basePrice - $discount), 2);
|
|
}
|
|
}
|