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.
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.
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.
segment_ref_type/id (sudah ada)
usulan tambahan
jembatan laba
titik masuk transaksi
Customer dan supplier sekarang punya kredit_limit, term_bayar_hari, dan saldo. Maskapai dan bandara punya master sendiri.
bookings adalah induk satu transaksi pelanggan: kategori layanan, customer, tanggal layanan, dan status pending → confirmed → completed / cancelled.
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.
invoices/invoice_items ke customer; purchase_orders/purchase_order_items ke tiap supplier. Satu booking bisa punya banyak invoice dan banyak PO.
invoice_item_costs memasangkan tiap item jual dengan item beli. Laba = line_total − cost_amount.
accounts_receivable (per invoice) dan accounts_payable (per PO) menyimpan sisa tagihan dan jatuh tempo untuk laporan umur piutang/hutang.
payments mencatat uang masuk/keluar di rekening kas_bank tertentu; payment_allocations membagi satu pembayaran ke satu atau beberapa AR/AP.
| Skema lama | Skema baru | Catatan |
|---|---|---|
tblrsvp | bookings | Nama & alamat customer tidak lagi disalin, cukup customer_id |
tblrsvpt1 | flight_bookings | optno = urutan rute; ddate + dtime digabung jadi departure_time |
tblrsvpt2 | booking_segments + invoice_items + purchase_order_items + invoice_item_costs | Satu baris lama dipecah empat: data penumpang, harga jual (amti), harga beli (amto), dan penghubungnya |
tblinvoice + tblar | invoices + accounts_receivable | Tidak lagi dua salinan: invoice = dokumen, AR = saldo & umur piutang |
tblorder + tblap | purchase_orders + accounts_payable | Pola yang sama di sisi supplier |
tblreceipt, tblreceipt2 | payments + payment_allocations + kas_bank | Tabel lama kosong; pembayaran baru benar-benar tercatat |
tblrsvph1 / tblrsvph2 | hotel_bookings / hotel_rooms | Hotel sekarang punya supplier_id di header |
tblrsvpg1 / tblrsvpg2 | bus_*, visa_*, passport_*, voucher_bookings | Dulu satu tabel umum untuk BUS/VISA/PASSPORT/OTHERS/TOUR |
tblcustomer, tblsupplier | customers, suppliers | + limit kredit, term bayar, saldo |
tblflight, kode bandara di tblrsvpt1 | airlines, airports | Bandara dulu tanpa master |
tblsalutation, tblcurrency | salutations, currencies | |
tblparameter | parameters + service_categories | Penomoran dokumen pindah ke prefix (INV-, ORD-, dst.) |
tbluser, tblauthority, tblmenu, tblpic | users, roles, userroles | PIC diganti created_by (FK ke users) |
tblticket (kosong) | booking_segments.no_tiket | + status_tiket |
tblhotel, tbllocation | — | Tidak dibawa; nama hotel hanya teks. Lihat catatan |
Kolom kanan berisi bukti dari query langsung ke database erp: angka-angka ini menunjukkan masalah yang benar-benar terjadi, bukan hanya teori.
| Aspek | Skema lama (erp) | Skema baru | Bukti di data lama |
|---|---|---|---|
| Identitas data | Primary key berupa teks bebas, mis. customer EMMY 2, A CEN ( HARTO TEDJA ). tblflight tanpa PK | id INT auto-increment + kode unik (CUS-0001, SUP-0001) | 870 customer dan 407 supplier ber-PK teks |
| Relasi | Tidak ada foreign key maupun trigger | 46 relasi; saldo diperbarui trigger | 0 FK, 0 trigger di information_schema |
| Tipe uang | DOUBLE (floating point) | DECIMAL(15,2) | SUM(amti − amto) = 1.744.302.299,800001 (sisa pecahan dari floating point) |
| Harga jual vs beli | Bercampur di satu baris penumpang (amti, amto) + 8 kolom rincian fare | Dipisah: invoice_items, purchase_order_items, dihubungkan invoice_item_costs | 8 kolom fare (fareamt, faretaxamt, iwamt, yqamt, grossamt, faredisci, faredisct, amtt) bernilai 0 di semua 24.625 baris |
| Dokumen vs saldo | tblinvoice dan tblar berisi kolom yang hampir sama; begitu juga tblorder/tblap | Invoice = dokumen; AR = saldo & umur piutang | Semua 12.485 baris tblar punya pasangan identik di tblinvoice |
| Pembayaran | Hanya tanggal lunas (paiddate); tabel receipt tidak dipakai | payments + payment_allocations + kas_bank: bisa cicil, satu transfer untuk banyak invoice, saldo per rekening | receiptamt = 0 di semua baris; tblreceipt & tblreceipt2 kosong; hanya paiddate terisi (12.143 dari 12.485) |
| Jatuh tempo | Kolom duedate ada tapi tidak pernah diisi | tgl_jatuh_tempo wajib, dihitung dari term_bayar_hari | duedate kosong di 12.485 AR dan 13.327 AP |
| Batas kredit | Tidak ada | kredit_limit di customers & suppliers | — |
| Pajak & diskon | Hanya totalamt | subtotal, tax_pct, tax_amount, diskon, total_amount, paid_amount, remaining_amount | — |
| Status | Kolom sts ada tapi tidak dipakai | Status eksplisit di booking, invoice, PO, AR/AP, dan tiket | sts NULL di 12.209 reservasi dan 24.625 baris penumpang |
| Nomor tiket | ticketno + tblticket | no_tiket + status_tiket (issued / refunded / rescheduled) | ticketno terisi 5 dari 24.625; tblticket kosong |
| Bandara & jam | Kode bandara tanpa master; jam disimpan sebagai tanggal 1899-12-30 | airports (IATA, kota, timezone); departure_time DATETIME utuh | Contoh dtime: 1899-12-30 13:20:39 |
| Kolom mati | tblrsvp.invoiceno/orderno, tblsupplier.categoryid | Relasi dari sisi anak (invoices.booking_id, purchase_orders.booking_id) | Kosong di 12.209 reservasi; categoryid kosong di 407 supplier |
| Penomoran | YYMM + blok counter per kategori di tblparameter (00001 tiket, 50001 hotel, 80001 umum) | Prefix per dokumen: BKG-, INV-, ORD-, AR-, AP-, RCV-, PAY- | — |
| Audit | modifiedby/modifiedon teks | created_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.
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.
BKG-, created_by = admin yang login, status pending.booking_segments per penumpang × rute.invoice_item_costs.no_tiket kosong. Di data lama nomor tiket hanya tercatat 5 dari 24.625.status_tiket = issued, booking → confirmed, PO supplier terkait → confirmed.suppliers.saldo_hutang.biaya_lain), airport_tax, atau diskon bila ada.INV-, membuat AR, menambah customers.saldo_piutang, mengunci item. Koreksi setelah aktif = void lalu invoice baru.RCV-, payment_type = receipt.PAY-, payment_type = disbursement.created_by).tgl_jatuh_tempo, serta mutasi dan saldo per rekening.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.
| Segmen | Penumpang | Rute | Supplier | Harga jual | Harga beli | Margin |
|---|---|---|---|---|---|---|
| #1 | Penumpang A · ADT | KNO → CGK | Konsolidator A | 3.330.000 | 3.262.250 | 67.750 |
| #2 | Penumpang B · ADT | KNO → CGK | Konsolidator A | 3.330.000 | 3.262.250 | 67.750 |
| #3 | Penumpang A · ADT | CGK → KNO | Konsolidator B | 5.650.000 | 5.558.000 | 92.000 |
| #4 | Penumpang B · ADT | CGK → KNO | Konsolidator B | 5.650.000 | 5.558.000 | 92.000 |
| Total tiket | 17.960.000 | 17.640.500 | 319.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.
| Item · segment_ref | Qty × harga | line_total |
|---|---|---|
| Tiket Penumpang A KNO-CGK ID6883 booking_segment #1 | 1 × 3.330.000 | 3.330.000 |
| Tiket Penumpang B KNO-CGK ID6883 booking_segment #2 | 1 × 3.330.000 | 3.330.000 |
| Tiket Penumpang A CGK-KNO GA190 booking_segment #3 | 1 × 5.650.000 | 5.650.000 |
| Tiket Penumpang B CGK-KNO GA190 booking_segment #4 | 1 × 5.650.000 | 5.650.000 |
| Biaya layanan tipe biaya_lain · other | 2 × 25.000 | 50.000 |
| subtotal | 18.010.000 | |
| tax_amount (tax_pct 0%) | 0 | |
| diskon | 0 | |
| total_amount | 18.010.000 | |
Cek kredit: 12.000.000 + 18.010.000 = 30.010.000 ≤ 50.000.000 → boleh kredit.
| Item · segment_ref | unit_cost |
|---|---|
| KNO-CGK Penumpang A #1 | 3.262.250 |
| KNO-CGK Penumpang B #2 | 3.262.250 |
| total_amount | 6.524.500 |
| Item · segment_ref | unit_cost |
|---|---|
| CGK-KNO Penumpang A #3 | 5.558.000 |
| CGK-KNO Penumpang B #4 | 5.558.000 |
| total_amount | 11.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.
| Tanggal | Kejadian | Tabel | AR-0001 | AP-0001 (A) | AP-0002 (B) | Kas BCA-01 (kumulatif) |
|---|---|---|---|---|---|---|
| 25 Sep | Invoice aktif, kedua PO confirmed | accounts_receivable, accounts_payable | 18.010.000 | 6.524.500 | 11.116.000 | 0 |
| 28 Sep | PAY-2026-0001 ke Konsolidator B (jatuh tempo) | payments · alokasi ap_id | 18.010.000 | 6.524.500 | 0 · lunas | −11.116.000 |
| 30 Sep | RCV-2026-0001 dari customer, cicilan | payments · alokasi ar_id | 8.010.000 · partial | 6.524.500 | 0 | −1.116.000 |
| 2 Okt | PAY-2026-0002 ke Konsolidator A (jatuh tempo) | payments · alokasi ap_id | 8.010.000 | 0 · lunas | 0 | −7.640.500 |
| 8 Okt | RCV-2026-0002 pelunasan customer | payments · alokasi ar_id | 0 · lunas | 0 | 0 | +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.
Terjadi di 190 reservasi lama, misalnya reservasi 230800078: invoice per penumpang, order per rute. Caranya cukup dengan membuat invoice baru.
paid_amount = 0); jika sudah ada pembayaran, alokasikan ulang dulu ke invoice baru.invoice_item_costs.Terjadi di 123 rute lama, misalnya karena kursi di supplier pertama habis.
purchase_order_items punya segment_ref (kolom tambahan yang diusulkan).-- 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;
-- 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;
| Dokumen | Alur status |
|---|---|
| bookings | pending → confirmed → completed · cancelled |
| booking_segments | pending → issued → refunded / rescheduled · cancelled |
| purchase_orders | draft → confirmed → partial → paid · void |
| invoices | draft → active → partial → paid · void (koreksi = invoice baru) |
| AR / AP | open → partial → lunas |