A DeliveryCalendarService 4. prioritása a WorkCalendarService::isWorkDay()-t hívta, ami a naptárban nem szereplő napoknál a hétvégére esett vissza. Így egy hétvégét is tartalmazó sablon szombat/vasárnap mezőit sosem érte el a kiértékelés (5. prioritás), és csak egyedi felülbírálással lehetett hétvégi szállítást engedni. - WorkCalendarService: új isHoliday(), ami csak a work_calendars ünnepnap rekordjaira ad true-t, a naptári hétvégére nem. Az isWorkDay() változatlan. - DeliveryCalendarService: a 4. prioritás mostantól isHoliday()-t használ, így a hétvégéről a heti sablon dönt. Ünnepnap továbbra is tilt. - NextDeliveryDateCalculatorService: a szállítási nap keresése kikerült az isWorkDay() blokkból, különben a hétvége a következő szállítási nap számításánál továbbra is kimaradt volna. Az átfutási időbe az új countsTowardLeadTime() szerint a sablon hétvégi napjai is beleszámítanak, a H-P sablonok viselkedése változatlan. - DeliveryCalendarWidget: a szürke "inaktív" festés csak a nem szállítási hétvégékre megy rá. Mellékesen a szállítási napok ciklusa $date->addDay()-jel mutálta a kollekció Carbon objektumait, ez copy()-ra javítva. 7 új teszt, köztük a H-P sablon regresszió-őre. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
127 lines
4.3 KiB
PHP
127 lines
4.3 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, $assignment);
|
||
}
|
||
|
||
/**
|
||
* Előreszámol $leadDays darab átfutási napot, majd az első olyan napot adja vissza,
|
||
* amelyik szállítási nap is.
|
||
*
|
||
* A szállítási nap keresése NINCS munkanaphoz kötve: hétvégi szállítást a heti sablon
|
||
* engedhet, ezt az isDeliveryDay() dönti el (EV3-466).
|
||
*/
|
||
private function findNextDeliveryDay(
|
||
Carbon $candidate,
|
||
Supplier $supplier,
|
||
ProfitCenter $pc,
|
||
int $leadDays,
|
||
ProfitCenterSupplierSchedule $assignment,
|
||
): ?Carbon {
|
||
$leadDaysCounted = 0;
|
||
|
||
// Max. 365 nap iteráció (végtelen ciklus-védelem)
|
||
for ($i = 0; $i < 365; $i++) {
|
||
if ($leadDaysCounted >= $leadDays) {
|
||
// Átfutási idő letelt – az első szállítási napot keressük
|
||
if ($this->deliveryCalendarService->isDeliveryDay($pc, $supplier, $candidate, $assignment)) {
|
||
return $candidate->copy();
|
||
}
|
||
} elseif ($this->countsTowardLeadTime($candidate, $assignment)) {
|
||
$leadDaysCounted++;
|
||
}
|
||
$candidate->addDay();
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* Eldönti, hogy az adott nap beleszámít-e az átfutási idő visszaszámolásába.
|
||
*
|
||
* A hivatalos munkanapokon túl beleszámítanak azok a hétvégi napok is, amelyeket a
|
||
* PC-hez rendelt heti sablon szállítási napként jelöl – egy 7 napos sablonnál tehát
|
||
* minden nap telik, egy H–P sablonnál viszont a hétvége változatlanul nem (EV3-466).
|
||
* Munkaszüneti napon sosem telik az átfutás.
|
||
*/
|
||
private function countsTowardLeadTime(Carbon $date, ProfitCenterSupplierSchedule $assignment): bool
|
||
{
|
||
if ($this->workCalendarService->isHoliday($date)) {
|
||
return false;
|
||
}
|
||
|
||
if ($this->workCalendarService->isWorkDay($date)) {
|
||
return true;
|
||
}
|
||
|
||
$schedule = $assignment->deliverySchedule;
|
||
|
||
if (! $schedule || ! $schedule->is_active) {
|
||
return false;
|
||
}
|
||
|
||
return (bool) $schedule->{strtolower($date->format('l'))};
|
||
}
|
||
}
|