# Dokumentasi Perhitungan Piutang

## Ringkasan

Dokumentasi ini menjelaskan rumus perhitungan yang digunakan dalam sistem Piutang untuk menampilkan:
- Total Tagihan per PIU
- Total Bayar per PIU
- Sisa Piutang
- Summary Global

## Konsep Dasar

### PIU (Piutang)
PIU adalah dokumen yang mencatat penerimaan pembayaran dari customer. Setiap PIU dapat berisi satu atau lebih transaksi.

### Jenis PIU

| Jenis | Keterangan |
|-------|-----------|
| **PIU Pertama** | PIU pertama kali dibuat untuk suatu transaksi (no_bukti) |
| **PIU Lanjutan** | PIU berikutnya untuk pembayaran selanjutnya dari transaksi yang sama |

## Struktur Data

### Tabel `tbayar_piutang`

| Kolom | Tipe | Keterangan |
|-------|------|-----------|
| `id` | INT | Primary Key |
| `no_bukti` | VARCHAR | Nomor transaksi (aktivitas) |
| `no_bukti_piutang` | VARCHAR | Nomor PIU |
| `no_bukti_pembayaran` | VARCHAR | Nomor bukti pembayaran |
| `jumlah_tagihan` | DECIMAL | Tagihan asli dari transaksi |
| `jumlah` | DECIMAL | Jumlah yang dibayar di PIU ini |
| `jumlah_sudah_dibayar` | DECIMAL | Total kumulatif yang sudah dibayar |
| `sisa_bayar` | DECIMAL | Sisa yang belum dibayar |
| `deleted_at` | TIMESTAMP | Soft-delete timestamp |

## Rumus Perhitungan

### 1. Menentukan PIU Pertama vs PIU Lanjutan

```php
// Untuk setiap transaksi (no_bukti), cari PIU pertama
first_piu = MIN(no_bukti_piutang) untuk setiap no_bukti

// Cek apakah PIU ini adalah yang pertama
is_first_PIU = true
foreach (no_bukti in PIU_ini) {
    if (first_piu[no_bukti] !== PIU_ini) {
        is_first_PIU = false
        break
    }
}
```

### 2. Nilai yang Ditampilkan per Kolom

#### Untuk PIU Pertama

| Kolom | Rumus | Keterangan |
|-------|-------|-----------|
| **Total Tagihan** | `SUM(jumlah_tagihan)` | Menampilkan tagihan asli |
| **Total Bayar** | `SUM(jumlah)` | Menampilkan yang dibayar di PIU ini |
| **Sisa** | `SUM(sisa_bayar)` | Menampilkan sisa pembayaran |

#### Untuk PIU Lanjutan

| Kolom | Rumus | Keterangan |
|-------|-------|-----------|
| **Total Tagihan** | `SUM(jumlah)` | Menampilkan yang dibayar di PIU ini |
| **Total Bayar** | `SUM(jumlah)` | Menampilkan yang dibayar di PIU ini |
| **Sisa** | `SUM(sisa_bayar)` | Menampilkan sisa pembayaran |

## Query SQL

### Query untuk Datatable (per PIU)

```sql
-- Ambil record terbaru per (no_bukti_piutang, no_bukti)
SELECT 
    p.no_bukti,
    p.tanggal,
    c.nama as customer,
    p.keterangan,
    SUM(bp.jumlah_tagihan) as jumlah_tagihan,
    SUM(bp.jumlah) as total_jumlah,
    SUM(bp.sisa_bayar) as sisa_bayar,
    COUNT(DISTINCT bp.no_bukti) as total_items,
    JSON_ARRAYAGG(bp.no_bukti) as no_transaksi_list
FROM tpiutang p
INNER JOIN (
    SELECT 
        bp1.no_bukti_piutang,
        bp1.no_bukti,
        bp1.jumlah_tagihan,
        bp1.jumlah,
        bp1.sisa_bayar
    FROM tbayar_piutang bp1
    INNER JOIN (
        SELECT no_bukti_piutang, no_bukti, MAX(id) as max_id
        FROM tbayar_piutang
        GROUP BY no_bukti_piutang, no_bukti
    ) bp2 ON bp1.no_bukti_piutang = bp2.no_bukti_piutang
         AND bp1.no_bukti = bp2.no_bukti
         AND bp1.id = bp2.max_id
) bp ON p.no_bukti = bp.no_bukti_piutang
INNER JOIN tcustomer c ON p.customer_id = c.id
WHERE p.deleted_at IS NULL
GROUP BY p.no_bukti
ORDER BY p.tanggal DESC, p.no_bukti DESC;
```

### Query untuk Summary Global

```sql
SELECT 
    COUNT(DISTINCT bp.no_bukti_piutang) as total_transactions,
    SUM(bp.jumlah_tagihan) as total_tagihan,
    SUM(bp.jumlah_sudah_dibayar) as total_bayar,
    SUM(bp.sisa_bayar) as total_sisa
FROM tbayar_piutang bp
INNER JOIN (
    SELECT no_bukti, MAX(id) as max_id
    FROM tbayar_piutang
    WHERE deleted_at IS NULL
    GROUP BY no_bukti
) latest ON bp.no_bukti = latest.no_bukti 
    AND bp.id = latest.max_id
INNER JOIN tpiutang p ON bp.no_bukti_piutang = p.no_bukti
WHERE bp.deleted_at IS NULL 
  AND p.deleted_at IS NULL;
```

## Contoh Perhitungan

### Skenario

**PIU/2605/0001 (PIU Pertama):**

| No. Bukti | Tagihan Asli | Bayar (PIU Ini) | Kumulatif | Sisa |
|-----------|-------------|-----------------|-----------|------|
| PN260512002 | 822.150.000 | 822.150.000 | 822.150.000 | 0 |
| PN260512001 | 430.000.000 | 400.000.000 | 400.000.000 | 30.000.000 |

```
Total Tagihan: 822.150.000 + 430.000.000 = 1.252.150.000
Total Bayar:   822.150.000 + 400.000.000 = 1.222.150.000
Sisa:          0 + 30.000.000 = 30.000.000
```

**PIU/2605/0002 (PIU Lanjutan):**

| No. Bukti | Tagihan Asli | Bayar (PIU Ini) | Kumulatif | Sisa |
|-----------|-------------|-----------------|-----------|------|
| PN260512001 | 430.000.000 | 30.000.000 | 430.000.000 | 0 |
| PN260512003 | 121.500.000 | 121.500.000 | 121.500.000 | 0 |

```
Total Tagihan: 30.000.000 + 121.500.000 = 151.500.000
Total Bayar:   30.000.000 + 121.500.000 = 151.500.000
Sisa:          0 + 0 = 0
```

### Summary Global (Latest Record per Transaksi)

| No. Bukti | PIU Terakhir | Tagihan | Sudah Dibayar | Sisa |
|-----------|-------------|---------|---------------|------|
| PN260512002 | PIU/2605/0001 | 822.150.000 | 822.150.000 | 0 |
| PN260512001 | PIU/2605/0002 | 430.000.000 | 430.000.000 | 0 |
| PN260512003 | PIU/2605/0002 | 121.500.000 | 121.500.000 | 0 |

```
Total Tagihan: 822.150.000 + 430.000.000 + 121.500.000 = 1.373.650.000
Total Bayar:   822.150.000 + 430.000.000 + 121.500.000 = 1.373.650.000
Sisa:          0 + 0 + 0 = 0
```

## Alur Pembuatan PIU

### 1. Membuat PIU Pertama

```
Input: Transaksi PN260512001 (tagihan 430.000.000), bayar 400.000.000

Record yang dibuat:
- no_bukti: PN260512001
- no_bukti_piutang: PIU/2605/0001
- jumlah_tagihan: 430.000.000 (tagihan asli)
- jumlah: 400.000.000 (yang dibayar)
- jumlah_sudah_dibayar: 400.000.000 (kumulatif)
- sisa_bayar: 30.000.000
```

### 2. Membuat PIU Lanjutan (Pelunasan)

```
Input: Transaksi PN260512001 (sisa 30.000.000), bayar 30.000.000

Record yang dibuat:
- no_bukti: PN260512001
- no_bukti_piutang: PIU/2605/0002
- jumlah_tagihan: 430.000.000 (tagihan asli, tetap)
- jumlah: 30.000.000 (yang dibayar di PIU ini)
- jumlah_sudah_dibayar: 430.000.000 (kumulatif: 400.000.000 + 30.000.000)
- sisa_bayar: 0 (lunas)

Action: Soft-delete record lama di PIU/2605/0001
```

## Konsep Kunci

### 1. Soft-Delete

Record yang di-soft-delete **tidak dihapus dari database**, hanya ditandai dengan `deleted_at`. Ini penting untuk:
- Menjaga histori lengkap
- PIU pertama tetap menampilkan transaksi aslinya
- Audit trail yang akurat

### 2. Latest Record

Summary global mengambil **record terbaru per no_bukti** berdasarkan `MAX(id)`. Ini memastikan:
- Status pembayaran selalu up-to-date
- Sisa piutang dihitung dengan benar
- Tidak ada double-counting

### 3. PIU Pertama = Tagihan Asli

PIU pertama untuk suatu transaksi selalu menampilkan:
- **Total Tagihan** = tagihan asli (jumlah_tagihan)
- Ini memberikan snapshot kondisi saat transaksi pertama kali dibuat

### 4. PIU Lanjutan = Yang Dibayar

PIU lanjutan menampilkan:
- **Total Tagihan** = jumlah yang dibayar di PIU ini (jumlah)
- Ini menunjukkan tambahan pembayaran yang dilakukan

## File Terkait

- **Controller**: `app/Http/Controllers/Web/PiutangController.php`
- **Model**: `app/Models/TBayarPiutang.php`
- **Model**: `app/Models/TPiutang.php`
- **View**: `resources/views/piutang/index.blade.php`

## Changelog

### 2026-05-14
- Menambahkan logic membedakan PIU pertama dan PIU lanjutan
- PIU pertama menampilkan tagihan asli di kolom "Total Tagihan"
- PIU lanjutan menampilkan jumlah yang dibayar di kolom "Total Tagihan"
- Soft-deleted records tetap dihitung untuk histori lengkap
- Summary global mengambil record terbaru per transaksi
- Menambahkan fitur restore otomatis saat hapus PIU
- Menambahkan endpoint API untuk update summary real-time

## Menghapus PIU (Restore)

### Perilaku Restore

Ketika sebuah PIU dihapus, sistem akan **otomatis me-restore** record yang sebelumnya di-soft-delete. Ini memastikan bahwa:
1. Total tagihan transaksi kembali seperti semula
2. Transaksi yang sudah lunas bisa kembali memiliki sisa
3. Histori pembayaran tetap konsisten

### Contoh Skenario

**Sebelum Hapus PIU/2605/0002:**

| No. Bukti | PIU Terakhir | Tagihan | Sudah Dibayar | Sisa |
|-----------|-------------|---------|---------------|------|
| PN260512001 | PIU/2605/0002 | 430.000.000 | 430.000.000 | 0 (lunas) |
| PN260512003 | PIU/2605/0002 | 121.500.000 | 121.500.000 | 0 (lunas) |

**Setelah Hapus PIU/2605/0002:**

| No. Bukti | PIU Terakhir | Tagihan | Sudah Dibayar | Sisa |
|-----------|-------------|---------|---------------|------|
| PN260512001 | PIU/2605/0001 | 430.000.000 | 400.000.000 | 30.000.000 |
| PN260512003 | - (tidak ada) | - | - | - |

### Logic Restore

```php
// 1. Get semua no_bukti dari PIU yang akan dihapus
noBuktiList = [PN260512001, PN260512003]

// 2. Restore soft-deleted records untuk no_bukti yang sama
TBayarPiutang::whereIn('no_bukti', noBuktiList)
    ->where('no_bukti_piutang', '<>', PIU_yang_dihapus)
    ->whereNotNull('deleted_at')
    ->update(['deleted_at' => null])

// 3. Hapus record PIU yang dihapus
TBayarPiutang::where('no_bukti_piutang', PIU_yang_dihapus)->delete()
```

### Query Restore

```sql
-- Restore soft-deleted records
UPDATE tbayar_piutang
SET deleted_at = NULL
WHERE no_bukti IN (SELECT no_bukti FROM tbayar_piutang WHERE no_bukti_piutang = 'PIU/2605/0002')
  AND no_bukti_piutang <> 'PIU/2605/0002'
  AND deleted_at IS NOT NULL;
```
