new_malcomm_oline/tests/Feature/AdvanceTest.php

351 lines
14 KiB
PHP

<?php
use App\Models\Advance;
use App\Models\AdvanceType;
use App\Models\User;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
beforeEach(function () {
Permission::firstOrCreate(['name' => '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');
});