Fix missing LGU/tenant relations and display in tables; Add LGU hierarchy tests

This commit is contained in:
ramram1515
2026-07-07 12:03:24 +08:00
parent 0379e3de0d
commit 76b70d3aea
31 changed files with 370 additions and 10 deletions

View File

@@ -31,6 +31,7 @@ class AdminBarangayController extends ApiController
$barangays = Barangay::query()
->with(['cityMunicipality.province'])
->withSum('households', 'household_size')
->where('city_municipality_id', $cityId)
->when($request->filled('q'), function ($q) use ($request) {
$term = '%'.$request->string('q').'%';

View File

@@ -33,7 +33,7 @@ class AdminDropOffPointController extends ApiController
$perPage = (int) $request->input('per_page', 25);
$points = DropOffPoint::query()
->with('barangay')
->with(['barangay', 'tenant'])
->when($request->filled('status'), fn ($q) => $q->where('status', $request->string('status')))
->when($request->filled('barangay_id'), fn ($q) => $q->where('barangay_id', $request->integer('barangay_id')))
->when($request->filled('q'), function ($q) use ($request) {

View File

@@ -30,7 +30,7 @@ class AdminDumpsiteController extends ApiController
$perPage = (int) $request->input('per_page', 25);
$dumpsites = Dumpsite::query()
->with('cityMunicipality')
->with(['cityMunicipality', 'tenant'])
->when($request->filled('status'), fn ($q) => $q->where('status', $request->string('status')))
->when(
$request->filled('city_municipality_id'),

View File

@@ -44,7 +44,7 @@ class AdminHouseholdController extends ApiController
$perPage = (int) $request->input('per_page', 25);
$households = Household::query()
->with(['head', 'barangay.serviceAreas'])
->with(['head', 'barangay.cityMunicipality', 'barangay.serviceAreas', 'assignedDropOffPoint', 'tenant'])
->withCount('members')
->when(
$request->filled('verification_status'),

View File

@@ -30,7 +30,7 @@ class AdminRouteController extends ApiController
$perPage = (int) $request->input('per_page', 25);
$routes = Route::query()
->with(['area', 'defaultDumpsite'])
->with(['area', 'defaultDumpsite', 'tenant'])
->withCount('stops')
->when($request->filled('status'), fn ($q) => $q->where('status', $request->string('status')))
->when($request->filled('area_id'), fn ($q) => $q->where('area_id', $request->integer('area_id')))

View File

@@ -28,7 +28,7 @@ class AdminTeamController extends ApiController
$perPage = (int) $request->input('per_page', 25);
$teams = CollectionTeam::query()
->with(['area', 'driver', 'scanner', 'truck', 'helpers.user', 'currentTrip'])
->with(['area', 'driver', 'scanner', 'truck', 'helpers.user', 'currentTrip', 'tenant'])
->when($request->filled('status'), fn ($q) => $q->where('status', $request->string('status')))
->when($request->filled('area_id'), fn ($q) => $q->where('area_id', $request->integer('area_id')))
->when($request->filled('q'), fn ($q) => $q->where('name', 'like', '%'.$request->string('q').'%'))

View File

@@ -30,7 +30,7 @@ class AdminTripController extends ApiController
$perPage = (int) $request->input('per_page', 25);
$trips = Trip::query()
->with(['route', 'team', 'truck', 'dumpsite'])
->with(['route', 'team', 'truck', 'dumpsite', 'tenant'])
->when($request->filled('status'), fn ($q) => $q->where('status', $request->string('status')))
->when($request->filled('date'), fn ($q) => $q->whereDate('scheduled_date', $request->string('date')))
->when($request->filled('team_id'), fn ($q) => $q->where('team_id', $request->integer('team_id')))

View File

@@ -25,6 +25,7 @@ class AdminTruckController extends ApiController
$perPage = (int) $request->input('per_page', 25);
$trucks = Truck::query()
->with('tenant')
->when($request->filled('status'), fn ($q) => $q->where('status', $request->string('status')))
->when($request->filled('q'), function ($q) use ($request) {
$term = '%'.$request->string('q').'%';

View File

@@ -74,7 +74,7 @@ class AdminUserController extends ApiController
->orderByDesc('id')
->paginate($perPage);
$users->load(['residentProfile', 'driverProfile', 'helperProfile', 'scannerProfile', 'storePartnerProfile']);
$users->load(['residentProfile', 'driverProfile', 'helperProfile', 'scannerProfile', 'storePartnerProfile', 'tenant']);
return $this->ok(
UserDetailResource::collection($users),

View File

@@ -30,6 +30,7 @@ class SuperAdminBarangayController extends ApiController
$barangays = Barangay::query()
->with(['cityMunicipality.province'])
->withSum('households', 'household_size')
->when($request->filled('city_municipality_id'), fn ($q) => $q->where('city_municipality_id', $request->integer('city_municipality_id')))
->when($request->filled('q'), function ($q) use ($request) {
$term = '%'.$request->string('q').'%';

View File

@@ -27,6 +27,7 @@ class BarangayResource extends JsonResource
'name' => $this->name,
'urban_rural' => $this->urban_rural,
'population' => $this->population,
'total_residents' => $this->households_sum_household_size ?? 0,
'city_municipality_id' => $this->city_municipality_id,
'centroid' => $this->centroid ? [
'lat' => $this->centroid->latitude,

View File

@@ -40,6 +40,10 @@ class CollectionTeamResource extends JsonResource
'performance_stats' => $this->getPerformanceStats(),
'current_trip' => TripResource::make($this->whenLoaded('currentTrip')),
'notes' => $this->notes,
'tenant' => $this->whenLoaded('tenant', fn () => [
'id' => $this->tenant->id,
'name' => $this->tenant->name,
]),
'created_at' => $this->created_at?->toIso8601String(),
];
}

View File

@@ -31,6 +31,10 @@ class DropOffPointResource extends JsonResource
isset($this->distance_meters),
fn () => round((float) $this->distance_meters, 1),
),
'tenant' => $this->whenLoaded('tenant', fn () => [
'id' => $this->tenant->id,
'name' => $this->tenant->name,
]),
'barangay' => BarangayResource::make($this->whenLoaded('barangay')),
];
}

View File

@@ -39,6 +39,10 @@ class DumpsiteResource extends JsonResource
'contact_person' => $this->contact_person,
'contact_phone' => $this->contact_phone,
'city_municipality' => CityMunicipalityResource::make($this->whenLoaded('cityMunicipality')),
'tenant' => $this->whenLoaded('tenant', fn () => [
'id' => $this->tenant->id,
'name' => $this->tenant->name,
]),
'created_at' => $this->created_at?->toIso8601String(),
];
}

View File

@@ -34,6 +34,10 @@ class HouseholdResource extends JsonResource
'member_count' => $this->whenCounted('members'),
'created_at' => $this->created_at?->toIso8601String(),
'updated_at' => $this->updated_at?->toIso8601String(),
'tenant' => $this->whenLoaded('tenant', fn () => [
'id' => $this->tenant->id,
'name' => $this->tenant->name,
]),
];
}
}

View File

@@ -23,6 +23,10 @@ class RouteResource extends JsonResource
'default_team_id' => $this->default_team_id,
'stops' => RouteStopResource::collection($this->whenLoaded('stops')),
'stop_count' => $this->whenCounted('stops'),
'tenant' => $this->whenLoaded('tenant', fn () => [
'id' => $this->tenant->id,
'name' => $this->tenant->name,
]),
'created_at' => $this->created_at?->toIso8601String(),
];
}

View File

@@ -50,6 +50,10 @@ class TripResource extends JsonResource
'dumpsite_attendant' => $r->dumpsite_attendant_name,
'notes' => $r->notes,
])),
'tenant' => $this->whenLoaded('tenant', fn () => [
'id' => $this->tenant->id,
'name' => $this->tenant->name,
]),
'created_at' => $this->created_at?->toIso8601String(),
];
}

View File

@@ -22,6 +22,10 @@ class TruckResource extends JsonResource
'lng' => $this->last_known_coordinates->longitude,
] : null,
'last_location_updated_at' => $this->last_location_updated_at?->toIso8601String(),
'tenant' => $this->whenLoaded('tenant', fn () => [
'id' => $this->tenant->id,
'name' => $this->tenant->name,
]),
];
}
}

View File

@@ -31,6 +31,10 @@ class UserDetailResource extends JsonResource
'created_at' => $this->created_at?->toIso8601String(),
'profile' => $profile?->toArray(),
'has_household' => $this->headedHousehold()->exists() || $this->householdMemberships()->exists(),
'tenant' => $this->whenLoaded('tenant', fn () => [
'id' => $this->tenant->id,
'name' => $this->tenant->name,
]),
];
}
}

View File

@@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use MatanYadaev\EloquentSpatial\Objects\Point;
use MatanYadaev\EloquentSpatial\Objects\Polygon;
@@ -50,4 +51,9 @@ class Barangay extends Model
return $this->belongsToMany(ServiceArea::class, 'service_area_barangay')
->withTimestamps();
}
public function households(): HasMany
{
return $this->hasMany(Household::class);
}
}

View File

@@ -39,7 +39,7 @@
<th>PSGC Code</th>
<th>Code</th>
<th>Type</th>
<th>Population</th>
<th class="text-center">Population (Registered)</th>
<th>Centroid</th>
<th class="text-right">Actions</th>
</tr>
@@ -241,7 +241,12 @@
<td class="font-mono text-xs">${b.psgc_code || 'N/A'}</td>
<td class="font-mono text-xs">${b.code || 'N/A'}</td>
<td class="capitalize">${b.urban_rural}</td>
<td>${b.population?.toLocaleString() || 'N/A'}</td>
<td class="text-center">
<div class="flex items-center justify-center gap-2">
<span class="font-medium text-green-600">${b.total_residents?.toLocaleString() || '0'}</span>
${b.population ? `<span class="text-xs text-neutral-400">/ ${b.population.toLocaleString()}</span>` : ''}
</div>
</td>
<td class="font-mono text-xs">${centroidStr}</td>
<td class="text-right space-x-2">
<button class="btn-ghost edit-btn" data-id="${b.id}">Edit</button>

View File

@@ -32,6 +32,7 @@
<thead>
<tr>
<th>Name</th>
<th>LGU</th>
<th>Code</th>
<th>Address</th>
<th>Capacity</th>
@@ -210,6 +211,7 @@
rows.innerHTML = items.map(d => `
<tr>
<td class="font-medium text-neutral-900">${window.Verde.escapeHtml(d.name)}</td>
<td class="text-xs text-neutral-500">${window.Verde.escapeHtml(d.tenant?.name || d.barangay?.city_municipality?.name || 'N/A')}</td>
<td class="font-mono text-xs text-neutral-600">${window.Verde.escapeHtml(d.code)}</td>
<td class="max-w-xs truncate text-neutral-600">${window.Verde.escapeHtml(d.address_line)}</td>
<td>${d.capacity_kg ?? '—'} kg</td>

View File

@@ -20,6 +20,7 @@
<thead>
<tr>
<th>Name</th>
<th>LGU</th>
<th>Code</th>
<th>Capacity</th>
<th>Permit #</th>
@@ -173,6 +174,7 @@
rows.innerHTML = items.map(d => `
<tr>
<td class="font-medium text-neutral-900">${window.Verde.escapeHtml(d.name)}</td>
<td class="text-xs text-neutral-500">${window.Verde.escapeHtml(d.city_municipality?.name || d.tenant?.name || 'N/A')}</td>
<td class="font-mono text-xs text-neutral-600">${window.Verde.escapeHtml(d.code)}</td>
<td>${d.capacity_tons ? `${d.capacity_tons} t` : '—'}</td>
<td class="font-mono text-xs">${window.Verde.escapeHtml(d.permit_number ?? '—')}</td>

View File

@@ -32,6 +32,7 @@
<thead>
<tr>
<th>Head</th>
<th>LGU</th>
<th>Address</th>
<th>Service Area</th>
<th>Members</th>
@@ -383,6 +384,7 @@
return `<tr>
<td>${head}</td>
<td class="text-xs text-neutral-500">${window.Verde.escapeHtml(h.tenant?.name || h.barangay?.city_municipality?.name || 'N/A')}</td>
<td class="max-w-xs truncate text-neutral-700">
<div class="truncate">${window.Verde.escapeHtml(h.address_line)}</div>
${mapLink}

View File

@@ -30,6 +30,7 @@
<thead>
<tr>
<th>Name</th>
<th>LGU</th>
<th>Code</th>
<th>Stops</th>
<th>Distance</th>
@@ -316,6 +317,7 @@
const dur = h > 0 ? `${h}h ${m}m` : `${m}m`;
return `<tr>
<td class="font-medium text-neutral-900">${window.Verde.escapeHtml(r.name)}</td>
<td class="text-xs text-neutral-500">${window.Verde.escapeHtml(r.tenant?.name || 'N/A')}</td>
<td class="font-mono text-xs text-neutral-600">${window.Verde.escapeHtml(r.code)}</td>
<td>${r.stop_count ?? 0}</td>
<td>${r.total_distance_km} km</td>

View File

@@ -245,7 +245,7 @@
<div class="bg-neutral-50 p-4 border-b border-neutral-100 flex justify-between items-start">
<div>
<h3 class="font-bold text-neutral-900">${window.Verde.escapeHtml(t.name)}</h3>
<p class="text-xs text-neutral-500 mt-0.5">${window.Verde.escapeHtml(t.area?.name || 'No Area')}</p>
<p class="text-xs text-neutral-500 mt-0.5">${window.Verde.escapeHtml(t.area?.name || 'No Area')} &bull; ${window.Verde.escapeHtml(t.tenant?.name || 'N/A')}</p>
</div>
${badge(t.status)}
</div>

View File

@@ -33,6 +33,7 @@
<tr>
<th>Trip #</th>
<th>Date</th>
<th>LGU</th>
<th>Route</th>
<th>Team</th>
<th>Load</th>
@@ -195,6 +196,7 @@
<tr>
<td class="font-mono text-xs font-medium text-neutral-900">${window.Verde.escapeHtml(t.trip_number)}</td>
<td>${window.Verde.escapeHtml(t.scheduled_date)}</td>
<td class="text-xs text-neutral-500">${window.Verde.escapeHtml(t.tenant?.name || 'N/A')}</td>
<td>${t.route ? window.Verde.escapeHtml(t.route.name) : '—'}</td>
<td>${t.team ? window.Verde.escapeHtml(t.team.name) : '—'}</td>
<td>${t.total_load_kg ? `${t.total_load_kg} kg` : '—'}</td>

View File

@@ -26,6 +26,7 @@
<thead>
<tr>
<th>Plate #</th>
<th>LGU</th>
<th>Model</th>
<th>Capacity</th>
<th>Status</th>
@@ -129,6 +130,7 @@
rows.innerHTML = items.map(t => `
<tr>
<td class="font-mono text-sm font-medium text-neutral-900">${window.Verde.escapeHtml(t.plate_number)}</td>
<td class="text-xs text-neutral-500">${window.Verde.escapeHtml(t.tenant?.name || 'N/A')}</td>
<td>${window.Verde.escapeHtml(t.model ?? '—')}</td>
<td>${t.capacity_kg ? `${t.capacity_kg.toLocaleString()} kg` : '—'}</td>
<td>${badge(t.status)}</td>

View File

@@ -39,6 +39,7 @@
<tr>
<th>Name</th>
<th>Email</th>
<th>LGU</th>
<th>Phone</th>
<th>Role</th>
<th>Status</th>
@@ -238,6 +239,7 @@
return `<tr>
<td class="font-medium text-neutral-900">${window.Verde.escapeHtml(u.full_name || '—')}</td>
<td class="text-neutral-700">${window.Verde.escapeHtml(u.email)}</td>
<td class="text-xs text-neutral-500">${window.Verde.escapeHtml(u.tenant?.name || 'N/A')}</td>
<td class="font-mono text-xs text-neutral-600">${window.Verde.escapeHtml(u.phone ?? '')}</td>
<td><span class="badge badge-info">${u.role}</span></td>
<td>${statusBadge(u.status)}</td>

View File

@@ -0,0 +1,136 @@
<?php
namespace Tests\Feature;
use App\Models\Tenant;
use App\Models\User;
use App\Models\DropOffPoint;
use App\Models\Dumpsite;
use App\Models\Route;
use App\Models\CollectionTeam;
use App\Models\Trip;
use App\Models\Truck;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class LguTenantIsolationTest extends TestCase
{
use RefreshDatabase;
protected $seed = true;
protected Tenant $lguA;
protected Tenant $lguB;
protected User $adminA;
protected User $adminB;
protected User $superAdmin;
protected function setUp(): void
{
parent::setUp();
$this->lguA = $this->defaultTenant;
$this->lguB = Tenant::factory()->create(['code' => 'LGU-B', 'name' => 'LGU B']);
$this->adminA = User::factory()->create([
'tenant_id' => $this->lguA->id,
'role' => User::ROLE_ADMIN,
]);
$this->adminB = User::factory()->create([
'tenant_id' => $this->lguB->id,
'role' => User::ROLE_ADMIN,
]);
$this->superAdmin = User::factory()->create([
'tenant_id' => null,
'role' => User::ROLE_SUPER_ADMIN,
]);
}
public function test_lgu_a_can_create_and_view_own_entities()
{
$this->actingAs($this->adminA);
$this->withHeaders(['X-Tenant-Code' => $this->lguA->code]);
$city = \App\Models\CityMunicipality::first();
$barangay = \App\Models\Barangay::first();
$dumpsiteResponse = $this->postJson('/api/v1/admin/dumpsites', [
'name' => 'LGU A Dumpsite',
'code' => 'DS-01',
'address_line' => '123 Test St',
'city_municipality_id' => $city->id,
'lat' => 14.0,
'lng' => 121.0,
'status' => 'active'
]);
$dumpsiteResponse->assertCreated();
// Create Drop-off Point
$dopResponse = $this->postJson('/api/v1/admin/drop-off-points', [
'name' => 'LGU A DOP',
'code' => 'DOP-01',
'address_line' => '456 Test Ave',
'barangay_id' => $barangay->id,
'lat' => 14.1,
'lng' => 121.1,
'status' => 'active'
]);
$dopResponse->assertCreated();
// Create Team
$teamResponse = $this->postJson('/api/v1/admin/teams', [
'name' => 'LGU A Team',
'status' => 'active'
]);
$teamResponse->assertCreated();
}
public function test_lgu_b_cannot_access_lgu_a_entities()
{
// Seed LGU A data
$dumpsiteA = Dumpsite::factory()->create(['tenant_id' => $this->lguA->id]);
$dopA = DropOffPoint::factory()->create(['tenant_id' => $this->lguA->id]);
$teamA = new CollectionTeam(['name' => 'Team A', 'status' => 'active']);
$teamA->tenant_id = $this->lguA->id;
$teamA->save();
$routeA = new Route(['name' => 'Route A', 'code' => 'RTA-01', 'geojson' => []]);
$routeA->tenant_id = $this->lguA->id;
$routeA->save();
// Act as LGU B Admin
$this->actingAs($this->adminB);
$this->withHeaders(['X-Tenant-Code' => $this->lguB->code]);
// 1. List Endpoints Should Not Contain LGU A Data
$this->getJson('/api/v1/admin/dumpsites')->assertJsonMissing(['id' => $dumpsiteA->id]);
$this->getJson('/api/v1/admin/drop-off-points')->assertJsonMissing(['id' => $dopA->id]);
$this->getJson('/api/v1/admin/teams')->assertJsonMissing(['id' => $teamA->id]);
$this->getJson('/api/v1/admin/routes')->assertJsonMissing(['id' => $routeA->id]);
// 2. Direct Access Should Fail (404/403)
$this->getJson("/api/v1/admin/routes/{$routeA->id}")->assertStatus(404);
// 3. API Tampering: Attempt to create a trip for LGU B using LGU A's Route
$truckB = new Truck(['plate_number' => 'ABC-1234', 'capacity_tons' => 5]);
$truckB->tenant_id = $this->lguB->id;
$truckB->save();
$teamB = new CollectionTeam(['name' => 'Team B', 'status' => 'active']);
$teamB->tenant_id = $this->lguB->id;
$teamB->save();
$response = $this->postJson('/api/v1/admin/trips', [
'route_id' => $routeA->id,
'truck_id' => $truckB->id,
'collection_team_id' => $teamB->id,
'scheduled_date' => now()->format('Y-m-d'),
]);
// Validation should fail because route_id doesn't belong to LGU B
$response->assertStatus(422);
}
}

View File

@@ -0,0 +1,163 @@
<?php
namespace Tests\Feature;
use App\Models\Tenant;
use App\Models\User;
use App\Models\DropOffPoint;
use App\Models\Dumpsite;
use App\Models\Route;
use App\Models\CollectionTeam;
use App\Models\Trip;
use App\Models\Truck;
use App\Models\CityMunicipality;
use App\Models\Barangay;
use App\Models\Household;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class SuperAdminHierarchyTest extends TestCase
{
use RefreshDatabase;
protected $seed = true;
protected Tenant $lguA;
protected Tenant $lguB;
protected User $superAdmin;
protected function setUp(): void
{
parent::setUp();
$this->lguA = $this->defaultTenant;
$this->lguB = Tenant::factory()->create(['code' => 'LGU-B', 'name' => 'LGU B']);
$this->superAdmin = User::factory()->create([
'tenant_id' => null,
'role' => User::ROLE_SUPER_ADMIN,
]);
$this->withoutHeader('X-Tenant-Code');
\App\Tenancy\Tenancy::clear();
}
protected function seedLguEntities(Tenant $tenant, string $suffix)
{
$city = CityMunicipality::first();
$barangay = Barangay::first();
$dumpsite = Dumpsite::factory()->create(['tenant_id' => $tenant->id, 'name' => "Dumpsite $suffix"]);
$dop = DropOffPoint::factory()->create(['tenant_id' => $tenant->id, 'name' => "DOP $suffix", 'barangay_id' => $barangay->id]);
$team = new CollectionTeam(['name' => "Team $suffix", 'status' => 'active']);
$team->tenant_id = $tenant->id;
$team->save();
$route = new Route(['name' => "Route $suffix", 'code' => "RT-$suffix", 'geojson' => []]);
$route->tenant_id = $tenant->id;
$route->save();
$truck = new Truck(['plate_number' => "ABC-123$suffix", 'capacity_tons' => 5]);
$truck->tenant_id = $tenant->id;
$truck->save();
$trip = new Trip([
'route_id' => $route->id,
'truck_id' => $truck->id,
'team_id' => $team->id,
'scheduled_date' => now()->format('Y-m-d'),
'status' => 'scheduled'
]);
$trip->tenant_id = $tenant->id;
$trip->save();
$household = Household::factory()->create(['tenant_id' => $tenant->id, 'barangay_id' => $barangay->id]);
return [
'dumpsite' => $dumpsite,
'dop' => $dop,
'team' => $team,
'route' => $route,
'truck' => $truck,
'trip' => $trip,
'household' => $household,
'barangay' => $barangay
];
}
public function test_super_admin_can_view_all_entities_across_all_lgus()
{
$dataA = $this->seedLguEntities($this->lguA, 'A');
$dataB = $this->seedLguEntities($this->lguB, 'B');
$this->actingAs($this->superAdmin);
// Verify Dumpsites
$this->getJson('/api/v1/admin/dumpsites')
->assertOk()
->assertJsonFragment(['id' => $dataA['dumpsite']->uuid])
->assertJsonFragment(['id' => $dataB['dumpsite']->uuid]);
// Verify Drop-off Points
$this->getJson('/api/v1/admin/drop-off-points')
->assertOk()
->assertJsonFragment(['id' => $dataA['dop']->uuid])
->assertJsonFragment(['id' => $dataB['dop']->uuid]);
// Verify Teams
$this->getJson('/api/v1/admin/teams')
->assertOk()
->assertJsonFragment(['id' => $dataA['team']->uuid])
->assertJsonFragment(['id' => $dataB['team']->uuid]);
// Verify Routes
$this->getJson('/api/v1/admin/routes')
->assertOk()
->assertJsonFragment(['id' => $dataA['route']->uuid])
->assertJsonFragment(['id' => $dataB['route']->uuid]);
// Verify Trips
$this->getJson('/api/v1/admin/trips')
->assertOk()
->assertJsonFragment(['id' => $dataA['trip']->uuid])
->assertJsonFragment(['id' => $dataB['trip']->uuid]);
}
public function test_super_admin_can_manipulate_all_entities_in_any_lgu()
{
$dataA = $this->seedLguEntities($this->lguA, 'A');
$dataB = $this->seedLguEntities($this->lguB, 'B');
$this->actingAs($this->superAdmin);
// Update Dumpsite in LGU A
$this->patchJson("/api/v1/admin/dumpsites/{$dataA['dumpsite']->uuid}", [
'name' => 'Updated Dumpsite A',
'status' => 'maintenance'
])->assertOk();
// Update DOP in LGU B
$this->patchJson("/api/v1/admin/drop-off-points/{$dataB['dop']->uuid}", [
'name' => 'Updated DOP B',
'status' => 'closed'
])->assertOk();
}
public function test_super_admin_can_query_by_barangay_and_lgu()
{
$dataA = $this->seedLguEntities($this->lguA, 'A');
$this->actingAs($this->superAdmin);
// Filter Drop-off points by Tenant
$this->getJson('/api/v1/admin/drop-off-points?tenant_id=' . $this->lguA->id)
->assertOk()
->assertJsonFragment(['id' => $dataA['dop']->uuid]);
// Filter Drop-off points by Barangay
$this->getJson('/api/v1/admin/drop-off-points?barangay_id=' . $dataA['barangay']->id)
->assertOk()
->assertJsonFragment(['id' => $dataA['dop']->uuid]);
}
}