# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project

SIMKATMAWA is a Laravel 12 / PHP 8.2+ app for SKPI student achievement workflows: students submit activities/certificates, prodi/university verify, SKPI certificates are previewed/printed with QR authenticity checks, and selected achievement data syncs to Dikti/Kemdiktisaintek APIs.

## Commands

- Install PHP deps: `composer install`
- Install JS/PDF deps: `npm install` (Puppeteer is needed by Browsershot)
- Full dev stack: `composer dev` (runs `php artisan serve`, queue listener, `pail`, and Vite)
- Frontend only: `npm run dev`
- Production assets: `npm run build`
- Migrate app DB: `php artisan migrate`
- Tests: `composer test` or `php artisan test`
- Single test class: `php artisan test --filter=RekognisiTest`
- Single test method: `php artisan test --filter=test_admin_can_create_rekognisi`
- Format PHP: `vendor/bin/pint`
- Scheduled sync command: `php artisan sync:dekan` (also scheduled weekly in `routes/console.php`)

## Architecture

- Auth uses Laravel UI routes/controllers plus Neosisdam OAuth (`OauthController`). Roles/permissions use `spatie/laravel-permission`; impersonation uses `lab404/laravel-impersonate`. Only `developer` can impersonate; developers cannot be impersonated (`User::canImpersonate`, `User::canBeImpersonated`).
- Route-level authorization uses the `role`, `permission`, and `role_or_permission` middleware aliases (`bootstrap/app.php`); impersonation is gated by `ImpersonateMiddleware`.
- Main route surface lives in `routes/web.php`. Authenticated prefixes: `developer/`, `master/`, `verifikasi/`, `pencatatan-kegiatan-mahasiswa/`, `hasil-nilai-kegiatan-mahasiswa/`, `akses-sertifikat-skpi/`, `master-dekan/`, and `prestasi/`.
- Frontend is Blade + Bootstrap 5/Tailwind 4 bundled by Vite. Vite entries are `resources/sass/app.scss` and `resources/js/app.js`.
- PDFs/certificates use both Dompdf and Browsershot; QR generation uses `simplesoftwareio/simple-qrcode`.
- DataTables server-side responses use `yajra/laravel-datatables-oracle`.

## Databases

`config/database.php` defines three important MySQL connections. Models/controllers explicitly choose the external ones where needed.

- `mysql` (default app DB): users, permissions, master data owned by this app, SKPI access, Dikti credential/sync tables, `master_dekan`.
- `mysql_siak`: external academic/SIAK data such as mahasiswa, prodi, SKPI activity tables, academic year. Treat as external/read-mostly; no migrations here own that schema.
- `mysql_neosisdam`: external Neosisdam SSO/HR data. Treat as external/read-mostly; no migrations here own that schema.

`.env.example` has default app and SIAK keys, but `config/database.php` and `config/services.php` are authoritative for actual env names. Neosisdam DB keys (`NEOSISDAM_DB_*`) are used even if not listed in `.env.example`.

## External services

- `NEOSISDAM_*`: OAuth login and API config.
- `FILE_STORAGE_SERTIFIKAT_KEMAH_*`: certificate upload API used by `FileUploadService`.
- `DIKTI_PRESTASI_MANDIRI_*`: Dikti API base/path/timeout config. Current bearer token is read via `DiktiApiCredential::currentToken()` by `DiktiPrestasiMandiriClient`.

## Domain modules

- SKPI activity capture/reporting: `PencatatanKegiatanMhsController`, `HasilSkorKegiatanMahasiswaController`, `AksesSertifikatSkpiController`, `SertifyAuthenticityCheck`.
- Master data: `NamaPrestasi`, `TingkatKegiatan`, `KomponenKegiatan`, `Kegiatan`, `BobotKegiatan`, `Akreditasi`, `LevelPrestasi`, `JenisRekognisi`, `PeringkatPrestasi`, `SyaratNilaiBobot` controllers/models.
- Dikti sync: `RekognisiController`, `SertifikasiController` use payload builders + sync services + `DiktiPrestasiMandiriClient`. Related child rows use mahasiswa/dosen tables and ULID parent IDs for newer prestige records.
- Access/developer admin: `Developer/*` controllers manage users/roles/permissions/permission groups; `UserAksesProdiController` maps users to prodi.
- Dean signing data: `MasterDekanController` and `SyncDekan` command read external staff data and maintain active dekan records.

## Tests

`phpunit.xml` forces in-memory SQLite, array cache/session/mail, and sync queue. Existing feature tests build only the tables they need inside `setUpDatabase()` instead of relying on project migrations. Follow that pattern for tests touching DB-heavy/external-DB code, and fake HTTP with Laravel `Http::fake()` for Dikti/API flows.

## Conventions

- Keep Indonesian route names, DB columns, labels, and domain terms when extending existing flows.
- Many older controllers use `storeOrUpdate`, `edit`, `getData`, `update`, `destroy` instead of pure resource actions; newer Dikti modules use resource controllers plus custom `sync` routes.
- Observers are registered manually in `app/Providers/AppServiceProvider.php` (`KegiatanMahasiswaObserver`, `UserAksesProdiObserver`).
- Generated IDE helper files (`_ide_helper.php`, `_ide_helper_models.php`) should not be edited by hand.
- Migration timestamps in 2026 are intentional in this repository; do not renumber them only for chronology.
