---
# System prepended metadata

title: CRM BAg - Pangkalan Susu - Kontrak Pemberi Kerja

---

# CREATE: Step 1 - Menambahkan Informasi Utama Kontrak (Header)

## 1. Spesifikasi Umum

| Keterangan | Detail |
| :--- | :--- |
| **Metode** | `POST` |
| **URL Endpoint** | `/api/v1/contract/client/header` |
| **Deskripsi** | Membuat entry baru di header kontrak dengan status DRAFT. |
| **Status Awal** | DRAFT |

***

## 2. Request Body (Informasi Utama)



| Field Name | Tipe Data | Wajib? | Unique? | Deskripsi |
| :--- | :--- | :--- | :--- | :--- |
| `contract_type` | String | Ya | Tidak | Jenis Kontrak (Shipment Operation / Pemeliharaan dan Pendampingan). |
| `contract_no_bag` | String | Ya | **Ya** | Nomor Kontrak Internal BAG. |
| `contract_no_client` | String | Ya | **Ya** | Nomor Kontrak dari Pemberi Kerja. |
| **`client_id`** | Integer | Ya | Tidak | ID Pemberi Kerja (Foreign Key ke Master Data Mitra). |
| `contract_date` | Date | Ya | Tidak | Tanggal Kontrak ditandatangani. |
| `effective_start_date` | Date | Ya | Tidak | Tanggal mulai berlaku (YYYY-MM-DD). |
| `effective_end_date` | Date | Ya | Tidak | Tanggal berakhir berlaku (YYYY-MM-DD). |
| **`document_file`** | File (Base64) | Ya | Tidak | File PDF kontrak (Max 16MB) yang akan diunggah ke Minio. |


## 3. Business Rules
1. Validasi File: Validasi bahwa document_file adalah PDF dan ukurannya maksimal 10MB.
2. File Upload: Unggah file PDF ke Minio Storage dan simpan path-nya ke document_path.
3. Status Default: Set field status di database menjadi 'DRAFT'.
4. Creator: Simpan ID user pembuat ke created_by.

## 4. Response Sukses
Status: `HTTP 201 Created`
Jika data header berhasil disimpan. Respon harus mengembalikan ID Kontrak yang akan digunakan untuk Step 2.

```json
// Contoh Response Sukses
{
  "status": "success",
  "message": "Informasi Utama Kontrak berhasil disimpan sebagai DRAFT. Lanjutkan ke pengisian Layanan.",
  "data": {
    "contract_id": 5678,
    "contract_no_bag": "BAG-2025-001"
  }
}
```
## 5. Response Error & Validasi

### 5.1 Error Validasi Input
Status: `HTTP 400 Bad Request`

```json
// Contoh Error jika tanggal tidak logis
{
  "status": "error",
  "message": "Validasi gagal",
  "errors": {
    "effective_end_date": "Tanggal Akhir Berlaku tidak boleh sebelum Tanggal Awal Berlaku."
  }
}
```

### 5.2 Error Konflik Unik
Status: `HTTP 409 Conflict`

```json
// Contoh Error jika Nomor Kontrak BAG sudah ada
{
  "status": "error",
  "message": "Data konflik",
  "detail": "Nomor Kontrak BAG 'BAG-2025-001' sudah digunakan oleh kontrak lain."
}
```

### 5.3 Internal Server Error
Status: `HTTP 500 Internal Server Error`

```json
{ 
    "data": null, 
    "details": "Terjadi kesalahan saat memproses update status kontrak.", 
    "message": "Kesalahan Server Internal."
}
```

# ➕ CREATE: Step 2 - Menambahkan Layanan Kontrak

## 1. Spesifikasi Umum

| Keterangan | Detail |
| :--- | :--- |
| **Metode** | `POST` |
| **URL Endpoint** | `/api/v1/contract/client/{contract_id}/services` |
| **Deskripsi** | Menambahkan satu atau lebih baris layanan pekerjaan ke Kontrak yang sudah dalam status DRAFT. |

***

## 2. Request Body (Array Layanan)

| Field Name | Tipe Data | Wajib? | Unique? | Deskripsi |
| :--- | :--- | :--- | :--- | :--- |
| `contract_id` | Integer | Ya | Tidak | ID Kontrak dari Step 1 (via URL Path). |
| **`services`** | Array of Objects | Ya | Tidak | Daftar Layanan Pekerjaan. |

**Detail Objek dalam Array `services`:**

| Field Name | Tipe Data | Wajib? | Unique? | Deskripsi |
| :--- | :--- | :--- | :--- | :--- |
| `service_id` | Integer | Ya | Tidak | ID Layanan Pekerjaan. |
| `service_name` | String | Ya | **Partial** | Deskripsi Layanan Pekerjaan (Text). |
| `category` | String | Ya | Tidak | Kategori Pekerjaan (Hardcoded: Jetty Management, Bongkar Muat Jetty, Trucking Jetty Ponton,  Trucking Dermaga, Pemeliharaan & Pendampingan, Coal Unloader). |
| `quantity_type` | String | Ya | Tidak | Tipe Kuantitas (Hardcoded: /Freight (MT), /Month, /Lumpsum, /Volume). |
| `price_unit` | BigInt | Ya | Tidak | Harga per unit (Bilangan bulat, misal: 150500). |

> **Partial Unique:** Kombinasi (`contract_header_id` + `service_name`) harus unik. Artinya, satu kontrak tidak boleh memiliki dua layanan dengan nama yang persis sama.


## 3. Response Sukses
Jika semua detail layanan berhasil disimpan.

Status: `HTTP 201 Created`

```json
// Contoh Response Sukses
{
  "message": "Detail layanan berhasil ditambahkan ke kontrak ID 101.",
  "data": [
    {
  "contract_type": "Shipment Operation",
  "contract_no_bag": "BAG-2025-001",
  "contract_no_client": "CLIENT-A-2025-X",
  "client_id": 1234,
  "contract_date": "2025-11-01",
  "effective_start_date": "2026-01-01",
  "effective_end_date": "2026-12-31",
  "document_file": "<Base64 string of the PDF content>" 
  }
```

## 4. Response Error 
### 4.1 Error Konflik Layanan Ganda
Status: `HTTP 409 Conflict`
```json
// Contoh Error jika layanan yang sama sudah ada di kontrak ini
{
  "status": "error",
  "message": "Data konflik",
  "detail": "Layanan 'Jasa Pemanfaatan Jetty' sudah ada di Kontrak ID 5678. Harap edit layanan tersebut."
}
```
### 4.2 Internal Server Error
Status: `HTTP 500 Internal Server Error`

```json
{ 
    "data": null, 
    "details": "Terjadi kesalahan saat memproses update status kontrak.", 
    "message": "Kesalahan Server Internal."
}
```

# ✏️ UPDATE: Mengubah Informasi Utama Kontrak

## 1. Spesifikasi Umum

| Keterangan | Detail |
| :--- | :--- |
| **Metode** | `PUT` |
| **URL Endpoint** | `/api/v1/contract/client/header/{contract_id}` |
| **Deskripsi** | Memperbarui data header kontrak berdasarkan ID. |

***

## 2. Request Body (Informasi Utama)

Data dikirimkan dalam format JSON. Semua *field* bersifat opsional, tetapi yang dikirimkan harus divalidasi.

| Field Name | Tipe Data | Wajib? | Unique? | Deskripsi |
| :--- | :--- | :--- | :--- | :--- |
| `contract_type` | String | Tidak | Tidak | Jenis Kontrak. |
| `contract_no_bag` | String | Tidak | **Ya** | Nomor Kontrak Internal BAG. |
| `contract_no_client` | String | Tidak | **Ya** | Nomor Kontrak dari Pemberi Kerja. |
| **`client_id`** | Integer | Tidak | Tidak | ID Pemberi Kerja. |
| `contract_date` | Date | Tidak | Tidak | Tanggal Kontrak ditandatangani. |
| `effective_start_date` | Date | Tidak | Tidak | Tanggal mulai berlaku. |
| `effective_end_date` | Date | Tidak | Tidak | Tanggal berakhir berlaku. |
| **`document_file`** | File (Base64) | Tidak | Tidak | File PDF baru (jika diunggah, akan menggantikan file lama). |

```json
// Contoh Payload Request: Hanya mengubah tanggal berakhir
    {
  "contract_type": "Shipment Operation",
  "contract_no_bag": "BAG-2025-001",
  "contract_no_client": "CLIENT-A-2025-X",
  "client_id": 1234,
  "contract_date": "2025-11-01",
  "effective_start_date": "2026-01-01",
  "effective_end_date": "2027-12-31", // data yang di udate
  "document_file": "<Base64 string of the PDF content>" 
  {
    "service_name": "Jasa Bongkar Muat Batubara di Jetty",
    "category": "Bongkar Muat Jetty", // Hardcoded
    "quantity_type": "/MT", // Hardcoded
    "price_unit": 35000 // Harga per Metrik Ton
  },
  {
    "service_name": "Sewa Jetty dan Fasilitas",
    "category": "Jetty Management", // Hardcoded
    "quantity_type": "/Month", // Hardcoded
    "price_unit": 800000000 // Harga Month
  },
  {
    "service_name": "Pendampingan TUKS",
    "category": "Jetty Management", // Hardcoded
    "quantity_type": "/Lumpsum", // Hardcoded
    "price_unit": 50000000 // Harga Lumpsum   
   }
}
```
***

## 3. Business Rules

1. Validasi Status: Cek status kontrak ``contract_id``. Jika bukan DRAFT atau REVISI, tolak dengan error 403 Forbidden.
2. Validasi File: Jika `document_file` dikirim, validasi PDF dan ukuran (Max 16MB).
3. File Update: Jika `document_file` dikirim, unggah file baru ke Minio dan perbarui `document_path`.
4. Validasi Unik: Jika `contract_no_bag` atau `contract_no_client` diubah, pastikan nilainya unik di database.


## 4. Response Sukses

Jika data header berhasil diubah.

Status: `HTTP 200 OK`

```json
// Contoh Response Sukses
{
  "status": "success",
  "message": "Informasi Utama Kontrak ID 5678 berhasil diperbarui.",
  "data": {
    "contract_id": 5678,
    "effective_end_date": "2027-12-31", // Nilai yang baru
    "status": "DRAFT" // Status tetap
    // ... field header lainnya
  }
}
```

## 5. Response Error & Validasi

### 5.1. Error Konflik Unik
Status: `HTTP 409 Conflict`

```json
{
  "status": "error",
  "message": "Data konflik",
  "detail": "Nomor Kontrak BAG 'BAG-2025-001' sudah digunakan oleh kontrak lain."
}
```

### 5.2 Internal Server Error
Status: `HTTP 500 Internal Server Error`

```json
{ 
    "data": null, 
    "details": "Terjadi kesalahan saat memproses update status kontrak.", 
    "message": "Kesalahan Server Internal."
}
```

# ✏️ UPDATE: Mengubah Layanan Kontrak 

## 1. Spesifikasi Umum

| Keterangan | Detail |
| :--- | :--- |
| **Metode** | `PUT` |
| **URL Endpoint** | `/api/v1/contract/client/service/{service_id}` |
| **Deskripsi** | Memperbarui detail satu baris layanan pekerjaan berdasarkan ID layanan. |
| **Status Diizinkan** | **DRAFT, REVISI** |
| **Aktor** | Staff Ophar |

***

## 2. Request Body (Layanan Detail)

| Field Name | Tipe Data | Wajib? | Unique? | Deskripsi |
| :--- | :--- | :--- | :--- | :--- |
| `service_name` | String | Tidak | **Partial** | Deskripsi Layanan Pekerjaan. |
| `category` | String | Tidak | Tidak | Kategori Pekerjaan (Hardcoded: Jetty Management, Bongkar Muat Jetty, Trucking Jetty Ponton,  Trucking Dermaga, Pemeliharaan & Pendampingan, Coal Unloader). |
| `quantity_type` | String | Tidak | Tidak | Kategori (Hardcoded: /Freight (MT), /Month, /Lumpsum, /Volume). |
| `price_unit` | BigInt | Tidak | Tidak | Harga per unit. |

```json
// Contoh Payload Request: Hanya mengubah tanggal berakhir
    {
  "contract_type": "Shipment Operation",
  "contract_no_bag": "BAG-2025-001",
  "contract_no_client": "CLIENT-A-2025-X",
  "client_id": 1234,
  "contract_date": "2025-11-01",
  "effective_start_date": "2026-01-01",
  "effective_end_date": "2027-12-31", 
  "document_file": "<Base64 string of the PDF content>" 
  {
    "service_name": "Jasa Bongkar Muat Batubara di Jetty",
    "category": "Bongkar Muat Jetty", 
    "quantity_type": "/MT", 
    "price_unit": 35000 // data yang di udate
  },
  {
    "service_name": "Sewa Jetty dan Fasilitas",
    "category": "Jetty Management", 
    "quantity_type": "/Month", 
    "price_unit": 800000000 
  },
  {
    "service_name": "Pendampingan TUKS",
    "category": "Jetty Management", 
    "quantity_type": "/Lumpsum", 
    "price_unit": 50000000  
   }
}
```

## 3. Business Rules

1. Validasi Status: Back-end harus mengambil `contract_header_id` dari `service_id` yang di-update, lalu memeriksa status kontrak. Hanya diizinkan jika status **DRAFT** atau **REVISI**.
2. Validasi Unik (Partial): Jika `service_name` diubah, pastikan kombinasi (`contract_header_id` + `service_name`) tetap unik.
3. Validasi ID: Pastikan `service_id` yang diminta benar-benar ada di database.
4. Perbarui Data: Perbarui hanya field yang dikirim dalam request body untuk detail layanan tersebut.

***

## 4. Response Sukses
Status: `HTTP 200 OK`

```json
// Contoh Response Sukses
{
  "status": "success",
  "message": "Layanan Pekerjaan ID 201 berhasil diperbarui.",
  "data": {
    "id": 201, 
    "contract_header_id": 101,
    "contract_type": "Shipment Operation",
  "contract_no_bag": "BAG-2025-001",
  "contract_no_client": "CLIENT-A-2025-X",
  "client_id": 1234,
  "contract_date": "2025-11-01",
  "effective_start_date": "2026-01-01",
  "effective_end_date": "2027-12-31", 
  "document_file": "<Base64 string of the PDF content>" 
  {
    "service_name": "Jasa Bongkar Muat Batubara di Jetty",
    "category": "Bongkar Muat Jetty", 
    "quantity_type": "/MT", 
    "price_unit": 35000 // data yang di udate
  },
  {
    "service_name": "Sewa Jetty dan Fasilitas",
    "category": "Jetty Management", 
    "quantity_type": "/Month", 
    "price_unit": 800000000 
  },
  {
    "service_name": "Pendampingan TUKS",
    "category": "Jetty Management", 
    "quantity_type": "/Lumpsum", 
    "price_unit": 50000000  
  }
}
```
***

## 5. Response Error & Validasi

### 5.1. Error Status Tidak Diizinkan
Status: `HTTP 403 Forbidden`

```json
// Contoh Error jika status Kontrak sudah APPROVED
{
  "status": "error",
  "message": "Aksi tidak diizinkan",
  "detail": "Detail layanan tidak dapat diubah karena Kontrak ID 101 berstatus 'APPROVED'."
}
```

### 5.2. Error Konflik Unik (Nomor Kontrak)
Status: `HTTP 409 Conflict`

```json
{
  "status": "error",
  "message": "Data konflik",
  "detail": "Layanan 'Bongkar Muat Kapal Tongkang' sudah ada di kontrak ini dengan nama yang berbeda. Harap gunakan nama yang unik."
}
```

### 5.2. Error ID Tidak Ditemukan
Status: `HTTP 404 Not Found`

```json
// Contoh Error jika service_id tidak ditemukan
{
  "status": "error",
  "message": "Data tidak ditemukan",
  "detail": "Layanan Pekerjaan dengan ID 999 tidak ditemukan."
}
```

### 5.3 Internal Server Error
Status: `HTTP 500 Internal Server Error`

```json
{ 
    "data": null, 
    "details": "Terjadi kesalahan.", 
    "message": "Kesalahan Server Internal."
}
```


# 🔍 READ: Melihat Daftar Kontrak Pemberi Kerja

## 1. Spesifikasi Umum

| Keterangan | Detail |
| :--- | :--- |
| **Metode** | `GET` |
| **URL Endpoint** | `/api/v1/contract/client` |
| **Deskripsi** | Mengambil daftar kontrak pemberi kerja dengan opsi pencarian, filter status, dan paginasi. |

***

## 2. Request Query Parameters (Pencarian & Filter)


| Field Name | Tipe Data | Wajib? | Deskripsi |
| :--- | :--- | :--- | :--- |
| `page` | Integer | Tidak | Nomor halaman yang diminta (Default: 1). |
| `limit` | Integer | Tidak | Jumlah data per halaman (Default: 10, Max: 100). |
| `search` | String | Tidak | Mencari berdasarkan Nomor Kontrak BAG, atau Nama Pemberi Kerja. |
| `status` | String | Tidak | Filter berdasarkan status Kontrak (DRAFT, REVIEW ASMEN, REVIEW MANAGER, REVISI, APPROVED ). |
| `sort_by` | String | Tidak | Kolom yang digunakan untuk mengurutkan. Nilai yang diizinkan: `created_at`, `effective_start_date`, `effective_end_date`. |
| `sort_order` | String | Tidak | Arah pengurutan. Nilai yang diizinkan: **`asc`** (terlama ke terbaru) atau **`desc`** (terbaru ke terlama). (Default: `desc`). |

```json
// Contoh URL Request
GET /api/v1/contract/client?page=1&limit=20&status=APPROVED&search=BAG-2025
```

***

## 3. Response Sukses
Status: `HTTP 200 OK`

Data yang dikembalikan di **kolom *list*** wajib sesuai dengan permintaan dan harus menyertakan `id` sebagai kunci.

```json
// Contoh Response Sukses (HTTP 200 OK)
{
  "status": "success",
  "message": "Daftar Kontrak Pemberi Kerja berhasil diambil.",
  "pagination": {
    "total_data": 55,
    "total_pages": 3,
    "current_page": 1,
    "per_page": 10
  },
  "data": [
    {
      "id": 101,
      "contract_no_bag": "BAG-KP-2025-001",
      "client_name": "PT ABC Energy Utama",
      "contract_date": "2025-01-15",
      "effective_end_date": "2025-12-31",
      "status": "APPROVED"
    },
    {
      "id": 105,
      "contract_no_bag": "BAG-KP-2025-005",
      "client_name": "PT XYZ Logistic",
      "contract_date": "2025-03-20",
      "effective_end_date": "2026-03-20",
      "status": "DRAFT"
    }
    // ... data kontrak lainnya
  ]
}
```

***

## 3. Response Error
Status: `HTTP 400 Bad Request`

```json
{
  "status": "error",
  "message": "Validasi Parameter Gagal",
  "detail": "Nilai untuk 'status' tidak valid. Gunakan DRAFT, SUBMITTED, REVIEW_ASMEN, REVIEW_MANAGER, REJECTED, atau APPROVED."
}
```


# 🔍 READ: Melihat Detail Kontrak Pemberi Kerja

## 1. Spesifikasi Umum

| Keterangan | Detail |
| :--- | :--- |
| **Metode** | `GET` |
| **URL Endpoint** | `/api/v1/contract/client/{contract_id}` |
| **Deskripsi** | Mengambil seluruh data header, layanan detail, dan riwayat persetujuan untuk kontrak tertentu. |
| **Aktor Diizinkan** | Divisi Ophar, Manajer Cabang, Staff Ophar |

***

## 2. Request Path Parameter

| Parameter | Lokasi | Tipe Data | Wajib? | Deskripsi |
| :--- | :--- | :--- | :--- | :--- |
| `contract_no_client` | URL Path | Integer | Ya | ID unik dari Kontrak yang ingin dilihat detailnya. |

***

## 3. Response Sukses

Status: `HTTP 200 OK`

Respon harus mengembalikan objek tunggal yang terstruktur, menggabungkan data dari tiga tabel: `CONTRACT_HEADERS`, `CONTRACT_SERVICES_DETAIL`, dan `CONTRACT_APPROVAL_HISTORY`.

```json
// Contoh Response Sukses 

  "status": "success",
  "message": "Detail Kontrak Pemberi Kerja berhasil diambil.",
  "data": {
      
    // === 2.1. INFORMASI HEADER UTAMA ===
    "header": {
      "contract_no_client": 5678,
      "contract_no_bag": "BAG-2025-001",
      "contract_no_client": "CLIENT-A-2025-X",
      "client_id": 1234,
      "client_name": "PT. ABC Energy Utama",
      "contract_type": "Shipment Operation",
      "contract_date": "2025-11-01",
      "effective_start_date": "2026-01-01",
      "effective_end_date": "2026-12-31",
      "status": "APPROVED",
      "document_path": "minio/path/kp-2025-001.pdf", // Link dokumen yang diupload
      "created_by_user": "Staff Ophar Budi",
      "created_at": "2025-11-12T14:00:00Z"
    },

    // === 2.2. DETAIL LAYANAN (SERVICES) ===
    "services": [
      {
        "service_id": 201,
        "service_name": "Jasa Bongkar Muat Batubara di Jetty",
        "category": "Bongkar Muat Jetty",
        "quantity_type": "MT",
        "price_unit": 35000,
        "is_active": true
      },
      {
        "service_id": 202,
        "service_name": "Sewa Jetty dan Fasilitas",
        "category": "Jetty Management",
        "quantity_type": "Month",
        "price_unit": 50000000,
        "is_active": true
      }
    ],

    // === 2.3. RIWAYAT APPROVAL (WORKFLOW) ===
    "approval_history": [
      {
        "action": "SUBMITTED",
        "acted_by": "Staff Ophar Budi",
        "to_status": "REVIEW_ASMEN",
        "notes": null,
        "acted_at": "2025-11-12T15:00:00Z"
      },
      {
        "action": "APPROVED",
        "acted_by": "Asmen Ophar Rina",
        "to_status": "REVIEW_MANAGER",
        "notes": "Sudah dicek, lanjutkan ke Manajer Cabang.",
        "acted_at": "2025-11-12T16:30:00Z"
      },
      {
        "action": "APPROVED",
        "acted_by": "Manajer Cabang Anton",
        "to_status": "APPROVED",
        "notes": "Disetujui.",
        "acted_at": "2025-11-13T09:00:00Z"
      }
    ]
  }
}
```

***

## 4. Response Error
Status: `HTTP 404 Not Found`

```json
{
  "status": "error",
  "message": "Data tidak ditemukan",
  "detail": "Kontrak Pemberi Kerja dengan ID 999 tidak ditemukan."
}
```

# 🗑️ DELETE: Menghapus Kontrak Pemberi Kerja (Header)

## 1. Spesifikasi Umum

| Keterangan | Detail |
| :--- | :--- |
| **Metode** | `DELETE` |
| **URL Endpoint** | `/api/v1/contract/client/header/{contract_id}` |
| **Deskripsi** | Menghapus kontrak beserta semua layanan detailnya. |
| **Status Diizinkan** | **DRAFT** |
| **Aktor** | Staf Ophar |

***

## 2. Request Path Parameter

| Parameter | Lokasi | Tipe Data | Wajib? | Deskripsi |
| :--- | :--- | :--- | :--- | :--- |
| `contract_id` | URL Path | Integer | Ya | `ID` unik dari Kontrak yang akan dihapus. |

***

## 3. Business Rules

1.  *Back-end* wajib memverifikasi bahwa status kontrak adalah **'DRAFT'**. Jika status lain (Review Asmen, Review Manajer, Approved, Revisi), *request* harus ditolak.
2.  Jika penghapusan header diizinkan, semua *entry* terkait di `CONTRACT_SERVICES_DETAIL` dan `CONTRACT_APPROVAL_HISTORY` harus ikut dihapus.
3.  Validasi Status: Cek status kontrak ``{contract_id}``. Jika bukan DRAFT, tolak dengan **Error 403 Forbidden**.
4.  Validasi Data: Pastikan Kontrak {contract_id} ditemukan (jika tidak, **Error 404 Not Found**).
5.  Hapus Data: Hapus data Header dan semua Detail Layanan yang terkait dengan Kontrak {id}. (Disarankan menggunakan hard delete jika status DRAFT).

***

## 4. Response Sukses

Status: `HTTP 200 OK`

Jika penghapusan berhasil.

```json
// Contoh Response Sukses (HTTP 200 OK)
{
  "status": "success",
  "message": "Kontrak Pemberi Kerja {contract_id} dan seluruh layanan terkait berhasil dihapus."
}
```

***

## 5. Response Error & Validasi

### 5.1 Error Status Tidak Diizinkan (Krusial)

Status: `HTTP 403 Forbidden`

```json
{
  "status": "error",
  "message": "Aksi tidak diizinkan",
  "detail": "Kontrak ID {contract_id} tidak dapat dihapus karena status saat ini adalah 'SUBMITTED'. Penghapusan hanya diizinkan pada status 'DRAFT'."
}
```
### 5.2 Error Error ID Tidak Ditemukan
Status: `HTTP 404 Not Found`

```json
{
  "status": "error",
  "message": "Data tidak ditemukan",
  "detail": "Kontrak Pemberi Kerja dengan ID 999 tidak ditemukan."
}
```

# 🗑️ DELETE: Menghapus Layanan Kontrak 

## 1. Spesifikasi Umum

| Keterangan | Detail |
| :--- | :--- |
| **Metode** | `DELETE` |
| **URL Endpoint** | `/api/v1/contract/client/service/{service_id}` |
| **Deskripsi** | Menghapus satu baris layanan detail dari kontrak. |
| **Status Diizinkan** |  **DRAFT** dan  **REVISI** |
| **Aktor** | Staff Ophar |

***

## 2. Business Rules

1. Validasi ID: Pastikan Layanan Pekerjaan ``{service_id}`` ditemukan.
2. Validasi Status Kontrak: Ambil `{contract_id}` Kontrak Header dari layanan ini. Cek status Kontrak. Jika bukan **DRAFT** atau **REVISI**.
3. Hapus Data: Hapus data Layanan Pekerjaan yang memiliki `ID` ``{service_id}``. (Soft Delete)

| Path Parameter | Tipe Data | Deskripsi |
| -------- | -------- | -------- |
| `service_id`| Integer | **ID** unik dari Layanan Pekerjaan yang akan dihapus.  |


***

## 3. Response Sukses

Status: `HTTP 200 OK`

```json
// Contoh Response Sukses
{
  "status": "success",
  "message": "Layanan Pekerjaan ID 202 berhasil dihapus dari kontrak."
}
```

***

## 4. Response Error & Validasi

### 4.1. Error Status Tidak Diizinkan

Status: `HTTP 403 Forbidden`
```json
// Contoh Error jika status Kontrak sudah SUBMITTED/APPROVED
{
  "status": "error",
  "message": "Aksi tidak diizinkan",
  "detail": "Layanan tidak dapat dihapus karena Kontrak berstatus 'APPROVED'."
}
```

### 4.1. Error ID Layanan Tidak Ditemukan

Status: `HTTP 404 Not Found`

```json
// Contoh Error jika service_id tidak ditemukan
{
  "status": "error",
  "message": "Data tidak ditemukan",
  "detail": "Layanan Pekerjaan dengan ID 999 tidak ditemukan."
}
```

# 🚀 Submit Kontrak Pemberi Kerja

## 1. Spesifikasi Umum

| Keterangan | Detail |
| :--- | :--- |
| **Metode** | `POST` |
| **URL Endpoint** | `/api/v1/contract/client/{contract_id}/submit` |
| **Deskripsi** | Mengajukan Kontrak ke tahap *approval* |
| **Status Awal** | DRAFT, REVISI |
| **Status Tujuan** | **REVIEW\_ASMEN** |
| **Aktor** | Staff Ophar |

## 2. Business Rules

1. Catat Riwayat: Tambahkan entri baru ke tabel Approval History (Aksi: SUBMITTED, Status: **REVIEW_ASMEN**, Aktor: **Staff Ophar**).
2. Validasi Kelengkapan Data: Pastikan Kontrak memiliki minimal satu Layanan Pekerjaan (Service) sebelum dapat diajukan (submit).
3. Perbarui Status: Ubah status Kontrak dari status awal ke **REVIEW_ASMEN**.

***

## 3. Response Sukses

Status: `HTTP 200 OK`

```json
// Contoh Response Sukses
{
  "data": 
    "contract_id": 5678,
  "details": " "
  "message": "Kontrak Pemberi Kerja {contract_id} berhasil diajukan dan kini berstatus REVIEW_ASMEN.",
}
```
***

## 4. Response Error

### 4.1. Error Layanan Kosong 

Status: `HTTP 400 Bad Request`
```json
// Contoh Error jika tidak ada detail layanan
{
 "data": " "
  "message": "Validasi gagal",
  "detail": "Kontrak harus memiliki minimal satu Layanan Pekerjaan sebelum dapat diajukan."
}
```

### 4.2 Error Status Tidak Diizinkan 
Status: `HTTP 500 Internal Server Error`

```json
{ 
    "data": null, 
    "details": "Terjadi kesalahan saat memproses update status kontrak.", 
    "message": "Kesalahan Server Internal."
}
```

Status: `HTTP 500 Internal Server Error`
Terjadi kegagalan di backend
```json
{ 
    "status": "error", 
    "message": "Kesalahan Server Internal", 
    "detail": "Terjadi kegagalan saat menghapus data di database."
}

```

# Approval Staff

## 1. Spesifikasi Umum

| Keterangan | Detail |
| :--- | :--- |
| **Metode** | `POST` |
| **URL Endpoint** | `/api/v1/contract/client/{id}/review` |
| **Deskripsi** | Melakukan aksi review (**APPROVE**/**REVISI**) pada kontrak. |
| **Status Awal** | **REVIEW\_ASMEN**, **REVIEW\_MANAGER** |
| **Status Tujuan** | **REVIEW\_MANAGER**, **REVISI**, **APPROVED** |
| **Aktor** | Asmen Ophar, Manajer Cabang |

***

## 2. Request Body

Body ini mengintegrasikan peran pengguna yang sedang login untuk menentukan *flow* yang benar.

```json
{"message": "SUCCESS",
"data": "entity_id"}
```






