Konteks
Central Plafon menjual bahan interior — plafon PVC, panel dinding, marmer PVC, lantai vinyl, rangka baja ringan — sekaligus jasa pemasangannya. Pelanggannya dua jenis: pembeli eceran dan kontraktor yang membeli dalam jumlah besar dengan tempo.
Usaha seperti ini biasanya hidup di tiga tempat terpisah: katalog online, spreadsheet gudang, dan mesin kasir di toko. Saya ingin satu sumber data untuk semuanya.
Yang saya bangun
Satu monorepo (Turborepo + Bun) berisi satu API dan tiga aplikasi:
- core-api — NestJS, Drizzle ORM, PostgreSQL. Produk, varian, stok per cabang, harga, pembelian, penggajian.
- shop — toko online Next.js dua bahasa, dengan kalkulator kebutuhan material.
- console — dashboard internal (React + TanStack Router) untuk gudang, purchase order, faktur, surat jalan, dan slip gaji.
- kasir — aplikasi kasir Electron yang tetap jalan saat internet putus dan mencetak struk langsung ke printer thermal.
Keputusan yang penting
Uang selalu integer. Harga disimpan dalam satuan terkecil mata uang, bukan float, supaya total faktur tidak pernah meleset satu rupiah.
/** Money disimpan sebagai integer minor unit (rupiah utuh untuk IDR). */ export interface Money { amount: number; currency: CurrencyCode; }
Kasir tidak boleh menolak pembeli. Saat offline, transaksi masuk antrean lokal dan disinkronkan begitu koneksi kembali. Kalau sinkronisasi membuat stok minus, pesanan tidak dibuang — ditandai untuk diperiksa manusia.
Pencarian katalog yang paham maksud. Setiap produk punya embedding (pgvector), jadi pencarian "plafon kamar mandi anti lembap" tetap menemukan produk yang tepat walau kata-katanya berbeda.