Logo
Daftar Isi

Bab 13 · Developer API

API Penjualan (Sales)

Endpoint /sales beserta item dan pembayaran yang menyatu, cara membaca blok amounts, ledger pemasukan /sales/payments, dan pekerjaan faset /sales/facets.

Ini domain yang paling sering dipakai. Satu objek sale = satu transaksi penjualan, sudah lengkap dengan item dan pembayarannya — Anda tidak perlu request tambahan per transaksi.

GET /sales

Parameter lengkap ada di Referensi Endpoint. Yang paling sering dipakai:

# semua perubahan sejak polling terakhir, termasuk penghapusan
curl -H "Authorization: Bearer $KEY" \
  "$BASE/sales?updated_since=2026-08-13T05:30:12.000Z&include_deleted=true&limit=200"

# backfill satu bulan untuk satu cabang
curl -H "Authorization: Bearer $KEY" \
  "$BASE/sales?transaction_date_from=2026-07-31T17:00:00Z&transaction_date_to=2026-08-31T16:59:59Z&branch_id=2"

Contoh objek

{
  "id": 18234,
  "branch": { "id": 2, "code": "PST", "name": "Cabang Pusat" },
  "sales_order_number": "SO-PST-1042",
  "invoice_number": "INV-PST-987",
  "is_point_of_sale": false,
  "transaction_date": "2026-08-13T02:11:40.000Z",
  "invoice_date": "2026-08-13T05:30:12.000Z",
  "status": "COMPLETED",
  "canceled_reason": null,
  "customer": { "id": 511, "name": "Budi Santoso" },
  "salesperson": { "id": 7, "name": "Dewi" },
  "memo": null,
  "amounts": {
    "sub_total": 1500000,
    "item_discount_total": 50000,
    "sale_discount": 100000,
    "total": 1350000,
    "insurance_total": 300000,
    "net_total": 1050000,
    "paid_total": 1050000,
    "outstanding": 0
  },
  "items": [
    {
      "id": 90101,
      "item_id": 3321,
      "name": "Frame CONTOH X-01 Black",
      "brand": "CONTOH",
      "category": "Frame",
      "quantity": 1,
      "unit_price": 1500000,
      "discount": 50000,
      "line_total": 1450000
    }
  ],
  "payments": [
    {
      "id": 44210,
      "type": "ASSURANCE",
      "amount": 300000,
      "paid_at": "2026-08-13T02:11:40.000Z",
      "bank": null,
      "insurance": { "id": 3, "name": "BPJS Kesehatan", "card_number": "00012345" }
    },
    {
      "id": 44211,
      "type": "QRIS",
      "amount": 1050000,
      "paid_at": "2026-08-13T05:30:12.000Z",
      "bank": { "id": 1, "name": "BCA" },
      "insurance": null
    }
  ],
  "created_at": "2026-08-13T02:11:40.000Z",
  "updated_at": "2026-08-13T05:30:12.000Z",
  "deleted_at": null
}

Field header transaksi

FieldTipeArti
idintKunci utama. Pakai ini untuk upsert.
branchobjekCabang tempat transaksi dibuat
sales_order_numberstringFormat SO-{kode cabang}-{urutan}; menjadi POS-... untuk transaksi kasir langsung
invoice_numberstring / nullFormat INV-{kode cabang}-{urutan}; null selama belum difakturkan
is_point_of_salebooltrue untuk transaksi kasir langsung (tanpa pesanan)
transaction_datedatetimeTanggal order — dasar rekap penjualan
invoice_datedatetime / nullTanggal faktur terbit
statusenumOPEN, COMPLETED, atau CANCELED
canceled_reasonstring / nullAlasan pembatalan
customerobjek / nullPelanggan; null untuk transaksi tanpa member
salespersonobjekPengguna Lensiro yang membuat transaksi
memostring / nullCatatan bebas dari kasir
created_at / updated_atdatetimeWaktu dibuat / terakhir diubah
deleted_atdatetime / nullTerisi berarti transaksi sudah dihapus

Arti status:

NilaiArti
OPENSudah dibuat, belum difakturkan — pesanan masih berjalan
COMPLETEDSudah difakturkan, invoice_number terisi
CANCELEDDibatalkan; alasannya di canceled_reason

Field item

FieldArti
idId baris item, bukan id barang
item_idId barang di katalog — sambungkan ke /warehouse/items
name, brand, categoryNama, merek, dan kategori barang
quantityJumlah
unit_priceHarga satuan
discountDiskon untuk seluruh baris, bukan per unit
line_totalunit_price × quantity − discount

Field pembayaran

FieldArti
typeCASH, DEBIT, CREDIT, QRIS, ASSURANCE, POINT, REFUND
amountNominal. REFUND bernilai negatif, jadi penjumlahan biasa sudah benar
paid_atWaktu pembayaran diterima
bankBank untuk transaksi kartu/transfer, kalau ada
insurancePenjamin untuk tipe ASSURANCE, berikut card_number

Membaca blok amounts

Semua angka sudah dihitung server memakai rumus yang sama persis dengan tampilan di aplikasi, jadi Anda tidak perlu menghitung ulang.

FieldRumus
sub_totalJumlah unit_price × quantity seluruh item — sebelum diskon apa pun
item_discount_totalJumlah discount seluruh item
sale_discountDiskon tingkat transaksi, bukan per item
totalsub_total − item_discount_total − sale_discount
insurance_totalJumlah pembayaran bertipe ASSURANCEinilah "potongan BPJS/asuransi"
net_totaltotal − insurance_total — yang harus dibayar pelanggan sendiri
paid_totalJumlah seluruh pembayaran dikurangi insurance_total
outstandingnet_total − paid_total — sisa tagihan

Contoh angka dari objek di atas:

sub_total            1.500.000   (1 frame x Rp 1.500.000)
item_discount_total     50.000
sale_discount          100.000
                    -----------
total                1.350.000
insurance_total        300.000   (BPJS menanggung sebagian)
                    -----------
net_total            1.050.000   <- tagihan ke pelanggan
paid_total           1.050.000   (QRIS)
                    -----------
outstanding                  0   <- lunas

Untuk laporan omzet, pakai net_total (pendapatan yang benar-benar ditagihkan ke pelanggan) atau total (nilai penjualan sebelum klaim asuransi) — pilih salah satu dan konsisten. outstanding lebih besar dari nol berarti piutang.

Sub-resource satu transaksi

EndpointIsi
GET /sales/{id}Satu transaksi, bentuk persis sama dengan elemen list
GET /sales/{id}/itemsHanya array items, dibungkus { "data": [...] }
GET /sales/{id}/paymentsHanya array payments, dibungkus { "data": [...] }
GET /sales/{id}/facetsPekerjaan faset transaksi itu, berpaginasi

Semuanya membalas 404 not_found kalau transaksi tidak ada atau sudah dihapus.

Endpoint per-id ditujukan untuk pencarian satu-satu, misalnya menampilkan detail di layar Anda. Untuk tarik data massal, selalu pakai list /sales — item dan pembayaran sudah ikut di dalamnya, jadi menelusuri per id hanya memboroskan kuota rate limit.

GET /sales/payments — ledger pemasukan

Satu baris per pembayaran, bukan per transaksi. Ini padanan langsung Laporan Pemasukan di aplikasi, dan bentuk yang paling nyaman untuk rekap arus kas: cukup jumlahkan per type per hari di sisi Anda.

curl -H "Authorization: Bearer $KEY" \
  "$BASE/sales/payments?paid_from=2026-08-01T17:00:00Z&type=QRIS"
{
  "id": 44211,
  "sale_id": 18234,
  "sales_order_number": "SO-PST-1042",
  "invoice_number": "INV-PST-987",
  "branch": { "id": 2, "code": "PST", "name": "Cabang Pusat" },
  "type": "QRIS",
  "amount": 1050000,
  "paid_at": "2026-08-13T05:30:12.000Z",
  "cashier": { "id": 7, "name": "Dewi" },
  "bank": { "id": 1, "name": "BCA" },
  "insurance": null,
  "created_at": "2026-08-13T05:30:12.000Z",
  "updated_at": "2026-08-13T05:30:12.000Z",
  "deleted_at": null
}

Bedanya dengan payments yang tertanam di objek sale: di sini setiap baris membawa konteks transaksinya (nomor SO, faktur, cabang) dan kasir yang menerima, serta bisa langsung difilter per tanggal bayar dan per metode.

Uang masuk bukan omzet. Satu transaksi bisa dibayar dicicil lintas hari, dan tanggal bayar bisa jauh berbeda dari tanggal order. Untuk laporan penjualan pakai /sales; untuk laporan kas pakai /sales/payments. Ingat juga bahwa pembayaran ASSURANCE bukan uang tunai yang masuk hari itu, dan POINT bukan uang sama sekali — pisahkan keduanya kalau Anda merekap kas.

GET /sales/facets — pekerjaan faset (Fasset)

Cerminan laporan Fasset: pekerjaan pemasangan dan pemotongan lensa ke frame.

curl -H "Authorization: Bearer $KEY" "$BASE/sales/facets?status=FAILED"
{
  "id": 5521,
  "sale_id": 18234,
  "sales_order_number": "SO-PST-1042",
  "invoice_number": "INV-PST-987",
  "branch": { "id": 2, "code": "PST", "name": "Cabang Pusat" },
  "status": "SUCCESS",
  "order_date": "2026-08-13T02:11:40.000Z",
  "worked_at": "2026-08-13T04:02:00.000Z",
  "customer_name": "Budi Santoso",
  "worker_name": "Agus",
  "memo": null,
  "created_at": "2026-08-13T04:02:00.000Z",
  "updated_at": "2026-08-13T06:00:00.000Z",
  "deleted_at": null
}
FieldArti
statusWORKING (dikerjakan), SUCCESS (berhasil), FAILED (gagal atau pecah)
order_dateTanggal transaksi induk
worked_atTanggal pekerjaan dicatat
worker_namePengguna Lensiro yang mengerjakan
memoCatatan; untuk FAILED biasanya berisi penyebab kegagalan

Nilai komisi pekerjaan sengaja tidak diekspos karena termasuk data penggajian internal.

Hal-hal yang sering menjebak

  • Nomor SO bukan kunci penyimpanan. Untuk upsert pakai id; nomor SO dan faktur untuk ditampilkan ke manusia.
  • invoice_number dan invoice_date bernilai null sampai transaksi difakturkan. Kalau rekap Anda memerlukan nomor faktur, saring status = "COMPLETED".
  • Transaksi bisa berubah setelah pertama Anda lihat: pembayaran menyusul, faktur terbit belakangan, item diperbaiki, atau transaksi dibatalkan. Selalu upsert.
  • Diskon per item adalah nilai seluruh baris, bukan per unit.
  • Data HPP / harga modal dan resep kacamata sengaja tidak ada di v1 — lihat Batasan, Versi & Rencana.

Solusi lengkap untuk toko optik modern dengan teknologi terdepan dan dukungan terbaik.

Fitur

  • Penjualan Optik
  • Faset & QC
  • Inventory & Gudang
  • Membership & After-Service
  • Multi-Cabang & Roles
  • Financial Statement

Solusi

Perusahaan

©Lensiro. All Rights Reserved.

Kebijakan Privasi