POST /adjust
Spesifikasi endpoint adjust untuk increment atau decrement field numerik
Endpoint /adjust digunakan untuk melakukan operasi increment atau decrement secara atomik pada field numerik dalam satu record.
Referensi Cepat (Quick Reference)
| Properti | Nilai |
|---|---|
| Method | POST |
| URL | /api/{project}/{endpoint}/adjust |
| Content-Type | application/json |
| Status Sukses | 200 OK |
| Database | PostgreSQL, MySQL, Oracle |
| Cache | Invalidasi otomatis setelah adjust berhasil |
| Distributed Lock | WRITE lock per-record (jika diaktifkan) |
| Event Lifecycle | onBeforeAdjust → ADJUST → onAfterAdjust |
Ikhtisar (Overview)
Berbeda dengan endpoint /update yang meng-overwrite nilai field secara absolut (SET stock = 90), endpoint /adjust menggunakan operasi aritmetika di level SQL (SET stock = stock + N). Pendekatan ini bersifat atomik sehingga aman dari race condition ketika beberapa request secara bersamaan mengubah field yang sama.
Request body wajib menyertakan primary key untuk mengidentifikasi record target dan array adjustments berisi satu atau lebih operasi adjustment. Setiap adjustment menentukan field yang akan diubah, delta value (positif untuk increment, negatif untuk decrement), serta opsional reason sebagai catatan alasan perubahan.
Endpoint ini dilengkapi mekanisme guard clause di level SQL yang mencegah nilai field turun di bawah batas minimum. Jika guard clause terlanggar, server mengembalikan 409 Conflict tanpa melakukan perubahan apapun.
Format Request (Request Format)
Parameter (Parameters)
| Parameter | Tipe | Wajib | Keterangan |
|---|---|---|---|
{primaryKey} | string | Ya | Primary key record yang akan di-adjust |
adjustments | array | Ya | Array minimal 1 item berisi operasi adjustment |
adjustments[].field | string | Ya | Nama field numerik yang akan di-adjust |
adjustments[].value | number | Ya | Delta value; positif = increment, negatif = decrement |
adjustments[].reason | string | Tidak | Alasan perubahan (wajib jika reasonRequired: true di konfigurasi) |
Contoh Request (Request Examples)
Decrement stock (pengurangan):
{
"item_product_id": "550e8400-e29b-41d4-a716-446655440000",
"adjustments": [
{
"field": "stock",
"value": -5,
"reason": "Penjualan harian"
}
]
}Increment stock (penambahan):
{
"item_product_id": "550e8400-e29b-41d4-a716-446655440000",
"adjustments": [
{
"field": "stock",
"value": 20,
"reason": "Restok dari supplier"
}
]
}Multi-field adjustment:
{
"item_product_id": "550e8400-e29b-41d4-a716-446655440000",
"adjustments": [
{
"field": "stock",
"value": -3,
"reason": "Penjualan"
},
{
"field": "reserved_qty",
"value": 3,
"reason": "Reservasi untuk order ORD-2026-0042"
}
]
}Body Options
Endpoint /adjust mendukung format request body alternatif {data, options} untuk mengirimkan opsi tambahan yang dapat dibaca oleh component handler dan processor. Detail lengkap tersedia di halaman Body Options.
{
"data": {
"item_product_id": "550e8400-e29b-41d4-a716-446655440000",
"adjustments": [
{
"field": "stock",
"value": -5,
"reason": "Penjualan harian"
}
]
},
"options": {
"notify_warehouse": true
}
}Format Response (Response Format)
Response Sukses (Success Response)
HTTP 200 OK
{
"success": true,
"message": "item_product data successfully adjusted",
"data": {
"item_product_id": "550e8400-e29b-41d4-a716-446655440000",
"item_code": "ITM-001",
"item_name": "Keyboard Mechanical RGB",
"stock": 95,
"reserved_qty": 3,
"updated_at": "2026-04-16T10:30:00.000Z",
"updated_by": "Input from API"
},
"timestamp": "2026-04-16T10:30:00.000Z"
}Response Error (Error Responses)
400 — Payload kosong:
{
"success": false,
"error": "Invalid payload",
"message": "Payload cannot be empty",
"timestamp": "2026-04-16T10:30:00.000Z"
}400 — Primary key tidak dikirim:
{
"success": false,
"error": "Invalid payload",
"message": "Primary key is required for adjust operation",
"timestamp": "2026-04-16T10:30:00.000Z"
}400 — Adjustments tidak valid:
{
"success": false,
"error": "Invalid payload",
"message": "Adjustments must be a non-empty array",
"timestamp": "2026-04-16T10:30:00.000Z"
}400 — Field tidak dikonfigurasi untuk adjust:
{
"success": false,
"error": "Invalid payload",
"message": "Field 'item_name' is not configured for adjustment",
"timestamp": "2026-04-16T10:30:00.000Z"
}404 — Record tidak ditemukan:
{
"success": false,
"error": "Not found",
"message": "item_product data not found",
"timestamp": "2026-04-16T10:30:00.000Z"
}409 — Guard clause terlanggar:
{
"success": false,
"error": "Constraint violation",
"message": "Adjustment would result in value below minimum for field 'stock'",
"timestamp": "2026-04-16T10:30:00.000Z"
}500 — Internal server error:
{
"success": false,
"error": "Internal server error",
"message": "An unexpected error occurred",
"timestamp": "2026-04-16T10:30:00.000Z"
}Guard Clause
Endpoint /adjust menerapkan guard clause di level SQL untuk mencegah field numerik turun di bawah batas minimum yang dikonfigurasi. Mekanisme ini diimplementasikan sebagai kondisi WHERE dalam statement UPDATE:
UPDATE item_product
SET stock = stock + (-5)
WHERE item_product_id = '550e8400...'
AND (stock + (-5)) >= 0Jika kondisi WHERE tidak terpenuhi (misalnya stock saat ini bernilai 3 tetapi diminta decrement 5), maka tidak ada row yang ter-update. Server mendeteksi kondisi ini dan mengembalikan 409 Conflict.
Guard clause berjalan sepenuhnya di level database, sehingga aman dari race condition meskipun beberapa request adjust diproses secara bersamaan pada record yang sama.
Konfigurasi (Configuration)
Perilaku endpoint /adjust dikontrol melalui objek adjustConfig dalam konfigurasi payload. Objek ini menentukan field mana saja yang dapat di-adjust beserta constraint masing-masing.
{
"adjustConfig": {
"reasonRequired": true,
"fields": {
"stock": {
"type": "integer",
"min": 0,
"allowNegativeResult": false
},
"reserved_qty": {
"type": "integer",
"min": 0,
"allowNegativeResult": false
}
}
}
}| Properti | Tipe | Keterangan |
|---|---|---|
reasonRequired | boolean | Jika true, setiap item adjustment wajib menyertakan field reason |
fields | object | Mapping nama field ke konfigurasi constraint |
fields.{name}.type | string | Tipe data field (integer, decimal, dll.) |
fields.{name}.min | number | Nilai minimum yang diperbolehkan setelah adjustment |
fields.{name}.allowNegativeResult | boolean | Jika false, guard clause mencegah hasil adjustment bernilai negatif |
Hanya field yang terdaftar di adjustConfig.fields yang dapat di-adjust. Request terhadap field yang tidak terdaftar akan menghasilkan error 400.
Perbandingan dengan /update (Comparison with /update)
| Aspek | /adjust | /update |
|---|---|---|
| Operasi SQL | SET stock = stock + N (relatif) | SET stock = 90 (absolut) |
| Atomisitas | Atomik di level database | Tidak atomik terhadap nilai saat ini |
| Race Condition | Aman dari race condition | Rentan race condition pada concurrent write |
| Guard Clause | Didukung (batas minimum via WHERE) | Tidak tersedia |
| Kasus Penggunaan | Stok, saldo, counter, kuota | Update field umum (nama, status, alamat) |
Untuk field numerik yang sering diubah secara bersamaan (concurrent), gunakan /adjust daripada /update. Operasi /update yang absolut dapat menyebabkan kehilangan data jika dua request membaca nilai yang sama lalu meng-overwrite satu sama lain.
Perilaku Cache (Cache Behavior)
Setelah operasi adjust berhasil, seluruh cache terkait endpoint ini di-invalidasi secara otomatis. Hal ini memastikan endpoint /read, /datatables, dan /lookup mengembalikan data terbaru pada request berikutnya.
Distributed Lock
Jika fitur distributed lock diaktifkan, endpoint /adjust mengakuisisi WRITE lock pada level record sebelum menjalankan operasi. Lock ini mencegah operasi tulis lain (adjust, update, delete) pada record yang sama berjalan secara bersamaan, sehingga menjamin konsistensi (consistency) data pada skenario high-concurrency.
Lock dilepaskan secara otomatis setelah operasi selesai, baik berhasil maupun gagal.
Event Lifecycle
Jika component engine dikonfigurasi di payload, endpoint /adjust menjalankan hook berikut:
onBeforeAdjust → ADJUST di database → onAfterAdjustHook onBeforeAdjust dapat memodifikasi data adjustment sebelum dieksekusi (misalnya menambah field reason secara otomatis atau memvalidasi logika bisnis tambahan). Hook onAfterAdjust dapat menjalankan side effect setelah adjustment berhasil (misalnya mengirim notifikasi stok rendah atau mencatat log audit).
Langkah Selanjutnya (Next Steps)
- POST /update untuk memperbarui field non-numerik
- POST /first untuk mengambil data record setelah adjustment
- Kode Error untuk referensi lengkap HTTP status code