# FASE 1 — ANALISIS WORKBOOK `AR RPP 2026.xlsx`

Dokumen ini adalah hasil pemeriksaan **seluruh 8 worksheet** pada berkas yang dilampirkan,
dilakukan pada level *formula* (bukan hanya nilai tampil), sehingga kerusakan rumus yang
selama ini tersembunyi di balik angka yang terlihat "normal" dapat diidentifikasi.

Tanggal analisis: 27 Juli 2026
Berkas: `AR RPP 2026.xlsx` (931 KB)

---

## 1.1 Inventaris Worksheet

| # | Nama Sheet | Baris Terisi | Jumlah Kolom | Rentang | Peran |
|---|------------|--------------|--------------|---------|-------|
| 1 | `JANUARI` | 483 | 36 (A–AJ) | A1:AJ484 | Buku piutang bulan Januari 2026 |
| 2 | `FEBRUARI` | 488 | 36 (A–AJ) | A1:AJ489 | Buku piutang bulan Februari 2026 |
| 3 | `MARET` | 474 | 36 (A–AJ) | A1:AJ475 | Buku piutang bulan Maret 2026 |
| 4 | `APRIL` | 481 | 36 (A–AJ) | A1:AJ482 | Buku piutang bulan April 2026 |
| 5 | `MEI` | 481 | 36 (A–AJ) | A1:AJ482 | Buku piutang bulan Mei 2026 |
| 6 | `JUNI` | 525 | 34 (A–AH) | A1:AH526 | Buku piutang bulan Juni 2026 |
| 7 | `JULI` | 143 | 34 (A–AH) | A1:AH1003 | Buku piutang bulan Juli 2026 (berjalan) |
| 8 | `Copy of KOLOM RF` | 30 | 7 (A–G) | A1:AB1000 | Register revisi/pembatalan invoice (RF) |
| | **TOTAL BARIS TRANSAKSI** | **3.075** | | | |

**Catatan struktural penting**

1. **Sheet bulanan bukan kumulatif.** Dari 3.075 baris, terdapat 3.073 nomor invoice unik.
   Artinya setiap invoice hanya muncul satu kali (bukan disalin ulang setiap bulan). Sheet
   bulanan = *invoice yang diterbitkan pada bulan tersebut*, bukan *snapshot saldo piutang
   per akhir bulan*. Implikasi: saldo AR akhir bulan tidak pernah bisa dibaca langsung dari
   satu sheet — harus menjumlahkan sisa saldo lintas seluruh sheet. Inilah alasan utama
   ACP tidak dapat dihitung akurat di Excel.
2. **Skema kolom tidak identik.** Sheet Januari–Mei memiliki kolom sisa `AI = "Column 1"`
   (artefak Excel Table), sheet Juni–Juli tidak. Sistem harus memetakan kolom **berdasarkan
   nama header**, bukan posisi.
3. **Sheet `JULI` memiliki 1.003 baris terformat** namun hanya 143 baris berisi data;
   861 baris sisanya adalah baris kosong yang sudah dipasangi rumus (`R`, `W`, `Y`, `AA`,
   `AB`, `AC`–`AF` terisi rumus sampai baris 1003). Importer harus mendeteksi *baris kosong
   berisi rumus* dan tidak menganggapnya sebagai data.
4. Tidak ada *merged cell* pada seluruh sheet — struktur tabel bersih dan dapat diimpor per baris.
5. `Copy of KOLOM RF` memiliki `max_col = 28` (AB) walau hanya A–G terisi; baris 31 kosong
   dan baris 32 berisi sampah (`E32 = "  "`, dua spasi). Register RF juga memuat data
   **tahun 2023–2025**, bukan hanya 2026.

---

## 1.2 Struktur Kolom Sheet Bulanan (Header Baris 1)

| Kol | Header Excel | Isi Aktual | Tipe Data Terdeteksi |
|-----|--------------|-----------|----------------------|
| A | `AREA` | JKT, BALI, BDG, RDN JKT, RDN BALI, RDN BDG | teks, 6 nilai |
| B | `JOBS` | Kategori pekerjaan | teks, 13 nilai |
| C | `NO. SO` | Nomor Sales Order (SA/SB/SL/…) | teks |
| D | `TGL INVOICE` | Tanggal invoice | datetime (3.073), kosong (2) |
| E | `SALES` | Nama sales / CSR / OFFICE | teks, 21 nilai |
| F | `NO INVOICE` | Nomor invoice | teks, 3.073 unik |
| G | `NO FAKTUR PAJAK` | 16–17 digit faktur pajak | **teks** (bukan angka) |
| H | `CUSTOMER` | Nama customer | teks, 1.218 unik |
| I | `OUTLET` | Nama outlet/cabang customer | teks, 1.276 unik |
| J | `NOMINAL` | Nilai invoice bruto (termasuk PPN) | int (3.039), float (35), kosong (1) |
| K | `PMSC/BY CASE` | Kategori pendapatan | teks, 12 nilai (banyak typo) |
| L | `STATUS` | **Jenis pekerjaan**, bukan status | teks, 68 nilai (banyak typo) |
| M | `CATATAN` | Catatan bebas | teks |
| N | `TGL WR` | Tanggal Work Request | datetime (1.382), kosong (1.691), spasi (2) |
| O | `TGL PRINT` | Tanggal cetak invoice | hampir kosong (7 terisi) |
| P | `TGL. INPUT` | Tanggal input | hampir kosong (9 terisi) |
| Q | `TGL. KIRIM` | Tanggal kirim invoice ke customer | datetime (2.953), kosong (122) |
| R | `JT TEMPO` | Jatuh tempo | **rumus** `=Q{n}+30` |
| S | `SEND / NOTES TO` | Kanal/PIC pengiriman | teks, 194 nilai |
| T | `POT PPH 23` | Potongan PPh 23 | rumus `=J/1.11*0.02` atau angka manual |
| U | `POT ADM` | Potongan administrasi | angka manual (90 baris) |
| V | `POT LAIN LAIN` | Potongan lain-lain | rumus `=J/1.11*0.03` atau manual (20 baris) |
| W | `TOTAL` | Total tagihan | **rumus** `=J-T-U-V` |
| X | `PEMBAYARAN` | Pembayaran diterima | angka manual (2.282 baris) |
| Y | `SALDO` | Sisa saldo | **rumus** `=W-X` |
| Z | `TGL BAYAR` | Tanggal bayar | datetime (2.282), kosong (793) |
| AA | `PAYMENT` | Status pembayaran | **rumus** IF berlapis |
| AB | `UMUR PIUTANG` | Umur piutang (hari) | **rumus** IF berlapis |
| AC | `0-45` | Bucket aging 0–45 hari | **rumus** `=IF(AB<=45,J,0)` |
| AD | `45-90` | Bucket aging 46–90 hari | **rumus — RUSAK, lihat §1.4** |
| AE | `90-120` | Bucket aging 91–120 hari | **rumus — RUSAK, lihat §1.4** |
| AF | `>120` | Bucket aging >120 hari | rumus `=IF(AB>=121,J,0)` |
| AG | `TOTAL REVENUE` | — | **kosong seluruhnya** |
| AH | `NET` | — | **kosong seluruhnya** |
| AI | `Column 1` | — | artefak Excel Table (Jan–Mei saja) |

### Rumus asli yang ditemukan (Januari, mewakili semua sheet)

```
R{n}  = Q{n}+30                                    -- jatuh tempo = tgl kirim + 30 hari
T{n}  = J{n}/1.11*0.02                             -- PPh 23 = 2% × DPP (DPP = bruto/1,11)
V{n}  = J{n}/1.11*0.03                             -- potongan lain = 3% × DPP
W{n}  = J{n}-T{n}-U{n}-V{n}                        -- total tagihan
Y{n}  = W{n}-X{n}                                  -- saldo
AA{n} = IF(Y=0,"LUNAS",IF(Z>0,"LUNAS",IF(R<TODAY(),"OUTSTAND","BELUM JATUH TEMPO")))
AB{n} = IF(AA="LUNAS",Z-R,IF(AA="OUTSTAND",TODAY()-R,IF(AA="BELUM JATUH TEMPO",TODAY()-R,0)))
AC{n} = IF(AB<=45,J,0)
AD{n} = IF(AND(AB>=46,AB<=90),I,0)                 -- ⚠ I = OUTLET (TEKS!)
AE{n} = IF(AND(AB>=91,AB<=120),I,0)                -- ⚠ I = OUTLET (TEKS!)
AF{n} = IF(AB>=121,J,0)
```

Kesimpulan penting dari pembacaan rumus:

- **PPN 11%** diasumsikan sudah termasuk di `NOMINAL`; DPP = `NOMINAL / 1,11`.
- **PPh 23 = 2% × DPP**, sesuai tarif jasa PPh 23 Indonesia.
- **Jatuh tempo dihitung dari TANGGAL KIRIM**, bukan tanggal invoice. Termin 30 hari.
- **`UMUR PIUTANG` bukan umur piutang** dalam pengertian akuntansi. Rumusnya adalah
  *selisih terhadap jatuh tempo* (`hari keterlambatan`). Untuk invoice lunas nilainya bisa
  **negatif** (bayar lebih cepat dari jatuh tempo). Ini bercampur dua metrik berbeda dalam
  satu kolom, dan menjadi dasar bucket aging — sehingga bucket aging pun tercampur antara
  invoice yang sudah lunas dan yang belum.

---

## 1.3 Temuan Kualitas Data — Ringkasan

| Kode | Temuan | Jumlah | Dampak Finansial | Tingkat |
|------|--------|--------|------------------|---------|
| DQ-01 | Rumus aging `AD`/`AE` menunjuk kolom **I (OUTLET = teks)** alih-alih **J (NOMINAL)** | 3.446 sel rumus | Rp 306.751.504 AR hilang dari laporan aging | 🔴 Kritis |
| DQ-02 | Revenue **dobel hitung** akibat RF (invoice lama & pengganti dua-duanya aktif) | 4 invoice | Rp 152.794.500 | 🔴 Kritis |
| DQ-03 | Status `LUNAS` padahal saldo masih > 0 (rumus `IF(Z>0,"LUNAS")` mengabaikan saldo) | 4 invoice | Rp 9.557.300 piutang tersembunyi | 🔴 Kritis |
| DQ-04 | `#REF!` di dalam rumus aging (kolom terhapus) | 17 sel | bucket aging = error | 🟠 Tinggi |
| DQ-05 | `#VALUE!` pada `UMUR PIUTANG` | 1 baris (MARET!r273) | umur & 2 bucket error | 🟠 Tinggi |
| DQ-06 | Rumus salah baris (off-by-row): `W55` pakai `T55`, `R441` di baris 442, `J86`, `I211`, `I349`, `R170/R171` | 11 sel | perhitungan silang baris | 🟠 Tinggi |
| DQ-07 | Saldo negatif (kelebihan bayar) tidak ditangani | 5 invoice | overpayment tidak terlacak | 🟠 Tinggi |
| DQ-08 | Pembayaran > total tagihan | 4 invoice | — | 🟠 Tinggi |
| DQ-09 | Nomor invoice duplikat lintas sheet | 2 (`FB/26/0203`, `FB/26/0286`) | Rp 1.589.130 dobel | 🟠 Tinggi |
| DQ-10 | Nomor faktur pajak duplikat | 1 (`04012600205018810`) | risiko pajak | 🟠 Tinggi |
| DQ-11 | `TGL. KIRIM` kosong → jatuh tempo = `0+30` (30-01-1900) | 122 baris | aging ekstrem/salah | 🟠 Tinggi |
| DQ-12 | `TGL. KIRIM` lebih awal dari `TGL INVOICE` | 4 baris | jatuh tempo mendahului invoice | 🟡 Sedang |
| DQ-13 | Selisih `TGL. KIRIM` − `TGL INVOICE` > 90 hari | 9 baris | jatuh tempo terlalu jauh | 🟡 Sedang |
| DQ-14 | `NO FAKTUR PAJAK` kosong | 18 baris | kepatuhan pajak | 🟡 Sedang |
| DQ-15 | `TGL INVOICE` kosong | 2 baris | tidak bisa dibukukan | 🟡 Sedang |
| DQ-16 | `NOMINAL` kosong | 1 baris | invoice tanpa nilai | 🟡 Sedang |
| DQ-17 | Nama customer dengan spasi awal/akhir → pecah master data | 357 baris | 1.218 "customer" padahal riil lebih sedikit | 🟡 Sedang |
| DQ-18 | Nama outlet dengan spasi awal/akhir | 838 baris | 1.276 "outlet" | 🟡 Sedang |
| DQ-19 | Typo kategori: `RESINDESIAL` (60×) vs `RESIDENSIAL`, `ROYALTI` vs `ROYALTY`, `BY CASE` vs `BYCASE` | 65+ baris | grouping laporan pecah | 🟡 Sedang |
| DQ-20 | Typo jenis pekerjaan: 68 nilai berbeda untuk ±25 jenis riil (`DRAIN CLEANING - MAIN LINE` vs `DRAIN CLEANING -MAIN LINE`, `INDUSTRIAL EQUIPEMENT`/`EQUIPEMNT`/`EWUIPMENT`/`INDUSRIAL`/`INDUSTRIAL EQUIPMENT`, `KICTHEN` vs `KITCHEN`) | 500+ baris | laporan per jenis pekerjaan tidak dapat dipercaya | 🟡 Sedang |
| DQ-21 | `PMSC/BY CASE` kosong | 279 baris | kategori pendapatan tak terisi | 🟡 Sedang |
| DQ-22 | Tarif PPh 23 tidak konsisten: 2% (752×), 4% (9×), **15% (5×)**, dan 8 nilai pecahan (0,55% / 0,76% / 1,47% / 1,51% / 1,58% / 1,83% / 2,22% / 2,93% / 3,09%) | 774 baris | potongan pajak tidak standar | 🟡 Sedang |
| DQ-23 | Kolom `TGL WR`, `JT TEMPO` berisi teks spasi `" "` | 3 sel | tipe data campur | 🟡 Sedang |
| DQ-24 | `TGL RF` & `TGL INVOICE LAMA` bercampur: datetime, teks `dd-mm-yyyy`, teks `dd-mm-yy`, teks `/31-05-26`, bahkan **angka `2025`** | 19 dari 30 baris | tanggal RF tidak bisa diurut | 🟠 Tinggi |
| DQ-25 | Nomor invoice RF terduplikasi di dalam sel: `FA/25/2567FA/25/2567` | 1 baris | tidak match ke invoice | 🟠 Tinggi |
| DQ-26 | Kolom `NO INVOICE YANG BARU` berisi teks status `INVOICE BATAL`, bukan nomor invoice | 2 baris | struktur data tercampur | 🟡 Sedang |
| DQ-27 | `KETERANGAN` RF berisi nomor RF sendiri (`RF/25/0033`) alih-alih alasan | 1 baris | alasan revisi hilang | 🟢 Rendah |
| DQ-28 | `TGL RF` = 30-09-**2026** untuk RF nomor seri 2025 (`RF/25/0017`, `RF/25/0018`) | 2 baris | tahun salah ketik | 🟡 Sedang |
| DQ-29 | Kolom `TOTAL REVENUE` (AG) & `NET` (AH) kosong 100% di seluruh 7 sheet | 3.075 baris | KPI revenue tidak pernah terhitung | 🟡 Sedang |
| DQ-30 | Baris kosong berisi rumus di `JULI` (baris 145–1003) | 861 baris | risiko impor baris hantu | 🟡 Sedang |
| DQ-31 | 12 nomor invoice tidak mengikuti pola `XX/YY/NNNN` (`KWI 25…`, `KWIT 73…`) | 2 baris | penomoran tidak standar | 🟢 Rendah |
| DQ-32 | Faktur pajak panjang tidak standar: 34 karakter (1×), 7 karakter (1×), `PROB…` (1×), kosong-string (1×) | 4 baris | format faktur pajak invalid | 🟡 Sedang |
| DQ-33 | Baris sampah di RF (`E32 = "  "`) | 1 baris | — | 🟢 Rendah |

### DQ-01 — Detail kerusakan bucket aging (temuan paling material)

Distribusi rumus per sheet pada kolom `AD (45-90)` dan `AE (90-120)`:

| Sheet | `AD` benar (`J`) | `AD` rusak (`I`/`H`/`#REF!`) | `AE` benar (`J`) | `AE` rusak |
|-------|------------------|------------------------------|------------------|------------|
| JANUARI | 87 | **396** | 51 | **432** |
| FEBRUARI | 105 | **383** | 64 | **424** |
| MARET | 84 | **390** | 84 | **390** |
| APRIL | 105 | **376** | 105 | **376** |
| MEI | 472 | 9 | 472 | 9 |
| JUNI | 0 | **525** | 0 | **525** |
| JULI | 0 | **503** | 0 | **503** |

Karena kolom `I` berisi **nama outlet (teks)**, rumus `IF(AND(AB>=46,AB<=90),I,0)` akan
mengembalikan nama outlet ketika invoice benar-benar berumur 46–90 hari. Excel tidak
menampilkan error — ia menampilkan teks. Ketika bucket dijumlahkan dengan `SUM()`, teks
dihitung sebagai **nol**.

Akibatnya: **59 invoice yang saat ini berumur 46–120 hari dengan nilai Rp 306.751.504
tidak muncul di bucket aging manapun.** Contoh yang terbukti:

```
JANUARI!AD66  → "ABUNAWAS RESTAURANT"
JANUARI!AD166 → "COACH GRAND INDONESIA"
JANUARI!AE251 → "BAKMI ASIU - KELAPA GADING"
JANUARI!AE473 → "RENAISSANCE BALI HOTEL"
JANUARI!AE476 → "DOUBLE SIX LUXURY HOTEL"
```

Ini persis kondisi "aging values using outlet names instead of invoice nominal values"
yang Anda curigai — terkonfirmasi, dan volumenya besar.

### DQ-02 — Detail revenue dobel hitung akibat RF

| No RF | Invoice Lama | Nominal Lama | Invoice Baru | Nominal Baru | Status Keduanya |
|-------|--------------|--------------|--------------|--------------|-----------------|
| RF/26/0002 | `FA/26/0002` (JANUARI r479) | 51.060.000 | `FA/26/0643` (FEBRUARI r278) | 127.650.000 | dua-duanya **aktif & LUNAS** |
| RF/26/0003 | `FA/26/0175` (JANUARI r480) | 76.590.000 | `FA/26/0643` (sama) | 127.650.000 | dua-duanya **aktif & LUNAS** |
| RF/26/0034 | `FA/26/0925` (MARET r437) | 3.000.000 | `FA/26/1480` (MEI r229) | 3.000.000 | dua-duanya **aktif & LUNAS** |
| RF/26/0035 | `FA/26/0635` (FEBRUARI r320) | 22.144.500 | `FA/26/1775` (JUNI r516) | 22.144.500 | dua-duanya **aktif & LUNAS** |

`FA/26/0002` (51.060.000) + `FA/26/0175` (76.590.000) = 127.650.000 = **nominal `FA/26/0643`**.
Jadi dua invoice DP digabung menjadi satu invoice pelunasan, tetapi **ketiganya tetap
tercatat sebagai pendapatan**. Total pendapatan yang tercatat dua kali:

```
51.060.000 + 76.590.000 + 3.000.000 + 22.144.500 = Rp 152.794.500
```

26 baris RF sisanya merujuk invoice tahun 2023–2025 yang tidak ada di workbook ini
(perlu diimpor terpisah atau ditandai sebagai `invoice_lama_tidak_ditemukan`).

### DQ-03 — Status LUNAS padahal saldo > 0

| Sheet | Invoice | Customer | Total | Dibayar | Saldo | Tgl Bayar |
|-------|---------|----------|-------|---------|-------|-----------|
| MARET r5 | `FA/26/0806` | HAI DI LAO MALL PIK AVENUE | 12.000.000 | 6.000.000 | **6.000.000** | 03-03-2026 |
| JUNI r368 | `FA/26/1838` | NEW DIZZI | 1.947.400 | 990.100 | **957.300** | 27-06-2026 |
| JUNI r397 | `FA/26/1742` | WALLACE GROUP INDONESIA | 2.500.000 | 1.250.000 | **1.250.000** | 01-06-2026 |
| JULI r109 | `FA/26/2057` | SPPG PANCORAN JAKARTA SELATAN | 2.700.000 | 1.350.000 | **1.350.000** | 13-07-2026 |

Penyebabnya cabang kedua rumus: `IF(Z>0,"LUNAS", …)` — begitu ada **tanggal bayar**,
status langsung `LUNAS` tanpa memeriksa saldo. Ini adalah kasus **pembayaran sebagian
(DP 50%)** yang salah diklasifikasi sebagai lunas. Rp 9.557.300 piutang menghilang dari
radar penagihan.

---

## 1.4 Rekapitulasi Finansial (nilai *cached* Excel apa adanya)

| Sheet | Baris | Nominal Bruto | PPh 23 | Pot. Adm | Pot. Lain | Total Tagihan | Pembayaran | Saldo |
|-------|------:|--------------:|-------:|---------:|----------:|--------------:|-----------:|------:|
| JANUARI | 483 | 1.299.197.872 | 17.539.547 | 45.900 | 127.824.054 | 1.154.408.666 | 1.085.410.734 | 68.997.932 |
| FEBRUARI | 488 | 1.296.694.559 | 19.408.151 | 12.722.200 | 22.318.554 | 1.242.245.654 | 1.104.760.527 | 137.485.127 |
| MARET | 474 | 1.167.687.437 | 15.695.640 | 564.958 | 3.174.054 | 1.148.252.785 | 1.015.924.586 | 132.328.200 |
| APRIL | 481 | 1.226.725.486 | 15.182.392 | 53.000 | 174.054 | 1.211.316.040 | 986.500.142 | 224.815.898 |
| MEI | 481 | 1.607.319.001 | 21.687.627 | 539.700 | 3.214.595 | 1.581.877.080 | 1.253.545.029 | 328.332.051 |
| JUNI | 525 | 1.586.947.018 | 4.010.111 | 44.240 | 1.576.554 | 1.581.316.112 | 526.573.868 | 1.054.742.245 |
| JULI | 143 | 766.782.597 | 4.332.600 | 0 | 0 | 762.449.997 | 43.033.850 | 719.416.147 |
| **TOTAL** | **3.075** | **8.951.353.970** | **97.856.068** | **13.969.998** | **158.281.865** | **8.681.866.334** | **6.015.748.734** | **2.666.117.599** |

> ⚠️ Angka `Saldo Rp 2.666.117.599` di atas **belum dikoreksi**. Setelah koreksi DQ-02
> (revenue dobel hitung Rp 152.794.500) dan DQ-09 (invoice duplikat Rp 1.589.130),
> saldo piutang riil akan berbeda. Sistem melakukan perhitungan ulang dari data sumber
> dan menampilkan kedua angka (sebelum & sesudah koreksi) di **Laporan Kualitas Data**.

Anomali yang langsung terlihat dari rekap ini:

- `Pot. Lain` Januari = **Rp 127.824.054** (9,8% dari nominal) sedangkan bulan lain rata-rata
  di bawah Rp 4 juta. Ini bukan potongan 3% biasa — ada input manual besar yang perlu
  ditelusuri. Sistem menandainya sebagai anomali potongan.
- `Pot. Adm` Februari = **Rp 12.722.200** vs bulan lain di bawah Rp 600 ribu.
- `PPh 23` Juni hanya Rp 4.010.111 dari nominal Rp 1,58 miliar (0,25%), sedangkan Mei
  Rp 21.687.627 (1,35%). Menandakan **pengisian PPh 23 belum selesai** untuk Juni–Juli.

---

## 1.5 Master Data yang Dapat Diekstrak

### AREA (6 nilai mentah → 3 area × 2 unit bisnis)

| Nilai Excel | Baris | → Area | → Unit Bisnis |
|-------------|------:|--------|---------------|
| `JKT` | 1.838 | JAKARTA | KOMERSIAL |
| `BALI` | 536 | BALI | KOMERSIAL |
| `RDN JKT` | 508 | JAKARTA | RESIDENSIAL |
| `BDG` | 106 | BANDUNG | KOMERSIAL |
| `RDN BALI` | 58 | BALI | RESIDENSIAL |
| `RDN BDG` | 29 | BANDUNG | RESIDENSIAL |

### JOBS — Kategori Pekerjaan (13 nilai)

`SEWER & DRAIN` (2.056) · `DRAIN CLEANER PRODUCT` (382) · `CLEANING DUCTING` (212) ·
`CAMERA` (151) · `MOBILISASI` (111) · `PLUMBING` (60) · `OTHERS` (56) · `PUMPING` (23) ·
`ROYALTY` (15) · `SPARE PART` (4) · `LEAK DETECTOR` (3) · `CLEANING GROUND WATER TANK` (1) ·
*(kosong)* (1)

### SALES (21 nilai)

`CSR` (510) · `OFFICE` (504) · `CHRIXANTUS` (389) · `FELICIA` (370) · `NIXON` (366) ·
`STEVE` (302) · `BALI` (157) · `SURYA` (127) · `RADITYA` (95) · `NURTIAWATI` (55) ·
`ARI` (43) · `YULIANTO` (35) · `IMAM` (29) · `JANSEN` (22) · `MARK` (15) · `DONNI` (14) ·
`ERLANGGA` (12) · `YUDI` (9) · `WAHYU` (8) · `ANDRI` (8) · `RASTI` (5)

Catatan: `CSR`, `OFFICE`, `BALI` bukan nama orang → dikategorikan sebagai tipe
`KANAL/UNIT` di master salesperson, bukan `INDIVIDU`.

### PMSC/BY CASE → Kategori Pendapatan (12 mentah → 7 kanonik)

| Mentah | Baris | → Kanonik |
|--------|------:|-----------|
| `PMSC` | 1.748 | PMSC |
| `BYCASE`, `BY CASE` | 531 + 1 | BY CASE |
| `RESIDENSIAL`, `RESINDESIAL` | 436 + 60 | RESIDENSIAL |
| *(kosong)* | 279 | TIDAK DIKATEGORIKAN |
| `ONLINE`, `PENJUALAN ONLINE` | 10 + 2 | ONLINE |
| `ROYALTY`, `ROYALTI` | 3 + 3 | ROYALTY |
| `HE` | 1 | HE |
| `REFERRAL FEE` | 1 | REFERRAL FEE |

### STATUS → Jenis Pekerjaan (68 mentah → 27 kanonik)

Contoh normalisasi yang dilakukan sistem:

```
DRAIN CLEANING - MAIN LINE (1.285) + DRAIN CLEANING -MAIN LINE (342)
  + DRAIN CLEANING -MAIN LINE varian            → DRAIN CLEANING - MAIN LINE  (1.627)
DRAIN CLEANING - KITCHEN (90) + DRAIN CLEANING -KITCHEN (36)
  + DRAIN CLEANING -KICTHEN (11)                → DRAIN CLEANING - KITCHEN    (137)
INDUSTRIAL EQUIPMENT (122) + INDUSTRIAL EQUIPEMENT (9) + INDUSTRIAL EQUIPEMNT (7)
  + INDUSTRIAL EWUIPMENT (2) + INDUSRIAL EQUIPMENT (2)  → INDUSTRIAL EQUIPMENT (142)
DRAIN CLEANING - FLOOR (63) + DRAIN CLEANING -FLOOR (56) → DRAIN CLEANING - FLOOR (119)
```

### SEND / NOTES TO (194 nilai) → 2 dimensi

Dipisah menjadi **kanal pengiriman** + **PIC**:

| Kanal | Contoh nilai mentah | Baris |
|-------|--------------------|------:|
| POS | `POS` | 1.012 |
| *(kosong)* | — | 719 |
| EMAIL | `EMAIL TIA`, `EMAIL SINTIA`, `EMAIL FINANCE`, `EMAIL MIKHA`, … | 719 |
| TUKAR FAKTUR | `TUKAR FAKTUR` | 140 |
| WHATSAPP | `WA GRACE`, `WA TIA`, `WA ANTO`, … | 350+ |
| PORTAL VENDOR | `PORTAL VENDOR` | 5 |
| LAIN-LAIN | `RENDI`, `VITA`, `INTAN`, `DI PAK MAHMUD`, … | sisanya |

### Customer & Outlet

- 1.218 nama customer unik (**sebelum** trim spasi) — 357 baris punya spasi awal/akhir
- 1.276 nama outlet unik — 838 baris punya spasi awal/akhir
- Relasi Customer → Outlet bersifat 1:N (satu PT punya banyak gerai)

### Format Nomor Faktur Pajak

3.054 dari 3.058 faktur pajak terisi berformat **17 karakter teks** dengan awalan:
`0400…` (3.008) · `0401…` (35) · `0402…` (3) · `0200…` (4) · `0201…` (2) · `0700…` (2) ·
`0701…` (1). Karena disimpan sebagai teks dan berawalan `0`, sistem **wajib** menyimpan
kolom ini sebagai `VARCHAR` dan mengekspor dengan format teks eksplisit agar tidak berubah
menjadi notasi ilmiah (`4.0026E+15`) atau kehilangan angka nol di depan.

### Pola Nomor Invoice

| Prefix | Jumlah | Interpretasi |
|--------|-------:|--------------|
| `FA` | 2.072 | Invoice jasa Jakarta/pusat |
| `FH` | 504 | Invoice residensial (home) |
| `FB` | 375 | Invoice produk (barang) |
| `FL` | 56 | Invoice Bali (Lokal) |
| `FD` | 28 | — |
| `FI` | 18 | — |
| `FS` | 17 | — |
| `FM` | 1 | — |
| tidak standar | 2 | `KWI 25…`, `KWIT 73…` (kwitansi) |

Pola: `{PREFIX}/{YY}/{NNNN}`.

---

## 1.6 Struktur Sheet `Copy of KOLOM RF`

| Kol | Header | Tipe Terdeteksi | Masalah |
|-----|--------|-----------------|---------|
| A | `TGL RF` | datetime (17), teks (11), kosong (2) | `30-06-2025`, `/31-05-26`, tahun salah (2026 untuk RF/25) |
| B | `NO RF` | teks | 1 baris kosong (r32) |
| C | `TGL INVOICE` (lama) | datetime (10), teks (8), **int `2025`** (1), kosong (11) | tipe campur berat |
| D | `NO INVOICE YANG DI RF` | teks | 1 baris duplikasi dalam sel |
| E | `NO INVOICE YANG BARU` | teks / `INVOICE BATAL` / kosong (12) | bercampur status & nomor |
| F | `NOMINAL` | numerik | — |
| G | `KETERANGAN ` (ada spasi di header!) | teks bebas | 1 baris berisi nomor RF |

### Klasifikasi 30 baris RF menurut alasan (ekstraksi dari `KETERANGAN`)

| Jenis Revisi | Jumlah | Contoh nomor RF |
|--------------|-------:|-----------------|
| Salah NPWP / perubahan NPWP | 9 | RF/25/0012, 0013, 0014, 0021, 0023, 0029–0032, RF/26/0034 |
| Pindah masa / lewat bulan | 6 | RF/25/0009, 0011, 0017, 0019, 0020, RF/26/0002, 0003 |
| Faktur pajak kadaluarsa | 2 | RF/25/0010, RF/26/0035 |
| Pembatalan invoice | 3 | RF/25/0015 (ada refund), 0016, 0026 |
| Pekerjaan gagal / tidak jadi | 3 | RF/25/0022, 0028, RF/26/0001 |
| Penghapusan tagihan (bad debt) | 1 | RF/25/0027 (resto tutup permanen) |
| Outstanding / lain-lain | 2 | RF/25/0024, 0033 |
| Baris kosong / sampah | 1 | r32 |

**Nomor RF `RF/25/0025` tidak ada** dalam register (loncat dari 0024 ke 0026) — sistem
menandai *gap penomoran RF*.

12 dari 30 baris punya kolom `NO INVOICE YANG BARU` **kosong** → artinya invoice lama
dibatalkan/dihapus **tanpa** invoice pengganti. Sistem membedakan ini secara eksplisit
dengan field `tindakan_invoice_lama` (Diganti / Dibatalkan / Direvisi / Dihapuskan).

---

## 1.7 Pemetaan Kolom Excel → Field Database

| Kol Excel | Header Excel | Tabel.Field Sistem | Transformasi Wajib |
|-----------|--------------|--------------------|--------------------|
| A | AREA | `invoices.area_id` + `invoices.business_unit_id` | Split `RDN xxx` → area + unit bisnis RESIDENSIAL |
| B | JOBS | `invoices.job_category_id` | Lookup / auto-create `job_categories` |
| C | NO. SO | `invoices.so_no` | `TRIM`, `UPPER` |
| D | TGL INVOICE | `invoices.invoice_date` | Serial Excel / teks / datetime → `DATE`; wajib |
| E | SALES | `invoices.salesperson_id` | Lookup `salespersons`, tipe INDIVIDU/KANAL |
| F | NO INVOICE | `invoices.invoice_no` | `TRIM`, `UPPER`; **UNIQUE** |
| G | NO FAKTUR PAJAK | `invoices.tax_invoice_no` | Paksa `VARCHAR`, pertahankan `0` di depan |
| H | CUSTOMER | `invoices.customer_id` | `TRIM`, kolaps spasi ganda, lookup/auto-create |
| I | OUTLET | `invoices.outlet_id` | `TRIM`, lookup/auto-create di bawah customer |
| J | NOMINAL | `invoices.gross_amount` | `DECIMAL(18,2)`; wajib > 0 |
| K | PMSC/BY CASE | `invoices.revenue_category_id` | Normalisasi typo via `value_aliases` |
| L | STATUS | `invoices.job_type_id` | Normalisasi 68 → 27 via `value_aliases` |
| M | CATATAN | `invoices.notes` | — |
| N | TGL WR | `invoices.wr_date` | Teks `" "` → `NULL` |
| O | TGL PRINT | `invoices.print_date` | — |
| P | TGL. INPUT | `invoices.input_date` | — |
| Q | TGL. KIRIM | `invoices.sent_date` | 122 kosong → tandai `DQ-11` |
| R | JT TEMPO | `invoices.due_date` | **JANGAN impor** — hitung ulang server |
| S | SEND / NOTES TO | `invoices.send_channel` + `invoices.send_pic` | Pisah kanal & PIC |
| T | POT PPH 23 | `invoice_deductions` (tipe `PPH23`) | Hitung ulang 2% × DPP; simpan tarif efektif |
| U | POT ADM | `invoice_deductions` (tipe `ADM`) | — |
| V | POT LAIN LAIN | `invoice_deductions` (tipe `LAIN`) | — |
| W | TOTAL | `invoices.net_amount` | **JANGAN impor** — hitung ulang server |
| X | PEMBAYARAN | `payments` + `payment_allocations` | Jadi 1 transaksi pembayaran |
| Y | SALDO | `invoices.outstanding_amount` | **JANGAN impor** — hitung ulang server |
| Z | TGL BAYAR | `payments.payment_date` | — |
| AA | PAYMENT | `invoices.payment_status_code` | **JANGAN impor** — hitung ulang server |
| AB | UMUR PIUTANG | `invoices.aging_days` / `collection_days` | **JANGAN impor** — hitung ulang; dipisah 2 metrik |
| AC–AF | 0-45 / 45-90 / 90-120 / >120 | *tidak disimpan* | Dihitung on-the-fly dari `aging_buckets` |
| AG | TOTAL REVENUE | `invoices.revenue_amount` | Dihitung: DPP bila `is_revenue_counted = 1` |
| AH | NET | `invoices.net_amount` | Dihitung server |
| AI | Column 1 | *diabaikan* | Artefak Excel Table |

**Kolom yang sengaja TIDAK diimpor** (semuanya kolom hasil rumus): `R`, `W`, `Y`, `AA`,
`AB`, `AC`, `AD`, `AE`, `AF`. Ini menerapkan instruksi Anda: *"Never trust existing
calculated Excel columns without validation."* Sistem menyimpan nilai Excel di
`import_rows.raw_json` untuk **rekonsiliasi** dan menampilkan selisih di Laporan Kualitas
Data, tetapi angka yang dipakai selalu hasil hitungan server.

### Pemetaan sheet RF

| Kol | Header | Field Sistem |
|-----|--------|--------------|
| A | TGL RF | `invoice_revisions.rf_date` (parser multi-format) |
| B | NO RF | `invoice_revisions.rf_no` (UNIQUE) |
| C | TGL INVOICE | `invoice_revisions.old_invoice_date` |
| D | NO INVOICE YANG DI RF | `invoice_revisions.old_invoice_no` + resolve `old_invoice_id` |
| E | NO INVOICE YANG BARU | `invoice_revisions.new_invoice_no` + resolve `new_invoice_id`; `INVOICE BATAL` → `old_invoice_action = cancelled` |
| F | NOMINAL | `invoice_revisions.original_amount` |
| G | KETERANGAN | `invoice_revisions.reason` + klasifikasi otomatis ke `revision_types` |

---

## 1.8 Asumsi yang Diambil (semuanya dapat diubah di menu Pengaturan)

| # | Asumsi | Dasar | Setting terkait |
|---|--------|-------|-----------------|
| A-01 | `NOMINAL` sudah termasuk PPN 11%; DPP = Nominal ÷ 1,11 | Rumus `J/1.11*0.02` | `pajak.tarif_ppn` = 11 |
| A-02 | PPh 23 = 2% × DPP | Rumus dominan (752 baris) | `pajak.tarif_pph23` = 2 |
| A-03 | Termin default 30 hari | Rumus `Q+30` di 3.075 baris | `ar.termin_default_hari` = 30 |
| A-04 | Dasar jatuh tempo = `TGL KIRIM`, fallback `TGL INVOICE` | Rumus `=Q+30`, `Q=D` di 589 baris | `ar.dasar_jatuh_tempo` = `sent_date` |
| A-05 | Revenue = DPP (tanpa PPN), hanya invoice `is_revenue_counted = 1` | Kaidah akuntansi + DQ-02 | `ar.basis_revenue` = `dpp` |
| A-06 | `RDN` = Residensial | Pola nama + `PMSC = RESIDENSIAL` beriringan | tabel `value_aliases` |
| A-07 | Aging dihitung atas **saldo**, bukan nominal bruto | Instruksi Anda §6 | `aging.basis` = `outstanding` |
| A-08 | Bucket default 0–45 / 46–90 / 91–120 / >120 (dari Excel) + preset alternatif | Kolom AC–AF | tabel `aging_buckets` |
| A-09 | `UMUR PIUTANG` Excel dipecah 2 metrik: `aging_days` (belum lunas) & `collection_days` (lunas) | Rumus `AB` mencampur keduanya | — |
| A-10 | Invoice lama pada RF dengan invoice baru → `doc_status = replaced`, `is_revenue_counted = 0` | Mencegah DQ-02 | `revision_types.affects_revenue` |
| A-11 | Tarif PPh 23 di luar 2% dipertahankan apa adanya + ditandai anomali | DQ-22 | Laporan Kualitas Data |
| A-12 | Pembayaran dari kolom `X` dibukukan sebagai 1 transaksi `PELUNASAN` (atau `SEBAGIAN` bila saldo tersisa) tanggal `Z` | Excel hanya punya 1 kolom bayar | — |
| A-13 | Mata uang tunggal IDR | Tidak ada kolom valuta | `umum.mata_uang` = IDR |
| A-14 | Perusahaan: Roto-Rooter Indonesia | Instruksi Anda §19 | `umum.nama_perusahaan` |
| A-15 | Baris tanpa `NO INVOICE`, `CUSTOMER`, dan `NOMINAL` sekaligus = baris kosong, dilewati | DQ-30 (861 baris hantu di JULI) | — |

---

## 1.9 Kesimpulan Fase 1

Workbook ini **fungsional secara operasional tetapi tidak dapat diandalkan secara
finansial**. Tiga masalah struktural yang tidak bisa diperbaiki di Excel:

1. **Bucket aging rusak sistemik** (DQ-01). Rp 306,7 juta AR tidak masuk laporan aging
   manapun karena satu huruf salah (`I` alih-alih `J`) yang tersalin ke 3.446 sel. Excel
   tidak memberi peringatan karena hasilnya teks, bukan error.
2. **ACP tidak mungkin dihitung** karena sheet bulanan bukan snapshot saldo. Tidak ada
   Saldo AR Awal maupun Saldo AR Akhir per periode — dua input wajib rumus ACP standar.
3. **Tidak ada integritas referensial.** Register RF terpisah dari buku piutang, sehingga
   invoice yang sudah di-RF tetap dihitung sebagai pendapatan aktif (Rp 152,8 juta dobel).

Sistem yang dibangun mengatasi ketiganya dengan: (a) menghitung ulang **seluruh** kolom
turunan di server dari data transaksi sumber, (b) menyimpan riwayat pembayaran per
transaksi sehingga saldo AR pada **tanggal apa pun** dapat direkonstruksi, dan (c) mengunci
relasi invoice lama ↔ invoice pengganti di database dengan *foreign key*.
