Sentra Rental — Database Design¶
Database menggunakan PostgreSQL sebagai sumber kebenaran utama dengan partisi berdasarkan branch_id dan tahun transaksi, serta Redis untuk kalender bitmask dan distributed lock.
4.1 Entity-Relationship Diagram (ERD)¶
erDiagram
BRANCHES ||--o{ USERS : "has"
BRANCHES ||--o{ ASSETS : "owns"
ASSETS ||--o{ ASSET_CONDITIONS : "has_logs"
ASSETS ||--o{ BOOKING_DETAILS : "booked_in"
ASSETS ||--o{ MAINTENANCE_LOGS : "undergoes"
BOOKINGS ||--o{ BOOKING_DETAILS : "contains"
BOOKINGS ||--|| DEPOSITS : "requires"
BOOKINGS ||--o{ HANDOVER_INSPECTIONS : "documents"
USERS ||--o{ BOOKINGS : "places"
USERS ||--o{ STAFF_COMMISSIONS : "earns"
BOOKING_DETAILS ||--o{ STAFF_COMMISSIONS : "triggers"
4.2 SQL DDL (PostgreSQL Schema)¶
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- 1. Tabel Cabang
CREATE TABLE branches (
branch_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
branch_name VARCHAR(100) NOT NULL,
address TEXT NOT NULL,
city VARCHAR(50) NOT NULL,
phone_number VARCHAR(20) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- 2. Tabel Pengguna (Pelanggan, Staff, Owner)
CREATE TABLE users (
user_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
branch_id UUID REFERENCES branches(branch_id),
full_name VARCHAR(150) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
phone_number VARCHAR(20) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
role VARCHAR(20) NOT NULL CHECK (role IN (
'OWNER', 'BRANCH_MANAGER', 'FRONTDESK_ADMIN',
'INSPECTOR', 'TECHNICIAN', 'CUSTOMER'
)),
ktp_number VARCHAR(16) UNIQUE,
sim_number VARCHAR(16),
is_verified BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- 3. Tabel Aset Utama
CREATE TABLE assets (
asset_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
branch_id UUID REFERENCES branches(branch_id),
asset_name VARCHAR(150) NOT NULL,
sku VARCHAR(50) NOT NULL,
serial_number VARCHAR(50) UNIQUE NOT NULL,
category VARCHAR(50) NOT NULL CHECK (category IN (
'VEHICLE', 'CAMERA_EQUIPMENT', 'PARTY_ITEMS',
'OUTDOOR_GEAR', 'PROJECT_TOOLS'
)),
purchase_date DATE NOT NULL,
purchase_price DECIMAL(15, 2) NOT NULL,
salvage_value DECIMAL(15, 2) NOT NULL,
useful_life_months INT NOT NULL, -- untuk depresiasi garis lurus
daily_rental_rate DECIMAL(12, 2) NOT NULL,
hourly_rental_rate DECIMAL(12, 2) NOT NULL,
current_status VARCHAR(20) NOT NULL CHECK (current_status IN (
'AVAILABLE', 'BOOKED', 'MAINTENANCE', 'DAMAGED', 'LOST', 'RETIRED'
)),
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_assets_sku ON assets(sku);
CREATE INDEX idx_assets_status ON assets(current_status);
-- 4. Tabel Histori Kondisi Aset (Lifecycle)
CREATE TABLE asset_conditions (
condition_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
asset_id UUID REFERENCES assets(asset_id) ON DELETE CASCADE,
checked_by UUID REFERENCES users(user_id),
overall_condition VARCHAR(15) NOT NULL CHECK (overall_condition IN (
'EXCELLENT', 'GOOD', 'FAIR', 'POOR', 'BROKEN'
)),
damage_notes TEXT,
photo_urls TEXT[] NOT NULL, -- Array URL ke S3
recorded_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- 5. Tabel Booking Utama
CREATE TABLE bookings (
booking_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
customer_id UUID REFERENCES users(user_id) NOT NULL,
branch_id UUID REFERENCES branches(branch_id) NOT NULL,
booking_code VARCHAR(15) UNIQUE NOT NULL, -- Format: SR/YYYYMMDD/XXXXX
total_amount DECIMAL(15, 2) NOT NULL,
ppn_amount DECIMAL(15, 2) NOT NULL DEFAULT 0.00, -- PPN 11%
payment_status VARCHAR(20) NOT NULL CHECK (payment_status IN (
'UNPAID', 'PAID', 'PARTIALLY_REFUNDED', 'REFUNDED'
)),
booking_status VARCHAR(20) NOT NULL CHECK (booking_status IN (
'PENDING', 'APPROVED', 'DISPATCHED', 'RETURNED', 'COMPLETED', 'CANCELLED'
)),
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- 6. Tabel Detail Item Booking
CREATE TABLE booking_details (
detail_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
booking_id UUID REFERENCES bookings(booking_id) ON DELETE CASCADE,
asset_id UUID REFERENCES assets(asset_id) NOT NULL,
start_time TIMESTAMP WITH TIME ZONE NOT NULL,
end_time TIMESTAMP WITH TIME ZONE NOT NULL,
price_applied DECIMAL(12, 2) NOT NULL, -- Harga pada saat booking
actual_return_time TIMESTAMP WITH TIME ZONE,
late_fee_applied DECIMAL(12, 2) DEFAULT 0.00,
damage_fee_applied DECIMAL(12, 2) DEFAULT 0.00
);
-- 7. Tabel Uang Jaminan (Deposit)
CREATE TABLE deposits (
deposit_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
booking_id UUID REFERENCES bookings(booking_id) ON DELETE CASCADE,
amount_held DECIMAL(12, 2) NOT NULL,
payment_gateway_ref VARCHAR(100) UNIQUE, -- Xendit/Midtrans pre-auth charge ID
deposit_status VARCHAR(20) NOT NULL CHECK (deposit_status IN (
'HELD', 'RELEASED', 'FORFEITED', 'CLAIMED_FOR_DAMAGES'
)),
held_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
released_at TIMESTAMP WITH TIME ZONE
);
-- 8. Tabel Inspeksi Handover (Check-Out / Check-In)
CREATE TABLE handover_inspections (
inspection_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
booking_id UUID REFERENCES bookings(booking_id) ON DELETE CASCADE,
asset_id UUID REFERENCES assets(asset_id) NOT NULL,
inspection_type VARCHAR(10) NOT NULL CHECK (inspection_type IN ('CHECK_OUT', 'CHECK_IN')),
inspector_id UUID REFERENCES users(user_id) NOT NULL,
odometer_reading INT, -- opsional untuk kendaraan
fuel_or_battery_level VARCHAR(20), -- opsional
checklist_json JSONB NOT NULL, -- fleksibel untuk berbagai jenis barang
photo_urls TEXT[] NOT NULL,
digital_signature_url VARCHAR(255) NOT NULL,
tamper_proof_hash CHAR(64) NOT NULL, -- SHA-256 dari seluruh payload
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- 9. Tabel Perawatan Unit (Maintenance)
CREATE TABLE maintenance_logs (
maintenance_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
asset_id UUID REFERENCES assets(asset_id) ON DELETE CASCADE,
technician_id UUID REFERENCES users(user_id),
maintenance_type VARCHAR(20) NOT NULL CHECK (maintenance_type IN (
'ROUTINE_SERVICE', 'REPAIR', 'CALIBRATION', 'CLEANING'
)),
description TEXT NOT NULL,
parts_replaced JSONB, -- daftar part yang diganti
cost_incurred DECIMAL(12, 2) NOT NULL,
downtime_started TIMESTAMP WITH TIME ZONE NOT NULL,
downtime_ended TIMESTAMP WITH TIME ZONE,
next_service_date DATE
);
-- 10. Tabel Komisi & Payroll Staff Operasional
CREATE TABLE staff_commissions (
commission_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
staff_id UUID REFERENCES users(user_id) NOT NULL,
booking_detail_id UUID REFERENCES booking_details(detail_id) ON DELETE SET NULL,
amount_earned DECIMAL(12, 2) NOT NULL,
incentive_type VARCHAR(20) NOT NULL CHECK (incentive_type IN (
'CHECKOUT_DISPATCH', 'CHECKIN_VERIFICATION', 'COMPLIANCE_BONUS'
)),
calculated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
4.3 Redis Key Conventions¶
| Key Pattern | Type | Keterangan |
|---|---|---|
lock:asset:<serial>:<date> |
String (SETNX/TTL) | Redlock per unit per hari, TTL 10 menit |
avail:<SKU>:<AssetID>:<YearMonth> |
Bitmap | 1 bit per hari dalam bulan; 0 = tersedia, 1 = dipesan |
waitlist:<SKU>:<Date> |
Sorted Set | Bobot = tier keanggotaan + timestamp pengajuan |
session:<user_id> |
Hash | JWT session store (baseline) |