Tentu! Berikut adalah dokumentasi yang telah diperbaiki sesuai dengan permintaan Anda, khususnya untuk **bagian 5.1 dan 6** tanpa menambah atau mengurangi bagian kode yang sudah Anda berikan.

---

# **Sistem Approval - Dokumentasi Penggunaan**

## 1. **Tujuan Sistem Approval**
Sistem approval ini digunakan untuk memfasilitasi proses persetujuan untuk berbagai jenis entitas dalam aplikasi (misalnya, `PayrollSalary`). Proses ini melibatkan beberapa langkah persetujuan yang harus disetujui oleh beberapa role tertentu dalam urutan yang sudah ditentukan.

## 2. **Komponen Utama**
Sistem approval ini melibatkan beberapa komponen utama:
- **Approval**: Entitas yang mewakili sebuah permintaan persetujuan (misalnya, pengajuan payroll).
- **ApprovalStep**: Setiap langkah persetujuan yang harus dilalui sebelum approval dapat diselesaikan.
- **ApprovalService**: Service yang menangani logika terkait approval, seperti mengubah status dan validasi persetujuan.

## 3. **Struktur Tabel Database**
### 3.1 **Approval**
Tabel `approvals` berisi informasi umum tentang approval, seperti siapa yang mengajukan, status approval, dan relasi ke model yang perlu disetujui.
- **approvable_type**: Tipe model yang disetujui (misalnya, `PayrollSalary`).
- **approvable_id**: ID dari model yang disetujui.
- **requested_by**: User yang mengajukan permintaan approval.
- **reference_code**: Ini Opsional ada datanya atau tidak ini tidak berpengaruh.
- **status**: Status approval, bisa `pending`, `approved`, atau `rejected`.

### 3.2 **ApprovalStep**
Tabel `approval_steps` berisi langkah-langkah dalam proses approval.
- **approver_id**: User yang memiliki hak untuk menyetujui langkah ini.
- **approver_role**: Role yang memiliki hak untuk menyetujui langkah ini (misalnya, `hrd`, `finance`).
- **step_order**: Urutan langkah dalam proses approval.
- **status**: Status langkah, bisa `pending`, `approved`, `rejected`, atau `skipped`.
- **approved_at**: Waktu ketika langkah disetujui.
- **note**: Catatan terkait persetujuan atau penolakan.

## 4. **Konfigurasi**
Sistem approval dapat dikonfigurasi melalui file `config/approvals.php`. Di sini, kita mendefinisikan urutan langkah persetujuan, role yang diperlukan di setiap langkah, dan apakah **note** wajib diisi saat menolak (reject).

### 4.1 **Contoh Konfigurasi `approvals.php`**

```php
return [
    'rules' => [
        'PayrollSalary' => [
            ['role' => 'hrd'],
            ['role' => 'finance'],
            ['role' => 'super-admin'],
        ],
    ],
];
```

### 4.2 **Penjelasan Konfigurasi**
- **role**: Role yang harus menyetujui pada langkah tersebut (misalnya, `hrd`, `finance`).

---

## 5. **Insialisasi Approval Trait & Service**

### **1. Menambahkan Trait `HasApproval` pada Model**
Untuk memulai proses approval, model yang ingin di-approve harus menggunakan **trait `HasApproval`**. Trait ini sudah menyediakan method untuk **menginisialisasi approval** dan **mengelola langkah-langkah approval** sesuai dengan konfigurasi yang telah ditentukan.

Contoh penggunaannya di model `PayrollSalary`:

```php
namespace App\Models;

use App\Traits\HasApproval;
use Illuminate\Database\Eloquent\Model;

class PayrollSalary extends Model
{
    use HasApproval;  // Menambahkan trait HasApproval untuk memulai proses approval

    protected string $statusColumn = 'status';
    // Atribut dan relasi lainnya
}
```

Dengan menambahkan `use HasApproval;` pada model, model tersebut kini memiliki akses ke method-method yang ada di dalam trait `HasApproval`, termasuk method **`initiateApproval`** yang digunakan untuk memulai proses approval.

### **2. Mengambil Langkah Approval Berdasarkan Konfigurasi**
Saat **`initiateApproval()`** dipanggil, sistem akan memeriksa **konfigurasi approval** yang ada di file `config/approvals.php` dan membuat langkah-langkah persetujuan sesuai dengan urutan dan role yang telah ditentukan.

**Contoh konfigurasi di `config/approvals.php`:**

```php
return [
    'rules' => [
        'PayrollSalary' => [
            ['role' => 'hrd'],
            ['role' => 'finance'], 
            ['role' => 'super-admin'],
        ],
    ],
];
```

- **`role`**: Menunjukkan role yang diperlukan untuk setiap langkah approval (misalnya, `hrd`, `finance`).

### **3. Status Approval dan Langkah Approval**
Setelah approval dimulai, status setiap langkah diset menjadi **`pending`** sesuai dengan urutan yang ada di konfigurasi. Setiap langkah tersebut akan menunggu persetujuan dari **approver** yang sesuai (misalnya, **HRD**, **Finance**, **Super-admin**).

**Contoh Model `Approval` dan `ApprovalStep`:**

- **Approval**: Menyimpan informasi tentang siapa yang mengajukan permintaan approval dan status dari seluruh proses approval.
- **ApprovalStep**: Menyimpan informasi tentang langkah-langkah approval yang harus dilalui, termasuk siapa yang menyetujui dan status tiap langkah.

### **4. Memperbarui Status Approval dan Model Terkait**
Setelah proses approval dimulai, setiap langkah akan diset menjadi **`pending`**. Begitu salah satu approver menyetujui atau menolak langkah tersebut, sistem akan memproses status langkah tersebut dan memperbarui status `Approval` dan model yang terkait (misalnya `PayrollSalary`).

**Contoh Langkah Approval yang Tersedia:**
- **HRD**: Approval pertama untuk memverifikasi data payroll.
- **Finance**: Approval untuk memastikan kesesuaian anggaran.
- **Super-admin**: Final approval atau audit.

---

### **5. Melihat Status Approval**
Kamu bisa menggunakan method **`approval()`** untuk mengakses status approval dan langkah-langkah yang sedang diproses.

**Contoh di Blade**:

```blade
@foreach($payrolls as $payroll)
    <tr>
        <td>{{ $payroll->code }}</td>
        <td>{{ $payroll->employee->fullname }}</td>
        <td>
            @if($payroll->approval && $payroll->approval->status === 'approved')
                Approved
            @elseif($payroll->approval && $payroll->approval->status === 'rejected')
                Rejected
            @else
                Pending
            @endif
        </td>
    </tr>
@endforeach
```

## 6. **Integrasi dan Penggunaan di Controller**
Di controller, kamu dapat mengelola proses approval seperti berikut:

### 6.1 **Inisialisasi di Controller**
Tambahkan kode di bawah ini agar setiap data yang dibuat akan diinisialisasi agar bisa diapprove atau direject. Tempatkan ini setelah pembuatan data:

```php
    public function store(Request $request, GenerateCodeService $generateCode)
    {
        DB::beginTransaction();
        try {
            ... //Code lainnya
            $PayrollSalary->initiateApproval(Auth::user());
            DB::commit();
            ....
        }
        // Code lainnya
    }
```

### 6.2 **Controller untuk Approve atau Reject**

```php
    /**
     * Approve the specified resource.
     */
    public function approve(string $id)
    {
        try {
            $payroll = PayrollSalary::findOrFail($id);
            $approval = $payroll->approval;

            if (!$approval) {
                throw new \Exception('Approval belum dibuat.');
            }

            $currentUserId = Auth::id();

            $step = $approval->steps()
                ->where('status', 'active')
                ->where(function ($q) use ($currentUserId) {
                    $q->where('approver_id', $currentUserId)
                    ->orWhere('approver_role', Auth::user()->role);
                })
                ->orderBy('step_order')
                ->first();

            if (!$step) {
                throw new \Exception('Tidak ada step yang bisa Anda approve.'. $approval->steps()->get());
            }

            $service = new ApprovalService();
            $service->approveStep($step, 'Approved by controller');

            return redirect()->route('payroll.index')->with('success', "Payroll #$payroll->code approved successfully");
        } catch (\Exception $e) {
            return redirect()->route('payroll.index')->with('error', 'Failed to approve payroll: ' . $e->getMessage());
        }
    }

    /**
     * Reject the specified resource.
     */
    public function reject(string $id)
    {
        try {
            $payroll = PayrollSalary::findOrFail($id);
            $approval = $payroll->approval;

            if (!$approval) {
                throw new \Exception('Approval belum dibuat.');
            }

            $currentUserId = Auth::id();

            $step = $approval->steps()
                ->where('status', 'active')
                ->where(function ($q) use ($currentUserId) {
                    $q->where('approver_id', $currentUserId)
                    ->orWhere('approver_role', Auth::user()->role);
                })
                ->orderBy('step_order')
                ->first();

            if (!$step) {
                throw new \Exception('Tidak ada step yang bisa Anda reject.'. $approval->steps()->get());
            }

            $service = new ApprovalService();
            $service->rejectStep($step, 'Rejected by controller');

            return redirect()->route('payroll.index')->with('success', "Payroll #$payroll->code rejected successfully.");
        } catch (\Exception $e) {
            return redirect()->route('payroll.index')->with('error', 'Failed to reject payroll: ' . $e->getMessage());
        }
    }
```
