Manufaktur di ERP: Desain Modul BOM dan MRP

Foto oleh Robert Scoble on flickr
Bill of materials single-level hanya mencantumkan komponen langsung dari satu item parent, hanya satu level. BOM multi-level, atau berjenjang, secara rekursif mengembangkan setiap sub-assembly hingga ke bahan baku, mengalikan kuantitas di setiap level sehingga kamu melihat pohon produksi secara lengkap. Kedua tampilan ini dihasilkan dari tabel baris BOM yang sama; perbedaannya murni pada cara kueri menyusuri hierarki tersebut.
Jika work order merujuk ke header BOM yang aktif dan engineering merevisi resep saat produksi berjalan, perubahan itu diam-diam menulis ulang apa yang seharusnya dikonsumsi oleh produksi yang sudah berjalan separuh jalan. Menyimpan snapshot baris BOM yang sudah diexplode ke tabel khusus work order pada saat dirilis menjaga resep persis seperti saat order itu dibuat, sehingga pelaporan varians biaya dan audit trail tetap akurat berbulan-bulan kemudian.
MRP dimulai dari permintaan independen, yaitu sales order dan prakiraan untuk barang jadi, kemudian mengeksplosinya level demi level melalui bill of materials untuk menghasilkan permintaan dependen bagi setiap komponen. Di setiap level, gross requirements dikurangi dengan stok yang tersedia atau sudah dipesan, lalu planned order yang dihasilkan digeser ke belakang dalam waktu sesuai lead time item tersebut, menghasilkan rencana bertahap waktu berupa requisisi pembelian dan work order.
Secara teknis bisa, tetapi itu pilihan desain yang buruk. Modul MRP yang dirancang dengan baik menghasilkan planned order sebagai saran yang bersifat advisory, bukan komitmen yang firm, sehingga planner dapat meninjau, mengelompokkan, dan mengesahkannya menjadi requisisi pembelian atau work order sungguhan. Pemesanan yang sepenuhnya otomatis cenderung memesan berlebihan begitu prakiraan ternyata meleset, karena tidak ada pemeriksaan manusia untuk menangkapnya.
Backflushing secara otomatis menghitung dan memposting konsumsi komponen ketika work order melaporkan kuantitas selesai, berdasarkan kuantitas per pada BOM dikalikan jumlah yang selesai, alih-alih mencatat setiap pengeluaran komponen saat terjadi. Ini adalah trade-off yang tepat untuk manufaktur repetitif berkecepatan tinggi di mana mengeluarkan setiap komponen secara manual akan menyumbat lantai produksi, tetapi tetap harus mendukung override manual untuk scrap dan rework, dan kurang cocok untuk assembly bersiklus panjang dan bernilai tinggi di mana visibilitas work in process yang ketat lebih penting daripada volume transaksi.

Foto oleh Robert Scoble on flickr
Sebagian besar modul ERP yang pernah saya bangun bersifat transaksional: sebuah invoice disetujui, permintaan cuti dirutekan, pembayaran dicatat. Manufaktur berbeda. Sifatnya rekursif, karena barang jadi dibangun dari sub-assembly, yang dibangun dari sub-assembly lain, yang dibangun dari bahan baku. Kesalahan pada model data di lapisan bill of materials tidak hanya merusak satu laporan nantinya, tetapi merusak setiap proses perencanaan, setiap roll-up biaya, dan setiap work order di hilir selama modul itu masih digunakan di produksi.
Tulisan ini membahas bagaimana saya mendesain modul bill of materials dan material requirements planning untuk sebuah produsen skala menengah, mencakup skema untuk BOM berjenjang, bagaimana work order mengonsumsi dan menghasilkan stok, bagaimana proses MRP mengubah prakiraan penjualan menjadi rencana pembelian dan produksi, dan mengapa backflushing saat penyelesaian biasanya menjadi pilihan yang lebih tepat dibanding pencatatan transaksi per pengeluaran barang.
Bill of materials adalah struktur parent-child yang rekursif: satu record item untuk parent, dan sekumpulan record baris BOM yang menunjuk ke item komponen, masing-masing dengan kuantitas per unit parent yang diproduksi. Jebakan yang paling sering dilakukan pembangun ERP pemula adalah mencoba meratakan struktur ini menjadi satu tabel dengan kolom tetap untuk komponen satu, komponen dua, dan seterusnya. Pendekatan itu langsung runtuh begitu sebuah produk memiliki lebih dari segelintir bahan, dan membuat eksplosi multi-level hampir mustahil untuk dikueri.
Dengan memisahkan header BOM dan baris BOM dari item master, dan memberi versi pada header berdasarkan tanggal efektif, kamu bisa menjawab pertanyaan tingkat perencanaan seperti berapa biaya membangun produk ini hari ini, sekaligus pertanyaan tingkat audit seperti apa yang sebenarnya dikonsumsi produk ini ketika work order ditutup enam bulan lalu.
Simpan kuantitas per sebagai desimal dengan setidaknya empat digit presisi. Membulatkan rasio komponen seperti 0,0625 kilogram per unit menjadi dua digit desimal akan berakumulasi menjadi selisih persediaan yang nyata begitu dikalikan sepanjang produksi sepuluh ribu unit.
BOM single-level hanya mencantumkan anak langsung dari satu item parent. BOM berjenjang, atau multi-level, secara rekursif mengembangkan setiap sub-assembly hingga ke bahan baku, menunjukkan pohon lengkap dengan kuantitas yang dikalikan di setiap level. Kedua tampilan ini berasal dari tabel baris BOM yang sama; perbedaannya murni pada kuerinya.
| Tampilan | Yang ditunjukkan | Kegunaan utama |
|---|---|---|
| BOM single-level | Komponen langsung dari satu parent, satu level saja | Membeli sub-assembly tertentu, pengecekan biaya cepat |
| BOM multi-level berjenjang | Pohon rekursif lengkap, kuantitas dikalikan di setiap level | Tinjauan engineering, analisis where-used menyeluruh |
| Where-used (BOM terbalik) | Setiap parent yang mengonsumsi komponen tertentu, menyusuri ke atas | Analisis dampak sebelum menghentikan atau mengubah sebuah part |
Di PostgreSQL, common table expression rekursif adalah cara alami untuk menyusuri pohon ke arah mana pun. Menyusuri ke bawah dari barang jadi ke bahan baku, kamu menggabungkan baris BOM dengan dirinya sendiri di mana item parent sama dengan item komponen dari baris sebelumnya; untuk laporan where-used ke atas, kamu membalik arah join tersebut. Batasi kedalaman rekursi secara defensif, karena kesalahan input data yang membuat sebuah item menjadi komponen tidak langsung dari dirinya sendiri akan membuat kueri berjalan tanpa henti.
Work order adalah record yang menghubungkan BOM dan routing ke sebuah proses produksi nyata: bangun sejumlah item ini, sebelum tanggal ini, mengonsumsi komponen-komponen ini. Record ini melewati state machine kecil seiring produksi berjalan.
Setiap work order harus membawa snapshot dari baris BOM yang menjadi dasarnya, bukan pointer langsung ke BOM yang aktif saat ini. Jika engineering merevisi resep bulan depan, work order yang sedang berjalan harus tetap mengonsumsi resep yang berlaku saat dirilis, kalau tidak pelaporan varians biaya menjadi tidak berarti.
Jangan pernah membiarkan daftar komponen work order menunjuk langsung ke header BOM yang aktif. Snapshot baris BOM yang sudah diexplode ke tabel khusus work order pada saat order dirilis. Kalau tidak, perubahan engineering di tengah bulan akan diam-diam menulis ulang apa yang seharusnya dikonsumsi oleh produksi yang sudah berjalan separuh jalan.
MRP mengambil permintaan independen, sales order dan prakiraan untuk barang jadi, dan mengubahnya menjadi permintaan dependen untuk setiap komponen di bawahnya, digeser ke belakang dalam waktu sesuai lead time. Algoritma klasik, diformalkan oleh Joseph Orlicky pada tahun 1970-an, berjalan level demi level: untuk setiap item, hitung gross requirements, kurangi apa yang sudah tersedia di stok atau sudah dipesan untuk mendapatkan net requirements, dan jika net requirements positif, hasilkan planned order yang harus jatuh tempo pada tanggal kebutuhan lalu digeser lebih awal sesuai lead time item tersebut. Kemudian dorong kuantitas planned order itu ke bawah sebagai gross requirements untuk komponen-komponennya sendiri, dan ulangi.
function explodeMrp(item, requiredQty, dueDate, onHand) {
const gross = requiredQty
const available = onHand.get(item.sku) ?? 0
const net = Math.max(gross - available, 0)
if (net === 0) return []
const orderDate = subtractLeadTime(dueDate, item.leadTimeDays)
const workOrder = { sku: item.sku, qty: net, dueDate, orderDate }
const children = []
for (const line of item.bomLines) {
const childQty = net * line.qtyPer * (1 + line.scrapFactor)
children.push(
...explodeMrp(line.component, childQty, orderDate, onHand)
)
}
return [workOrder, ...children]
}Fungsi rekursif di bawah ini menggambarkan inti dari proses eksplosi tersebut. Dalam implementasi nyata kamu juga perlu memperhitungkan aturan lot sizing, karena memesan tepat sejumlah net requirement jarang praktis ketika pemasok memiliki kuantitas pesanan minimum atau kamu ingin mengelompokkan produksi dalam ukuran lot tetap, dan kamu perlu memisahkan item buat, yang menghasilkan work order, dari item beli, yang menghasilkan requisisi pembelian.
Output dari sebuah proses MRP adalah sekumpulan planned order, sebagian menjadi requisisi pembelian untuk item beli dan sebagian menjadi work order untuk item buat. Modul MRP yang didesain dengan baik tidak pernah menulis langsung ke purchase order atau work order yang firm; ia menghasilkan planned order yang ditinjau dan disahkan oleh seorang planner, karena sistem yang sepenuhnya otomatis menembakkan purchase order tanpa tinjauan manusia cenderung memesan berlebihan begitu prakiraan meleset.
Pola manusia-dalam-lingkaran ini yang membedakan modul MRP yang benar-benar dipakai dari sekadar spreadsheet yang secara teknis melakukan perhitungan yang sama. Planner jauh lebih percaya pada sistem yang menampilkan pengecualian dan membiarkan mereka mengesahkan komitmen, dibanding sistem yang diam-diam mengikat modal atas nama mereka.
Perlakukan setiap proses MRP sebagai keluaran yang bersifat advisory, bukan transaksi. Menjalankan ulang MRP setiap malam harus aman dilakukan setiap hari tanpa efek samping, karena proses itu hanya menghasilkan ulang planned order dan tidak pernah menyentuh purchase order yang sudah firm atau work order yang sudah dirilis.
Ada dua cara untuk mencatat konsumsi komponen terhadap sebuah work order. Forward flushing mengeluarkan setiap komponen ke work order saat secara fisik diambil dari gudang, memberikan visibilitas real-time atas persediaan di lantai produksi tetapi membutuhkan satu transaksi untuk setiap pengambilan. Backflushing sebaliknya menunggu sampai work order melaporkan kuantitas selesai, lalu secara otomatis menghitung dan memposting konsumsi komponen berdasarkan kuantitas per pada BOM, dikalikan dengan kuantitas yang selesai.
Pola yang menyatukan semua ini adalah memperlakukan BOM sebagai satu-satunya sumber kebenaran untuk apa yang seharusnya dikonsumsi, work order sebagai record dari apa yang benar-benar dikonsumsi, dan MRP sebagai lapisan perencanaan yang mengubah permintaan yang diprakirakan menjadi rencana bertahap waktu terhadap keduanya. Pisahkan ketiga peran ini dengan bersih di skema kamu, dan fitur pelaporan, costing, serta perencanaan yang kamu bangun di atasnya akan tetap koheren bahkan ketika katalog produk bertumbuh menjadi ribuan part di puluhan level.