# Changelog — LPK TIP v2

Format berdasarkan [Keep a Changelog](https://keepachangelog.com/id/1.1.0/).
Versi mengikuti **Calendar Versioning**: `YYYY.MM.DD-<inkremen>`.

---

## [2026.08.28-2] — Rekap Bagi Uang: Pindah ke Payroll & LPK + Versi Staff (print ala legacy)
**Tanggal:** 2026-08-28

### Changed
- **Menu Payroll & LPK** (bukan Laporan):
  - **Rekap Bagi Uang Staff** (`payroll.rekap-bagi-uang-staff`) — kolom NIK / Subject / **Uang Makan** (THP) / **Nota** (BKK ke karyawan, non gaji & UM `pay_type <> 9,10`) / **Total** (replikasi legacy `rptlpkbysubject`)
  - **Rekap Bagi Uang Tenaga Kerja** (`payroll.rekap-bagi-uang-tk`) — kolom NIK / Subject / **Upah** (THP) / Total (legacy `rptlpkbysubjectlabor`)
- Print A4 landscape meniru gaya legacy (PT. TROCON INDAH PERKASA, judul, PERIODE/PROYEK, tabel + TOTAL).
- Route/menu `laporan.rekap-bagi-uang` (sementara) dihapus; file laporan lama diganti `App\Livewire\Payroll\RekapBagiUang`.

---

## [2026.08.28-1] — Laporan Rekap Bagi Uang Tenaga Kerja (dari legacy rptlpkbysubjectlabor)
**Tanggal:** 2026-08-28

### Added
- **Menu Laporan → Rekap Bagi Uang Tenaga Kerja** (`/laporan/rekap-bagi-uang`): daftar Tenaga Kerja (staff_status 2) per periode (opsional per proyek) — kolom NIK / Subject / Proyek / Upah (THP dari `payroll_results`) + total & tombol Cetak.
- Cetak A4 landscape (`print/rekap-bagi-uang`) — replika legacy (header PT, periode, tabel NIK/Subject/Upah, TOTAL).
- Menu ditambahkan ke seeder & DB (lokal + produksi).

---

## [2026.08.27-15] — Perlakuan Tenaga Kerja vs Staff: Hari Libur/Minggu
**Tanggal:** 2026-08-27

### Changed
- **Tenaga Kerja (staff_status 2)**: hari libur nasional/Minggu kini diperlakukan **seperti hari biasa** — lembur = kelebihan jam dari normal shift, **bukan** seluruh jam (tidak ada lembur efektif), tarif 1×.
- **Staff (staff_status 1)**: tetap **seluruh jam** dihitung lembur (efektif) + tarif 2× di hari libur/Minggu.
- Operator **Bor Efektif**: tetap seluruh jam sejak clock-in (setiap hari) — tidak terpengaruh aturan ini.
- Berlaku di `OvertimeCalculator` (param `liburFull`), PayrollGenerate, Log Absensi, & print payroll detail.

### Test
- `ShiftTarifTest`: TK libur/Minggu → seperti hari biasa (0 / 3 jam); Staff libur/Minggu → 8 jam & 2×.
- `OvertimeCalculatorTest`: liburFull=false (TK) vs true (Staff); bor efektif tetap seluruh.

---

## [2026.08.27-14] — Cetak Upah Harian (TK): Kolom Insentif
**Tanggal:** 2026-08-27

### Added
- **Print payroll detail type 2 (Upah Harian/Tenaga Kerja)**: kolom **Insentif** kini tampil (sebelumnya hanya type 1 Staff yang punya) — nilai dari `payroll_results.incentive`, konsisten dgn generate (menu input Insentif).

### Test
- `PayrollDetailPrintTest`: type 2 menampilkan "Insentif".

---

## [2026.08.27-13] — Print Detail: Baris Absen Kosong Tidak Tampil Jam Shift Palsu
**Tanggal:** 2026-08-27

### Fixed
- **Print payroll detail**: baris absen KOSONG (hasil import Excel baris tanpa jam — `actual_in/out NULL`, contoh TIP/K-0174 A Farihin 30/08) sebelumnya menampilkan jam masuk **08:00** palsu karena `effectiveIn` di-fallback ke jam mulai shift saat tidak ada jam. Kini hanya menampilkan jam bila `actual_in`/`actual_out` benar-benar ada — baris kosong tampil "-" (tidak absen). Sama utk export Excel Log Absensi (Check In/Out LPK).

### Test
- `PayrollDetailPrintTest` +1: karyawan dgn 1 baris kosong → print tidak mengandung "08:00" palsu.

---

## [2026.08.27-12] — API History Absensi: Sertakan Upload Web + Accuracy Float (fix blank mobile)
**Tanggal:** 2026-08-27

### Fixed
- **`/attendance/history` & `/attendance/history/{date}` (mobile)**: absen yang di-upload dari web (import Excel, `attendance_type NULL` + `is_manual`) kini **disertakan** — sebelumnya difilter sehingga hari upload tidak tampil, dan saat dibuka detail kosong/blank. Baris manual dipecah menjadi record **masuk & keluar** utk ditampilkan mobile.
- **`accuracy` dikirim sebagai angka (float)** — kolom DECIMAL MySQL dibaca PDO sbg string, sehingga `(r['accuracy'] as num?)` di Flutter melempar TypeError → layar detail **blank abu-abu**. Kini di-cast `(float)`.
- `rekapAdminDetail` (admin) juga menyertakan absen upload web.

### Test
- `ApiAttendanceTest` +1: history menampilkan hari upload web (hadir) & historyDetail memecah manual jadi in/out; accuracy terkirim float (12.34), bukan string.

---

## [2026.08.27-11] — Mobile Absensi: Tombol Refresh Lokasi (lebih akurat)
**Tanggal:** 2026-08-27

### Added (Mobile `lpk-mobile`)
- **Layar Absensi**: ikon refresh lokasi (pojok kanan atas peta) utk mengambil ulang posisi GPS dgn akurasi tinggi — titik lokasi user diperbarui & akurasi (`±meter`) ditampilkan (hijau ≤ 50 m, kuning bila lebih). Spinner saat proses; jika izin lokasi ditolak → pesan ramah (bukan diam).

---

## [2026.08.27-10] — Import Absensi: Validasi Format Jam (bukan error aplikasi)
**Tanggal:** 2026-08-27

### Fixed
- **Import Absensi (Tenaga Kerja/Staff)**: baris dengan format jam tidak valid (mis. `22:` tanpa menit) TIDAK lagi melempar error aplikasi (`QueryException` `Incorrect time value`) — baris dicatat ke daftar error dengan pesan jelas (NIK, tanggal, baris, nilai jam, contoh format `HH:MM`) & dilewati; baris valid tetap diproses.

### Test
- `AttendancesRenderTest` +1: import dgn jam `22:` → pesan "Format jam tidak valid", tidak crash, baris valid tetap tersimpan & baris invalid di-skip.

---

## [2026.08.27-9] — Hari Minggu/Libur: Lembur Seluruh Jam untuk Semua Karyawan
**Tanggal:** 2026-08-27

### Fixed
- **Hari Minggu & libur nasional kini dihitung lembur SELURUH jam sejak masuk utk SEMUA karyawan** (bukan hanya bor efektif). Sebelumnya hanya jika kolom `is_holiday`=1 — absen mobile/manual baru sering tidak meng-set kolom tsb utk hari Minggu sehingga dihitung sebagai hari biasa. Kini libur dideteksi dari **tanggal** (Minggu / master hari libur) via `OvertimeCalculator::isHoliday()` — dipakai PayrollGenerate, Log Absensi, & print payroll detail.
- Konsisten dgn badge "Day Off" di Log Absensi.

### Test
- `OvertimeCalculatorTest` +1: isHoliday dari tanggal (Minggu / master / kolom).
- `ShiftTarifTest` +1: Minggu is_holiday=0, 08:00–16:00 → lembur 8 jam + tarif 2×.

---

## [2026.08.27-8] — Isoma Konsisten: Keluar Efektif ≥ 19:00 Dipotong 1 Jam
**Tanggal:** 2026-08-27

### Fixed
- **Isoma (istirahat 18–19) konsisten di semua menu**: `OvertimeCalculator` (PayrollGenerate, print payroll detail, Log Absensi) kini memotong 1 jam bila **keluar efektif ≥ 19:00** — sama dgn export Excel Log Absensi. Sebelumnya print/payroll memotong hanya bila keluar ≥ 20:00 sehingga 08:00–19:00 di print = 3 jam tapi di Log Absensi = 2. Kini keduanya **2 jam** (contoh TIP/O-1163 Ahmad Herdiana).

### Test
- `OvertimeCalculatorTest`: 08:00–19:00 → 2; 08:00–18:00 → 2 (tidak dipotong); 08:00–20:00 → 3.

---

## [2026.08.27-7] — Lembur Dihitung dari Jam Efektif LPK (masuk sebelum shift tidak dihitung)
**Tanggal:** 2026-08-27

### Fixed
- **Lembur kini dihitung dari JAM EFEKTIF LPK** (`OvertimeCalculator`): masuk efektif = jadwal shift bila absen lebih awal (datang 07:16, shift 08:00 → dihitung mulai 08:00), keluar efektif = pembulatan menit. Sebelumnya memakai jam mentah sehingga 07:16–15:50 (8,57 jam) dihitung 0,57 → 1 jam lembur padahal tampilan (Check In/Out LPK) menunjukkan 08:00–16:00 = 8 jam → 0. Kini tampilan & perhitungan konsisten.
- Konsisten dgn export Excel Log Absensi (Check In LPK / Check Out LPK) & PayrollGenerate.

### Test
- `OvertimeCalculatorTest` +1: 07:16–15:50, 07:20–16:09, dst dgn shift 08:00 → lembur 0; keluar 17:00 → 1.
- `ShiftTarifTest`, `ShiftFeatureTest`, `AbsensiLogEditTest`, `PayrollDetailPrintTest` disesuaikan ke aturan jam efektif.

---

## [2026.08.27-6] — Print Payroll Detail: Pilih Baris Absen yang Punya Jam (fix duplikat)
**Tanggal:** 2026-08-27

### Fixed
- **Print payroll detail**: per hari, route memilih baris `attendance_type=NULL` **pertama** — jika karyawan punya 2 baris (baris kosong `project_id NULL` + baris actual, contoh data migrasi TIP/K-0174 A Farihin), tampilan per tanggal jadi tidak konsisten (baris kosong terpilih → jam/lembur kosong). Kini memilih **baris yang punya nilai jam** (actual_in/actual_out), sehingga 08:00–16:00 tampil benar & lembur 0.

### Test
- `PayrollDetailPrintTest` +1: baris duplikat kosong tidak mengalahkan baris actual (masuk 08:00, keluar 17:00, lembur 1 tetap tampil).

---

## [2026.08.27-5] — Print Payroll Detail: Baris Bawah = Jam Lembur (konsisten legacy)
**Tanggal:** 2026-08-27

### Fixed
- **Print payroll detail (angka di bawah kolom Masuk/Keluar)**: kembali menampilkan **JAM LEMBUR** per hari (konsisten legacy `rptpayrolldetail` yang menampilkan `absence.workhour`) — dihitung dari `actual_in`/`out` memakai `OvertimeCalculator`; kolom Masuk/Keluar tetap jam efektif LPK (absen 07:00 shift 08:00 → tampil 08:00).
- Verifikasi: karyawan **Lembur Extra = No** (extraovertime=0, mis. TIP/K-0142 Philip Joshua N) dihitung **tidak efektif** (hanya jam di atas normal shift), bukan seluruh jam sejak masuk.

### Test
- `PayrollDetailPrintTest`: absen 07:00–18:00 shift pagi → masuk efektif 08:00 & lembur 3 (11−8), bukan 0.

---

## [2026.08.27-4] — Print Payroll Detail: Jam Efektif LPK (Check In/Out LPK)
**Tanggal:** 2026-08-27

### Fixed
- **Print payroll detail (kolom Masuk/Keluar & angka di bawahnya)**: kini memakai **jam efektif LPK** seperti export Excel Log Absensi — jika absen lebih awal dari shift (mis. shift pagi 08:00, absen 07:00) → dianggap masuk **08:00**; jam keluar dibulatkan (menit ≥ 30 naik). Angka di bawah jam = **jam kerja LPK** (keluar−masuk efektif, potong 1 jam isoma bila keluar ≥ 20:00).
- **`App\Support\AttendanceTime`** (baru): helper `effectiveIn` / `effectiveOut` / `workHours` — dipakai print payroll detail & export Excel Log Absensi (menghapus duplikasi `timeMax`/`roundTime` di AbsensiLog).

### Test
- `AttendanceTimeTest` (unit) +8: masuk efektif mengikuti shift, terlambat tetap asli, pembulatan keluar, jam kerja, isoma, break_1819.
- `PayrollDetailPrintTest`: seedData diberi shift pagi; test work kini memverifikasi absen 07:00 → tampil masuk 08:00 & jam kerja 10.

---

## [2026.08.27-3] — Print Payroll Detail: Jam Lembur Dihitung dari Jam Absen
**Tanggal:** 2026-08-27

### Fixed
- **Print payroll detail (matriks per tanggal)**: angka di bawah kolom Masuk/Keluar sebelumnya dibaca dari kolom `attendances.work_hour` (hanya terisi utk data migrasi legacy → 0 utk absen mobile/manual baru). Kini **dihitung** dari `actual_in`/`actual_out` memakai `OvertimeCalculator` — konsisten dgn PayrollGenerate & Log Absensi.

### Test
- `PayrollDetailPrintTest` +1: absen `work_hour=0` tapi 08:00–20:00 → matriks menampilkan lembur 3 jam (bukan 0).

---

## [2026.08.27-2] — Log Absensi: Kolom Jml Jam Lembur
**Tanggal:** 2026-08-27

### Added
- **Log Absensi: kolom "Jml Jam Lembur"** (konsisten dgn legacy `createtime_detail` / `absence.workhour`) — dihitung per hari dari `actual_in`/`actual_out` memakai aturan yg sama dgn Generate Payroll (normal shift, lintas malam +24, isoma hormati `break_1819`, bor efektif, proyek POOL ÷2, pembulatan menit).
- **`App\Support\OvertimeCalculator`** — satu sumber kebenaran hitung lembur harian, dipakai `PayrollGenerate` & `AbsensiLog` (sebelumnya logika inline di PayrollGenerate).

### Test
- `OvertimeCalculatorTest` (unit) +9: normal, isoma, pembulatan, libur, bor efektif, POOL ÷2, lintas malam, break_1819, stored overtime.
- `AbsensiLogEditTest`: cek kolom "Jml Jam Lembur" ter-render + nilai 2 jam (07:30-17:05).

---

## [2026.08.27-1] — Perhitungan Jam Lembur Legacy → V2
**Tanggal:** 2026-08-27

### Fixed
- **`total_day_job` (payroll_results)**: kini menyimpan **total JAM lembur** (`overtime_hours`) — sebelumnya terisi jumlah hari (`day_job`), tidak konsisten dgn legacy `totaldayjob = SUM(workhour)` & data migrasi.
- **`work_hour_addition` ikut `day_job`**: +0.5 hari utk absen libur produksi (legacy `dayjob = SUM(hari hadir) + SUM(workhouraddition)`).
- **Potongan istirahat utk lintas malam `in<18` (opsi (a), keputusan user)**: legacy memotong 2 jam istirahat (12–13 & 18–19) dgn normal 7 → setara **potong 1 jam dgn normal shift 8** agar hasil SAMA persis dgn legacy — contoh 08:00–02:00 = 9 jam lembur (sebelumnya 10), bor efektif = 17 (sebelumnya 18); hari libur tetap seluruh jam (tanpa potong).
- **Lintas tengah malam** (PayrollGenerate): durasi negatif (`actual_out < actual_in`, mis. shift malam 20:00–03:00) ditambah 24 jam — sebelumnya dihitung 0 sehingga shift malam tidak pernah menghasilkan lembur. Konsisten legacy `24 + durasi`.
- **Potongan isoma hormati `break_1819`**: jam istirahat 18:00–19:00 tidak dipotong bila `break_1819=1` (karyawan tidak istirahat → jam tsb dihitung kerja). Konsisten legacy `flag18 + break1819`.
- **Migrasi tarif lembur salah**: legacy `employee.extraovertime` (flag bor efektif 0/1) sempat dipetakan ke `overtime_rate` (tarif per jam) → 16 karyawan tarif 1 & 356 karyawan tarif 0. Kini `overtime_rate` diambil dari legacy `tunjlembur`, `is_bor_efektif` dari `extraovertime`; data lama diperbaiki via command baru `lpk:fix-overtime-data`.

### Added
- **Proyek POOL CIKARANG (id 6/24) + bor efektif → lembur ÷2** (konsisten legacy createtime `workhour/2`; data historis 13.402 absen proyek 24).

### Test
- `ShiftTarifTest` +6: lintas tengah malam (shift malam 20:00–03:00 & 20:00–05:00), lintas tengah malam bor efektif, `break_1819` tidak potong isoma, bor efektif proyek 24 ÷2, `work_hour_addition` ikut day_job, `total_day_job` = jam lembur.
- `PeriodeBadgeTest` diperbarui ke label MasterCrud `Aktif (Open)` / `Ditutup (Closed)`.
- `DashboardEnterpriseTest` D3: siapkan data BKK sendiri agar section "BKK PER PROYEK" selalu teruji (sebelumnya bergantung data global).
- `UploadFotoTest` dihapus (basi — method `openUpload` sudah dihapus dari `EmployeesIndex`; upload foto kini via `EmployeeForm`, sudah dicakup `EmployeeFormFotoTest`).

---
**Tanggal:** 2026-08-14 · **Status:** Deployed di `trocon-lpk.aplikasigo.com`

### Fixed
- **MasterCrud redirect**: `redirect($indexRoute)` → `redirectRoute()` (sebelumnya URL jadi `/master/shift/master.shift` → 404)
- **Absensi API `is_mocked`**: rule `nullable` + parse `boolean()` (mobile mengirim string `"false"` ditolak rule `boolean` lama)
- **QR print memory**: ganti ke `chillerlan/php-qrcode` (ringan) + cetak ikut filter & limit 200
- **Sidebar pagination 404**: `$routeName` private → public + `key()` dari `indexRoute`
- **Vite manifest**: `public/build` kini ikut repo (fix `ViteManifestNotFoundException`)
- **Composer PHP 8.3**: Symfony 8.1 (butuh PHP 8.4) → pin **Symfony 7.4** + `platform.php 8.3.0`
- **.htaccess**: versi aman (hapus paksa handler PHP yang memicu 403)
- **Sidebar**: href pakai `route()` asli (fix `/material/index`); aktif utk sub-halaman create/edit/show; rename grup "Data Induk" → "Master Data"

### Added (Web)
- **Fitur Shift**: master Shift Kerja (jam normal per shift), `shift_id` di karyawan, lembur dihitung berbasis shift (bukan tetap 8 jam)
- **Log Absensi**: riwayat check-in/out mobile & manual (thumbnail foto selfie + popup, link Google Maps per koordinat)
- **Master Lokasi Absensi**: CRUD lokasi + radius (anti fake location mobile)
- **Notifikasi (Fase N1)**: `Notifier`, type/link/icon, auto-notify 5 peristiwa, Topbar polling + mark-read, halaman enterprise
- **Dashboard enterprise**: KPI link+delta, ApexCharts 2 kolom (Aktivitas 7 hari + BKK per Proyek), tabel widget, aksi cepat
- **QR Code Material**: cetak label QR (per item / hasil filter), `QrCodeService`
- **Konfirmasi hapus modal** global (Alpine) + ikon SVG di semua tombol
- **Standar form**: full-width, label lengkap, combobox searchable di semua relasi

### Added (API v1 — Mobile)
- Sprint M0–M5: Auth (login username/NIK, Sanctum), Dashboard summary, Notifikasi, Material & stok (list/detail/movements), **Scan QR (`qr-check`)**, **Absensi geotagging** (config/check/history, anti fake location: mock/accuracy/radius/duplikat), rate limit & audit login

### Added (Mobile — Flutter `lpk-mobile`)
- Arsitektur: `ApiClient` (dio + token secure), design system TIP, bottom-nav 5 tab (custom), header logo
- Login full-screen biru (logo Trocon), splash icon Trocon, launcher icon Trocon
- Dashboard: header + aksi cepat + KPI klikable (gradient) + stok menipis
- Material: list enterprise (filter chips, avatar, indikator stok) + detail 3 tab (Info/Stok/Riwayat mutasi)
- Scan QR: overlay sudut + garis animasi + flash + **beep audio (audioplayers)** + getar
- Absensi: status card, **peta OpenStreetMap gratis** (flutter_map + radius), selfie, riwayat
- Izin lokasi saat buka aplikasi, konfirmasi back (PopScope), base URL produksi

---

## [2026.08.13-2] — Migrasi Data & Fase 4
**Tanggal:** 2026-08-13

### Added
- **Migrasi data legacy** `trocon_lpk` → `trocon_lpk_v2`: 62 tabel + 130 FK (master, purchasing, warehouse, transfer, solar, payroll, users 24/24)
- **Fase 4**: `TransactionIndex`/`TransactionCreate`/`TransactionShow` generik utk 13 modul transaksi (filter tanggal+status+relasi, export, pagination, create/show terpisah)
- PO dari PR, MT dari SPB, Opname — komponen khusus

### Fixed
- Named param `unitId` utk `StockService::movement`

---

## [2026.08.13-1] — Fase 1–3 (Refactor Tabel & Form)
**Tanggal:** 2026-08-13

### Added
- **Fase 1**: `InteractsWithTable`, `TableExporter`, `<x-table>`, `<x-table-pagination>`, `<x-table-loading>`, `<x-form-card>`; pilot Material
- **Fase 2**: `MasterCrud` generik (index/create/edit terpisah) utk 9 master
- **Fase 3**: sub-menu Material (jenis/satuan/tipe/grup/merk), User Type; `UsersIndex`/`UserForm`, `EmployeesIndex`/`EmployeeForm` (5 tab)

---

## [2026.08.12-0] — Fondasi (Sprint 0–9)
**Tanggal:** 2026-08-12

### Added
- Laravel 13 + Livewire 4 + Tailwind 4 + MySQL `trocon_lpk_v2`
- Auth kompatibel hash legacy (`LegacyCompatibleHasher`) + auto-rehash bcrypt
- RBAC (menu access), 9 utilitas, 11 master/relasi
- Material + upload Excel, Pembelian, Gudang (ledger stok `StockService`/`StockRebuilder`), Transfer + laporan, Solar, Payroll & LPK
- Login `admin/123456`

---

## Roadmap (yang direncanakan)
- [x] Fase 1–6 refactor tabel & form
- [x] Dashboard enterprise + ApexCharts
- [x] Notifikasi N1
- [x] Fitur Shift + lembur berbasis shift
- [x] API Mobile M0–M5 + aplikasi Flutter
- [ ] Notifikasi N2–N3 (real-time, push FCM)
- [ ] Otorisasi broadcast admin
- [ ] Keystore produksi & distribusi Play Store
