'zaliczki_access']); }); function userWithAdvanceAccess(string $roleName = 'pracownik'): User { $user = User::factory()->create(); $role = Role::firstOrCreate(['name' => $roleName]); $role->givePermissionTo('zaliczki_access'); $user->assignRole($role); return $user; } test('employee sees only own advances', function () { $employee = userWithAdvanceAccess(); $otherUser = User::factory()->create(); $type = AdvanceType::create(['name' => 'Paliwo']); Advance::create(['date' => '2026-08-01', 'user_id' => $employee->id, 'amount' => 100, 'advance_type_id' => $type->id]); Advance::create(['date' => '2026-08-02', 'user_id' => $otherUser->id, 'amount' => 200, 'advance_type_id' => $type->id]); $response = $this->actingAs($employee)->get(route('zaliczki')); $response->assertOk(); $response->assertSee('100,00'); $response->assertDontSee('200,00'); }); test('advance is stored for authenticated user', function () { $employee = userWithAdvanceAccess(); $otherUser = User::factory()->create(); $type = AdvanceType::create(['name' => 'Hotel']); $response = $this->actingAs($employee)->post(route('zaliczki.store'), [ 'date' => '2026-08-28', 'user_id' => $otherUser->id, 'amount' => '250.50', 'advance_type_id' => $type->id, ]); $response->assertRedirect(route('zaliczki')); $this->assertDatabaseHas('advances', [ 'user_id' => $employee->id, 'advance_type_id' => $type->id, 'amount' => -250.50, ]); }); test('manager can create an advance for another user', function () { $manager = userWithAdvanceAccess('kierownik'); $employee = User::factory()->create(); $type = AdvanceType::create(['name' => 'Wpłata']); $response = $this->actingAs($manager)->post(route('zaliczki.store'), [ 'date' => '2026-08-28', 'user_id' => $employee->id, 'amount' => '300.00', 'advance_type_id' => $type->id, ]); $response->assertRedirect(route('zaliczki')); $this->assertDatabaseHas('advances', [ 'user_id' => $employee->id, 'advance_type_id' => $type->id, 'amount' => 300.00, ]); }); test('manager can access system settings and add an advance type', function () { Permission::firstOrCreate(['name' => 'users_access']); $manager = User::factory()->create(); $managerRole = Role::firstOrCreate(['name' => 'kierownik']); $managerRole->givePermissionTo(['zaliczki_access', 'users_access']); $manager->assignRole($managerRole); $indexResponse = $this->actingAs($manager)->get(route('settings.index')); $indexResponse->assertOk(); $storeResponse = $this->actingAs($manager)->post(route('settings.advance-types.store'), [ 'name' => 'Koncesja', ]); $storeResponse->assertRedirect(route('settings.index')); $this->assertDatabaseHas('advance_types', ['name' => 'Koncesja']); }); test('manager can edit and delete an advance type from settings', function () { Permission::firstOrCreate(['name' => 'users_access']); $manager = User::factory()->create(); $managerRole = Role::firstOrCreate(['name' => 'kierownik']); $managerRole->givePermissionTo(['zaliczki_access', 'users_access']); $manager->assignRole($managerRole); $type = AdvanceType::create(['name' => 'Koncesja']); $editResponse = $this->actingAs($manager)->put(route('settings.advance-types.update', $type), [ 'name' => 'Koncesja VIP', ]); $editResponse->assertRedirect(route('settings.index')); $this->assertDatabaseHas('advance_types', ['id' => $type->id, 'name' => 'Koncesja VIP']); $deleteResponse = $this->actingAs($manager)->delete(route('settings.advance-types.destroy', $type)); $deleteResponse->assertRedirect(route('settings.index')); $this->assertDatabaseMissing('advance_types', ['id' => $type->id]); }); test('manager can update role permissions from settings', function () { Permission::firstOrCreate(['name' => 'users_access']); Permission::firstOrCreate(['name' => 'raporty_access']); Permission::firstOrCreate(['name' => 'flota_access']); $manager = User::factory()->create(); $managerRole = Role::firstOrCreate(['name' => 'kierownik']); $managerRole->givePermissionTo('users_access'); $manager->assignRole($managerRole); $response = $this->actingAs($manager)->post(route('settings.role-permissions.store'), [ 'permissions' => [ $managerRole->id => ['users_access', 'raporty_access'], ], ]); $response->assertRedirect(route('settings.index')); $this->assertTrue($managerRole->fresh()->hasPermissionTo('raporty_access')); $this->assertFalse($managerRole->fresh()->hasPermissionTo('flota_access')); }); test('manager cannot delete an advance type that is still used in advances', function () { Permission::firstOrCreate(['name' => 'users_access']); $manager = User::factory()->create(); $managerRole = Role::firstOrCreate(['name' => 'kierownik']); $managerRole->givePermissionTo(['zaliczki_access', 'users_access']); $manager->assignRole($managerRole); $type = AdvanceType::create(['name' => 'Paliwo']); $employee = User::factory()->create(); Advance::create(['date' => '2026-08-28', 'user_id' => $employee->id, 'amount' => 100, 'advance_type_id' => $type->id]); $response = $this->actingAs($manager)->from(route('settings.index'))->delete(route('settings.advance-types.destroy', $type)); $response->assertRedirect(route('settings.index')); $response->assertSessionHas('error', 'Nie można usunąć rodzaju zaliczki, który jest już przypisany do istniejących zaliczek.'); $this->assertDatabaseHas('advance_types', ['id' => $type->id, 'name' => 'Paliwo']); }); test('employee can edit own advance but cannot access another users advance', function () { $employee = userWithAdvanceAccess(); $otherUser = User::factory()->create(); $type = AdvanceType::create(['name' => 'Hotel']); $advance = Advance::create(['date' => '2026-08-28', 'user_id' => $employee->id, 'amount' => 100, 'advance_type_id' => $type->id]); $otherAdvance = Advance::create(['date' => '2026-08-28', 'user_id' => $otherUser->id, 'amount' => 200, 'advance_type_id' => $type->id]); $this->actingAs($employee)->get(route('zaliczki.edit', $otherAdvance))->assertForbidden(); $response = $this->actingAs($employee)->put(route('zaliczki.update', $advance), [ 'date' => '2026-08-27', 'amount' => '150.00', 'advance_type_id' => $type->id, ]); $response->assertRedirect(route('zaliczki')); $this->assertDatabaseHas('advances', ['id' => $advance->id, 'amount' => -150, 'date' => '2026-08-27 00:00:00']); }); test('manager can delete an advance', function () { $manager = userWithAdvanceAccess('kierownik'); $employee = User::factory()->create(); $type = AdvanceType::create(['name' => 'Paliwo']); $advance = Advance::create(['date' => '2026-08-28', 'user_id' => $employee->id, 'amount' => 100, 'advance_type_id' => $type->id]); $response = $this->actingAs($manager)->delete(route('zaliczki.destroy', $advance)); $response->assertRedirect(route('zaliczki')); $this->assertDatabaseMissing('advances', ['id' => $advance->id]); }); test('expenses are negative and deposits are positive regardless of input sign', function () { $employee = userWithAdvanceAccess(); $fuel = AdvanceType::create(['name' => 'Paliwo']); $deposit = AdvanceType::create(['name' => 'Wpłata']); Advance::create(['date' => '2026-08-28', 'user_id' => $employee->id, 'amount' => 100, 'advance_type_id' => $fuel->id]); Advance::create(['date' => '2026-08-28', 'user_id' => $employee->id, 'amount' => -200, 'advance_type_id' => $deposit->id]); $this->assertDatabaseHas('advances', ['advance_type_id' => $fuel->id, 'amount' => -100]); $this->assertDatabaseHas('advances', ['advance_type_id' => $deposit->id, 'amount' => 200]); }); test('manager can filter advances by text type and date range', function () { $manager = userWithAdvanceAccess('kierownik'); $employee = User::factory()->create(['name' => 'Anna', 'surname' => 'Nowak']); $fuel = AdvanceType::create(['name' => 'Paliwo']); $hotel = AdvanceType::create(['name' => 'Hotel']); Advance::create(['date' => '2026-08-10', 'user_id' => $employee->id, 'amount' => 100, 'advance_type_id' => $fuel->id]); Advance::create(['date' => '2026-08-20', 'user_id' => $employee->id, 'amount' => 200, 'advance_type_id' => $hotel->id]); $response = $this->actingAs($manager)->get(route('zaliczki', [ 'search' => 'Anna', 'type' => $fuel->id, 'date_from' => '2026-08-01', 'date_to' => '2026-08-15', ])); $response->assertOk(); $response->assertSee('100,00'); $response->assertDontSee('200,00'); }); test('manager can open employee profile and see current advance balance', function () { Permission::firstOrCreate(['name' => 'users_access']); $manager = User::factory()->create(); $managerRole = Role::firstOrCreate(['name' => 'kierownik']); $managerRole->givePermissionTo(['zaliczki_access', 'users_access']); $manager->assignRole($managerRole); $employee = User::factory()->create(['name' => 'Jan', 'surname' => 'Kowalski']); $fuel = AdvanceType::create(['name' => 'Paliwo']); $deposit = AdvanceType::create(['name' => 'Wpłata']); Advance::create(['date' => '2026-08-10', 'user_id' => $employee->id, 'amount' => 120, 'advance_type_id' => $fuel->id]); Advance::create(['date' => '2026-08-11', 'user_id' => $employee->id, 'amount' => 500, 'advance_type_id' => $deposit->id]); $response = $this->actingAs($manager)->get(route('users.show', $employee)); $response->assertOk(); $response->assertSee('Jan Kowalski'); $response->assertSee('380,00'); }); test('advance balance is recalculated after create update and delete', function () { $employee = userWithAdvanceAccess(); $fuel = AdvanceType::create(['name' => 'Paliwo']); $advance = Advance::create(['date' => '2026-08-10', 'user_id' => $employee->id, 'amount' => 120, 'advance_type_id' => $fuel->id]); $this->assertDatabaseHas('user_advance_balances', [ 'user_id' => $employee->id, 'balance' => -120, ]); $advance->update(['amount' => 300]); $this->assertDatabaseHas('user_advance_balances', [ 'user_id' => $employee->id, 'balance' => -300, ]); $advance->delete(); $this->assertDatabaseHas('user_advance_balances', [ 'user_id' => $employee->id, 'balance' => 0, ]); }); test('dashboard shows the lowest advance balances for employees', function () { $manager = userWithAdvanceAccess('kierownik'); $fuel = AdvanceType::create(['name' => 'Paliwo']); Role::firstOrCreate(['name' => 'pracownik']); $worstEmployee = User::factory()->create(['name' => 'Anna', 'surname' => 'Najgorsza']); $worstEmployee->assignRole('pracownik'); $betterEmployee = User::factory()->create(['name' => 'Piotr', 'surname' => 'Lepszy']); $betterEmployee->assignRole('pracownik'); Advance::create(['date' => '2026-08-10', 'user_id' => $worstEmployee->id, 'amount' => 200, 'advance_type_id' => $fuel->id]); Advance::create(['date' => '2026-08-11', 'user_id' => $betterEmployee->id, 'amount' => 50, 'advance_type_id' => $fuel->id]); $response = $this->actingAs($manager)->get(route('dashboard')); $response->assertOk(); $response->assertSee('Najniższy bilans zaliczek'); $response->assertSee('Anna Najgorsza'); $response->assertSee('-200,00'); }); test('dashboard prioritizes negative balances over zero balances', function () { $manager = userWithAdvanceAccess('kierownik'); $fuel = AdvanceType::create(['name' => 'Paliwo']); Role::firstOrCreate(['name' => 'pracownik']); $negativeEmployee = User::factory()->create(['name' => 'Kasia', 'surname' => 'Minus']); $negativeEmployee->assignRole('pracownik'); $zeroEmployee = User::factory()->create(['name' => 'Marek', 'surname' => 'Zero']); $zeroEmployee->assignRole('pracownik'); Advance::create(['date' => '2026-08-01', 'user_id' => $negativeEmployee->id, 'amount' => 100, 'advance_type_id' => $fuel->id]); Advance::create(['date' => '2026-08-02', 'user_id' => $zeroEmployee->id, 'amount' => 0, 'advance_type_id' => $fuel->id]); $response = $this->actingAs($manager)->get(route('dashboard')); $response->assertOk(); $response->assertSeeInOrder(['Kasia Minus']); $response->assertDontSee('Marek Zero'); }); test('dashboard hides employees with zero or positive balance from the negative ledger', function () { $manager = userWithAdvanceAccess('kierownik'); $fuel = AdvanceType::create(['name' => 'Paliwo']); $deposit = AdvanceType::create(['name' => 'Wpłata']); Role::firstOrCreate(['name' => 'pracownik']); $negativeEmployee = User::factory()->create(['name' => 'Kasia', 'surname' => 'Minus']); $negativeEmployee->assignRole('pracownik'); $zeroEmployee = User::factory()->create(['name' => 'Marek', 'surname' => 'Zero']); $zeroEmployee->assignRole('pracownik'); $positiveEmployee = User::factory()->create(['name' => 'Ola', 'surname' => 'Plus']); $positiveEmployee->assignRole('pracownik'); Advance::create(['date' => '2026-08-01', 'user_id' => $negativeEmployee->id, 'amount' => 100, 'advance_type_id' => $fuel->id]); Advance::create(['date' => '2026-08-02', 'user_id' => $zeroEmployee->id, 'amount' => 0, 'advance_type_id' => $fuel->id]); Advance::create(['date' => '2026-08-03', 'user_id' => $positiveEmployee->id, 'amount' => 50, 'advance_type_id' => $deposit->id]); $response = $this->actingAs($manager)->get(route('dashboard')); $response->assertOk(); $response->assertSee('Kasia Minus'); $response->assertDontSee('Marek Zero'); $response->assertDontSee('Ola Plus'); }); test('dashboard shows negative balances for users regardless of role', function () { $manager = userWithAdvanceAccess('kierownik'); $fuel = AdvanceType::create(['name' => 'Paliwo']); $user = User::factory()->create(['name' => 'Zbyszek', 'surname' => 'Minus']); $user->syncRoles([]); Advance::create(['date' => '2026-08-10', 'user_id' => $user->id, 'amount' => 150, 'advance_type_id' => $fuel->id]); $response = $this->actingAs($manager)->get(route('dashboard')); $response->assertOk(); $response->assertSee('Zbyszek Minus'); $response->assertSee('-150,00'); });