diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index 8b751e6..8435b62 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Models\User; +use App\Models\Vehicle; use Illuminate\View\View; use Spatie\Permission\Models\Role; @@ -38,13 +39,23 @@ class DashboardController extends Controller ->take(10) ->values(); + $vehiclesDueSoon = Vehicle::with('user') + ->where(function ($query) { + $query->whereBetween('oc_expiry_date', [now()->startOfDay(), now()->addDays(14)->endOfDay()]) + ->orWhereBetween('technical_inspection_date', [now()->startOfDay(), now()->addDays(14)->endOfDay()]); + }) + ->orderBy('oc_expiry_date') + ->orderBy('technical_inspection_date') + ->get(); + return view('dashboard', compact( 'totalUsers', 'adminCount', 'managerCount', 'workerCount', 'recentUsers', - 'lowestAdvanceBalances' + 'lowestAdvanceBalances', + 'vehiclesDueSoon' )); } } diff --git a/app/Models/User.php b/app/Models/User.php index 1a05cb0..bf50f79 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -61,6 +61,11 @@ class User extends Authenticatable return $this->hasMany(Advance::class); } + public function vehicles(): HasMany + { + return $this->hasMany(Vehicle::class); + } + public function advanceBalance(): HasOne { return $this->hasOne(UserAdvanceBalance::class); diff --git a/database/seeders/AdvanceSeeder.php b/database/seeders/AdvanceSeeder.php new file mode 100644 index 0000000..8ecff9f --- /dev/null +++ b/database/seeders/AdvanceSeeder.php @@ -0,0 +1,60 @@ +orderBy('id')->pluck('id')->all(); + $types = AdvanceType::query()->pluck('id', 'name')->all(); + + if (empty($users) || empty($types)) { + return; + } + + $entries = [ + ['user_index' => 0, 'type' => 'Paliwo', 'amount' => 180.50, 'days_ago' => 1], + ['user_index' => 1, 'type' => 'Hotel', 'amount' => 240.00, 'days_ago' => 2], + ['user_index' => 2, 'type' => 'Wpłata', 'amount' => 500.00, 'days_ago' => 3], + ['user_index' => 0, 'type' => 'Paliwo', 'amount' => 95.20, 'days_ago' => 6], + ['user_index' => 1, 'type' => 'Hotel', 'amount' => 310.40, 'days_ago' => 8], + ['user_index' => 3, 'type' => 'Paliwo', 'amount' => 150.10, 'days_ago' => 9], + ['user_index' => 4, 'type' => 'Wpłata', 'amount' => 750.00, 'days_ago' => 10], + ['user_index' => 2, 'type' => 'Hotel', 'amount' => 420.00, 'days_ago' => 12], + ['user_index' => 5, 'type' => 'Paliwo', 'amount' => 205.30, 'days_ago' => 14], + ['user_index' => 6, 'type' => 'Wpłata', 'amount' => 600.00, 'days_ago' => 15], + ['user_index' => 0, 'type' => 'Hotel', 'amount' => 280.00, 'days_ago' => 17], + ['user_index' => 1, 'type' => 'Paliwo', 'amount' => 120.00, 'days_ago' => 18], + ['user_index' => 3, 'type' => 'Wpłata', 'amount' => 900.00, 'days_ago' => 20], + ['user_index' => 4, 'type' => 'Hotel', 'amount' => 360.75, 'days_ago' => 22], + ['user_index' => 5, 'type' => 'Paliwo', 'amount' => 175.25, 'days_ago' => 24], + ['user_index' => 6, 'type' => 'Wpłata', 'amount' => 850.00, 'days_ago' => 26], + ['user_index' => 2, 'type' => 'Paliwo', 'amount' => 165.15, 'days_ago' => 28], + ['user_index' => 0, 'type' => 'Wpłata', 'amount' => 1200.00, 'days_ago' => 29], + ['user_index' => 1, 'type' => 'Hotel', 'amount' => 540.20, 'days_ago' => 30], + ['user_index' => 3, 'type' => 'Paliwo', 'amount' => 210.45, 'days_ago' => 33], + ]; + + foreach ($entries as $index => $entry) { + $userId = $users[$entry['user_index']] ?? $users[0]; + $typeId = $types[$entry['type']] ?? null; + + if (! $typeId) { + continue; + } + + Advance::create([ + 'date' => now()->subDays($entry['days_ago'])->toDateString(), + 'user_id' => $userId, + 'amount' => $entry['amount'], + 'advance_type_id' => $typeId, + ]); + } + } +} diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index c8a8733..39018cd 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -53,6 +53,7 @@ class DatabaseSeeder extends Seeder $this->call([ AdvanceTypeSeeder::class, UserSeeder::class, + AdvanceSeeder::class, VehicleSeeder::class, ]); } diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 4ca0485..fe3115f 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -133,6 +133,58 @@ + {{-- Samochody do uwagi --}} +
+
+

+ Samochody do uwagi +

+ + do 14 dni + +
+ + @if(($vehiclesDueSoon ?? collect())->isNotEmpty()) +
+ @foreach($vehiclesDueSoon as $vehicle) + @php + $ocDue = $vehicle->oc_expiry_date && $vehicle->oc_expiry_date->between(now()->startOfDay(), now()->addDays(14)->endOfDay()); + $inspectionDue = $vehicle->technical_inspection_date && $vehicle->technical_inspection_date->between(now()->startOfDay(), now()->addDays(14)->endOfDay()); + @endphp + +
+
+

+ {{ $vehicle->brand }} {{ $vehicle->model ?? '' }} +

+

+ {{ $vehicle->registration_number }} · {{ $vehicle->user?->name }} {{ $vehicle->user?->surname }} +

+
+ +
+ @if($ocDue) + + OC: {{ $vehicle->oc_expiry_date->format('d.m.Y') }} + + @endif + + @if($inspectionDue) + + Przegląd: {{ $vehicle->technical_inspection_date->format('d.m.Y') }} + + @endif +
+
+ @endforeach +
+ @else +

+ Brak pojazdów z terminem OC lub przeglądu w najbliższych 14 dniach. +

+ @endif +
+ {{-- Najniższy bilans zaliczek --}}

diff --git a/resources/views/users/show.blade.php b/resources/views/users/show.blade.php index e913997..171e194 100644 --- a/resources/views/users/show.blade.php +++ b/resources/views/users/show.blade.php @@ -85,6 +85,43 @@

+
+

Przypisany pojazd

+ + @php($assignedVehicle = $user->vehicles()->first()) + + @if($assignedVehicle) +
+
+
Samochód
+
{{ $assignedVehicle->brand }} {{ $assignedVehicle->model }}
+
+
+
Nr rejestracyjny
+
{{ $assignedVehicle->registration_number }}
+
+
+
Status
+
{{ $assignedVehicle->status }}
+
+
+
OC
+
{{ $assignedVehicle->oc_expiry_date?->format('d.m.Y') ?? '—' }}
+
+
+
Przegląd
+
{{ $assignedVehicle->technical_inspection_date?->format('d.m.Y') ?? '—' }}
+
+
+
Ubezpieczenie
+
{{ $assignedVehicle->insurance_provider ?? '—' }}
+
+
+ @else +

Brak przypisanego pojazdu.

+ @endif +
+

Podsumowanie finansowe

diff --git a/tests/Feature/AdvanceTest.php b/tests/Feature/AdvanceTest.php index ae36149..fdcaf99 100644 --- a/tests/Feature/AdvanceTest.php +++ b/tests/Feature/AdvanceTest.php @@ -20,6 +20,14 @@ function userWithAdvanceAccess(string $roleName = 'pracownik'): User return $user; } +test('database seeder creates 20 sample advances', function () { + $this->seed(); + + $seededAdvances = Advance::count(); + + expect($seededAdvances)->toBe(20); +}); + test('employee sees only own advances', function () { $employee = userWithAdvanceAccess(); $otherUser = User::factory()->create(); diff --git a/tests/Feature/VehicleTest.php b/tests/Feature/VehicleTest.php index 498ff37..b5119eb 100644 --- a/tests/Feature/VehicleTest.php +++ b/tests/Feature/VehicleTest.php @@ -129,3 +129,81 @@ test('manager can delete a vehicle', function () { $response->assertRedirect(route('flota')); $this->assertDatabaseMissing('vehicles', ['id' => $vehicle->id]); }); + +test('dashboard shows vehicles with upcoming technical inspection or oc expiry within 14 days', function () { + $manager = createFleetUser('kierownik'); + $user = User::factory()->create(); + + $urgentVehicle = Vehicle::create([ + 'brand' => 'Toyota', + 'model' => 'Corolla', + 'registration_number' => 'DW1234A', + 'user_id' => $user->id, + 'year' => 2021, + 'purchase_date' => '2021-05-12', + 'oc_expiry_date' => now()->addDays(10)->toDateString(), + 'oil_change_km' => 12000, + 'insurance_provider' => 'PZU', + 'technical_inspection_date' => now()->addDays(20)->toDateString(), + 'mileage_km' => 95000, + 'status' => 'używany', + ]); + + $safeVehicle = Vehicle::create([ + 'brand' => 'Ford', + 'model' => 'Transit', + 'registration_number' => 'WA9876Z', + 'user_id' => $user->id, + 'year' => 2019, + 'purchase_date' => '2019-07-20', + 'oc_expiry_date' => now()->addDays(30)->toDateString(), + 'oil_change_km' => 8000, + 'insurance_provider' => 'Warta', + 'technical_inspection_date' => now()->addDays(40)->toDateString(), + 'mileage_km' => 175000, + 'status' => 'aktywny', + ]); + + $response = $this->actingAs($manager)->get(route('dashboard')); + + $response->assertOk(); + $response->assertSee('Samochody do uwagi'); + $response->assertSee($urgentVehicle->brand); + $response->assertSee('OC'); + $response->assertDontSee($safeVehicle->brand); +}); + +test('employee profile shows assigned vehicle with expire dates and insurance details', function () { + Permission::firstOrCreate(['name' => 'users_access']); + + $manager = User::factory()->create(); + $managerRole = Role::firstOrCreate(['name' => 'kierownik']); + $managerRole->givePermissionTo(['users_access', 'flota_access']); + $manager->assignRole($managerRole); + + $user = User::factory()->create(); + + $vehicle = Vehicle::create([ + 'brand' => 'BMW', + 'model' => 'X5', + 'registration_number' => 'PO1111A', + 'user_id' => $user->id, + 'year' => 2022, + 'purchase_date' => '2022-02-10', + 'oc_expiry_date' => now()->addDays(9)->toDateString(), + 'oil_change_km' => 12000, + 'insurance_provider' => 'PZU', + 'technical_inspection_date' => now()->addDays(12)->toDateString(), + 'mileage_km' => 55000, + 'status' => 'używany', + ]); + + $response = $this->actingAs($manager)->get(route('users.show', $user)); + + $response->assertOk(); + $response->assertSee('Przypisany pojazd'); + $response->assertSee($vehicle->brand); + $response->assertSee('PZU'); + $response->assertSee('OC'); + $response->assertSee('Przegląd'); +});