# 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"}
```