# Apotek BAZNAS — Sistem Informasi Apotek Rumah Sehat BAZNAS Provinsi Banten

Sistem informasi apotek berbasis web untuk mendukung pengelolaan obat pada
**Rumah Sehat BAZNAS Provinsi Banten**, mencakup pengadaan, pengeluaran/dispensing,
stok, dan pelaporan obat. Dibangun sebagai proyek skripsi (Program Studi Sistem
Informasi, Universitas Bina Bangsa) menggantikan proses pencatatan manual
berbasis Microsoft Excel dan kartu stok.

## Ringkasan

- **Framework**: CodeIgniter 4 (PHP ^8.2)
- **Database**: MySQL (skema `apotek_baznas.sql` di root, 11 tabel)
- **Server lokal**: Laragon (Apache + MySQL + PHP dalam satu paket)
- **Frontend**: Bootstrap, CSS kustom, Chart.js (grafik dashboard & laporan)
- **Library tambahan**: `phpoffice/phpspreadsheet` (export laporan ke Excel)
- **Metode pengujian**: Black Box Testing

## Peran Pengguna (Role-Based Access)

| Role | Hak Akses |
|---|---|
| `admin_apotek` | Akses penuh: data master (obat, petugas, pemasok), transaksi pengadaan & pengeluaran, seluruh laporan, backup, histori aktivitas |
| `petugas_pelayanan` | Transaksi pengeluaran obat, lihat data obat (view-only), dapat diberi izin tambahan per-petugas (permission fleksibel) |
| `kepala_klinik` | Akses baca (read-only) ke data master dan seluruh laporan |

Selain role dasar, tersedia sistem **izin tambahan per petugas** (tabel
`petugas_permissions`) untuk fine-tuning hak akses di luar role default.

## Konsep Domain Utama

- **Mustahik**: pasien penerima manfaat BAZNAS yang menerima obat gratis (dibedakan dari pasien "Umum" via `kat_pasien`).
- **PBF / Pemasok**: Pedagang Besar Farmasi, sumber pengadaan obat (dilengkapi no. izin & no. telp).
- **Pengadaan**: pencatatan pembelian obat dari PBF, otomatis menambah stok.
- **Pengeluaran**: pencatatan dispensing obat ke pasien (umum/mustahik), otomatis mengurangi stok. Mendukung data NIK, jenis kelamin, umur pasien, dan biaya admin.
- **Stok Opname**: penyesuaian stok sistem vs stok fisik, dengan pencatatan selisih dan alasan.
- **Dual-unit satuan**: setiap obat punya *satuan besar* (mis. strip/botol/box saat pengadaan) dan *satuan kecil* (mis. tablet/kapsul/ml saat pengeluaran), dikonversi via `isi_per_satuan`.

## Struktur Basis Data

Skema hasil normalisasi hingga 3NF, terdiri dari tabel master dan transaksi:

**Master**
- `obat` — data obat (nama, golongan, kelas terapi, tanggal kedaluwarsa, satuan besar/kecil)
- `pbf` — data pemasok (nama, alamat, no. izin, no. telp)
- `petugas` — data pengguna sistem (username, password ter-hash, role, status aktif)
- `stok_obat` — jumlah stok per obat

**Transaksi**
- `pengadaan` + `detail_pengadaan` — transaksi pembelian obat dari PBF
- `pengeluaran` + `detail_pengeluaran` — transaksi dispensing obat ke pasien
- `penyesuaian_stok` — riwayat stok opname (stok sistem vs fisik, selisih)
- `petugas_permissions` — izin akses tambahan per petugas
- `histori_aktivitas` — log aktivitas pengguna (login/logout/tambah/edit/hapus/backup)

Semua relasi transaksi-ke-master dijaga dengan **foreign key constraints**
(`ON DELETE RESTRICT`/`CASCADE` sesuai konteks).

## Modul & Fitur

| Modul | Fitur |
|---|---|
| **Autentikasi** | Login/logout, session fingerprinting (deteksi session hijacking), idle timeout 30 menit, regenerasi session, cookie `SameSite=Strict` |
| **Dashboard** | Ringkasan data obat & stok, grafik Chart.js, akses cepat ke menu lain |
| **Data Master** | CRUD obat, petugas (dengan toggle aktif/nonaktif), pemasok/PBF |
| **Transaksi Pengadaan** | Input pembelian obat dari PBF, detail per item, update stok otomatis |
| **Transaksi Pengeluaran** | Input dispensing obat, kategori pasien (Umum/Mustahik), data pasien (NIK, jenis kelamin, umur), biaya admin, cetak kwitansi per pasien |
| **Stok Opname** | Penyesuaian stok sistem vs fisik dengan pencatatan alasan selisih |
| **Laporan** | Laporan pengadaan, pengeluaran, dan stok obat — bisa difilter per periode, cetak, dan export ke Excel (PhpSpreadsheet) |
| **Histori Aktivitas** | Log seluruh aktivitas pengguna, ditampilkan sebagai widget dashboard |
| **Backup Database** | Backup manual via `mysqldump`, download file backup, kirim via email (Gmail SMTP + App Password), penjadwalan backup otomatis |

## Keamanan

- Password petugas di-hash (bcrypt)
- Filter `AuthFilter` custom: cek status login, fingerprint browser (User-Agent), dan idle timeout di setiap request ber-autentikasi
- Kontrol akses berbasis role di seluruh controller dan sidebar menu
- Cookie session `SameSite=Strict`

## Struktur Proyek (ringkas)

```
app/
├── Controllers/     # AuthController, DashboardController, ObatController,
│                     PemasokController, PetugasController,
│                     PengadaanController, PengeluaranController,
│                     StokOpnameController, LaporanController,
│                     HistoriController, BackupController
├── Models/           # ObatModel, PemasokModel, PetugasModel,
│                     PengadaanModel, DetailPengadaanModel,
│                     PengeluaranModel, DetailPengeluaranModel,
│                     StokObatModel, StokOpnameModel, HistoriModel
├── Views/            # per-modul: auth, dashboard, obat, petugas, pemasok,
│                     pengadaan, pengeluaran, stok_opname, laporan,
│                     histori, backup, template (header/footer & print partials)
├── Filters/          # AuthFilter (login, fingerprint, idle timeout)
├── Helpers/          # role_helper.php
└── Config/           # Routes.php, Filters.php, dll.

apotek_baznas.sql     # dump skema database (siap import)
writable/backups/     # riwayat file backup database (.sql)
```

## Instalasi & Menjalankan Secara Lokal (Laragon)

1. Clone/ekstrak proyek ke folder Laragon (`www/apotek_baznas`).
2. Buat database MySQL, import `apotek_baznas.sql`.
3. Salin/atur `.env` (baseURL, kredensial database, path `mysqldump`, kredensial SMTP untuk fitur backup email).
4. Install dependency PHP: `composer install`.
5. Jalankan Laragon (Apache + MySQL), akses via `http://apotek_baznas.test` (atau sesuai konfigurasi virtual host Laragon).
6. Login menggunakan akun petugas yang terdaftar di tabel `petugas` (role menentukan menu yang tampil).

## Catatan Pengembangan

- Ruang lingkup sistem **tidak mencakup** transaksi pembayaran maupun rekam medis pasien secara detail.
- Perancangan mengacu pada dokumen skripsi BAB IV (Rancangan Sistem yang Diusulkan): flowchart, use case diagram, activity diagram, normalisasi hingga 3NF, dan pengujian Black Box.
- Saran pengembangan lanjutan (dari BAB V): fitur rekam medis/pembayaran, penguatan keamanan & backup otomatis, pelatihan pengguna, pemeliharaan berkala, serta integrasi dengan sistem BAZNAS lain.

---

## Alur Proses per Modul (Prosedur Sistem — bahan untuk Flowchart, Use Case & Activity Diagram)

Bagian ini menjabarkan **urutan langkah dan titik keputusan (percabangan)** aktual
di tiap modul, diambil langsung dari logika controller. Tujuannya agar bagian ini
bisa langsung dipakai sebagai dasar penyusunan flowchart program, use case
diagram, dan activity diagram di BAB IV — baik dikerjakan manual maupun dengan
bantuan AI.

### 1. Autentikasi (Login / Logout)

**Aktor**: Admin Apotek, Petugas Pelayanan, Kepala Klinik (semua pengguna terdaftar)

**Alur Login**:
1. Pengguna membuka halaman login. Jika session login masih aktif → langsung diarahkan ke Dashboard (tidak perlu login ulang).
2. Pengguna input username & password → submit.
3. Sistem cek: username/password kosong? → jika ya, tolak dengan pesan "Username dan password wajib diisi."
4. Sistem cari akun berdasarkan username. Jika tidak ditemukan **atau** password tidak cocok (`password_verify`) → tolak dengan pesan "Username atau password salah." (pesan digeneralisasi, tidak membedakan "user tidak ada" vs "password salah", untuk keamanan).
5. Sistem cek status `is_aktif` akun. Jika 0 (nonaktif) → tolak dengan pesan "Akun Anda telah dinonaktifkan. Hubungi Admin Apotek."
6. Jika semua lolos: sistem regenerasi session ID (cegah session fixation), buat fingerprint browser (hash dari User-Agent), simpan data sesi (id_petugas, nama, role, waktu login, waktu aktivitas terakhir).
7. Sistem mencatat histori aktivitas "login".
8. Redirect ke Dashboard.

**Alur akses halaman ber-autentikasi (tiap request)** — dijalankan oleh `AuthFilter`:
1. Cek apakah sudah login → jika tidak, redirect ke login.
2. Cek fingerprint browser vs yang tersimpan di session → jika beda (indikasi session dicuri/dipakai di device lain), session dihancurkan, redirect ke login.
3. Cek idle timeout (>30 menit tanpa aktivitas) → jika lewat, session dihancurkan, redirect ke login.
4. Jika semua lolos, waktu aktivitas terakhir diperbarui, request dilanjutkan.

**Alur Logout**: catat histori "logout" → hancurkan session → hapus cookie session secara eksplisit → redirect ke login.

### 2. Data Master — Obat

**Aktor**: Admin Apotek (CRUD penuh) · Petugas Pelayanan & Kepala Klinik (lihat saja)

- **Lihat daftar obat**: semua role bisa akses, menampilkan data obat + stok terkini (join ke `stok_obat`).
- **Tambah obat**: hanya admin_apotek (atau petugas dengan permission `obat_edit`). Input: nama, golongan (Bebas/Bebas Terbatas/Keras), kelas terapi, tanggal kedaluwarsa, satuan besar, isi per satuan, satuan kecil, stok awal (opsional). Validasi gagal → kembali ke form dengan pesan error per field. Jika sukses: sistem generate ID obat baru, simpan ke tabel `obat`, sekaligus membuat baris awal di `stok_obat` (pakai stok awal jika diisi, default 0).
- **Edit obat**: sama seperti tambah, tapi tidak bisa mengubah stok awal langsung dari form ini (stok hanya berubah lewat transaksi pengadaan/pengeluaran/opname).
- **Hapus obat**: sistem cek dulu apakah obat pernah dipakai di `detail_pengadaan` atau `detail_pengeluaran`. **Jika sudah pernah bertransaksi → hapus ditolak** ("Obat tidak dapat dihapus karena sudah memiliki riwayat transaksi."). Jika belum pernah dipakai sama sekali → hapus diizinkan (data obat + baris stok dihapus).

### 3. Data Master — Petugas

**Aktor**: Admin Apotek penuh · petugas dengan permission `petugas_edit` (akses terbatas, lihat pembatasan di bawah)

- **Tambah petugas**: input nama, jabatan, username (unik), password (min. 6 karakter, di-hash bcrypt), role.
  - **Percabangan penting**: jika yang menambahkan **bukan** admin_apotek murni (hanya mengandalkan permission `petugas_edit`), role dipaksa default `petugas_pelayanan` — tidak boleh membuat akun admin_apotek baru, dan tidak boleh memberi permission tambahan apa pun ke akun baru itu.
  - Jika yang menambahkan admin_apotek asli: role bebas dipilih, dan bisa memberi permission tambahan (dari daftar: pengadaan, obat_edit, pbf, laporan, stok_opname, backup, petugas_edit) — kecuali role yang dipilih sudah `admin_apotek` (otomatis full akses, tidak perlu permission tambahan).
- **Edit petugas**: sama pola pembatasannya — non-admin dengan permission `petugas_edit` **tidak bisa** mengubah role atau permission siapa pun (termasuk dirinya sendiri), untuk mencegah *self-privilege-escalation*.
- **Nonaktifkan/Aktifkan petugas (toggle)**: hanya admin_apotek murni. Tidak bisa menonaktifkan akun yang sedang dipakai login saat itu.
- **Hapus petugas**: hanya admin_apotek murni, tidak bisa hapus akun sendiri. Sistem cek riwayat transaksi pengeluaran atas nama petugas tsb — jika ada riwayat, hapus ditolak dan disarankan pakai tombol Non-Aktifkan sebagai gantinya.

### 4. Data Master — Pemasok (PBF)

**Aktor**: Admin Apotek (CRUD) · semua role (lihat)

- CRUD standar (nama, alamat, no. izin, no. telp).
- **Hapus PBF**: dicek dulu apakah PBF sudah pernah dipakai di transaksi `pengadaan`. Jika sudah pernah → hapus ditolak.

### 5. Transaksi Pengadaan Obat

**Aktor**: Admin Apotek (atau petugas dengan permission `pengadaan`)

**Alur Tambah Pengadaan**:
1. Admin buka form tambah pengadaan → sistem tampilkan ID pengadaan baru (auto-generate), daftar PBF, daftar obat beserta stok saat ini.
2. Admin isi tanggal pengadaan, pilih PBF, lalu tambah baris obat (bisa lebih dari satu item obat per transaksi): pilih obat, isi jumlah dalam **satuan besar** (mis. jumlah box), isi harga per satuan besar. Sistem (di sisi form/JS) otomatis menghitung jumlah setara satuan kecil = jumlah besar × isi_per_satuan.
3. Submit → sistem validasi: tanggal wajib & format benar, PBF wajib dipilih, tiap baris obat wajib ada obat/jumlah/harga > 0. Gagal validasi → kembali ke form dengan pesan error.
4. Sistem cek tidak ada obat yang sama dipilih dua kali dalam satu transaksi (harus 1 baris per obat) → jika duplikat, tolak.
5. Sistem cek tiap ID obat yang dipilih benar-benar ada di master obat.
6. **Mulai transaksi database**: simpan header `pengadaan`, lalu untuk tiap baris: simpan `detail_pengadaan` (jumlah dalam satuan kecil, harga per satuan besar sesuai faktur), dan **tambahkan** stok obat (`stok_obat.jml_stok += jumlah_kecil`).
7. Jika semua berhasil → commit, catat histori "tambah pengadaan", redirect ke daftar pengadaan dengan notifikasi stok telah diperbarui.
8. Jika ada error di tengah proses → rollback semua perubahan (transaksi database dibatalkan total, stok tidak berubah sama sekali), tampilkan pesan error.

**Alur Edit Pengadaan**: strategi *rollback-lalu-insert-ulang* —
1. Ambil data transaksi lama beserta detailnya.
2. Validasi input baru (sama seperti tambah).
3. **Kurangi dulu** stok sesuai jumlah pada detail transaksi lama (membatalkan efek pengadaan lama).
4. Update header pengadaan, hapus semua baris detail lama.
5. Insert ulang baris detail baru sesuai input, dan **tambahkan** kembali stok sesuai jumlah baru.
6. Commit jika sukses; jika gagal di tengah jalan, rollback total (data pengadaan & stok kembali seperti semula, tidak ada perubahan setengah-jalan).

**Alur Hapus Pengadaan**: ambil detail transaksi → kurangi stok sesuai jumlah tiap item (membatalkan efek pengadaan) → hapus detail → hapus header → commit. Stok otomatis "dikembalikan" ke kondisi sebelum pengadaan itu ada.

### 6. Transaksi Pengeluaran Obat (Dispensing)

**Aktor**: Admin Apotek & Petugas Pelayanan (tambah/lihat) · hanya Admin Apotek yang boleh **edit/hapus**

**Alur Tambah Pengeluaran**:
1. Petugas buka form tambah pengeluaran → sistem tampilkan ID baru, daftar petugas, daftar obat + stok.
2. Petugas isi: tanggal, kategori pasien (Umum/Mustahik), NIK (opsional, jika diisi harus 16 digit), nama pasien, jenis kelamin, umur, biaya admin (opsional), petugas yang melayani, lalu daftar obat yang dikeluarkan beserta jumlah (dalam satuan kecil, mis. tablet).
3. Submit → validasi field wajib (lihat aturan di atas). Gagal → kembali ke form dengan pesan error per field.
4. Sistem cek tidak ada obat duplikat dalam satu transaksi.
5. **Validasi stok** — untuk tiap obat yang diminta, sistem bandingkan jumlah diminta vs stok tersedia. **Jika ada satu saja obat yang stoknya kurang, seluruh transaksi ditolak** (bukan sebagian diproses) — pesan error menyebutkan nama obat, stok tersedia (dikonversi juga ke satuan besar jika relevan), dan jumlah yang diminta.
6. Jika stok semua cukup: **mulai transaksi database** — simpan header `pengeluaran`, untuk tiap baris: ambil harga satuan kecil terakhir dari histori pengadaan obat tsb (untuk keperluan pelaporan nilai), simpan `detail_pengeluaran`, dan **kurangi** stok obat.
7. Commit jika sukses → catat histori "tambah pengeluaran" (termasuk nama pasien) → redirect dengan notifikasi stok telah dikurangi.
8. Jika gagal di tengah → rollback total.

**Alur Edit Pengeluaran** (hanya Admin Apotek): strategi sama seperti edit pengadaan (rollback-lalu-insert-ulang) —
1. Ambil transaksi lama.
2. Validasi input baru.
3. **Kembalikan (tambahkan) stok** sesuai jumlah pada detail lama.
4. Update header, hapus detail lama.
5. Untuk tiap baris baru: **cek ulang stok cukup atau tidak** (karena rollback di langkah 3 sudah menaikkan stok sementara). Jika tidak cukup → gagal, transaksi database di-rollback total (tidak ada perubahan tersimpan).
6. Jika cukup: insert detail baru, kurangi stok.
7. Commit jika semua sukses.

**Alur Hapus Pengeluaran** (hanya Admin Apotek): kembalikan stok sesuai detail transaksi → hapus detail → hapus header → commit. Efek pengeluaran dibatalkan sepenuhnya.

**Kwitansi**: Admin Apotek & Petugas Pelayanan bisa mencetak kwitansi per transaksi pengeluaran (tampilan cetak, bukan mengubah data).

### 7. Stock Opname (Penyesuaian Stok)

**Aktor**: Admin Apotek (atau petugas dengan permission `stok_opname`)

**Alur**:
1. Admin buka form opname → sistem tampilkan daftar obat beserta stok sistem saat ini.
2. Admin input tanggal opname, lalu untuk tiap obat yang diperiksa: isi hasil hitung stok fisik (hasil hitung manual di gudang) dan alasan (opsional, misalnya "obat rusak", "selisih hitung").
3. Submit → validasi field wajib.
4. Sistem cek tidak ada obat duplikat dalam satu sesi opname.
5. Untuk tiap baris: sistem ambil stok sistem saat ini, hitung **selisih = stok_fisik − stok_sistem** (positif = lebih, negatif = kurang), simpan sebagai record opname baru, lalu **stok sistem langsung ditimpa** menjadi sama dengan stok fisik yang diinput (bukan ditambah/dikurang, tapi *set* langsung).
6. Commit → catat histori jumlah item yang di-opname pada tanggal tsb.

**Riwayat opname** bisa difilter berdasarkan rentang tanggal.

### 8. Laporan

**Aktor**: Admin Apotek & Kepala Klinik (atau petugas dengan permission `laporan`)

Halaman laporan berupa **satu halaman dengan 3 tab** (Pengeluaran, Pengadaan, Stok), masing-masing punya filter tanggal sendiri:

- **Tab Pengeluaran**: filter rentang tanggal + kategori pasien (Umum/Mustahik/semua). Menampilkan daftar transaksi, total transaksi, total item keluar, total nilai, breakdown nilai & jumlah transaksi per kategori pasien, plus grafik (tren harian, proporsi kategori, top obat terlaris).
- **Tab Pengadaan**: filter rentang tanggal. Menampilkan daftar transaksi, total nilai pembelian, total item, grafik (tren harian, top PBF, top obat dibeli).
- **Tab Stok**: menampilkan seluruh obat dengan status stok (aman / menipis / habis — menipis jika stok ≤ isi_per_satuan, habis jika stok = 0) dan status kedaluwarsa (aman / segera kedaluwarsa dalam 3 bulan / sudah kadaluwarsa), total stok keseluruhan, jumlah jenis obat.
- **Export Excel**: masing-masing tab (pengadaan/pengeluaran/stok) punya tombol export ke `.xlsx` (via PhpSpreadsheet) sesuai filter yang aktif, plus opsi cetak (print-friendly view lewat template `print_header`/`print_footer`).

### 9. Histori Aktivitas

Semua aksi tambah/edit/hapus/login/logout/backup di seluruh modul di atas otomatis dicatat ke tabel `histori_aktivitas` (siapa, kapan, aksi apa, di modul apa, keterangan). Ditampilkan sebagai widget di Dashboard dan halaman histori tersendiri, hanya untuk role yang berwenang.

---

## Matriks Hak Akses per Use Case

Berdasarkan pemeriksaan `denyIfNotRole()` di tiap controller. "✅ (izin)" berarti role dasar tidak otomatis dapat akses, tapi bisa didapat lewat permission tambahan per-petugas.

| Use Case | Admin Apotek | Petugas Pelayanan | Kepala Klinik |
|---|---|---|---|
| Login / Logout | ✅ | ✅ | ✅ |
| Lihat data obat | ✅ | ✅ | ✅ |
| Tambah/Edit/Hapus obat | ✅ | ✅ (izin `obat_edit`) | ❌ |
| Lihat/CRUD petugas | ✅ | ✅ (izin `petugas_edit`, terbatas — lihat catatan modul 3) | ❌ |
| Nonaktifkan/Hapus petugas | ✅ (khusus admin murni) | ❌ | ❌ |
| Lihat/CRUD pemasok (PBF) | ✅ | ✅ (izin `pbf`) | ❌ (lihat saja) |
| Transaksi pengadaan (tambah/edit/hapus) | ✅ | ✅ (izin `pengadaan`) | ❌ |
| Transaksi pengeluaran — tambah, lihat | ✅ | ✅ | ❌ |
| Transaksi pengeluaran — edit, hapus | ✅ | ❌ | ❌ |
| Cetak kwitansi | ✅ | ✅ | ❌ |
| Stock opname | ✅ | ✅ (izin `stok_opname`) | ❌ |
| Lihat & export laporan | ✅ | ✅ (izin `laporan`) | ✅ |
| Backup database | ✅ | ✅ (izin `backup`) | ❌ |
| Lihat histori aktivitas | ✅ | tergantung implementasi | tergantung implementasi |

Catatan: kolom "✅ (izin `xxx`)" berarti petugas_pelayanan default **tidak** punya akses, tapi Admin Apotek bisa mengaktifkan izin tersebut secara individual lewat menu Data Petugas.

---

## Skenario Pengujian (Black Box) per Modul — bahan untuk BAB IV.5 Testing

| Modul | Skenario | Input | Hasil yang Diharapkan |
|---|---|---|---|
| Login | Login dengan akun valid & aktif | username/password benar | Masuk ke dashboard, histori "login" tercatat |
| Login | Login dengan password salah | password salah | Ditolak, pesan "Username atau password salah." |
| Login | Login dengan akun nonaktif | akun `is_aktif=0` | Ditolak, pesan akun dinonaktifkan |
| Login | Akses halaman tanpa login | buka URL langsung, mis. `/dashboard` | Redirect ke `/login` |
| Login | Idle > 30 menit | biarkan sesi diam 30+ menit lalu request | Session berakhir, redirect ke login |
| Obat | Tambah obat baru | data valid lengkap | Obat tersimpan + baris stok awal terbentuk |
| Obat | Hapus obat yang sudah pernah bertransaksi | id_obat yang ada di `detail_pengadaan`/`detail_pengeluaran` | Ditolak, pesan "sudah memiliki riwayat transaksi" |
| Pengadaan | Tambah pengadaan 1 obat | jumlah besar & harga valid | Detail tersimpan, stok bertambah sesuai konversi satuan |
| Pengadaan | Tambah pengadaan dengan obat duplikat dalam 1 transaksi | 2 baris obat sama | Ditolak, pesan "Terdapat obat yang sama" |
| Pengeluaran | Keluarkan obat dengan stok cukup | jumlah ≤ stok tersedia | Stok berkurang, detail tersimpan |
| Pengeluaran | Keluarkan obat dengan stok tidak cukup | jumlah > stok tersedia | Ditolak, pesan stok tidak mencukupi (menyebutkan nama obat & sisa stok) |
| Pengeluaran | Edit transaksi pengeluaran | ubah jumlah obat pada transaksi lama | Stok lama dikembalikan dulu, lalu dikurangi ulang sesuai data baru |
| Stock Opname | Input stok fisik berbeda dari stok sistem | stok_fisik ≠ stok_sistem | Selisih tercatat, stok sistem disesuaikan mengikuti stok fisik |
| Laporan | Filter laporan pengeluaran per kategori pasien | kategori = Mustahik | Hanya transaksi kategori Mustahik yang tampil |
| Laporan | Export laporan ke Excel | klik export pada tab aktif | File `.xlsx` terunduh sesuai filter aktif |
| Hak Akses | Petugas Pelayanan coba akses halaman Pengadaan | login sebagai petugas_pelayanan tanpa izin `pengadaan` | Ditolak, redirect ke dashboard dengan pesan akses ditolak |
