153 lines
5.5 KiB
PHP
153 lines
5.5 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Public;
|
|
|
|
use App\Models\Application;
|
|
use App\Models\Position;
|
|
use App\Models\PositionQuota;
|
|
use App\Models\PusatMengundi;
|
|
use App\Models\SaluranMengundi;
|
|
use App\Models\StaffAssignment;
|
|
use App\Models\User;
|
|
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
|
use Illuminate\Support\Collection;
|
|
|
|
class KtmVacancyService
|
|
{
|
|
private const RESERVED_APPLICATION_STATUSES = [
|
|
'submitted',
|
|
'under_ppm_review',
|
|
'approved',
|
|
];
|
|
|
|
/**
|
|
* @param array<int, string> $roleCodes
|
|
* @return array<string, array<int, array{id: int, label: string, remaining: int}>>
|
|
*/
|
|
public function optionsForRoles(PusatMengundi $pusatMengundi, array $roleCodes = ['KP', 'KPDP'], ?int $ignoreApplicationId = null): array
|
|
{
|
|
$options = [];
|
|
|
|
foreach ($roleCodes as $roleCode) {
|
|
$options[$roleCode] = $this->availableKtmAssignments($pusatMengundi, $roleCode, $ignoreApplicationId)
|
|
->map(function (StaffAssignment $assignment) use ($roleCode, $ignoreApplicationId): array {
|
|
$user = $assignment->user;
|
|
$saluranMengundi = $assignment->saluranMengundi;
|
|
|
|
return [
|
|
'id' => $assignment->id,
|
|
'label' => trim(
|
|
($user instanceof User ? $user->name : 'KTM tanpa nama')
|
|
.' - Saluran '
|
|
.($saluranMengundi instanceof SaluranMengundi ? $saluranMengundi->number : '-'),
|
|
),
|
|
'remaining' => $this->remainingVacancy($assignment, $roleCode, $ignoreApplicationId),
|
|
];
|
|
})
|
|
->values()
|
|
->all();
|
|
}
|
|
|
|
return $options;
|
|
}
|
|
|
|
public function hasVacancyForAssignment(int $assignmentId, PusatMengundi $pusatMengundi, string $roleCode, ?int $ignoreApplicationId = null): bool
|
|
{
|
|
$ktmPosition = Position::query()->where('code', 'KTM')->first();
|
|
|
|
if ($ktmPosition === null) {
|
|
return false;
|
|
}
|
|
|
|
$assignment = StaffAssignment::query()
|
|
->with(['saluranMengundi', 'user'])
|
|
->where('id', $assignmentId)
|
|
->where('election_id', $pusatMengundi->election_id)
|
|
->where('pusat_mengundi_id', $pusatMengundi->id)
|
|
->where('position_id', $ktmPosition->id)
|
|
->where('status', 'active')
|
|
->whereNotNull('saluran_mengundi_id')
|
|
->first();
|
|
|
|
return $assignment instanceof StaffAssignment
|
|
&& $this->remainingVacancy($assignment, $roleCode, $ignoreApplicationId) > 0;
|
|
}
|
|
|
|
public function remainingForAssignment(StaffAssignment $assignment, string $roleCode, ?int $ignoreApplicationId = null): int
|
|
{
|
|
return $this->remainingVacancy($assignment, $roleCode, $ignoreApplicationId);
|
|
}
|
|
|
|
/**
|
|
* @return Collection<int, StaffAssignment>
|
|
*/
|
|
private function availableKtmAssignments(PusatMengundi $pusatMengundi, string $roleCode, ?int $ignoreApplicationId = null): Collection
|
|
{
|
|
return $this->ktmAssignments($pusatMengundi)
|
|
->filter(fn (StaffAssignment $assignment): bool => $this->remainingVacancy($assignment, $roleCode, $ignoreApplicationId) > 0)
|
|
->values();
|
|
}
|
|
|
|
/**
|
|
* @return EloquentCollection<int, StaffAssignment>
|
|
*/
|
|
private function ktmAssignments(PusatMengundi $pusatMengundi): EloquentCollection
|
|
{
|
|
$ktmPosition = Position::query()->where('code', 'KTM')->first();
|
|
|
|
if ($ktmPosition === null) {
|
|
return new EloquentCollection;
|
|
}
|
|
|
|
return StaffAssignment::query()
|
|
->with(['saluranMengundi', 'user'])
|
|
->where('election_id', $pusatMengundi->election_id)
|
|
->where('pusat_mengundi_id', $pusatMengundi->id)
|
|
->where('position_id', $ktmPosition->id)
|
|
->where('status', 'active')
|
|
->whereNotNull('saluran_mengundi_id')
|
|
->whereNotNull('user_id')
|
|
->orderBy('saluran_mengundi_id')
|
|
->get();
|
|
}
|
|
|
|
private function remainingVacancy(StaffAssignment $ktmAssignment, string $roleCode, ?int $ignoreApplicationId = null): int
|
|
{
|
|
$position = Position::query()->where('code', $roleCode)->first();
|
|
|
|
if ($position === null || $ktmAssignment->saluran_mengundi_id === null) {
|
|
return 0;
|
|
}
|
|
|
|
$quota = PositionQuota::query()
|
|
->where('election_id', $ktmAssignment->election_id)
|
|
->where('pusat_mengundi_id', $ktmAssignment->pusat_mengundi_id)
|
|
->where('saluran_mengundi_id', $ktmAssignment->saluran_mengundi_id)
|
|
->where('position_id', $position->id)
|
|
->value('quota');
|
|
|
|
if ($quota === null) {
|
|
return 0;
|
|
}
|
|
|
|
$assignedCount = StaffAssignment::query()
|
|
->where('reports_to_assignment_id', $ktmAssignment->id)
|
|
->where('position_id', $position->id)
|
|
->where('status', 'active')
|
|
->count();
|
|
|
|
$reservedQuery = Application::query()
|
|
->where('selected_ktm_assignment_id', $ktmAssignment->id)
|
|
->where('requested_position_id', $position->id)
|
|
->whereIn('status', self::RESERVED_APPLICATION_STATUSES);
|
|
|
|
if ($ignoreApplicationId !== null) {
|
|
$reservedQuery->where('id', '!=', $ignoreApplicationId);
|
|
}
|
|
|
|
$reservedCount = $reservedQuery->count();
|
|
|
|
return max(0, (int) $quota - $assignedCount - $reservedCount);
|
|
}
|
|
}
|