Alur Data ERP Travel
Analisis skema · penjualan tiket

Alur data ERP Travel

Perbandingan database lama erp dengan skema baru di dbdocs, lalu alur penjualan tiket yang dibeli dari beberapa supplier: bagaimana harga dihitung, invoice dibuat, dan uangnya dicatat sampai lunas. ERP ini untuk internal: semua transaksi diinput admin, customer dan supplier tidak login.

Skema baru: dbdocs · ERP Travel v4, 33 tabel, 340 kolom, 46 relasi Skema lama: database erp (MariaDB lokal), 28 tabel + 1 view, data Nov 2017 – Sep 2023

Buka Simulasi Final ERP Travel →Form admin 6 langkah dengan 4 studi kasus: Budi, PT Maju Jaya, Keluarga Wijaya, PT Contoh Sawit.

1.106reservasi tiket memakai 2 order supplier atau lebih
123rute dengan penumpang yang dibeli dari order berbeda, walau rutenya sama
190reservasi tiket yang ditagih dengan 2 invoice atau lebih
0foreign key di skema lama. Skema baru punya 46 relasi
Struktur baru

Tiga hal yang dulu bercampur, sekarang dipisah

Di skema lama, satu baris tblrsvpt2 menyimpan sekaligus data penumpang, harga jual (amti), harga beli (amto), nomor invoice, dan nomor order. Skema baru memecahnya jadi tiga jalur: data layanan (siapa terbang ke mana), dokumen jual ke customer, dan dokumen beli ke supplier. Dokumen jual dan beli bertemu lagi di invoice_item_costs untuk menghitung laba. Setelah itu uang bergerak lewat payments → payment_allocations → piutang/hutang dan saldo kas_bank.

  1. Master data

    Customer dan supplier sekarang punya kredit_limit, term_bayar_hari, dan saldo. Maskapai dan bandara punya master sendiri.

  2. Booking

    bookings adalah induk satu transaksi pelanggan: kategori layanan, customer, tanggal layanan, dan status pending → confirmed → completed / cancelled.

  3. Detail layanan

    Satu pasangan tabel per kategori. Untuk tiket: flight_bookings = satu rute (leg), booking_segments = satu penumpang di satu rute. Unit inilah yang ditagih dan dibeli.

  4. Dokumen jual & beli

    invoices/invoice_items ke customer; purchase_orders/purchase_order_items ke tiap supplier. Satu booking bisa punya banyak invoice dan banyak PO.

  5. Jembatan laba

    invoice_item_costs memasangkan tiap item jual dengan item beli. Laba = line_total − cost_amount.

  6. Piutang & hutang

    accounts_receivable (per invoice) dan accounts_payable (per PO) menyimpan sisa tagihan dan jatuh tempo untuk laporan umur piutang/hutang.

  7. Kas

    payments mencatat uang masuk/keluar di rekening kas_bank tertentu; payment_allocations membagi satu pembayaran ke satu atau beberapa AR/AP.

Peta tabel lama → baru

Skema lamaSkema baruCatatan
tblrsvpbookingsNama & alamat customer tidak lagi disalin, cukup customer_id
tblrsvpt1flight_bookingsoptno = urutan rute; ddate + dtime digabung jadi departure_time
tblrsvpt2booking_segments + invoice_items + purchase_order_items + invoice_item_costsSatu baris lama dipecah empat: data penumpang, harga jual (amti), harga beli (amto), dan penghubungnya
tblinvoice + tblarinvoices + accounts_receivableTidak lagi dua salinan: invoice = dokumen, AR = saldo & umur piutang
tblorder + tblappurchase_orders + accounts_payablePola yang sama di sisi supplier
tblreceipt, tblreceipt2payments + payment_allocations + kas_bankTabel lama kosong; pembayaran baru benar-benar tercatat
tblrsvph1 / tblrsvph2hotel_bookings / hotel_roomsHotel sekarang punya supplier_id di header
tblrsvpg1 / tblrsvpg2bus_*, visa_*, passport_*, voucher_bookingsDulu satu tabel umum untuk BUS/VISA/PASSPORT/OTHERS/TOUR
tblcustomer, tblsuppliercustomers, suppliers+ limit kredit, term bayar, saldo
tblflight, kode bandara di tblrsvpt1airlines, airportsBandara dulu tanpa master
tblsalutation, tblcurrencysalutations, currencies
tblparameterparameters + service_categoriesPenomoran dokumen pindah ke prefix (INV-, ORD-, dst.)
tbluser, tblauthority, tblmenu, tblpicusers, roles, userrolesPIC diganti created_by (FK ke users)
tblticket (kosong)booking_segments.no_tiket+ status_tiket
tblhotel, tbllocation—Tidak dibawa; nama hotel hanya teks. Lihat catatan
Lama vs baru

Yang diperbaiki di skema baru

Kolom kanan berisi bukti dari query langsung ke database erp: angka-angka ini menunjukkan masalah yang benar-benar terjadi, bukan hanya teori.

AspekSkema lama (erp)Skema baruBukti di data lama
Identitas dataPrimary key berupa teks bebas, mis. customer EMMY 2, A CEN ( HARTO TEDJA ). tblflight tanpa PKid INT auto-increment + kode unik (CUS-0001, SUP-0001)870 customer dan 407 supplier ber-PK teks
RelasiTidak ada foreign key maupun trigger46 relasi; saldo diperbarui trigger0 FK, 0 trigger di information_schema
Tipe uangDOUBLE (floating point)DECIMAL(15,2)SUM(amti − amto) = 1.744.302.299,800001 (sisa pecahan dari floating point)
Harga jual vs beliBercampur di satu baris penumpang (amti, amto) + 8 kolom rincian fareDipisah: invoice_items, purchase_order_items, dihubungkan invoice_item_costs8 kolom fare (fareamt, faretaxamt, iwamt, yqamt, grossamt, faredisci, faredisct, amtt) bernilai 0 di semua 24.625 baris
Dokumen vs saldotblinvoice dan tblar berisi kolom yang hampir sama; begitu juga tblorder/tblapInvoice = dokumen; AR = saldo & umur piutangSemua 12.485 baris tblar punya pasangan identik di tblinvoice
PembayaranHanya tanggal lunas (paiddate); tabel receipt tidak dipakaipayments + payment_allocations + kas_bank: bisa cicil, satu transfer untuk banyak invoice, saldo per rekeningreceiptamt = 0 di semua baris; tblreceipt & tblreceipt2 kosong; hanya paiddate terisi (12.143 dari 12.485)
Jatuh tempoKolom duedate ada tapi tidak pernah diisitgl_jatuh_tempo wajib, dihitung dari term_bayar_hariduedate kosong di 12.485 AR dan 13.327 AP
Batas kreditTidak adakredit_limit di customers & suppliers—
Pajak & diskonHanya totalamtsubtotal, tax_pct, tax_amount, diskon, total_amount, paid_amount, remaining_amount—
StatusKolom sts ada tapi tidak dipakaiStatus eksplisit di booking, invoice, PO, AR/AP, dan tiketsts NULL di 12.209 reservasi dan 24.625 baris penumpang
Nomor tiketticketno + tblticketno_tiket + status_tiket (issued / refunded / rescheduled)ticketno terisi 5 dari 24.625; tblticket kosong
Bandara & jamKode bandara tanpa master; jam disimpan sebagai tanggal 1899-12-30airports (IATA, kota, timezone); departure_time DATETIME utuhContoh dtime: 1899-12-30 13:20:39
Kolom matitblrsvp.invoiceno/orderno, tblsupplier.categoryidRelasi dari sisi anak (invoices.booking_id, purchase_orders.booking_id)Kosong di 12.209 reservasi; categoryid kosong di 407 supplier
PenomoranYYMM + blok counter per kategori di tblparameter (00001 tiket, 50001 hotel, 80001 umum)Prefix per dokumen: BKG-, INV-, ORD-, AR-, AP-, RCV-, PAY-—
Auditmodifiedby/modifiedon tekscreated_by (FK users), created_at, updated_at—

Yang sudah benar di skema lama dan perlu dipertahankan: total invoice selalu cocok dengan jumlah harga jual penumpang (10.269 invoice tiket, 0 selisih), begitu pula total order dengan harga beli (11.014 order, 0 selisih). Aturan ini perlu tetap dijaga di skema baru: invoices.subtotal = Σ invoice_items.line_total.

Proses · diinput admin

Alur penjualan tiket dengan supplier berbeda

ERP ini dipakai internal. Customer menghubungi kantor lewat telepon, WA, atau datang langsung. Admin mengecek kursi dan harga di sistem supplier/konsolidator, melakukan booking di sana, lalu menginput transaksinya ke ERP. Karena saat input admin sudah tahu supplier dan harga beli, satu layar input bisa langsung menghasilkan PO ke tiap supplier dan draft invoice, jadi admin tidak membuat dokumen satu per satu.

Unit terkecilnya adalah satu penumpang di satu rute (booking_segments). Setiap unit punya tepat satu harga jual (item invoice) dan satu harga beli (item PO). Karena itu invoice bisa dikelompokkan per customer atau per penumpang, dan PO per supplier, tanpa saling mengganggu.

  1. Input booking tiket

    Layar: Booking Tiket
    Admin mengisi
    • Customer, dipilih dari master. Customer baru didaftarkan dulu.
    • Rute: maskapai, no. penerbangan, bandara asal/tujuan, jam berangkat/tiba, kelas, PNR. Pulang-pergi = 2 rute; transit = 1 rute per leg.
    • Penumpang: nama sesuai dokumen, sapaan, tipe pax (ADT/CHD/INF).
    • Untuk setiap penumpang di setiap rute: supplier, harga beli (NTA), harga jual.
    Sistem otomatis
    • Nomor BKG-, created_by = admin yang login, status pending.
    • Membuat 1 baris booking_segments per penumpang × rute.
    • Menampilkan margin per baris dan total, dengan peringatan bila harga jual ≤ harga beli. Di data lama ada 8 transaksi rugi dan 32 impas.
    • Saat disimpan: PO draft per supplier (1 item per segmen), invoice draft (1 item per segmen), dan pasangan invoice_item_costs.
    bookingsflight_bookingsbooking_segmentspurchase_orderspurchase_order_itemssegment_ref (usulan)invoicesinvoice_itemsinvoice_item_costs
    purchase_order_items.subtotal = unit_cost × qty → PO.total = Σ subtotal + tax invoice_items.line_total = harga_satuan × qty − diskon_item invoice_item_costs.cost_amount = unit_cost item PO dengan segmen yang sama (allocation_pct 100)
  2. Issued tiket

    Layar: Booking Tiket › Issued
    Admin mengisi
    • Nomor e-ticket per penumpang per rute dari supplier.
    • PNR per penumpang bila berbeda dari PNR rute.
    Sistem otomatis
    • Menolak issued bila no_tiket kosong. Di data lama nomor tiket hanya tercatat 5 dari 24.625.
    • status_tiket = issued, booking → confirmed, PO supplier terkait → confirmed.
    • Membuat 1 AP per PO (outstanding = total PO, status open) dan menambah suppliers.saldo_hutang.
    booking_segmentspurchase_orders→ accounts_payable→ suppliers.saldo_hutang
    AP.tgl_jatuh_tempo = po_date + suppliers.term_bayar_hari
  3. Periksa dan aktifkan invoice

    Layar: Invoice
    Admin mengisi
    • Memeriksa draft invoice dari tahap 1.
    • Menambah biaya layanan (biaya_lain), airport_tax, atau diskon bila ada.
    • Bila customer minta tagihan terpisah: buat invoice baru untuk penumpang tertentu selagi masih draft.
    • Klik Aktifkan (atau ajukan ke atasan bila memakai approval), lalu cetak/kirim invoice.
    Sistem otomatis
    • Menghitung subtotal, pajak, total, dan tanggal jatuh tempo.
    • Cek limit kredit. Bila melebihi, invoice tidak bisa aktif kecuali dibayar tunai.
    • Menolak segmen yang sudah ada di invoice lain yang belum void.
    • Nomor INV-, membuat AR, menambah customers.saldo_piutang, mengunci item. Koreksi setelah aktif = void lalu invoice baru.
    invoicesinvoice_items→ accounts_receivable→ customers.saldo_piutang
    subtotal = Σ line_total (item status active) tax_amount = subtotal × tax_pct / 100 total_amount = subtotal + tax_amount − diskon tgl_jatuh_tempo = tgl_invoice + customers.term_bayar_hari syarat kredit : saldo_piutang + total_amount ≤ kredit_limit (kredit_limit = 0 → wajib tunai)
  4. Catat pembayaran customer

    Layar: Penerimaan
    Admin mengisi
    • Pilih customer; sistem menampilkan invoice yang belum lunas.
    • Tanggal, rekening kas/bank, metode (cash/transfer/QRIS/kartu/giro), jumlah, no. bukti transfer.
    • Pilih invoice yang dibayar dan jumlah untuk masing-masing. Satu transfer boleh untuk beberapa invoice.
    • Bila customer punya deposit, isi Pakai deposit untuk melunasi dengan saldo deposit.
    Sistem otomatis
    • Nomor RCV-, payment_type = receipt.
    • Menolak alokasi yang melebihi sisa tagihan. Kelebihan uang tidak ditolak: disimpan sebagai deposit customer.
    • Memperbarui AR, status invoice, saldo rekening, dan saldo piutang customer.
    paymentspayment_allocations→ AR, invoices, kas_bank, customers
    AR.outstanding_amount = original_amount − Σ amount_allocated AR.status = open (belum ada bayar) · partial (sebagian) · lunas (outstanding = 0) invoices.paid_amount = Σ alokasi; remaining_amount = total_amount − paid_amount kas_bank.saldo_saat_ini += amount (seluruh uang diterima) customers.saldo_piutang −= Σ alokasi (bukan uang diterima) payments.unallocated_amount = amount − Σ alokasi → deposit customer customers.saldo_deposit = Σ unallocated_amount
  5. Bayar supplier

    Layar: Pembayaran Supplier
    Admin mengisi
    • Pilih supplier; sistem menampilkan PO yang belum lunas, diurutkan dari jatuh tempo terdekat.
    • Tanggal, rekening sumber, metode, jumlah, no. bukti transfer, dan PO yang dibayar.
    Sistem otomatis
    • Nomor PAY-, payment_type = disbursement.
    • Peringatan bila saldo rekening sumber tidak cukup.
    • Memperbarui AP, status PO, saldo rekening, dan saldo hutang supplier.
    paymentspayment_allocations→ AP, purchase_orders, kas_bank, suppliers
  6. Selesai dan laporan

    Layar: Laporan
    Admin
    • Menandai booking selesai setelah tanggal terbang. Ini juga bisa otomatis oleh sistem.
    • Membaca laporan untuk tindak lanjut penagihan dan pembayaran.
    Sistem otomatis
    • Booking → completed.
    • Laba kotor per booking, per supplier, dan per admin (created_by).
    • Umur piutang dan hutang dari tgl_jatuh_tempo, serta mutasi dan saldo per rekening.
Contoh hitungan

Dua penumpang, pulang-pergi, dua supplier

Harga per penumpang diambil dari reservasi 230900088 di data lama (nama disamarkan), lalu dibuat untuk 2 penumpang dan ditambah biaya layanan Rp 25.000/pax untuk menunjukkan item tanpa biaya beli. Pajak 0% seperti data lama.

Jalankan di simulator →Pilih studi kasus PT Contoh Sawit: pesanan, PO, dan invoice Rp 18.010.000-nya sama dengan contoh ini. Tanggal dan cara bayarnya sedikit berbeda, karena di simulator customer memakai deposit lama dan kelebihannya menjadi deposit baru.

BKG-2026-0001 PT Contoh Sawit · corporate · term 14 hari · limit kredit Rp 50.000.000 · saldo piutang saat ini Rp 12.000.000
Rute 1 · flight_bookings #1KNO → CGKID 6883 · Economy · 1 Okt 2026Konsolidator A · term 7 hari
Rute 2 · flight_bookings #2CGK → KNOGA 190 · Economy · 3 Okt 2026Konsolidator B · term 3 hari

booking_segments: satu baris per penumpang per rute

SegmenPenumpangRuteSupplierHarga jualHarga beliMargin
#1Penumpang A · ADTKNO → CGKKonsolidator A3.330.0003.262.25067.750
#2Penumpang B · ADTKNO → CGKKonsolidator A3.330.0003.262.25067.750
#3Penumpang A · ADTCGK → KNOKonsolidator B5.650.0005.558.00092.000
#4Penumpang B · ADTCGK → KNOKonsolidator B5.650.0005.558.00092.000
Total tiket17.960.00017.640.500319.500

Yang diketik admin di layar Booking Tiket hanya customer, 2 rute, 2 penumpang, serta supplier, harga beli, dan harga jual per baris di atas. Biaya layanan ditambahkan di layar Invoice. Semua angka lain di bawah (invoice, PO, AR/AP, laba) dihitung sistem.

INV-2026-0001tgl 25 Sep 2026 · jatuh tempo 9 Okt 2026 (+14 hari)
Item · segment_refQty × hargaline_total
Tiket Penumpang A KNO-CGK ID6883
booking_segment #1
1 × 3.330.0003.330.000
Tiket Penumpang B KNO-CGK ID6883
booking_segment #2
1 × 3.330.0003.330.000
Tiket Penumpang A CGK-KNO GA190
booking_segment #3
1 × 5.650.0005.650.000
Tiket Penumpang B CGK-KNO GA190
booking_segment #4
1 × 5.650.0005.650.000
Biaya layanan
tipe biaya_lain · other
2 × 25.00050.000
subtotal18.010.000
tax_amount (tax_pct 0%)0
diskon0
total_amount18.010.000

Cek kredit: 12.000.000 + 18.010.000 = 30.010.000 ≤ 50.000.000 → boleh kredit.

ORD-2026-0001Konsolidator A · jatuh tempo 2 Okt 2026 (+7)
Item · segment_refunit_cost
KNO-CGK Penumpang A #13.262.250
KNO-CGK Penumpang B #23.262.250
total_amount6.524.500
ORD-2026-0002Konsolidator B · jatuh tempo 28 Sep 2026 (+3)
Item · segment_refunit_cost
CGK-KNO Penumpang A #35.558.000
CGK-KNO Penumpang B #45.558.000
total_amount11.116.000

Satu invoice ke customer, dua PO ke dua supplier. invoice_item_costs berisi 4 baris: item 1–2 ↔ ORD-0001, item 3–4 ↔ ORD-0002. Biaya layanan tidak punya pasangan.

Pergerakan piutang, hutang, dan kas

TanggalKejadianTabelAR-0001AP-0001 (A)AP-0002 (B)Kas BCA-01 (kumulatif)
25 SepInvoice aktif, kedua PO confirmedaccounts_receivable, accounts_payable18.010.0006.524.50011.116.0000
28 SepPAY-2026-0001 ke Konsolidator B (jatuh tempo)payments · alokasi ap_id18.010.0006.524.5000 · lunas−11.116.000
30 SepRCV-2026-0001 dari customer, cicilanpayments · alokasi ar_id8.010.000 · partial6.524.5000−1.116.000
2 OktPAY-2026-0002 ke Konsolidator A (jatuh tempo)payments · alokasi ap_id8.010.0000 · lunas0−7.640.500
8 OktRCV-2026-0002 pelunasan customerpayments · alokasi ar_id0 · lunas00+369.500

Supplier B jatuh tempo lebih dulu daripada customer membayar, sehingga kas sempat minus Rp 11.116.000. Karena itu tgl_jatuh_tempo per PO dan per invoice perlu dipantau di laporan umur hutang/piutang.

18.010.000Total jual · Σ invoice_items.line_total
17.640.500Total beli · Σ invoice_item_costs.cost_amount
369.500Laba kotor (2,05%) = kas akhir setelah semua lunas

Dua variasi yang sering terjadi

Invoice terpisah per penumpang

Terjadi di 190 reservasi lama, misalnya reservasi 230800078: invoice per penumpang, order per rute. Caranya cukup dengan membuat invoice baru.

  • Belum ada invoice: buat 2 invoice untuk booking yang sama. INV-0001 berisi segmen #1, #3 + biaya layanan 25.000 = 9.005.000; INV-0002 berisi #2, #4 + 25.000 = 9.005.000. Masing-masing punya AR sendiri.
  • Sudah ada invoice gabungan yang aktif: void invoice lama (AR-nya ikut ditutup, saldo piutang dikurangi), lalu buat invoice baru per penumpang. Void hanya untuk invoice yang belum dibayar (paid_amount = 0); jika sudah ada pembayaran, alokasikan ulang dulu ke invoice baru.
  • PO tidak berubah karena PO dikelompokkan per supplier, bukan per invoice. Item invoice baru dihubungkan lagi ke item PO yang sama lewat invoice_item_costs.

Penumpang di rute yang sama, beda supplier

Terjadi di 123 rute lama, misalnya karena kursi di supplier pertama habis.

  • Penumpang B rute 1 dibeli dari Konsolidator C: buat ORD-0003 berisi 1 item (segmen #2). ORD-0001 tinggal 1 item (segmen #1).
  • Invoice ke customer tidak berubah sama sekali.
  • Hanya bisa dilacak rapi jika purchase_order_items punya segment_ref (kolom tambahan yang diusulkan).
Referensi

Rumus dan query laba

Laba kotor per booking

-- biaya dijumlah per item dulu agar tidak dobel
SELECT b.no_booking,
       SUM(ii.line_total)                     AS total_jual,
       SUM(COALESCE(c.cost, 0))               AS total_beli,
       SUM(ii.line_total - COALESCE(c.cost, 0)) AS laba_kotor
FROM invoice_items ii
JOIN invoices i ON i.id = ii.invoice_id
                AND i.status NOT IN ('draft', 'void')
JOIN bookings b ON b.id = i.booking_id
LEFT JOIN (
    SELECT invoice_item_id, SUM(cost_amount) AS cost
    FROM invoice_item_costs
    GROUP BY invoice_item_id
) c ON c.invoice_item_id = ii.id
WHERE ii.status = 'active'
GROUP BY b.id, b.no_booking;

Laba kotor per supplier

-- asumsi: 1 item jual ↔ 1 item beli (tiket)
-- invoice void diabaikan agar biaya tidak terhitung dobel
SELECT s.nama                              AS supplier,
       SUM(c.cost_amount)                  AS total_beli,
       SUM(ii.line_total)                  AS total_jual,
       SUM(ii.line_total - c.cost_amount)  AS laba_kotor
FROM invoice_item_costs c
JOIN invoice_items ii        ON ii.id = c.invoice_item_id
                            AND ii.status = 'active'
JOIN invoices i              ON i.id = ii.invoice_id
                            AND i.status NOT IN ('draft', 'void')
JOIN purchase_order_items pi ON pi.id = c.purchase_order_item_id
JOIN purchase_orders po      ON po.id = pi.po_id
                            AND po.status <> 'void'
JOIN suppliers s             ON s.id = po.supplier_id
GROUP BY s.id, s.nama;

Transisi status

DokumenAlur status
bookingspending → confirmed → completed · cancelled
booking_segmentspending → issued → refunded / rescheduled · cancelled
purchase_ordersdraft → confirmed → partial → paid · void
invoicesdraft → active → partial → paid · void (koreksi = invoice baru)
AR / APopen → partial → lunas