API Documentation

Dokumentasi lengkap RESTful API untuk sistem manajemen Lazizah. Semua endpoint di-prefix dengan /api/v1 dan mengembalikan JSON kecuali di endpoint download laporan.

Pengantar & Autentikasi

Base URL

https://<domain>/api/v1

Format

Request body: application/json atau multipart/form-data (jika ada upload file)

Header Autentikasi

Authorization: Bearer <token>
Accept: application/json

Struktur Response

{
  "success": true,
  "message": "string",
  "data": {} | [],
  "meta": // pagination (jika ada)
}

Hierarki Role

  • cashier Level terendah — kasir, POS
  • staff Akses inventory, resep, expense
  • manager Cash book, analytics, laporan
  • owner Manajemen user & settings
  • superuser Akses penuh termasuk hapus user

HTTP Status Codes

  • 200 OK — sukses
  • 201 Created — data berhasil dibuat
  • 401 Unauthorized — token tidak valid / salah login
  • 403 Forbidden — role tidak cukup / akun nonaktif
  • 404 Not Found — resource tidak ditemukan
  • 422 Unprocessable — validasi gagal / stok kurang
  • 429 Too Many Requests — rate limit tercapai
  • 500 Server Error

Pagination

Endpoint list mendukung parameter per_page (default bervariasi). Response meta berisi current_page, per_page, total, last_page.

ID Format

Semua resource menggunakan UUID sebagai public ID (bukan integer ID database). Kirim UUID di path parameter dan request body untuk reference ke resource lain.

Auth

POST /api/v1/auth/login Public

Login dengan username/email dan password. Mengembalikan access token. Throttle: 5 attempt/menit per identifier, 60 request/menit per IP.

Request Body
FieldTipeWajibKeterangan
loginstringcond.Username atau email (gunakan login atau email)
emailstringcond.Alternatif field login
passwordstringrequired
remember_mebooleanoptionalJika true, response menyertakan refresh_token
Response 200
{
  "success": true,
  "message": "Login successful.",
  "data": {
    "user": {
      "id":         "uuid",
      "name":       "string",
      "username":   "string|null",
      "initials":   "string|null",
      "email":      "string",
      "role":       1,
      "role_label": "Cashier",
      "phone":      "string|null",
      "is_active":  true
    },
    "token":         "1|accessToken...",
    "refresh_token": "2|refreshToken..." // hanya jika remember_me
  }
}
POST /api/v1/auth/refresh refresh token

Merotasi pasangan token. Kirim refresh_token sebagai Bearer. Menghapus semua token sesi lama dan menerbitkan pasangan baru.

Header: Authorization: Bearer <refresh_token>
Response 200
{
  "success": true,
  "data": {
    "token":         "new_access_token",
    "refresh_token": "new_refresh_token"
  }
}
POST /api/v1/auth/logout cashier+

Mencabut semua token aktif milik user yang sedang login.

Response 200
{ "success": true, "message": "Logout successful." }
GET /api/v1/auth/me cashier+

Mengambil data user yang sedang login.

Response 200 — data (User Object)
{
  "id": "uuid", "name": "string", "username": "string",
  "initials": "string", "email": "string",
  "role": 1, "role_label": "Cashier",
  "phone": "string|null", "is_active": true
}
PUT /api/v1/auth/me cashier+

Update profil user yang sedang login.

FieldTipeWajibKeterangan
namestringsometimesMax 120 karakter
usernamestring|nullsometimesMax 50, alpha_dash, unique
emailstringsometimesEmail valid, unique
phonestring|nulloptionalMax 20 karakter
PUT /api/v1/auth/me/password cashier+

Ganti password. Setelah berhasil, semua token dicabut — user harus login ulang.

FieldTipeWajib
current_passwordstringrequired
passwordstringrequired
password_confirmationstringrequired

Dashboard

GET /api/v1/dashboard cashier+

Data ringkasan untuk halaman utama dashboard. Mencakup stat card, transaksi terbaru, produk terlaris hari ini, dan order yang jatuh tempo hari ini.

Response 200 — data
{
  "stat_cards": {
    "today_revenue":     0,
    "revenue_delta_pct": 12.5,   // % vs kemarin
    "today_orders":      0,
    "orders_delta":      0,        // selisih absolut vs kemarin
    "cash_on_hand":      0,        // saldo terakhir cash book
    "today_expenses":    0,
    "expense_delta_pct": 0.0
  },
  "recent_transactions": [
    {
      "ref": "INV-001", "description": "Kue Coklat ×2, ...",
      "time": "09:30", "date": "01 Jan 2025",
      "type": "sale", "payment": "CASH",
      "amount": 85000, "status": "paid"
    }
    // expense: amount negatif (-), type: "expense"
  ],
  "top_products": [
    { "name": "string", "image": "path|null",
      "qty": 12, "revenue": 240000, "bar_pct": 100 }
  ],
  "quick_stats": {
    "busiest_hour":    "10:00–11:00",
    "avg_order_value": 75000,
    "low_stock_count": 3,
    "critical_count":  1,
    "product_count":   24,
    "profit_margin":   42.5
  },
  "orders_due_today": [ /* order objects */ ]
}

Sales

Otomatisasi penting: POST/DELETE mempengaruhi stok inventory dan Cash Book secara otomatis. Penjualan berstatus paid membuat entri kredit di Cash Book. Perubahan status cancelled mengembalikan stok.
GET /api/v1/sales cashier+

Daftar penjualan dengan filter dan ringkasan statistik (hari ini / minggu ini / bulan ini).

Query Parameters
ParameterTipeKeterangan
statusstringpaid | pending | cancelled
payment_methodstringcash | qris | transfer
date_fromdateFormat YYYY-MM-DD
date_todateFormat YYYY-MM-DD
searchstringCari di order_number / customer_name
per_pageintegerDefault: 15
Response 200 — data
{
  "items": [ /* Sale objects */ ],
  "summary": {
    "today_revenue":  0, "today_orders":  0,
    "week_revenue":   0, "week_orders":   0,
    "month_revenue":  0, "month_orders":  0
  }
}
POST /api/v1/sales cashier+

Catat transaksi penjualan baru. Harga diambil otomatis dari selling_price produk. Stok dipotong otomatis untuk status non-cancelled.

Stok divalidasi sebelum transaksi disimpan. Jika stok tidak cukup, API mengembalikan 422 dengan detail shortfall per produk.
Request Body
FieldTipeWajib
customer_namestring|nulloptional
payment_methodstringrequired
statusstringrequired
notesstring|nulloptional
sale_datedate|nulloptional
itemsarrayrequired
items[].product_iduuidrequired
items[].quantityintegerrequired
Enum Values
payment_method: cash | qris | transfer
status:         paid | pending | cancelled
Response 422 — stok kurang
{
  "success": false,
  "message": "Insufficient stock...",
  "errors": {
    "stock": [
      { "product": "Kue Coklat",
        "available": 2,
        "requested": 5 }
    ]
  }
}
GET /api/v1/sales/{sale} cashier+

Detail satu transaksi. Menggunakan route model binding (id = UUID). Memuat relasi items.product.category dan user.

PUT /api/v1/sales/{sale} cashier+

Update metadata atau status penjualan. Transisi status secara otomatis mengelola stok dan Cash Book.

Transisi status otomatis:
active → cancelled: stok dikembalikan
cancelled → active: stok dipotong (validasi dulu)
non-paid → paid: entri kredit Cash Book dibuat
paid → non-paid: entri Cash Book dihapus
FieldTipeWajib
customer_namestring|nulloptional
payment_methodstringsometimes
statusstringsometimes
notesstring|nulloptional
DELETE /api/v1/sales/{sale} cashier+

Hapus transaksi. Stok dikembalikan (jika bukan cancelled). Entri Cash Book dihapus (jika paid).

Orders (Pre-order)

GET /api/v1/orders cashier+

Daftar pre-order diurutkan berdasarkan tanggal dan waktu jatuh tempo.

Query Parameters
ParameterTipeKeterangan
statusstringComma-separated: baru,diproses,siap,selesai,batal
payment_statusstringbelum_bayar | dp | lunas
due_datedateFilter tanggal jatuh tempo tepat
date_from / date_todateRange tanggal jatuh tempo
searchstringCari di nama customer, telepon, order_number
per_pageintegerDefault: 15
POST /api/v1/orders cashier+

Buat pre-order baru. Item dapat berisi produk custom (tanpa product_id) atau produk dari catalog. Harga diinput manual.

FieldTipeWajib
customer_namestringrequired
customer_phonestring|nulloptional
due_datedaterequired
due_timeHH:MMoptional
fulfillmentstringoptional
notesstring|nulloptional
items[].product_iduuid|nulloptional
items[].product_namestringrequired
items[].qtyintegerrequired
items[].unit_priceintegerrequired
items[].item_notesstring|nulloptional
Enum Values
fulfillment: ambil | antar  (default: ambil)

status (awal): baru
Response 201 — Order Object
{
  "id":             "uuid",
  "order_number":   "ORD-001",
  "customer_name":  "string",
  "due_date":       "YYYY-MM-DD",
  "due_time":       "HH:MM|null",
  "status":         "baru",
  "payment_status": "belum_bayar",
  "total_amount":   150000,
  "amount_paid":    0,
  "remaining":      150000,
  "fulfillment":    "ambil",
  "items":          []
}
PUT /api/v1/orders/{order} cashier+

Update status, pembayaran, atau data order. Jika amount_paid ≥ total_amount, payment_status otomatis di-set ke lunas.

FieldTipeKeterangan
statusstringbaru | diproses | siap | selesai | batal
payment_statusstringbelum_bayar | dp | lunas
amount_paidintegerJika ≥ total → auto lunas
due_date / due_timedate/timeUpdate jadwal jatuh tempo
fulfillmentstringambil | antar
DELETE /api/v1/orders/{order} cashier+

Hapus pre-order.

Events

GET /api/v1/events cashier+

Daftar events/pengingat (hari besar, promo, produksi, dll). Query: from, to, type, per_page (default 30).

POST /api/v1/events cashier+
FieldTipeWajib
titlestringrequired
typestringrequired
start_datedaterequired
end_datedate|nulloptional
notesstring|nulloptional

type: hari_besar | promo | produksi | lainnya
GET /api/v1/events/{event}  ·  PUT  ·  DELETE cashier+

CRUD standar. PUT menggunakan body yang sama dengan POST.

Products

GET /api/v1/products cashier+
Query Parameters
ParameterTipeKeterangan
sortstringname(default) | price_asc | price_desc | stock_asc | stock_desc
categorystringSlug kategori
category_iduuidUUID kategori
statusstringactive | inactive
searchstringCari nama produk
price_min / price_maxintegerRange harga
stock_statusstringout | low | in
per_pageintegerDefault: 24
POST /api/v1/products manager+

Buat produk baru beserta data inventory awal. Gunakan multipart/form-data jika menyertakan gambar. Dijalankan dalam satu transaksi DB.

Content-Type: multipart/form-data jika ada field image
FieldTipeWajib
category_iduuidrequired
namestringrequired
selling_priceintegerrequired
cost_priceintegerrequired
imagefileoptional
descriptionstring|nulloptional
initial_stockintegeroptional
min_stockintegeroptional
unitstringoptional
statusstringoptional
GET /api/v1/products/{product} cashier+

Detail produk beserta relasi category dan inventory.

PUT /api/v1/products/{product} manager+

Update data produk. Gambar lama dihapus otomatis jika ada upload baru. Semua field bersifat sometimes.

category_id // sometimes|string (uuid)
name          // sometimes|string|max:120
image         // nullable|file — mengganti gambar lama
description   // nullable|string
selling_price // sometimes|integer
cost_price    // sometimes|integer
status        // sometimes|active|inactive
DELETE /api/v1/products/{product} manager+

Hapus produk (soft delete).

Categories

GET /api/v1/categories cashier+

Daftar semua kategori, diurutkan alfabetis. Setiap item menyertakan product_count.

POST /api/v1/categories manager+

Buat kategori baru. Slug di-generate otomatis dari nama. Gunakan multipart/form-data jika ada gambar.

FieldTipeWajib
namestringrequired
imagefileoptional
DELETE /api/v1/categories/{category} manager+
422 Returned jika kategori masih memiliki produk.

Profile

Endpoint profil terpisah dari /auth/me. Fungsionalitas serupa tapi diletakkan di path /profile.
GET /api/v1/profile cashier+

Data profil user yang sedang login.

PUT /api/v1/profile cashier+
FieldTipeWajib
namestringrequired
emailstringrequired
phonestring|nulloptional
PUT /api/v1/profile/password cashier+
FieldTipeWajib
current_passwordstringrequired
passwordstringrequired
password_confirmationstringrequired

Inventory

GET /api/v1/inventory staff+

Seluruh data stok produk, disertai ringkasan valuasi dan outflow 30 hari terakhir.

Query Parameters
ParameterKeterangan
statuscritical (stok=0) | low (di bawah min) | ok
Response 200 — data
{
  "items": [
    {
      "id":              "uuid",
      "product_id":     "uuid",
      "product_name":   "string",
      "product_image":  "path|null",
      "category":       "string",
      "current_stock":  50,
      "min_stock":      10,
      "unit":           "pcs",
      "stock_status":   "ok | low | critical",
      "stock_pct":      80,
      "cost_value":     500000,
      "retail_value":   750000,
      "outgoing_30d":   24,
      "last_updated_at": "datetime"
    }
  ],
  "summary": {
    "total": 30, "ok": 25, "low": 3, "critical": 2,
    "total_units": 1200,
    "total_cost_value": 12000000,
    "total_retail_value": 18000000
  }
}
GET /api/v1/inventory/alerts staff+

Daftar produk dengan stok low dan critical, digabung dalam satu array. Berguna untuk notifikasi.

PUT /api/v1/inventory/{productId} staff+

Adjust stok produk. {productId} adalah UUID produk.

FieldTipeWajib
actionstringrequired
quantityintegerrequired
min_stockintegeroptional
unitstringoptional
notesstringoptional
action: add    // tambah stok
        remove // kurangi stok
        set    // set langsung

Recipes

GET /api/v1/recipes staff+

Daftar resep. Query: search, category, include_inactive (boolean, default false), per_page (default 50).

POST /api/v1/recipes staff+

Buat resep baru beserta daftar bahan. HPP dihitung otomatis dari bahan.

FieldTipeWajib
namestringrequired
yieldintegeroptional
yield_unitstringoptional
product_iduuidoptional
stepsstringoptional
ingredientsarrayrequired
ingredients[].namestringrequired
ingredients[].qtynumericrequired
ingredients[].cost_per_unitintegerrequired
ingredients[].unitstringoptional
Response 201 — Computed Fields
{
  "total_cost":   85000,    // total biaya bahan
  "hpp_per_unit": 8500,     // HPP per yield unit
  "margin_pct":   40.5,     // margin vs harga jual produk
  "selling_price": 14000,   // dari produk terkait
  "ingredients": [
    {
      "id": "uuid", "name": "Tepung",
      "qty": 0.5, "unit": "kg",
      "cost_per_unit": 12000,
      "line_cost": 6000
    }
  ]
}
PUT /api/v1/recipes/{recipe} staff+

Update resep. Seluruh daftar bahan diganti (replace, bukan merge). Field tambahan: is_active (boolean).

Expenses

Otomatisasi: POST membuat entri debit di Cash Book. DELETE menghapus entri Cash Book terkait. Recalculate balance berjalan otomatis.
GET /api/v1/expenses staff+

Daftar pengeluaran dengan statistik bulan ini per kategori.

Query Parameters
ParameterKeterangan
categoryFilter kategori detail
date_from / date_toRange tanggal
searchCari di notes, ref_number, supplier, dan description detail
per_pageDefault: 15
Response 200 — data
{
  "items":       [ /* paginated Expense objects */ ],
  "month_total": 3500000,
  "by_category": {
    "ingredients": { "total": 2000000, "count": 5 },
    "packaging":  { "total": 500000,  "count": 2 }
    // ... kategori lain
  }
}
POST /api/v1/expenses staff+
Header Fields
FieldTipeWajib
payment_methodstringrequired
expense_datedateoptional
supplierstringoptional
reference_nostringoptional
notesstringoptional

Detail Items (details[])
FieldTipeWajib
descriptionstringrequired
categorystringrequired
amountintegerrequired
quantityintegeroptional
unit_priceintegeroptional
unitstringoptional
Enum Values
payment_method:
  cash | transfer | qris

category:
  ingredients
  packaging
  utilities
  rent
  payroll
  other
PUT /api/v1/expenses/{expense} manager+

Update pengeluaran. Jika details[] dikirim, seluruh detail lama diganti. Cash Book entry disinkronisasi otomatis dan balance di-recalculate.

DELETE /api/v1/expenses/{expense} manager+

Hapus pengeluaran dan entri Cash Book yang terkait. Balance di-recalculate.

Cash Book

Entri otomatis (dari sales/expenses) tidak bisa diedit atau dihapus langsung. Harus diedit melalui transaksi asalnya. Hanya entri manual yang bisa dimodifikasi.
GET /api/v1/cash-book/summary manager+

Ringkasan saldo dan statistik Cash Book bulan ini.

Response 200 — data
{
  "current_balance": 5000000,
  "total_credit":    8000000,  // pemasukan bulan ini
  "total_debit":     3000000,  // pengeluaran bulan ini
  "total_entries":   45,
  "opening_balance": 0,
  "updated_at":      "ISO8601"
}
GET /api/v1/cash-book manager+

Daftar entri Cash Book per bulan, diurutkan terbaru ke lama.

Query ParamKeterangan
yearDefault: tahun ini
monthDefault: bulan ini (1–12)
typecredit | debit
per_pageDefault: 15
POST /api/v1/cash-book manager+

Buat entri manual di Cash Book.

FieldTipeWajib
descriptionstringrequired
typestringrequired
amountintegerrequired
entry_datedateoptional
notesstringoptional

type: credit // pemasukan | debit // pengeluaran

Analytics

GET /api/v1/analytics manager+

Data analitik lengkap: KPI, grafik harian, breakdown per kategori, dan top sellers — dengan perbandingan periode sebelumnya.

Query Parameters
ParameterTipeKeterangan
periodinteger|string7 | 30(default) | 90 | 365 | week | month | year
date_fromdateMenggantikan period jika diisi
date_todateDefault: hari ini
Response 200 — data
{
  "period": 30, "date_from": "YYYY-MM-DD", "date_to": "YYYY-MM-DD",
  "revenue_change": 12.5,  // % vs periode sebelumnya
  "expense_change": -3.2,
  "kpi": {
    "revenue": 0, "expenses": 0, "orders": 0,
    "net_profit": 0, "gross_margin": 0.0,
    "avg_daily_orders": 0.0,
    "best_day": { "date": "YYYY-MM-DD", "label": "Monday", "revenue": 0 },
    "avg_order_value": 0,
    "busiest_hour": "10:00",
    "expense_ratio": 0.0
  },
  "daily_revenue": [
    { "date": "YYYY-MM-DD", "label": "Mon",
      "total": 0, "expenses": 0, "orders": 0 }
  ],
  "by_category": [
    { "category": "Roti", "total": 0, "pct": 45.2 }
  ],
  "top_sellers": [
    { "name": "string", "qty": 0, "revenue": 0, "bar_pct": 100 }
  ]
}

Reports

GET /api/v1/reports manager+

Riwayat 25 laporan yang pernah di-generate.

POST /api/v1/reports/generate manager+

Generate laporan. Format json mengembalikan data. Format csv, excel, pdf mengembalikan file download.

FieldTipeWajib
typestringrequired
date_fromdaterequired
date_todaterequired
formatstringrequired
group_bystringoptional
Enum Values
type:
  sales      // daftar transaksi penjualan
  expenses   // daftar pengeluaran
  cashbook   // buku kas
  inventory  // snapshot stok saat ini
  profit     // laba/rugi per periode
  summary    // ringkasan + top produk

format: json | csv | excel | pdf

group_by: day | week | month
Format selain json mengembalikan file download langsung, bukan JSON.

Users

Owner hanya dapat melihat dan mengelola user dengan role di bawah mereka sendiri. Hapus user hanya bisa dilakukan oleh superuser.
GET /api/v1/users owner+
Query Parameters
ParameterKeterangan
roleFilter berdasarkan nilai role (integer)
activetrue | false
searchCari nama atau email
per_pageDefault: 20
POST /api/v1/users owner+
FieldTipeWajib
namestringrequired
emailstringrequired
passwordstringrequired
roleintegerrequired
phonestringoptional
PUT /api/v1/users/{user} owner+

Update data user. Tidak bisa menarget diri sendiri. Jika role berubah, semua token user dicabut.

PATCH /api/v1/users/{user}/active owner+

Aktifkan atau nonaktifkan akun user. Jika dinonaktifkan, semua token dicabut.

FieldTipeWajib
is_activebooleanrequired
403 jika mencoba menarget diri sendiri atau user dengan role sama/lebih tinggi.
DELETE /api/v1/users/{user} superuser

Hapus user permanen. Hanya superuser. Token dicabut sebelum dihapus. Tidak bisa menghapus diri sendiri.

Settings

GET /api/v1/settings owner+

Ambil seluruh konfigurasi sistem (StoreSetting singleton).

PUT /api/v1/settings owner+

Update setting. Body berisi key-value sesuai field fillable StoreSetting. Field yang tidak dikenali diabaikan.

POST /api/v1/settings/danger owner+
⚠ DESTRUKTIF — DATA TIDAK BISA DIPULIHKAN. Menghapus seluruh data penjualan atau buku kas.
FieldTipeNilai yang diterima
actionstringsales | cashbook
confirm_phrasestringHarus persis: DELETE