364 lines
14 KiB
PHP
364 lines
14 KiB
PHP
<?php
|
||
|
||
namespace Tests\Feature;
|
||
|
||
use App\Models\Kategori;
|
||
use App\Models\ObatKeluar;
|
||
use App\Models\ObatMasuk;
|
||
use App\Models\Satuan;
|
||
use App\Models\User;
|
||
use Tests\DatabaseTestCase;
|
||
|
||
class ObatKeluarTest extends DatabaseTestCase
|
||
{
|
||
private User $user;
|
||
private ObatMasuk $obatMasuk;
|
||
|
||
protected function setUp(): void
|
||
{
|
||
parent::setUp();
|
||
$this->user = User::factory()->create(['role' => 'apoteker']);
|
||
$this->actingAs($this->user);
|
||
|
||
$kategori = Kategori::create(['nama' => 'Antibiotik']);
|
||
$satuan = Satuan::create(['nama' => 'Tablet']);
|
||
|
||
$this->obatMasuk = ObatMasuk::create([
|
||
'nama_obat' => 'Amoxicillin',
|
||
'kategori_id' => $kategori->id,
|
||
'satuan_id' => $satuan->id,
|
||
'kode_batch' => 'BTH-001',
|
||
'stok' => 100,
|
||
'tanggal_penerimaan' => now()->toDateString(),
|
||
'tanggal_kadaluarsa' => now()->addYear()->toDateString(),
|
||
'user_id' => $this->user->id,
|
||
]);
|
||
}
|
||
|
||
private function validData(array $overrides = []): array
|
||
{
|
||
return array_merge([
|
||
'obat_masuk_id' => $this->obatMasuk->id,
|
||
'kode_batch' => 'BTH-001',
|
||
'jumlah' => 10,
|
||
'tujuan_pemakaian' => 'Penggunaan internal',
|
||
'tanggal_kadaluarsa' => now()->addYear()->toDateString(),
|
||
'tanggal_pengeluaran' => now()->toDateString(),
|
||
'nama_petugas' => 'Petugas A',
|
||
'nama_penerima' => 'Penerima B',
|
||
], $overrides);
|
||
}
|
||
|
||
// ─────────────────────────────────────────
|
||
// index
|
||
// ─────────────────────────────────────────
|
||
|
||
public function test_index_returns_200(): void
|
||
{
|
||
$response = $this->get(route('obat-keluar.index'));
|
||
$response->assertStatus(200);
|
||
}
|
||
|
||
public function test_index_guest_redirects_to_login(): void
|
||
{
|
||
auth()->logout();
|
||
$response = $this->get(route('obat-keluar.index'));
|
||
$response->assertRedirect(route('login'));
|
||
}
|
||
|
||
public function test_index_filter_by_status(): void
|
||
{
|
||
ObatKeluar::create(array_merge($this->validData(['jumlah' => 5]), [
|
||
'nama_obat' => 'Amoxicillin',
|
||
'status' => 'proses',
|
||
'user_id' => $this->user->id,
|
||
]));
|
||
|
||
$response = $this->get(route('obat-keluar.index', ['status' => 'proses']));
|
||
$response->assertStatus(200);
|
||
}
|
||
|
||
public function test_index_search(): void
|
||
{
|
||
ObatKeluar::create(array_merge($this->validData(), [
|
||
'nama_obat' => 'Amoxicillin',
|
||
'kode_batch' => 'BTH-001',
|
||
'user_id' => $this->user->id,
|
||
]));
|
||
|
||
ObatKeluar::create(array_merge($this->validData(), [
|
||
'nama_obat' => 'Paracetamol',
|
||
'kode_batch' => 'BTH-002',
|
||
'user_id' => $this->user->id,
|
||
]));
|
||
|
||
$response = $this->get(route('obat-keluar.index', ['search' => 'Paracetamol']));
|
||
$data = $response->viewData('obatKeluars');
|
||
$names = $data->pluck('nama_obat')->toArray();
|
||
|
||
$this->assertContains('Paracetamol', $names);
|
||
$this->assertNotContains('Amoxicillin', $names);
|
||
}
|
||
|
||
// ─────────────────────────────────────────
|
||
// create, show, edit
|
||
// ─────────────────────────────────────────
|
||
|
||
public function test_create_returns_200(): void
|
||
{
|
||
$response = $this->get(route('obat-keluar.create'));
|
||
$response->assertStatus(200);
|
||
}
|
||
|
||
public function test_show_returns_200(): void
|
||
{
|
||
$keluar = ObatKeluar::create(array_merge($this->validData(), [
|
||
'nama_obat' => 'Amoxicillin',
|
||
'status' => 'proses',
|
||
'user_id' => $this->user->id,
|
||
]));
|
||
$response = $this->get(route('obat-keluar.show', $keluar));
|
||
$response->assertStatus(200);
|
||
}
|
||
|
||
public function test_edit_returns_200(): void
|
||
{
|
||
$keluar = ObatKeluar::create(array_merge($this->validData(), [
|
||
'nama_obat' => 'Amoxicillin',
|
||
'status' => 'proses',
|
||
'user_id' => $this->user->id,
|
||
]));
|
||
$response = $this->get(route('obat-keluar.edit', $keluar));
|
||
$response->assertStatus(200);
|
||
}
|
||
|
||
// ─────────────────────────────────────────
|
||
// store — berhasil
|
||
// ─────────────────────────────────────────
|
||
|
||
public function test_store_creates_obat_keluar_and_decrements_stok(): void
|
||
{
|
||
$initialStok = $this->obatMasuk->stok; // 100
|
||
|
||
$response = $this->post(route('obat-keluar.store'), $this->validData(['jumlah' => 10]));
|
||
|
||
$response->assertRedirect(route('obat-keluar.index'));
|
||
$response->assertSessionHas('success');
|
||
|
||
$this->assertDatabaseHas('obat_keluars', [
|
||
'obat_masuk_id' => $this->obatMasuk->id,
|
||
'jumlah' => 10,
|
||
'nama_obat' => 'Amoxicillin',
|
||
]);
|
||
|
||
// Stok berkurang dari 100 menjadi 90
|
||
$this->assertEquals($initialStok - 10, $this->obatMasuk->fresh()->stok);
|
||
}
|
||
|
||
public function test_store_sets_user_id_and_nama_obat_automatically(): void
|
||
{
|
||
$this->post(route('obat-keluar.store'), $this->validData(['jumlah' => 5]));
|
||
|
||
$this->assertDatabaseHas('obat_keluars', [
|
||
'user_id' => $this->user->id,
|
||
'nama_obat' => 'Amoxicillin',
|
||
]);
|
||
}
|
||
|
||
// ─────────────────────────────────────────
|
||
// store — gagal karena stok
|
||
// ─────────────────────────────────────────
|
||
|
||
public function test_store_fails_when_jumlah_exceeds_stok(): void
|
||
{
|
||
$response = $this->post(route('obat-keluar.store'), $this->validData(['jumlah' => 200])); // stok cuma 100
|
||
|
||
// Harus kembali ke form dengan error jumlah
|
||
$response->assertSessionHasErrors('jumlah');
|
||
}
|
||
|
||
public function test_store_stok_unchanged_when_validation_fails(): void
|
||
{
|
||
$this->post(route('obat-keluar.store'), $this->validData(['jumlah' => 200]));
|
||
|
||
$this->assertEquals(100, $this->obatMasuk->fresh()->stok);
|
||
}
|
||
|
||
// ─────────────────────────────────────────
|
||
// store — validasi field wajib
|
||
// ─────────────────────────────────────────
|
||
|
||
public function test_store_fails_without_required_fields(): void
|
||
{
|
||
$response = $this->post(route('obat-keluar.store'), []);
|
||
$response->assertSessionHasErrors([
|
||
'obat_masuk_id',
|
||
'kode_batch',
|
||
'jumlah',
|
||
'tujuan_pemakaian',
|
||
'tanggal_kadaluarsa',
|
||
'tanggal_pengeluaran',
|
||
'nama_petugas',
|
||
'nama_penerima',
|
||
]);
|
||
}
|
||
|
||
// ─────────────────────────────────────────
|
||
// update — perubahan jumlah
|
||
// ─────────────────────────────────────────
|
||
|
||
public function test_update_adjusts_stok_when_jumlah_increases(): void
|
||
{
|
||
// Buat obat keluar awal dengan jumlah 10 → stok jadi 90
|
||
$keluar = ObatKeluar::create(array_merge($this->validData(['jumlah' => 10]), [
|
||
'nama_obat' => 'Amoxicillin',
|
||
'status' => 'proses',
|
||
'user_id' => $this->user->id,
|
||
]));
|
||
$this->obatMasuk->decrement('stok', 10); // stok = 90
|
||
|
||
// Update jumlah menjadi 30 (selisih +20, stok dari 90 → 70)
|
||
$response = $this->put(
|
||
route('obat-keluar.update', $keluar),
|
||
$this->validData(['jumlah' => 30])
|
||
);
|
||
|
||
$response->assertRedirect(route('obat-keluar.index'));
|
||
$this->assertEquals(70, $this->obatMasuk->fresh()->stok);
|
||
}
|
||
|
||
public function test_update_adjusts_stok_when_jumlah_decreases(): void
|
||
{
|
||
// Buat obat keluar awal dengan jumlah 20 → stok jadi 80
|
||
$keluar = ObatKeluar::create(array_merge($this->validData(['jumlah' => 20]), [
|
||
'nama_obat' => 'Amoxicillin',
|
||
'status' => 'proses',
|
||
'user_id' => $this->user->id,
|
||
]));
|
||
$this->obatMasuk->decrement('stok', 20); // stok = 80
|
||
|
||
// Update jumlah menjadi 10 (selisih −10, stok dari 80 → 90)
|
||
$response = $this->put(
|
||
route('obat-keluar.update', $keluar),
|
||
$this->validData(['jumlah' => 10])
|
||
);
|
||
|
||
$response->assertRedirect(route('obat-keluar.index'));
|
||
$this->assertEquals(90, $this->obatMasuk->fresh()->stok);
|
||
}
|
||
|
||
public function test_update_fails_when_increased_jumlah_exceeds_available_stok(): void
|
||
{
|
||
// Stok awal 100, keluar 10 → stok = 90
|
||
$keluar = ObatKeluar::create(array_merge($this->validData(['jumlah' => 10]), [
|
||
'nama_obat' => 'Amoxicillin',
|
||
'status' => 'proses',
|
||
'user_id' => $this->user->id,
|
||
]));
|
||
$this->obatMasuk->decrement('stok', 10);
|
||
|
||
// Coba update ke 200 (selisih = +190, stok hanya 90)
|
||
$response = $this->put(
|
||
route('obat-keluar.update', $keluar),
|
||
$this->validData(['jumlah' => 200])
|
||
);
|
||
|
||
$response->assertSessionHasErrors('jumlah');
|
||
}
|
||
|
||
public function test_update_fails_without_required_fields(): void
|
||
{
|
||
$keluar = ObatKeluar::create(array_merge($this->validData(['jumlah' => 10]), [
|
||
'nama_obat' => 'Amoxicillin',
|
||
'user_id' => $this->user->id,
|
||
]));
|
||
|
||
$response = $this->put(route('obat-keluar.update', $keluar), []);
|
||
|
||
$response->assertSessionHasErrors([
|
||
'obat_masuk_id',
|
||
'kode_batch',
|
||
'jumlah',
|
||
'tujuan_pemakaian',
|
||
'tanggal_kadaluarsa',
|
||
'tanggal_pengeluaran',
|
||
'nama_petugas',
|
||
'nama_penerima',
|
||
]);
|
||
}
|
||
|
||
// ─────────────────────────────────────────
|
||
// destroy — stok dikembalikan
|
||
// ─────────────────────────────────────────
|
||
|
||
public function test_destroy_deletes_obat_keluar_and_restores_stok(): void
|
||
{
|
||
// Buat keluar jumlah 15 → stok jadi 85
|
||
$keluar = ObatKeluar::create(array_merge($this->validData(['jumlah' => 15]), [
|
||
'nama_obat' => 'Amoxicillin',
|
||
'status' => 'proses',
|
||
'user_id' => $this->user->id,
|
||
]));
|
||
$this->obatMasuk->decrement('stok', 15);
|
||
|
||
$response = $this->delete(route('obat-keluar.destroy', $keluar));
|
||
|
||
$response->assertRedirect(route('obat-keluar.index'));
|
||
$response->assertSessionHas('success');
|
||
|
||
// Stok dikembalikan → 100
|
||
$this->assertEquals(100, $this->obatMasuk->fresh()->stok);
|
||
$this->assertDatabaseMissing('obat_keluars', ['id' => $keluar->id]);
|
||
}
|
||
|
||
// ─────────────────────────────────────────
|
||
// superadmin access
|
||
// ─────────────────────────────────────────
|
||
|
||
public function test_superadmin_can_access_index(): void
|
||
{
|
||
$superadmin = User::factory()->create(['role' => 'superadmin']);
|
||
$this->actingAs($superadmin);
|
||
|
||
$response = $this->get(route('obat-keluar.index'));
|
||
$response->assertStatus(200);
|
||
}
|
||
|
||
public function test_superadmin_can_access_show(): void
|
||
{
|
||
$superadmin = User::factory()->create(['role' => 'superadmin']);
|
||
$this->actingAs($superadmin);
|
||
|
||
$keluar = ObatKeluar::create(array_merge($this->validData(), [
|
||
'nama_obat' => 'Amoxicillin',
|
||
'status' => 'proses',
|
||
'user_id' => $this->user->id,
|
||
]));
|
||
$response = $this->get(route('obat-keluar.show', $keluar));
|
||
$response->assertStatus(200);
|
||
}
|
||
|
||
public function test_superadmin_can_access_create_store_edit_update_destroy(): void
|
||
{
|
||
$superadmin = User::factory()->create(['role' => 'superadmin']);
|
||
$this->actingAs($superadmin);
|
||
|
||
$keluar = ObatKeluar::create(array_merge($this->validData(), [
|
||
'nama_obat' => 'Amoxicillin',
|
||
'status' => 'proses',
|
||
'user_id' => $this->user->id,
|
||
]));
|
||
|
||
$this->get(route('obat-keluar.create'))->assertStatus(200);
|
||
$this->post(route('obat-keluar.store'), $this->validData(['jumlah' => 10]))
|
||
->assertRedirect(route('obat-keluar.index'));
|
||
$this->assertDatabaseHas('obat_keluars', ['user_id' => $superadmin->id, 'jumlah' => 10]);
|
||
$this->get(route('obat-keluar.edit', $keluar))->assertStatus(200);
|
||
$this->put(route('obat-keluar.update', $keluar), $this->validData(['jumlah' => 8]))
|
||
->assertRedirect(route('obat-keluar.index'));
|
||
$this->assertDatabaseHas('obat_keluars', ['id' => $keluar->id, 'jumlah' => 8]);
|
||
$this->delete(route('obat-keluar.destroy', $keluar))->assertRedirect(route('obat-keluar.index'));
|
||
$this->assertDatabaseMissing('obat_keluars', ['id' => $keluar->id]);
|
||
}
|
||
}
|