Pekerja lapangan — teknisi, pengemudi pengiriman, staf gudang di lokasi terpencil, tenaga penjual yang mengunjungi pelanggan — selalu menjadi mil terakhir adopsi ERP. Mereka perlu mencatat perintah kerja, merekam pengiriman, memeriksa inventaris, dan mengirimkan formulir dari lapangan. Tetapi pekerja lapangan Indonesia sering beroperasi di area dengan konektivitas yang buruk atau terputus-putus. Progressive Web Apps (PWA) memecahkan ini dengan elegan.
Untuk alat pekerja lapangan internal, PWA menawarkan keseimbangan kemampuan dan biaya yang tepat. PWA adalah aplikasi web yang berperilaku seperti aplikasi native: dapat ditambahkan ke layar beranda, menerima notifikasi push, bekerja offline, dan berjalan layar penuh. Biaya pengembangan dan pemeliharaan adalah sebagian kecil dari pengembangan aplikasi native karena menggunakan codebase yang sama dengan ERP web.
Kemampuan offline dalam PWA dibangun di atas dua API browser. Service Workers: file JavaScript yang berjalan independen dari halaman, mencegat permintaan jaringan, dan melayani respons yang di-cache saat jaringan tidak tersedia. IndexedDB: database sisi klien yang menyimpan data terstruktur di browser. Pekerja lapangan dapat membaca catatan dan menulis catatan baru bahkan saat offline.
Sinkronisasi data offline menciptakan konflik ketika catatan yang sama dimodifikasi baik secara offline maupun di server sebelum sinkronisasi. Strategi sinkronisasi Anda harus mendefinisikan cara konflik diselesaikan. Untuk sebagian besar kasus penggunaan ERP lapangan, strategi 'last write wins' dapat diterima untuk data rendah risiko. Untuk data transaksional, sinkronisasi harus mengantrekan operasi secara berurutan.
PWA Field ERP Architecture — Offline-First
ONLINE MODE: OFFLINE MODE:
┌──────────────────────────┐ ┌──────────────────────────┐
│ Field Worker Device │ │ Field Worker Device │
│ │ │ │
│ PWA App Shell (cached) │ │ PWA App Shell (from cache)│
│ │ │ │ │ │
│ Service Worker │ │ Service Worker │
│ ├─ fetch interceptor │ │ ├─ serves from cache │
│ ├─ cache strategy │ │ ├─ queues writes │
│ └─ sync queue │ │ └─ Background Sync API │
│ │ │ │ │ │
│ IndexedDB (local) ◄────┐│ │ IndexedDB (local) │
│ ├─ today's work orders ││ │ ├─ reads: all available │
│ ├─ customer data ││ │ └─ writes: queued sync │
│ └─ pending sync queue ││ │ │
└───────────┬──────────────┘│ └──────────┬────────────────┘
│ (sync) │ │ (reconnect)
▼ │ ▼
┌──────────────────────────┐│ ┌──────────────────────────┐
│ ERP Backend (NestJS) ││ │ Background Sync fires │
│ POST /api/work-orders ││ │ Service Worker processes │
│ GET /api/sync/today ││ │ queued operations in order│
│ PATCH /api/deliveries │└────────│ Conflict resolution runs │
└──────────────────────────┘ └──────────────────────────┘
Offline data set per field worker (downloaded each morning):
• Assigned work orders for today
• Customer address + contact data for those work orders
• Product/service catalog for their service category
• Reference data (fault codes, resolution types)
Size estimate: ~500KB per worker — fast sync on any connectionDari pengalaman saya mengimplementasikan ERP di Commsult: rancang dataset offline sesekecil mungkin. Teknisi lapangan membutuhkan perintah kerja yang ditugaskan kepada mereka hari ini, informasi alamat dan kontak pelanggan, dan katalog produk/layanan untuk domain mereka. Mereka tidak memerlukan seluruh database pelanggan. Batasi dataset offline ke apa yang dibutuhkan untuk pekerjaan hari itu.
PWA memerlukan tiga hal agar dapat diinstal: Web App Manifest (file JSON yang mendefinisikan nama aplikasi, ikon, mode tampilan, dan URL awal), Service Worker terdaftar, dan HTTPS (diperlukan agar service worker berfungsi). Di Android, browser secara otomatis akan meminta 'Tambahkan ke Layar Beranda'. Di iOS, pengguna harus menggunakan menu Share secara manual.
// service-worker.ts — ERP Field PWA
const CACHE_VERSION = 'erp-field-v1';
const APP_SHELL = ['/offline.html', '/manifest.webmanifest'];
// Cache app shell on install
self.addEventListener('install', (event: ExtendableEvent) => {
event.waitUntil(
caches.open(CACHE_VERSION).then(cache => cache.addAll(APP_SHELL))
);
});
// Network-first for API calls; cache-first for static assets
self.addEventListener('fetch', (event: FetchEvent) => {
const { request } = event;
if (request.url.includes('/api/')) {
// API: try network, fallback to IndexedDB data
event.respondWith(
fetch(request).catch(() => {
// Network unavailable — queue the write for later
if (request.method !== 'GET') {
queueOfflineAction(request.clone());
return new Response(
JSON.stringify({ queued: true, message: 'Saved offline, will sync when connected' }),
{ headers: { 'Content-Type': 'application/json' } }
);
}
// For GETs, try cached data
return caches.match(request) ?? Response.error();
})
);
} else {
// Static assets: cache-first
event.respondWith(
caches.match(request).then(cached => cached ?? fetch(request))
);
}
});
// Background sync — fires when connection is restored
self.addEventListener('sync', (event: SyncEvent) => {
if (event.tag === 'erp-sync-queue') {
event.waitUntil(processOfflineQueue());
}
});
async function processOfflineQueue() {
const db = await openIndexedDB('erp-offline', 1);
const queue = await db.getAll('sync-queue');
for (const item of queue) {
try {
await fetch(item.url, { method: item.method, body: item.body });
await db.delete('sync-queue', item.id);
} catch {
break; // Still offline — stop processing, retry next sync
}
}
}
// Web App Manifest (public/manifest.webmanifest)
const manifest = {
name: "Commsult Field ERP",
short_name: "Field ERP",
display: "standalone",
background_color: "#0f172a",
theme_color: "#3b82f6",
start_url: "/field",
icons: [
{ src: "/icons/icon-192.png", sizes: "192x192", type: "image/png" },
{ src: "/icons/icon-512.png", sizes: "512x512", type: "image/png" }
]
};Notifikasi push PWA memungkinkan ERP berkomunikasi dengan pekerja lapangan tanpa mengharuskan mereka membiarkan aplikasi tetap terbuka. Kasus penggunaan: perintah kerja baru telah ditugaskan, rute pengiriman telah berubah, pelanggan telah dihubungi untuk mengonfirmasi janji temu, inventaris di lokasi pengambilan telah diperbarui.
PWA offline menyimpan data di perangkat. Jika perangkat pekerja lapangan hilang atau dicuri, data yang di-cache — termasuk informasi pelanggan, harga, detail perintah kerja — berpotensi dapat diakses. Terapkan persyaratan enkripsi tingkat perangkat dan kemampuan penghapusan jarak jauh sebelum men-deploy PWA lapangan yang berkemampuan offline.
Jaringan seluler Indonesia sangat bervariasi berdasarkan lokasi. PWA lapangan Anda harus berkinerja pada koneksi 3G di area pedesaan. Optimalkan untuk kinerja jaringan: kompres semua gambar (format WebP), lazy-load sumber daya yang tidak kritis, gunakan HTTP/2 atau HTTP/3 di server, dan terapkan antrean permintaan dalam service worker yang mem-batch operasi sinkronisasi.
PWA sangat baik untuk alur kerja lapangan berbasis formulir. Ini berjuang dengan integrasi hardware (pemindai Bluetooth, pembaca NFC, printer khusus) — ini memerlukan aplikasi native atau React Native. Untuk pekerja lapangan Indonesia yang kebutuhan utamanya adalah mencatat pekerjaan, mengirimkan formulir, dan mengakses data referensi, PWA mencakup 95% kasus penggunaan dengan sebagian kecil biaya aplikasi native.