305 lines
12 KiB
PHP
305 lines
12 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Modules\Formation\Entities\Formation;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
|
|
class VisibilityService
|
|
{
|
|
/**
|
|
* Apply visibility scoping to a query builder based on user permissions
|
|
*
|
|
* @param bool $includeUnitsWithoutFormation When true (process models), SEL PERO 91 REJ
|
|
* users can see units without formations. Government users never see units without formations.
|
|
*/
|
|
public function applyVisibilityToQuery(Builder $query, $user, string $unitColumn = 'unit_id', bool $includeUnitsWithoutFormation = false): Builder
|
|
{
|
|
// Check if user has akses peringkat keseluruhan permission (super admin)
|
|
if ($user->can('akses peringkat keseluruhan', Unit::class)) {
|
|
return $query;
|
|
}
|
|
|
|
// Get user's unit
|
|
$userUnit = Unit::find($user->unit_id);
|
|
if (!$userUnit) {
|
|
return $query->whereRaw('1 = 0');
|
|
}
|
|
|
|
// Government permission: Can view all units under their government
|
|
// Government users do NOT see units without formations
|
|
// SEL PERO 91 REJ (process models): ONLY see units without formation - not their gov/formation units
|
|
if ($user->can('akses peringkat formasi')) {
|
|
if ($includeUnitsWithoutFormation && $user->hasRole('SEL PERO 91 REJ')) {
|
|
$unitIds = Unit::whereNull('formation_id')
|
|
->whereNull('repair_formation_id')
|
|
->pluck('id')
|
|
->toArray();
|
|
} else {
|
|
$unitIds = $this->getUnitsUnderGovernment($userUnit);
|
|
$unitIds = $this->excludeUnitsWithoutFormation($unitIds);
|
|
}
|
|
|
|
if (!empty($unitIds)) {
|
|
return $query->whereIn($unitColumn, $unitIds);
|
|
}
|
|
return $query->whereRaw('1 = 0');
|
|
}
|
|
|
|
// DIV permission: Can view all units under their formation (including repair formations)
|
|
if ($user->can('akses peringkat divisyen')) {
|
|
$unitIds = $this->getVisibleUnitIdsForFormationUser($user, $userUnit, $includeUnitsWithoutFormation);
|
|
|
|
if (!empty($unitIds)) {
|
|
return $query->whereIn($unitColumn, $unitIds);
|
|
}
|
|
return $query->whereRaw('1 = 0');
|
|
}
|
|
|
|
// Unit permission: Can only view data within their unit
|
|
return $query->where($unitColumn, $user->unit_id);
|
|
}
|
|
|
|
/**
|
|
* Apply visibility scoping for models with indirect unit relationships
|
|
*
|
|
* @param bool $includeUnitsWithoutFormation When true (process models), SEL PERO 91 REJ
|
|
* users can see units without formations. Government users never see units without formations.
|
|
*/
|
|
public function applyVisibilityToIndirectQuery(Builder $query, $user, array $unitRelationship, bool $includeUnitsWithoutFormation = false): Builder
|
|
{
|
|
// Check if user has akses peringkat keseluruhan permission (super admin)
|
|
if ($user->can('akses peringkat keseluruhan', Unit::class)) {
|
|
return $query;
|
|
}
|
|
|
|
// Get user's unit
|
|
$userUnit = Unit::find($user->unit_id);
|
|
if (!$userUnit) {
|
|
return $query->whereRaw('1 = 0');
|
|
}
|
|
|
|
// Government permission: Can view all units under their government
|
|
// SEL PERO 91 REJ (process models): ONLY see units without formation - not their gov/formation units
|
|
if ($user->can('akses peringkat formasi')) {
|
|
if ($includeUnitsWithoutFormation && $user->hasRole('SEL PERO 91 REJ')) {
|
|
$unitIds = Unit::whereNull('formation_id')
|
|
->whereNull('repair_formation_id')
|
|
->pluck('id')
|
|
->toArray();
|
|
} else {
|
|
$unitIds = $this->getUnitsUnderGovernment($userUnit);
|
|
$unitIds = $this->excludeUnitsWithoutFormation($unitIds);
|
|
}
|
|
|
|
if (!empty($unitIds)) {
|
|
return $query->whereHas($unitRelationship['relationship'], function ($subQuery) use ($unitIds, $unitRelationship) {
|
|
$subQuery->whereIn($unitRelationship['unitColumn'], $unitIds);
|
|
});
|
|
}
|
|
return $query->whereRaw('1 = 0');
|
|
}
|
|
|
|
// DIV permission: Can view all units under their formation (including repair formations)
|
|
if ($user->can('akses peringkat divisyen')) {
|
|
$unitIds = $this->getVisibleUnitIdsForFormationUser($user, $userUnit, $includeUnitsWithoutFormation);
|
|
|
|
if (!empty($unitIds)) {
|
|
return $query->whereHas($unitRelationship['relationship'], function ($subQuery) use ($unitIds, $unitRelationship) {
|
|
$subQuery->whereIn($unitRelationship['unitColumn'], $unitIds);
|
|
});
|
|
}
|
|
return $query->whereRaw('1 = 0');
|
|
}
|
|
|
|
// Unit permission: Can only view data within their unit
|
|
return $query->whereHas($unitRelationship['relationship'], function ($subQuery) use ($user, $unitRelationship) {
|
|
$subQuery->where($unitRelationship['unitColumn'], $user->unit_id);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get all units under the same government as the user's unit
|
|
* Handles both direct government relationships and formation-government relationships
|
|
* Priority: Direct government_id takes precedence over formation government
|
|
*
|
|
* @param Unit $userUnit
|
|
* @return array
|
|
*/
|
|
public function getUnitsUnderGovernment(Unit $userUnit): array
|
|
{
|
|
$unitIds = collect();
|
|
|
|
// Case 1: User's unit has direct government relationship (HIGHEST PRIORITY)
|
|
if ($userUnit->government_id) {
|
|
$directUnits = Unit::where('government_id', $userUnit->government_id)
|
|
->pluck('id');
|
|
$unitIds = $unitIds->merge($directUnits);
|
|
|
|
// Also get units in formations under the same direct government
|
|
$formationUnits = Unit::whereHas('formation', function ($query) use ($userUnit) {
|
|
$query->where('government_id', $userUnit->government_id);
|
|
})->pluck('id');
|
|
$unitIds = $unitIds->merge($formationUnits);
|
|
|
|
return $unitIds->unique()->toArray();
|
|
}
|
|
|
|
// Case 2: User's unit belongs to a formation that has a government (FALLBACK)
|
|
if ($userUnit->formation_id) {
|
|
$formation = Formation::find($userUnit->formation_id);
|
|
if ($formation && $formation->government_id) {
|
|
// Get all units in formations under the same government
|
|
$formationUnits = Unit::whereHas('formation', function ($query) use ($formation) {
|
|
$query->where('government_id', $formation->government_id);
|
|
})->pluck('id');
|
|
$unitIds = $unitIds->merge($formationUnits);
|
|
|
|
// Also get direct government units under the same government
|
|
$directGovUnits = Unit::where('government_id', $formation->government_id)
|
|
->pluck('id');
|
|
$unitIds = $unitIds->merge($directGovUnits);
|
|
}
|
|
}
|
|
|
|
return $unitIds->unique()->toArray();
|
|
}
|
|
|
|
/**
|
|
* Get the repair formation ID for a unit
|
|
* Priority: repair_formation_id > formation_id > null
|
|
*
|
|
* @param Unit $unit
|
|
* @return int|null
|
|
*/
|
|
public function getRepairFormationId(Unit $unit): ?int
|
|
{
|
|
// Priority 1: Check repair_formation_id (custom repair formation)
|
|
if ($unit->repair_formation_id) {
|
|
return $unit->repair_formation_id;
|
|
}
|
|
|
|
// Priority 2: Fall back to regular formation_id
|
|
if ($unit->formation_id) {
|
|
return $unit->formation_id;
|
|
}
|
|
|
|
// Priority 3: No formation (government-level only)
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get visible unit IDs for a user
|
|
*
|
|
* @param bool $includeUnitsWithoutFormation When true (process models), SEL PERO 91 REJ
|
|
* users can see units without formations. Government users never see units without formations.
|
|
*/
|
|
public function getVisibleUnitIds($user, bool $includeUnitsWithoutFormation = false): array
|
|
{
|
|
// Check if user has akses peringkat keseluruhan permission (super admin)
|
|
if ($user->can('akses peringkat keseluruhan', Unit::class)) {
|
|
return Unit::pluck('id')->toArray();
|
|
}
|
|
|
|
// Get user's unit
|
|
$userUnit = Unit::find($user->unit_id);
|
|
if (!$userUnit) {
|
|
return [];
|
|
}
|
|
|
|
// Government permission: Can view all units under their government
|
|
// SEL PERO 91 REJ (process models): ONLY see units without formation - not their gov/formation units
|
|
if ($user->can('akses peringkat formasi')) {
|
|
if ($includeUnitsWithoutFormation && $user->hasRole('SEL PERO 91 REJ')) {
|
|
return Unit::whereNull('formation_id')
|
|
->whereNull('repair_formation_id')
|
|
->pluck('id')
|
|
->toArray();
|
|
}
|
|
$unitIds = $this->getUnitsUnderGovernment($userUnit);
|
|
return $this->excludeUnitsWithoutFormation($unitIds);
|
|
}
|
|
|
|
// DIV permission: Can view all units under their formation (including repair formations)
|
|
if ($user->can('akses peringkat divisyen')) {
|
|
return $this->getVisibleUnitIdsForFormationUser($user, $userUnit, $includeUnitsWithoutFormation);
|
|
}
|
|
|
|
// Unit permission: Can only view data within their unit
|
|
return [$user->unit_id];
|
|
}
|
|
|
|
/**
|
|
* Exclude units that have no formation (both formation_id and repair_formation_id are null).
|
|
* Government users do not see these units.
|
|
*/
|
|
protected function excludeUnitsWithoutFormation(array $unitIds): array
|
|
{
|
|
if (empty($unitIds)) {
|
|
return [];
|
|
}
|
|
|
|
return Unit::whereIn('id', $unitIds)
|
|
->where(function ($q) {
|
|
$q->whereNotNull('formation_id')
|
|
->orWhereNotNull('repair_formation_id');
|
|
})
|
|
->pluck('id')
|
|
->toArray();
|
|
}
|
|
|
|
/**
|
|
* Get visible unit IDs for a user with akses peringkat divisyen permission.
|
|
*
|
|
* Asset view (includeUnitsWithoutFormation=false): Only units where formation_id matches.
|
|
* Excludes units that have repair_formation_id but formation_id null (e.g. 10 SKN RAJD uses
|
|
* 3 DIV for repair only - 3 DIV users see their repairs but NOT their asset data).
|
|
*
|
|
* Process view (includeUnitsWithoutFormation=true): Units where repair_formation_id OR
|
|
* formation_id matches. SEL PERO 91 REJ also sees units without any formation.
|
|
*/
|
|
protected function getVisibleUnitIdsForFormationUser($user, Unit $userUnit, bool $includeUnitsWithoutFormation): array
|
|
{
|
|
$userRepairFormationId = $this->getRepairFormationId($userUnit);
|
|
|
|
if ($userRepairFormationId) {
|
|
if (!$includeUnitsWithoutFormation) {
|
|
// Asset view: Only units that belong to the formation (formation_id matches).
|
|
// Exclude units that use formation only for repair (formation_id null, repair_formation_id set).
|
|
return Unit::where('formation_id', $userRepairFormationId)
|
|
->pluck('id')
|
|
->toArray();
|
|
}
|
|
|
|
// SEL PERO 91 REJ: ONLY see units without formation - not their formation's units
|
|
// (formation units are handled by SEL PERO DIV)
|
|
if ($user->hasRole('SEL PERO 91 REJ')) {
|
|
return Unit::whereNull('formation_id')
|
|
->whereNull('repair_formation_id')
|
|
->pluck('id')
|
|
->toArray();
|
|
}
|
|
|
|
// Process view: Units in user's formation (repair_formation_id or formation_id)
|
|
return Unit::where(function ($q) use ($userRepairFormationId) {
|
|
$q->where('repair_formation_id', $userRepairFormationId)
|
|
->orWhere(function ($q2) use ($userRepairFormationId) {
|
|
$q2->whereNull('repair_formation_id')
|
|
->where('formation_id', $userRepairFormationId);
|
|
});
|
|
})->pluck('id')->toArray();
|
|
}
|
|
|
|
// User's unit has no formation - check if they have SEL PERO 91 REJ (process models only)
|
|
if ($includeUnitsWithoutFormation && $user->hasRole('SEL PERO 91 REJ')) {
|
|
return Unit::whereNull('formation_id')
|
|
->whereNull('repair_formation_id')
|
|
->pluck('id')
|
|
->toArray();
|
|
}
|
|
|
|
return [];
|
|
}
|
|
}
|
|
|