ADD NextDeliveryDateCalculatorService
This commit is contained in:
parent
9facc6a3a7
commit
1e00304eda
@ -5,6 +5,7 @@
|
||||
use App\Models\ProfitCenter;
|
||||
use App\Models\ProfitCenterSupplierSchedule;
|
||||
use App\Models\Supplier;
|
||||
use App\Services\NextDeliveryDateCalculatorService;
|
||||
use Filament\Pages\Page;
|
||||
|
||||
class CalendarTest extends Page
|
||||
@ -21,8 +22,16 @@ class CalendarTest extends Page
|
||||
|
||||
public ?int $profitCenterId = null;
|
||||
|
||||
public string $selectedDate = '';
|
||||
|
||||
public string $selectedTime = '';
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$now = now();
|
||||
$this->selectedDate = $now->format('Y-m-d');
|
||||
$this->selectedTime = $now->format('H:i');
|
||||
|
||||
$first = ProfitCenterSupplierSchedule::first();
|
||||
|
||||
if ($first) {
|
||||
@ -31,6 +40,49 @@ public function mount(): void
|
||||
}
|
||||
}
|
||||
|
||||
public function getSupplier(): ?Supplier
|
||||
{
|
||||
if (!$this->supplierId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Supplier::find($this->supplierId);
|
||||
}
|
||||
|
||||
public function getNextDeliveryDate(): ?string
|
||||
{
|
||||
if (! $this->supplierId || ! $this->profitCenterId || ! $this->selectedDate || ! $this->selectedTime) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$dt = \Illuminate\Support\Carbon::parse("{$this->selectedDate} {$this->selectedTime}");
|
||||
|
||||
$result = app(NextDeliveryDateCalculatorService::class)->calculate(
|
||||
supplierId: $this->supplierId,
|
||||
profitCenterId: $this->profitCenterId,
|
||||
orderDateTime: $dt,
|
||||
);
|
||||
|
||||
return $result?->translatedFormat('Y. F j., l');
|
||||
}
|
||||
|
||||
public function getNextDeliveryDateRaw(): ?string
|
||||
{
|
||||
if (! $this->supplierId || ! $this->profitCenterId || ! $this->selectedDate || ! $this->selectedTime) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$dt = \Illuminate\Support\Carbon::parse("{$this->selectedDate} {$this->selectedTime}");
|
||||
|
||||
$result = app(NextDeliveryDateCalculatorService::class)->calculate(
|
||||
supplierId: $this->supplierId,
|
||||
profitCenterId: $this->profitCenterId,
|
||||
orderDateTime: $dt,
|
||||
);
|
||||
|
||||
return $result?->format('Y-m-d');
|
||||
}
|
||||
|
||||
public function getSuppliers(): array
|
||||
{
|
||||
return Supplier::orderBy('name')->pluck('name', 'id')->toArray();
|
||||
@ -59,11 +111,28 @@ public function updatedSupplierId(): void
|
||||
}
|
||||
|
||||
$this->dispatch('calendar-filter-changed', supplierId: $this->supplierId, profitCenterId: $this->profitCenterId);
|
||||
$this->dispatchNextDeliveryDateChanged();
|
||||
}
|
||||
|
||||
public function updatedProfitCenterId(): void
|
||||
{
|
||||
$this->dispatch('calendar-filter-changed', supplierId: $this->supplierId, profitCenterId: $this->profitCenterId);
|
||||
$this->dispatchNextDeliveryDateChanged();
|
||||
}
|
||||
|
||||
public function updatedSelectedDate(): void
|
||||
{
|
||||
$this->dispatchNextDeliveryDateChanged();
|
||||
}
|
||||
|
||||
public function updatedSelectedTime(): void
|
||||
{
|
||||
$this->dispatchNextDeliveryDateChanged();
|
||||
}
|
||||
|
||||
protected function dispatchNextDeliveryDateChanged(): void
|
||||
{
|
||||
$this->dispatch('next-delivery-date-changed', date: $this->getNextDeliveryDateRaw());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -30,6 +30,9 @@ class DeliveryCalendarWidget extends FullCalendarWidget
|
||||
|
||||
public ?int $profitCenterId = null;
|
||||
|
||||
/** Következő szállítási nap dátuma (YYYY-MM-DD formátum), kék kiemeléshez. */
|
||||
public ?string $nextDeliveryDate = null;
|
||||
|
||||
/**
|
||||
* Inaktív (nem választható) napok listája.
|
||||
*
|
||||
@ -64,6 +67,13 @@ public function onFilterChanged(?int $supplierId, ?int $profitCenterId): void
|
||||
$this->refreshRecords();
|
||||
}
|
||||
|
||||
#[On('next-delivery-date-changed')]
|
||||
public function onNextDeliveryDateChanged(?string $date): void
|
||||
{
|
||||
$this->nextDeliveryDate = $date;
|
||||
$this->refreshRecords();
|
||||
}
|
||||
|
||||
protected function loadRecordFromFilter(): void
|
||||
{
|
||||
if ($this->record instanceof ProfitCenterSupplierSchedule) {
|
||||
@ -171,6 +181,23 @@ public function fetchEvents(array $info): array
|
||||
$current->addDay();
|
||||
}
|
||||
|
||||
// 5. Következő szállítási nap – kék háttér
|
||||
if ($this->nextDeliveryDate) {
|
||||
$nextDate = Carbon::parse($this->nextDeliveryDate);
|
||||
if ($nextDate->between($start, $end)) {
|
||||
$events[] = [
|
||||
'id' => 'next-delivery-' . $nextDate->format('Y-m-d'),
|
||||
'title' => '',
|
||||
'start' => $nextDate->format('Y-m-d'),
|
||||
'end' => $nextDate->copy()->addDay()->format('Y-m-d'),
|
||||
'allDay' => true,
|
||||
'display' => 'background',
|
||||
'backgroundColor' => '#3B82F6',
|
||||
'extendedProps' => ['type' => 'next-delivery'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$this->dispatch('calendar-events-done');
|
||||
return $events;
|
||||
}
|
||||
|
||||
97
app/Services/NextDeliveryDateCalculatorService.php
Normal file
97
app/Services/NextDeliveryDateCalculatorService.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\ProfitCenter;
|
||||
use App\Models\ProfitCenterSupplierSchedule;
|
||||
use App\Models\Supplier;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class NextDeliveryDateCalculatorService extends ServiceBase
|
||||
{
|
||||
public function __construct(
|
||||
protected DeliveryCalendarService $deliveryCalendarService,
|
||||
protected WorkCalendarService $workCalendarService,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Meghatározza a következő szállítási napot egy adott supplier–profitcenter párosnál,
|
||||
* a megrendelés leadásának dátuma és időpontja alapján.
|
||||
*
|
||||
* Ha a suppliernek van szállítási korlátozása (hasDeliveryConstraint), figyelembe veszi:
|
||||
* - orderCutOffTime: leadási határidő (óra), ha előtte adjuk le, a nap beleszámít az átfutásba
|
||||
* - deliveryLeadTime: szállítási átfutási idő (óra → munkanapban számítva)
|
||||
*
|
||||
* @param int $supplierId
|
||||
* @param int $profitCenterId
|
||||
* @param Carbon $orderDateTime A leadás dátuma és időpontja
|
||||
* @return Carbon|null A következő lehetséges szállítási nap
|
||||
*/
|
||||
public function calculate(
|
||||
int $supplierId,
|
||||
int $profitCenterId,
|
||||
Carbon $orderDateTime
|
||||
): ?Carbon {
|
||||
$assignment = ProfitCenterSupplierSchedule::where('supplier_id', $supplierId)
|
||||
->where('profit_center_id', $profitCenterId)
|
||||
->with('deliverySchedule')
|
||||
->first();
|
||||
|
||||
if (! $assignment?->deliverySchedule) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$supplier = Supplier::find($supplierId);
|
||||
$pc = ProfitCenter::find($profitCenterId);
|
||||
|
||||
if (! $supplier || ! $pc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$leadDays = 0;
|
||||
$startDay = $orderDateTime->copy()->startOfDay();
|
||||
|
||||
if ($supplier->hasDeliveryConstraint) {
|
||||
$cutOffHour = (int) $supplier->orderCutOffTime;
|
||||
$leadDays = (int) ($supplier->deliveryLeadTime / 24);
|
||||
|
||||
// Ha a leadási határidő UTÁN vagyunk, holnap az első számítandó munkanap
|
||||
if ($orderDateTime->hour >= $cutOffHour) {
|
||||
$startDay->addDay();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->findNextDeliveryDay($startDay, $supplier, $pc, $leadDays);
|
||||
}
|
||||
|
||||
/**
|
||||
* Munkanapokban számol előre ($leadDays darabot), majd az első olyan napot adja vissza,
|
||||
* amelyik szállítási nap is.
|
||||
*/
|
||||
private function findNextDeliveryDay(
|
||||
Carbon $candidate,
|
||||
Supplier $supplier,
|
||||
ProfitCenter $pc,
|
||||
int $leadDays,
|
||||
): ?Carbon {
|
||||
$workingDaysCounted = 0;
|
||||
|
||||
// Max. 365 nap iteráció (végtelen ciklus-védelem)
|
||||
for ($i = 0; $i < 365; $i++) {
|
||||
if ($this->workCalendarService->isWorkDay($candidate)) {
|
||||
if ($workingDaysCounted >= $leadDays) {
|
||||
// Átfutási idő letelt – az első szállítási napot keressük
|
||||
if ($this->deliveryCalendarService->isDeliveryDay($pc, $supplier, $candidate)) {
|
||||
return $candidate->copy();
|
||||
}
|
||||
} else {
|
||||
$workingDaysCounted++;
|
||||
}
|
||||
}
|
||||
$candidate->addDay();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -33,6 +33,62 @@ class="w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Szállítási korlátozások --}}
|
||||
<div class="mb-4 rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
||||
<h3 class="mb-3 text-sm font-semibold text-gray-700 dark:text-gray-300">Szállítási korlátozások</h3>
|
||||
<div class="flex flex-wrap items-center gap-6 text-sm">
|
||||
@if ($supplierId && ($supplier = $this->getSupplier()))
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-gray-500 dark:text-gray-400">Szállítási korlátozás:</span>
|
||||
@if ($supplier->hasDeliveryConstraint)
|
||||
<span class="inline-flex items-center rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-800 dark:bg-green-900 dark:text-green-200">Igen</span>
|
||||
@else
|
||||
<span class="inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-600 dark:bg-gray-700 dark:text-gray-300">Nem</span>
|
||||
@endif
|
||||
</div>
|
||||
@if ($supplier->hasDeliveryConstraint)
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-gray-500 dark:text-gray-400">Leadási határidő:</span>
|
||||
<span class="font-semibold text-gray-800 dark:text-gray-200">{{ $supplier->orderCutOffTime }}:00</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-gray-500 dark:text-gray-400">Szállítási átfutási idő:</span>
|
||||
<span class="font-semibold text-gray-800 dark:text-gray-200">{{ $supplier->deliveryLeadTime }} óra</span>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="text-gray-500 dark:text-gray-400" for="selectedDate">Dátum:</label>
|
||||
<input
|
||||
type="date"
|
||||
id="selectedDate"
|
||||
wire:model.live="selectedDate"
|
||||
class="rounded-lg border border-gray-300 bg-white px-2 py-1 text-sm text-gray-900 shadow-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||
>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="text-gray-500 dark:text-gray-400" for="selectedTime">Időpont:</label>
|
||||
<input
|
||||
type="time"
|
||||
id="selectedTime"
|
||||
wire:model.live="selectedTime"
|
||||
class="rounded-lg border border-gray-300 bg-white px-2 py-1 text-sm text-gray-900 shadow-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||
>
|
||||
</div>
|
||||
@php($nextDelivery = $this->getNextDeliveryDate())
|
||||
@if ($nextDelivery)
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-gray-500 dark:text-gray-400">Következő szállítási nap:</span>
|
||||
<span class="font-semibold text-green-700 dark:text-green-400">{{ $nextDelivery }}</span>
|
||||
</div>
|
||||
@elseif ($supplierId && $profitCenterId)
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xs text-red-500">Nem található következő szállítási nap.</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Jelmagyarázat --}}
|
||||
<div class="mb-4 flex flex-wrap items-center gap-4 text-sm">
|
||||
<span class="font-semibold text-gray-700 dark:text-gray-300">Jelmagyarázat:</span>
|
||||
@ -50,7 +106,7 @@ class="w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-
|
||||
</span>
|
||||
<span class="inline-flex items-center gap-1.5">
|
||||
<span class="inline-block w-4 h-4 rounded" style="background-color: #3B82F6;"></span>
|
||||
Megrendelési határidő
|
||||
Következő szállítási nap
|
||||
</span>
|
||||
<span class="inline-flex items-center gap-1.5">
|
||||
<span class="inline-block w-4 h-4 rounded" style="background-color: #E5E7EB;"></span>
|
||||
@ -79,9 +135,11 @@ class="absolute inset-0 z-10 flex items-center justify-center rounded-xl bg-whit
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@php($nextDeliveryRaw = $this->getNextDeliveryDateRaw())
|
||||
@livewire(\App\Filament\Widgets\DeliveryCalendarWidget::class, [
|
||||
'supplierId' => $supplierId,
|
||||
'profitCenterId' => $profitCenterId,
|
||||
'nextDeliveryDate' => $nextDeliveryRaw,
|
||||
])
|
||||
</div>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user