# DATABASE SCHEMA - Aplikasi Billing B481 **Database Type:** SQLite 3 (better-sqlite3) **Location:** `database/billing.db` **Journal Mode:** WAL (Write-Ahead Logging) **Foreign Keys:** Enabled --- ## Schema Overview ### 1. Configuration & Settings - `settings` - Key-value store untuk runtime configuration - `expense_categories` - Master kategori pengeluaran ### 2. Master Data - `packages` - Paket layanan ISP - `customers` - Data pelanggan - `routers` - MikroTik router configuration - `olts` - OLT devices - `odps` - ODP distribution points ### 3. Billing & Payment - `invoices` - Tagihan pelanggan - `payments` - Pembayaran/transaksi - `payment_approvals` - Approval workflow kolektor ### 4. Network Management (PPPoE) - `pppoe_users` - User PPPoE list - `pppoe_profiles` - Speed profile PPPoE - `pppoe_traffic_samples` - Traffic monitoring samples - `pppoe_sessions` - Active PPPoE sessions ### 5. Hotspot Management - `hotspot_users` - Hotspot user list - `hotspot_profiles` - Hotspot speed profiles - `hotspot_sessions` - Active hotspot sessions ### 6. Voucher Management - `voucher_batches` - Batch voucher grouping - `vouchers` - Individual voucher codes ### 7. Device Management (ACS/TR-069) - `acsdevices` - Daftar perangkat ACS (ONU/CPE) - `acs_sessions` - TR-069 session tracking - `acs_device_params` - Device parameters per ONU ### 8. OLT & ONU Management - `onu_mapping` - Pemetaan ONU ke customer - `onu_provision` - Status provisioning ONU - `olu_ports` - OLT port information ### 9. Operational Data - `audit_trail` - Log aktivitas sensitif - `expenses` - Catatan pengeluaran operasional - `cash_in` - Catatan pemasukan uang - `technicians` - Data teknisi + login - `cashiers` - Data kasir - `agents` - Data agen penjualan - `collectors` - Data kolektor - `inventory_items` - Item gudang - `attendance` - Kehadiran karyawan - `tickets` - Tiket support pelanggan ### 10. Payment & Digital - `vomit_log` - Log webhook pembayaran (generic) --- ## Detailed Table Structures ### settings ```sql CREATE TABLE settings ( id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT NOT NULL UNIQUE, value TEXT, type TEXT DEFAULT 'string', created_at DATETIME DEFAULT (NOW_LOCAL()), updated_at DATETIME DEFAULT (NOW_LOCAL()) ); ``` **Purpose:** Store dynamic settings without restart **Examples:** - `company_name`, `company_phone`, `company_email` - `billing_day`, `isolir_day` - Feature flags: `whatsapp_enabled`, `login_otp_enabled` --- ### packages ```sql CREATE TABLE packages ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, price INTEGER NOT NULL DEFAULT 0, -- Harga reguler (Rp) promo_price INTEGER DEFAULT NULL, -- Harga promo (Rp) promo_cycles INTEGER DEFAULT 0, -- Jumlah siklus promo speed_down INTEGER DEFAULT 0, -- Kecepatan download (Mbps) speed_up INTEGER DEFAULT 0, -- Kecepatan upload (Mbps) description TEXT DEFAULT '', is_active INTEGER DEFAULT 1, jam_kalong_enabled INTEGER DEFAULT 0, -- Night profile feature jam_kalong_profile_malam TEXT, -- Night profile name fup_enabled INTEGER DEFAULT 0, -- Fair Usage Policy fup_limit INTEGER DEFAULT 0, -- GB limit fup_profile_turun TEXT, -- Degraded profile is_hotspot INTEGER DEFAULT 0, created_at DATETIME DEFAULT (NOW_LOCAL()) ); ``` **Purpose:** Define service packages offered **Example:** ``` id=1, name='Premium 10Mbps', price=100000, speed_down=10, speed_up=5 ``` --- ### customers ```sql CREATE TABLE customers ( id INTEGER PRIMARY KEY AUTOINCREMENT, no_pelanggan TEXT UNIQUE NOT NULL, -- Customer ID nama_pelanggan TEXT NOT NULL, email TEXT, phone TEXT NOT NULL, alamat TEXT, paket_id INTEGER REFERENCES packages(id), pppoe_user TEXT UNIQUE, -- PPPoE username pppoe_secret TEXT, -- PPPoE password password_wifi TEXT, -- Wi-Fi password install_date DATE, -- Installation date latitude REAL, -- GPS coordinate longitude REAL, odp_id INTEGER REFERENCES odps(id), -- ODP location router_id INTEGER REFERENCES routers(id), -- MikroTik router genieacs_tag TEXT, -- GenieACS device tag status TEXT DEFAULT 'active', -- active, suspended, inactive isolir_date DATE, -- Isolate date hari_isolir INTEGER DEFAULT 20, -- Day to isolate isolir_otomatis INTEGER DEFAULT 1, -- Auto-isolate flag tipe_koneksi TEXT DEFAULT 'pppoe', -- pppoe, hotspot, onu promo_cycles_used INTEGER DEFAULT 0, -- Promo cycle counter last_payment_date DATE, notes TEXT, created_at DATETIME DEFAULT (NOW_LOCAL()) ); ``` **Indexes:** - `no_pelanggan` (UNIQUE) - `pppoe_user` (UNIQUE) - `email` - `phone` **Foreign Keys:** - `paket_id` → `packages(id)` - `odp_id` → `odps(id)` - `router_id` → `routers(id)` --- ### invoices ```sql CREATE TABLE invoices ( id INTEGER PRIMARY KEY AUTOINCREMENT, customer_id INTEGER NOT NULL REFERENCES customers(id), period_year INTEGER NOT NULL, -- 2026 period_month INTEGER NOT NULL, -- 1-12 amount INTEGER NOT NULL, -- Total Rp amount_paid INTEGER DEFAULT 0, -- Sudah dibayar Rp status TEXT DEFAULT 'pending', -- pending, paid, overdue, cancelled due_date DATE, -- Jatuh tempo payment_date DATE, -- Tanggal pembayaran payment_method TEXT, -- cash, transfer, qris payment_gateway TEXT, -- midtrans, tripay, manual qris_code TEXT, -- QRIS payload qris_unique_id TEXT UNIQUE, -- Kode unik untuk matching notes TEXT, -- AUTO: promo siklus X, AUTO: prorata created_at DATETIME DEFAULT (NOW_LOCAL()), updated_at DATETIME DEFAULT (NOW_LOCAL()) ); ``` **Indexes:** - `customer_id, period_year, period_month` (UNIQUE composite) - `status` - `due_date` --- ### payments ```sql CREATE TABLE payments ( id INTEGER PRIMARY KEY AUTOINCREMENT, invoice_id INTEGER NOT NULL REFERENCES invoices(id), amount INTEGER NOT NULL, -- Jumlah bayar Rp method TEXT DEFAULT 'cash', -- cash, transfer, ewallet gateway TEXT, -- midtrans, tripay, xendit, manual gateway_ref_id TEXT UNIQUE, -- Reference ID dari gateway proof_image_url TEXT, -- Bukti transfer (foto) status TEXT DEFAULT 'pending', -- pending, verified, failed notes TEXT, recorded_by_user TEXT, -- User yang input recorded_by_role TEXT, -- admin, kasir, agen created_at DATETIME DEFAULT (NOW_LOCAL()) ); ``` --- ### routers (MikroTik) ```sql CREATE TABLE routers ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, host TEXT NOT NULL, port INTEGER DEFAULT 8728, username TEXT NOT NULL, password TEXT NOT NULL, is_active INTEGER DEFAULT 1, is_default INTEGER DEFAULT 0, -- Default router notes TEXT, last_connection_test DATETIME, connection_status TEXT DEFAULT 'unknown', created_at DATETIME DEFAULT (NOW_LOCAL()) ); ``` --- ### olts (OLT Devices) ```sql CREATE TABLE olts ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, host TEXT NOT NULL, snmp_community TEXT DEFAULT 'public', snmp_port INTEGER DEFAULT 161, snmp_version TEXT DEFAULT '2c', brand TEXT, -- ZTE, Huawei, FiberHome model TEXT, web_user TEXT, -- Web interface login web_password TEXT, telnet_port INTEGER, -- Telnet untuk command telnet_password TEXT, telnet_enable_password TEXT, -- ZTE enable password api_base_url TEXT, -- go-api-c320 endpoint is_active INTEGER DEFAULT 1, created_at DATETIME DEFAULT (NOW_LOCAL()) ); ``` --- ### odps (ODP - Distribution Points) ```sql CREATE TABLE odps ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, latitude REAL NOT NULL, longitude REAL NOT NULL, port_count INTEGER DEFAULT 8, -- Banyak port fiber port_used INTEGER DEFAULT 0, address TEXT, olt_id INTEGER REFERENCES olts(id), is_active INTEGER DEFAULT 1, notes TEXT, created_at DATETIME DEFAULT (NOW_LOCAL()) ); ``` --- ### pppoe_users ```sql CREATE TABLE pppoe_users ( id INTEGER PRIMARY KEY AUTOINCREMENT, customer_id INTEGER NOT NULL REFERENCES customers(id), router_id INTEGER NOT NULL REFERENCES routers(id), username TEXT NOT NULL UNIQUE, secret TEXT NOT NULL, profile_name TEXT NOT NULL, -- Speed profile status TEXT DEFAULT 'active', -- active, inactive, disabled date_added DATE, last_seen DATETIME, created_at DATETIME DEFAULT (NOW_LOCAL()) ); ``` --- ### invoices (dengan fields lengkap) ```sql CREATE TABLE invoices ( id INTEGER PRIMARY KEY AUTOINCREMENT, customer_id INTEGER NOT NULL REFERENCES customers(id), -- Period period_year INTEGER NOT NULL, period_month INTEGER NOT NULL, due_date DATE, -- Amount amount INTEGER NOT NULL, amount_paid INTEGER DEFAULT 0, -- Status status TEXT DEFAULT 'pending', payment_date DATE, -- Payment Method payment_method TEXT, -- cash, transfer, qris payment_gateway TEXT, -- midtrans, tripay, xendit qris_code TEXT, qris_unique_id TEXT UNIQUE, gateway_ref_id TEXT, -- Metadata notes TEXT, created_at DATETIME DEFAULT (NOW_LOCAL()), updated_at DATETIME DEFAULT (NOW_LOCAL()) ); ``` --- ### pppoe_traffic_samples ```sql CREATE TABLE pppoe_traffic_samples ( id INTEGER PRIMARY KEY AUTOINCREMENT, pppoe_user_id INTEGER NOT NULL REFERENCES pppoe_users(id), bytes_in INTEGER DEFAULT 0, bytes_out INTEGER DEFAULT 0, timestamp DATETIME DEFAULT (NOW_LOCAL()) ); ``` **Purpose:** Track data usage per session **Auto-cleanup:** Runs every 10 minutes via cron --- ### audit_trail ```sql CREATE TABLE audit_trail ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT, -- User ID / username user_role TEXT, -- admin, kasir, etc action TEXT NOT NULL, -- login, update_customer, payment table_name TEXT, record_id INTEGER, old_value TEXT, -- JSON of old data new_value TEXT, -- JSON of new data ip_address TEXT, status TEXT DEFAULT 'success', -- success, failed notes TEXT, created_at DATETIME DEFAULT (NOW_LOCAL()) ); ``` **Auto-cleanup:** Delete records older than 90 days (configurable) --- ### tickets ```sql CREATE TABLE tickets ( id INTEGER PRIMARY KEY AUTOINCREMENT, customer_id INTEGER NOT NULL REFERENCES customers(id), title TEXT NOT NULL, description TEXT NOT NULL, status TEXT DEFAULT 'open', -- open, assigned, resolved, closed priority TEXT DEFAULT 'normal', -- low, normal, high assigned_to_technician TEXT, -- Technician ID attachment_url TEXT, -- Foto bukti resolution TEXT, created_at DATETIME DEFAULT (NOW_LOCAL()), updated_at DATETIME DEFAULT (NOW_LOCAL()) ); ``` --- ### agents ```sql CREATE TABLE agents ( id INTEGER PRIMARY KEY AUTOINCREMENT, nama_agen TEXT NOT NULL, phone TEXT NOT NULL, email TEXT, alamat TEXT, username TEXT NOT NULL UNIQUE, password TEXT NOT NULL, -- MUST use bcrypt! saldo_agen INTEGER DEFAULT 0, -- Saldo Rp status TEXT DEFAULT 'active', created_at DATETIME DEFAULT (NOW_LOCAL()) ); ``` --- ### voucher_batches ```sql CREATE TABLE voucher_batches ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, quantity INTEGER NOT NULL, nominal INTEGER NOT NULL, -- Harga voucher price_per_unit INTEGER NOT NULL, -- Harga beli router_id INTEGER REFERENCES routers(id), status TEXT DEFAULT 'active', generated_count INTEGER DEFAULT 0, printed_count INTEGER DEFAULT 0, used_count INTEGER DEFAULT 0, created_at DATETIME DEFAULT (NOW_LOCAL()) ); ``` --- ### vouchers ```sql CREATE TABLE vouchers ( id INTEGER PRIMARY KEY AUTOINCREMENT, batch_id INTEGER NOT NULL REFERENCES voucher_batches(id), code TEXT NOT NULL UNIQUE, status TEXT DEFAULT 'available', -- available, used, expired sold_to_agent TEXT, -- Agent ID used_by_customer TEXT, expiry_date DATE, created_at DATETIME DEFAULT (NOW_LOCAL()) ); ``` --- ## Query Examples ### Generate Invoice (Monthly) ```sql SELECT c.id, c.paket_id, p.price, c.promo_cycles_used, p.promo_cycles FROM customers c JOIN packages p ON c.paket_id = p.id WHERE c.status = 'active' AND NOT EXISTS ( SELECT 1 FROM invoices WHERE customer_id = c.id AND period_year = 2026 AND period_month = 6 ); ``` ### Get Overdue Invoices ```sql SELECT c.id, c.nama_pelanggan, i.amount FROM invoices i JOIN customers c ON i.customer_id = c.id WHERE i.status = 'pending' AND i.due_date < DATE('now') ORDER BY i.due_date ASC; ``` ### Check Customer Usage (Daily) ```sql SELECT pu.id, pu.username, SUM(pts.bytes_in) / 1000000 as total_gb_in, SUM(pts.bytes_out) / 1000000 as total_gb_out FROM pppoe_users pu LEFT JOIN pppoe_traffic_samples pts ON pu.id = pts.pppoe_user_id WHERE DATE(pts.timestamp) = DATE('now') GROUP BY pu.id; ``` ### Find Payments Pending Verification ```sql SELECT p.*, i.amount, c.nama_pelanggan FROM payments p JOIN invoices i ON p.invoice_id = i.id JOIN customers c ON i.customer_id = c.id WHERE p.status = 'pending' ORDER BY p.created_at DESC; ``` --- ## Optimization Tips 1. **Add Indexes for Performance:** ```sql CREATE INDEX idx_invoices_customer ON invoices(customer_id); CREATE INDEX idx_payments_invoice ON payments(invoice_id); CREATE INDEX idx_pppoe_users_customer ON pppoe_users(customer_id); CREATE INDEX idx_audit_trail_action ON audit_trail(action, created_at); ``` 2. **Regular Maintenance:** ```sql -- Cleanup old traffic samples (older than 30 days) DELETE FROM pppoe_traffic_samples WHERE timestamp < datetime('now', '-30 days'); -- Cleanup old audit trail (older than 90 days) DELETE FROM audit_trail WHERE created_at < datetime('now', '-90 days'); -- Vacuum database to reclaim space VACUUM; ``` 3. **Monitor Database Size:** ```bash ls -lh database/billing.db ``` --- ## Backup & Recovery ### Backup ```bash sqlite3 database/billing.db ".backup 'backup-2026-06-19.db'" ``` ### Restore ```bash sqlite3 database/billing.db ".restore 'backup-2026-06-19.db'" ``` ### Manual SQL Backup ```bash sqlite3 database/billing.db ".dump" > backup.sql ``` ### Restore from SQL ```bash sqlite3 database/billing.db < backup.sql ``` --- **Document Version:** 1.0 **Last Updated:** Juni 2026