# CLAUDE.md — Manajemen Toko (CodeIgniter 4)

Aplikasi manajemen toko **CV Ikhtiar Sejahtera Bersama**. Login: `superadmin@gmail.com` / `admin`.
Stack: CodeIgniter 4.7, PHP 8.3, MySQL (`manajementoko_ci4`, collate `utf8mb4_general_ci`), SB Admin 2 (`public/assets/panel`), DataTables serverside via `hermawan/codeigniter4-datatables`, Chart.js 2.9, Select2, SweetAlert2 (`public/assets/vendor`).

## Aturan kerja (dari user)
1. Ikuti gaya kode user: penamaan file/function, struktur folder, gaya penulisan.
2. Kode ringkas supaya mudah di-maintain.
3. **Tanpa migration & seeder.** Perubahan DB langsung ke MySQL (`CREATE TABLE` / `ALTER TABLE` via Laragon/phpMyAdmin/HeidiSQL/CLI). CodeIgniter: **tidak pakai Model**, pakai query builder `$this->db->table()`.
4. **Tanpa foreign key.** Relasi (`customer_id`, `produk_id`, dst.) dilakukan di kode, bukan di SQL.
5. Jika tabel DB berubah, update daftar tabel di bawah dan beritahu user tabel apa saja yang berubah.
6. Collate MySQL: `utf8mb4_general_ci`.
7. Teliti & cek bug sebelum selesai; beri tahu user **estimasi waktu** pemeriksaan bug agar user tidak chat sebelum selesai.
8. Data bahan/dataset proyek diletakkan di folder `datasets/` (logo, daftar produk, daftar customer).

## Struktur
```
app/Controllers   MasterController (CRUD generik) -> Produk, Kategori, Supplier, Customer, Karyawan
                  TrxController (transaksi multi-baris) -> Order, Pengeluaran, Pemusnahan
                  Dashboard, Laporan, Profil, Auth
app/Libraries     Keuangan.php   (pendapatan, HPP, pengeluaran, laba, data grafik)
                  Pdf.php        (cetak PDF dompdf (kop opsional), `Pdf::stream($view, $data, $namaFile)`)
                  Excel.php      (export Excel PhpSpreadsheet: `(new Excel)->sheet(nama, judul, sub, cols, rows, opsi)->stream(file)`, multi-sheet, tema navy+emas)
app/Filters       Auth.php       (semua route wajib login kecuali /login)
app/Helpers       app_helper.php (rupiah, angka, tgl, periode_where, nomor_trx, produk_options, badge)
app/Views         auth/login (di luar panel)
app/Views/panel   pdf/ (layout kop + order|pengeluaran|pemusnahan|laporan), layouts/main, master/index (+_aksi), partials/filter, order|pengeluaran|pemusnahan|karyawan|laporan|dashboard|profil
URL               /login, /logout di luar panel; semua halaman setelah login di bawah /panel (panel, panel/produk, panel/order, dst.)
                  — route dalam `$routes->group('panel')`, `view('panel/...')`, `site_url('panel/...')`; JS memakai `BASE` = site_url('panel/')
public/assets     css/app.css (tema navy+emas), js/app.js (helper ajax, datatable, form multi-baris)
```

## Konvensi
- Tambah/edit master memakai **halaman terpisah** (`panel/{master}/tambah`, `panel/{master}/edit/{id}`, view `panel/master/form`), bukan modal.
- **Master baru**: buat controller `extends MasterController`, isi `$table, $title, $route, $icon, $fields, $columns`, daftarkan di array `foreach` pada `Routes.php`. Override `builder()` (join), `options()` (select), `beforeSave()` (validasi), `usedBy()` (cegah hapus jika dipakai), `tune()`/`extraAction()`.
- **Transaksi multi-baris** (order, pengeluaran, pemusnahan, form gaji karyawan): form `class="multi-form"` + `<template id="tpl">` dengan input `items[__i__][field]`; `addRow()`/`hitung()` ada di `app.js`. Server mengambil baris lewat `TrxController::rows()`.
- **DataTables serverside**: `DataTable::of($builder)->addNumbering('no')->format()/add()`; kolom HTML harus `->escape('kolom', false)`. Kolom agregat (SUM/GROUP BY) dibungkus derived table (lihat `Laporan::hpp`). Filter bulan/tahun lewat `periode_where($builder, 'kolom_tanggal')` (query string `tahun`, `bulan`; bulan 0 = semua).
- Aksi ajax mengembalikan `{ok, msg, redirect?}` lewat `BaseController::json()`. CSRF: `session`, `regenerate=false`, token dikirim header `X-CSRF-TOKEN` (meta `csrf` di layout).
- **Cetak = PDF dompdf** (inline/stream, bukan `window.print()`): route `{order|pengeluaran|pemusnahan}/pdf/{id}` dan `laporan/pdf?tahun&bulan`; view di `panel/pdf/` (layout.php berisi kop; default tanpa kop; kop bisa dinyalakan dengan data `'kop' => true`). Dokumen cetak baru: tambah view `panel/pdf/{nama}` + `Pdf::stream()` di controller.
- **Export Excel** (PhpSpreadsheet, `app/Libraries/Excel.php`): master `{master}/excel` (generik di MasterController, kolom lewat `$export`/`$columns`), transaksi `{order|pengeluaran|pemusnahan}/excel?tahun&bulan[&jenis]` (list) + `/excel/{id}` (detail), `karyawan/excel/{id}` (riwayat gaji), `laporan/excel` (4 sheet). Tombol hijau "Excel" di tiap halaman.
- Semua nama route/URL/menu memakai bahasa Indonesia.

## Aturan bisnis
- **Pembelian** menambah stok dan menghitung ulang `produk.hpp` (rata-rata tertimbang). Pembelian hanya bisa dihapus jika stok hasil pembelian masih utuh.
- **Order** mengurangi stok, menyimpan snapshot HPP per item (`order_items.hpp`). Hapus order mengembalikan stok. Stok tidak boleh minus.
- **Pemusnahan** mengurangi stok; kerugian = qty × HPP saat itu. Hapus mengembalikan stok.
- **Laporan**: Laba Kotor = Pendapatan − HPP. Laba Bersih = Laba Kotor − Penggajian − Operasional − Kerugian pemusnahan. Pembelian **tidak** dikurangkan langsung (sudah masuk HPP saat terjual); tetap tampil di total pengeluaran.
- Nomor transaksi: `INV-YYYYMMDD-001` (order), `PEM-` (pembelian), `PEN-` (penggajian), `OPE-` (operasional), `MSN-` (pemusnahan), via `nomor_trx()`.

## Tabel database (tanpa FK)
| Tabel | Kolom utama |
|---|---|
| `users` | id, nama, email, password(hash), foto, created_at |
| `kategori` | id, nama |
| `produk` | id, kode, nama, kategori_id, satuan, hpp, harga_jual, stok, stok_minimum, created_at |
| `supplier` | id, nama, telepon, alamat |
| `customer` | id, nama, telepon, alamat |
| `karyawan` | id, nama, jabatan, telepon, alamat, gaji_pokok |
| `orders` | id, no_invoice, tanggal, customer_id, subtotal, diskon, total, total_hpp, status_bayar(lunas/belum), catatan, created_at |
| `order_items` | id, order_id, produk_id, qty, harga, hpp, subtotal |
| `pengeluaran` | id, no_transaksi, tanggal, jenis(pembelian/penggajian/operasional), supplier_id, periode(YYYY-MM), total, catatan, created_at |
| `pengeluaran_items` | id, pengeluaran_id, produk_id, karyawan_id, keterangan, qty, harga, subtotal |
| `pemusnahan` | id, no_transaksi, tanggal, total_kerugian, catatan, created_at |
| `pemusnahan_items` | id, pemusnahan_id, produk_id, qty, hpp, subtotal, alasan |

Data awal (langsung ke MySQL): 1 user superadmin, 7 kategori, 52 produk (kode SKU dari user), 49 customer (master data user). Supplier & karyawan kosong — isi dari menu.
