98 lines
3.2 KiB
PHP
98 lines
3.2 KiB
PHP
<?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;
|
||
}
|
||
}
|