Sistem barcode mengurangi kesalahan manusia dengan mengotomatisasi entri data, dan pemindaian barcode lebih cepat dari entri manual untuk proses penerimaan dan picking, memberikan pembaruan inventaris real-time. Untuk gudang Indonesia yang masih mengandalkan lembar penerimaan tulisan tangan dan kartu inventaris manual, integrasi barcode scanning merupakan perubahan signifikan dalam akurasi dan kecepatan.
Ada tiga pilihan hardware untuk barcode scanning dalam integrasi gudang ERP: scanner barcode genggam khusus (Zebra, Honeywell — tahan banting, cepat, mahal ~Rp 3-8 juta per unit), ponsel dengan pemindaian berbasis kamera (gunakan smartphone perusahaan yang ada — hardware gratis tapi pemindaian lebih lambat), dan pembaca barcode tetap di pintu dok untuk penerimaan (Zebra fixed reader — ideal untuk penerimaan volume tinggi, Rp 15-30 juta). Untuk klien kami, kami menggunakan hybrid: pembaca tetap di dok penerimaan dan ponsel dengan aplikasi scanning PWA untuk staf gudang.
Kami membangun Progressive Web App khusus untuk operasi gudang — aplikasi React mobile-first yang berjalan di browser tanpa instalasi app store. Pemindaian barcode diimplementasikan menggunakan QuaggaJS (barcode 1D seperti Code 128, EAN-13) dan @zxing/browser (kode QR dan barcode 2D). Pemindaian yang berhasil memicu getaran haptic dan bunyi bip audio, kemudian memuat detail item yang dipindai.
Warehouse Barcode Scanning System
Hardware Layer:
┌───────────────────┐ ┌─────────────────────┐
│ Smartphone Camera │ │ Zebra ZT410 Printer │
│ (PWA scanning) │ │ → prints Code 128 │
│ QuaggaJS + zxing │ │ labels via ZPL │
└─────────┬─────────┘ └─────────────────────┘
│
▼ (offline-first via IndexedDB)
┌───────────────────────────────────────────────┐
│ React PWA (Cloudflare Pages) │
│ │
│ Scan → Lookup item → Confirm qty → Submit │
│ │
│ Offline mode: queue scans in IndexedDB │
│ Sync when online: POST /api/movements/batch │
└─────────────────────┬─────────────────────────┘
│ (online sync)
▼
┌───────────────────────────────────────────────┐
│ NestJS WarehouseModule │
│ │
│ POST /goods-receipts (GR scanning) │
│ POST /pick-orders/scan (picking) │
│ POST /cycle-count/submit (stocktake) │
│ │
│ → optimistic locking for concurrent scans │
│ → create_movement() PostgreSQL function │
└───────────────────────────────────────────────┘
Label format (Code 128 + GS1-128 for lots):
┌─────────────────────────────────────────────┐
│ ▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌ │
│ SKU: MAT-001234 Lot: L2025-0412 │
│ Bahan Baku A — 25kg │
│ Exp: 2026-04-12 │
└─────────────────────────────────────────────┘Dari pengalaman saya membangun sistem ERP di Commsult: cetak barcode Anda sendiri di label dan tempelkan pada kemasan produk Anda — jangan mengandalkan barcode produsen. Barcode produsen (GS1) sering tidak ada dalam master item ERP Anda, dan pemindaiannya tidak menghasilkan kecocokan. Kami mencetak barcode Code 128 yang mengkodekan kode item internal kami (SKU) menggunakan ZPL (Zebra Programming Language) yang dikirim ke printer label Zebra.
Alur penerimaan barang (GR): staf gudang menerima pengiriman fisik, membuka layar GR di PWA, memilih Purchase Order yang cocok dari daftar pengiriman yang diharapkan, dan memindai setiap barcode item. Setiap pemindaian: mencari item di ERP, menampilkan nama item dan kuantitas yang diharapkan dari PO, meminta kuantitas yang diterima (default 1, dapat disesuaikan), dan menambahkan ke daftar GR.
Untuk picking keluar, alur picking adalah: staf gudang membuka Pick Order, memindai setiap item saat dipilih dari rak, mengkonfirmasi lokasi bin dengan memindai barcode rak, dan menandai item sebagai dipilih. Sistem memvalidasi item yang dipindai cocok dengan baris pick order — jika item yang salah dipindai, suara error diputar dan pesan peringatan ditampilkan. Pendekatan scan-to-verify ini mencegah kesalahan pick.
// React PWA: Barcode scanning with QuaggaJS
import Quagga from 'quagga';
import { openDB } from 'idb'; // IndexedDB for offline storage
function BarcodeScanner({ onScan }: { onScan: (code: string) => void }) {
useEffect(() => {
Quagga.init({
inputStream: {
type: 'LiveStream',
target: document.querySelector('#scanner-container'),
constraints: { facingMode: 'environment' },
},
decoder: { readers: ['code_128_reader', 'ean_reader'] },
}, (err) => {
if (err) { console.error(err); return; }
Quagga.start();
});
Quagga.onDetected((result) => {
const code = result.codeResult.code;
if (code) {
navigator.vibrate?.(100); // haptic feedback
onScan(code);
Quagga.stop();
}
});
return () => Quagga.stop();
}, [onScan]);
return <div id="scanner-container" className="w-full h-64" />;
}
// Offline-first: queue scan when offline, sync when online
const db = await openDB('warehouse-offline', 1, {
upgrade(db) {
db.createObjectStore('pending-scans', { keyPath: 'id', autoIncrement: true });
},
});
async function submitScan(scan: WarehouseScan) {
if (navigator.onLine) {
await fetch('/api/movements', { method: 'POST', body: JSON.stringify(scan) });
} else {
await db.add('pending-scans', { ...scan, timestamp: Date.now() });
}
}
// Sync on reconnect
window.addEventListener('online', async () => {
const pending = await db.getAll('pending-scans');
for (const scan of pending) {
await fetch('/api/movements/batch', {
method: 'POST', body: JSON.stringify(scan)
});
await db.delete('pending-scans', scan.id);
}
});Sistem scanning terdiri dari: NestJS WarehouseModule (API penerimaan barang, pick order, dan manajemen bin), React PWA yang di-deploy ke Cloudflare Pages (untuk CDN global dan HTTPS tanpa konfigurasi), dan printer label Zebra ZT410 untuk pembuatan label barcode. API NestJS menggunakan optimistic locking saat menulis pergerakan stok.
Gudang sering memiliki cakupan WiFi yang buruk di sudut, di belakang rak, dan dekat struktur logam besar. Aplikasi scanning yang memerlukan konektivitas internet terus-menerus akan membuat frustasi staf gudang ketika koneksi terputus di tengah penerimaan. Kami mengimplementasikan pemindaian offline-first: PWA meng-cache PO hari ini dan master item secara lokal menggunakan IndexedDB. Pemindaian bekerja offline — pemindaian mengantri secara lokal. Ketika konektivitas dipulihkan, antrian disinkronkan ke server secara otomatis.
Untuk klien manufaktur kami yang menangani material yang mudah rusak, pelacakan lot sangat penting. Setiap GR masuk menangkap nomor lot dan tanggal produksi. Barcode mengkodekan nomor lot (menggunakan GS1-128 dengan Application Identifier 10 untuk lot dan 17 untuk tanggal kedaluwarsa). Sistem inventaris melacak stok berdasarkan item + lot + lokasi. Picking FEFO (First Expired First Out) secara otomatis memilih lot dengan tanggal kedaluwarsa paling awal.
Cycle count reguler (alih-alih stocktake penuh tahunan) lebih akurat dan tidak terlalu mengganggu. Sistem kami mendukung tugas cycle count terjadwal: staf gudang menerima tugas penghitungan di PWA mereka yang menunjukkan bin mana yang harus dihitung. Mereka pergi ke setiap bin, memindai barcode bin (untuk mengkonfirmasi lokasi), kemudian memindai setiap item dan memasukkan hitungan fisik.