53 lines
1.6 KiB
PHP
53 lines
1.6 KiB
PHP
<?php
|
||
|
||
namespace App\Support;
|
||
|
||
use App\Models\ActivityLog;
|
||
use App\Models\Service;
|
||
use Carbon\Carbon;
|
||
|
||
final class RenewalPeriodGuard
|
||
{
|
||
public static function violation(Service $service, int $months): ?string
|
||
{
|
||
$start = $service->expires_at && $service->expires_at->isFuture() ? $service->expires_at->copy() : now();
|
||
$end = $start->copy()->addMonths($months);
|
||
|
||
if ($end->lte(now())) {
|
||
return 'Không thể gia hạn cho khoảng thời gian trong quá khứ.';
|
||
}
|
||
|
||
$overlapping = ActivityLog::query()
|
||
->where('subject_type', Service::class)
|
||
->where('subject_id', $service->id)
|
||
->where('event', 'renewed')
|
||
->get(['old_values', 'new_values'])
|
||
->first(fn (ActivityLog $log) => self::overlaps($log, $start, $end));
|
||
|
||
if (! $overlapping) {
|
||
return null;
|
||
}
|
||
|
||
return sprintf(
|
||
'Khoảng thời gian %s – %s đã được gia hạn trước đó, không thể gia hạn trùng.',
|
||
$start->format('d/m/Y'),
|
||
$end->format('d/m/Y'),
|
||
);
|
||
}
|
||
|
||
private static function overlaps(ActivityLog $log, Carbon $start, Carbon $end): bool
|
||
{
|
||
$loggedEnd = $log->new_values['expires_at'] ?? null;
|
||
if (! $loggedEnd) {
|
||
return false;
|
||
}
|
||
|
||
$loggedEnd = Carbon::parse($loggedEnd);
|
||
$loggedStart = ($log->old_values['expires_at'] ?? null)
|
||
? Carbon::parse($log->old_values['expires_at'])
|
||
: $loggedEnd->copy()->subMonths((int) ($log->new_values['months'] ?? 1));
|
||
|
||
return $loggedStart->lt($end) && $loggedEnd->gt($start);
|
||
}
|
||
}
|