ERPNext v16 - Stock/Inventory (Versi BMP)
Semua penjelasan di level perilaku aplikasi, dengan jangkar codebase (branch version-16, erpnext/stock/doctype/). Stock adalah kembar fisik dari Accounting: bila Accounting mencatat nilai, Stock mencatat kuantitas + lokasi + identitas. Keduanya dijahit oleh perpetual inventory - setiap gerakan fisik yang bernilai menulis baris di dua ledger sekaligus (SLE di Stock, GL Entry di Accounting).
Peta dokumen:

Chapter 0 - Struktur & Cross-check
-
- Grup struktur ↔ doctype utama (erpnext/stock/doctype/)

0.2 Kesimpulan struktur
- Stock Ledger Entry (SLE) = kembar GL Entry: immutabel, event-sourced; saldo stok = agregasi, tidak pernah di-update.
- Stock Entry = mover universal (receipt/issue/transfer/repack/manufacture) - analog Journal Entry di Accounting.
- Serial and Batch Bundle (refactor v14+): identitas serial/batch dibungkus satu bundle child yang dirujuk transaksi, bukan field tersebar.
- Warehouse = tree - pola yang sama dengan Cost Center & CoA.
0.3 Register Delta v16 (Stock)
- Stock Closing Entry & Balance (BARU v16) - sisi fisik dari Period Closing. Period Closing Voucher kini memvalidasi stok; saldo di-snapshot dan dibekukan. Simetri sempurna dengan Account Closing Balance.
- Valuation Method di Stock Settings: 3 opsi di branch v16 (Standard Cost hanya di develop).
- Inventory Dimension unlimited - kembar fisik Accounting Dimension.
- Stock Reservation Entry - reservasi stok untuk Sales Order/produksi.
- Landed cost diperluas ke stock entry & subcontracting receipt.
- Perbaikan presisi UOM (mengunci conversion_factor ke presisi 9).
Chapter A - Masters & Struktur
Mesin apa: Kamus dan peta untuk semua pergerakan barang. Tidak ada transaksi stok yang bisa disubmit tanpa menunjuk ke entitas di chapter ini. Master di sini juga menjembatani Stock dengan Accounting (karena di sinilah akun GL default untuk stock-in-hand, COGS, dan expense ditentukan).
Perilaku per doctype:
- Item (erpnext/stock/doctype/item/) - entitas sentral. Atribut penentu perilaku terpenting adalah is_stock_item (Yes = barang fisik yang divaluasi dan ditulis ke SLE; No = jasa, subkon, atau item non-stok yang hanya untuk billing) dan valuation_method (FIFO / Moving Average / Standard). Item memiliki child UOMs (satuan alternatif + faktor konversi) dan child Reorder (pemicu auto-order).
- Item Group - tree (nested set). Berfungsi ganda: sebagai pengelompok UI (filter di list) dan sebagai pemberi default akun GL (Income, Expense, COGS, Stock-in-hand). Saat Item baru dibuat, dia mewarisi default akun dari Item Group-nya (bisa di-override per Item).
- Warehouse - tree (nested set) per Company. Punya warehouse_type (Storage, Work In Progress, Rejected, Scrap, Transit). Perilaku kunci: Group warehouse tidak bisa menampung stok (hanya folder); child warehouse mewarisi akun GL induk (kecuali di-override di warehouse.account).
- UOM (Unit of Measure) - master satuan (Pcs, Box, Kg, Liter).
- UOM Conversion Factor - master konversi (misal: 1 Box = 12 Pcs). Transaksi selalu disimpan dalam stock_uom (satuan dasar); satuan lain dikonversi saat input/output.
- Price List - daftar harga (Beli/Jual/Transfer) per mata uang. Berisi child Item Price. Dipasang sebagai default di Customer (untuk jual) atau Supplier (untuk beli).
- Bin - bukan doctype yang diisi manual. Ini adalah view/agregasi yang menampilkan kuantitas aktual per pasangan (Item, Warehouse). Setiap kali SLE tercipta, sistem melakukan upsert (update/insert) ke Bin. Ini adalah "angka stok" yang dilihat user di form Item.
Lintas domain:
- Item & Warehouse dipakai masif di Buying, Selling, dan Manufacturing.
- Item Group dan Warehouse menyediakan akun default untuk Accounting (Chapter A) saat perpetual inventory menulis GL (Stock-in-hand ↔ COGS).
- Price List dibaca oleh Quotation/Sales Order (Selling) dan Purchase Order (Buying).
Edge case:
- Mengubah is_stock_item dari Yes → No (atau sebaliknya) pada Item yang sudah punya riwayat SLE = bom. Sistem akan menolak, atau memaksa stock reconciliation terlebih dahulu.
- Mengubah akun default di Item Group tidak mengubah Item yang sudah dibuat sebelumnya (Item yang sudah lahir membawa salinan akunnya sendiri). Admin sering bingung "kenapa akunnya tidak ikut berubah".
- Mengubah UOM Conversion Factor setelah ada transaksi yang memakai satuan tersebut akan merusak konversi historis (korupsi data kuantitas).
- Stok di Bin negatif (actual_qty < 0) = pelanggaran logika fisik. ERPNext secara default memblokir ini (lewat setting Allow Negative Stock), tapi membiarkannya aktif adalah sumber mimpi buruk audit.
Delta v16 (subset register Chapter 0.3): Perbaikan presisi UOM (Commit putaway_rule). Sebelumnya, conversion_factor di transaksi kadang ikut presisi tampilan (2 desimal), yang secara silencio mengkorupsi semua kuantitas turunan sebesar ~0,8%. Di v16, presisi dikunci di 9 desimal di seluruh transaksi untuk menjaga integritas rasio.
Catatan scope BMP:
- Adopsi penuh: Item, Item Group, Warehouse tree, UOM, Bin. Konsep Bin sebagai agregasi SLE wajib dipertahankan di SQLite kita.
- Adaptasi: Price List di BMP disederhanakan. ERPNext mendukung matriks harga yang sangat kompleks (berlaku berdasarkan tanggal, kuantitas, grup customer, dll). Untuk UMKM, cukup 1-2 Price List per company (Harga Grosir / Harga Retail) tanpa matriks waktu yang rumit.
- Keputusan desain BMP: Blokir mutlak stok negatif. Tidak ada setting Allow Negative Stock di BMP. Membiarkan stok minus berarti sistem berbohong tentang realitas fisik, yang pada akhirnya akan merusak valuasi COGS dan laporan keuangan.
Chapter B - Transaksi Stok
Mesin apa: semua cara barang berpindah - masuk, keluar, pindah, olah - tanpa kecuali menulis Stock Ledger Entry (Chapter D). Chapter ini adalah "lalu lintas fisik": setiap dokumen di sini adalah peristiwa (barang diterima, dikirim, dipindah, diminta), bukan keadaan. Polanya simetris dengan Accounting: bila di sana ada Journal Entry sebagai mover universal, di sini ada Stock Entry; bila di sana invoice melahirkan klaim, di sini Purchase Receipt / Delivery Note melahirkan gerakan yang dijahit ke Buying/Selling.
Perilaku per doctype:
- Stock Entry (erpnext/stock/doctype/stock_entry/) - mover universal. Semantik ditentukan stock_entry_type: Material Receipt (masuk saja), Material Issue (keluar saja), Material Transfer (pindah warehouse), Material Transfer for Manufacture, Manufacture (konsumsi bahan +产出 finished good via BOM), Repack (multi-masuk → multi-keluar dengan valuasi baru), Send to Subcontractor ⚠️. Child items: item + qty + s_warehouse/t_warehouse + rate + serial/batch bundle. Submit = SLE per arah (out dari source, in ke target) + GL bila perpetual (issue ke expense, receipt dari expense). Child additional_costs (ongkos operasi untuk Repack/Manufacture) ikut masuk valuasi barang hasil ⚠️.
- Stock Entry Type - master semantik: arah gerakan + penamaan; dapat ditambah user (custom type) - pola konfigurasi, bukan kode. Perilaku: type menentukan validasi arah (receipt tak boleh punya s_warehouse, dst.).
- Material Request - sinyal kebutuhan internal, bukan gerakan: type Purchase / Material Transfer / Material Issue / Manufacture; menjadi hulu bagi PO (Buying), Stock Entry, atau Work Order. Status lifecycle Pending → Ordered/Transferred/Issued/Received ⚠️. Perilaku kunci kedua: auto-reorder - scheduler membaca reorder level + safety stock di Item dan melahirkan MR otomatis; mesin "stok menipis → minta beli" tanpa campur tangan manusia.
- Purchase Receipt - GRN: barang masuk terhadap PO (atau berdiri sendiri). Submit = SLE in + GL perpetual: debit Stock-in-Hand, kredit Stock-Received-But-Not-Billed (akrual barang datang, tagihan belum) - pasangan ini baru tertutup saat Purchase Invoice tiba. is_return = retur ke supplier (SLE terbalik). Qty tertolak masuk rejected warehouse lewat Quality Inspection (Quality module). Over-receipt dibatasi allowance ⚠️. Landed Cost Voucher (Chapter D) bisa menambah valuasinya kemudian.
- Delivery Note - pengiriman keluar terhadap SO (atau berdiri sendiri). Submit = SLE out + GL perpetual: debit COGS, kredit Stock-in-Hand - momen biaya diakui. is_return = retur customer (masuk kembali). Memperbarui delivered qty di SO; over-delivery allowance ⚠️.
- Pick List - instruksi pungut untuk petugas gudang: lahir dari SO/DN/Work Order; menyarankan lokasi sumber (batch tertua/FIFO-aware) ⚠️; menjembatani "apa yang diminta" ke "di mana barangnya". Status mengikuti pemenuhan.
- Putaway Rule - kebalikan pick: aturan menaruh barang masuk - item + qty → warehouse tujuan dengan kapasitas. Diterapkan saat receipt untuk menyarankan/memaksa penempatan ⚠️. (Di v16 dapat perbaikan presisi conversion_factor - edge case presisi yang kita verifikasi di Chapter 0.)
- Stock Reservation Entry - mengunci ketersediaan tanpa memindahkan barang: lahir untuk Sales Order (atau produksi); available qty = actual − reserved. Serial/batch-aware; terlepas saat delivery atau cancel. Perilaku: mencegah dua SO menjual fisik yang sama - kebenaran "stok tersedia" yang dilihat sales.
Lintas domain: PR/DN/MR adalah ujung fisik dari Buying & Selling (PO/SO mapping); semua menulis SLE (Chapter D); perpetual menulis GL (Accounting Chapter A) - stock-in-hand, received-not-billed, COGS; auto-reorder dijalankan scheduler (Framework); reservation memberi angka ketersediaan ke Selling; rejected warehouse terhubung Quality Inspection.
Edge case:
- PR submit tanpa PI = akrual received-not-billed menggantung; PI menyusul dengan harga berbeda = selisih harga yang harus mendarat di akun variance/COGS ⚠️ - sumber kebingungan "kenapa ada jurnal aneh".
- Backdated transaction (PR/DN tanggal lampau) mengubah antrean FIFO semua gerakan setelahnya → repost_item_valuation antre (Chapter D); semakin lama keterlambatan pencatatan, semakin besar ledakan repost.
- Over-delivery/over-receipt di luar allowance = blokir; allowance 0 kaku untuk timbangan riil (barang timbang tak pernah pas) - konfigurasi harus sadar realitas lapangan.
- DN tanpa stok cukup = blokir (BMP: stok negatif dilarang mutlak) - tapi di ERPNext ada allow negative stock yang bila aktif menjadi sumber mimpi buruk audit (keputusan Chapter A Stock).
- Pick list basi: stok pindah antara pembuatan pick list dan DN → lokasi pungut invalid; perlu re-pick ⚠️.
- Auto-reorder dengan safety stock salah konfigurasi = scheduler membanjiri MR setiap malam - spam permintaan beli yang terlihat sah.
- Retur keluar untuk batch bervaluasi batch memakai current batch avg rate (perbaikan v16), bukan rate asli - pilihan kebijakan valuasi retur yang harus dipahami akuntan.
Delta v16 (subset register Chapter 0.3): landed cost diperluas ke stock entry & subcontracting receipt; presisi conversion_factor dikunci 9 desimal (putaway & seluruh transaksi); stock reservation entry stabil dengan validasi & permission fixes; stock closing entry memvalidasi stok sebelum period closing (Chapter D).
Catatan scope BMP:
- Adopsi penuh: Stock Entry + type configurable, Material Request + auto-reorder, Purchase Receipt, Delivery Note, Stock Reservation Entry - ini inti operasional distributor/manufaktur ringan.
- Adaptasi: allowance over-delivery/receipt default 0 dengan setting eksplisit per item (bukan global longgar); transaksi backdated dibolehkan terbatas (role-gated, jendela hari konfigurasi) karena UMKM sering mencatat terlambat - dengan konsekuensi repost async yang jujur dilaporkan.
- Tunda (fase 2): Pick List & Putaway Rule - gudang UMKM kecil tidak butuh instruksi pungut/penempatan formal; keduanya menambah permukaan tanpa ROI awal.
Chapter C - Serial & Batch
Mesin apa: lapisan identitas di atas ledger kuantitas. Bila SLE (Chapter D) menjawab "berapa banyak di mana", chapter ini menjawab "yang mana persisnya" - per unit (Serial) atau per lot (Batch). Ini ledger ketiga dalam sistem: GL = identitas nilai finansial, SLE = identitas kuantitas-fisik, Serial/Batch = identitas objek. Sejak refactor v14+, identitas tidak lagi tersebar sebagai field di tiap transaksi, melainkan dibungkus satu wadah first-class: Serial and Batch Bundle.
Perilaku per doctype:
- Serial No (erpnext/stock/doctype/serial_no/) - identitas per unit: string unik + item + warehouse + status (Active / Delivered / Inactive ⚠️) + jejak beli (supplier, rate, tanggal) dan jejak jual (customer, tanggal) + expiry/warranty ⚠️. Perilaku: item ber-has_serial_no memaksa transaksi menyebut tepat sejumlah serial; validasi keras saat submit: serial harus ada & Active di warehouse sumber; dua transaksi tak boleh memakai serial yang sama bersamaan. Lifecycle: lahir saat receipt, Delivered saat keluar ke customer, pulih Active saat retur/cancel - jalur cancel yang wajib mengembalikan status, area yang rawan bug.
- Batch - identitas per lot: batch_id + item + manufacturing/expiry date ⚠️ + (opsional) valuasi sendiri bila batch-wise valuation aktif (per-batch moving average). Perilaku: item ber-has_batch_no memaksa pemilihan batch saat keluar; auto-create via naming series saat masuk; expiry blocking **kondisional** via flag item.has_shelf_life - aktif hanya untuk kategori yang benar-benar bermasa simpan (skincare, makanan); untuk parfum (EDP/EDC) flag = False: expiry date adalah compliance field (label BPOM), **bukan pemblokir transaksi**. Saran pengambilan: FEFO hanya untuk item has_shelf_life = True; parfum memakai FIFO atau pemilihan batch manual (batch lebih tua justru sering dipilih karena lebih matured).. Batch = senjata utama recall: "lot mana yang cacat, tersebar ke mana". **Batch Inheritance (manufaktur parfum):** nomor batch **lahir di Bulk** (output BOM1 mixing) bersama manufacturing date + status QC; saat Bulk dikonsumsi filling (BOM2), FG **mewarisi** nomor batch Bulk yang dikonsumsi - bukan naming series baru. Satu nomor di label botol = satu kunci telusur FG → Bulk → lot RM. Validasi: satu run filling yang mengonsumsi dua batch Bulk berbeda diblokir (atau wajib pilih eksplisit) agar nomor label tetap bermakna tunggal.
- Serial and Batch Bundle - wadah child-table yang dirujuk baris transaksi apa pun (stock_entry_detail, purchase_receipt_item, delivery_note_item, sales_invoice_item ber-update_stock, dll.): baris serial_no/batch_no + qty + rate pergerakan. Perilaku: submit = validasi identitas (serial Active/ada; qty batch cukup; expiry) lalu menulis SLE berdetail identitas; cancel = bundle ikut cancel, identitas dilepas kembali. Menentukan rate keluar: serial = rate masuknya; batch = batch avg atau antrean sesuai metode ⚠️. Selector UI melahirkan bundle; untuk receipt, batch bisa auto-create; untuk issue, auto-select FEFO/FIFO bila setting aktif ⚠️. Pola yang sama dengan Item Wise Tax Detail & Payment Ledger: data yang dulu field/JSON menjadi entitas relasional first-class.
Lintas domain: identitas mengalir ke Quality (inspeksi per batch), Manufacturing (batch bahan & finished good), Buying/Selling (jejak supplier↔customer per unit/lot), Accounting (valuasi per batch mengubah COGS & GL), Reports (traceability, stock ageing per batch); warranty & service membaca serial. SLE (Chapter D) menyimpan rujukan bundle - tanpa bundle, SLE hanya angka tanpa wajah.
Edge case:
- Ghost batch: batch_id manual dengan typo = dua batch untuk satu lot fisik; stok "terpecah" dan ageing bohong. Auto-naming series = pencegahan termurah.
- Konkurensi: dua user mengeluarkan batch yang sama bersamaan = risiko qty batch negatif bila locking lemah; validasi harus di server, saat submit, bukan di client ⚠️.
- Expired override: berlaku hanya bagi item has_shelf_life = True (skincare/makanan) - blokir expiry tanpa jalur override = operasional berhenti saat darurat; override tanpa jejak = governance bolong; keduanya wajib berpasangan (role + audit trail). Untuk parfum tidak ada blokir sama sekali, sehingga tidak butuh override - parfum "expired" secara label bukan cacat kualitas.
- Cancel yang tidak memulihkan status serial = serial "hilang" (terkunci Delivered padahal barang kembali) - pola kerusakan paling umum di lapisan identitas.
- Switch batch-wise valuation di tengah riwayat = diskontinuitas valuasi (antrean lama item-level, baru batch-level) - keputusan satu arah seperti perpetual vs periodic ⚠️.
- Retur keluar batch (purchase return) kini dinilai current batch avg rate, bukan rate asli (perbaikan v16) - barang keluar kembali dengan "harga hari ini"; akuntan harus paham pilihan kebijakan ini.
- Bundle memindahkan kompleksitas ke satu tempat: bagus untuk konsistensi, tapi berarti bug di bundle = bug di semua transaksi stok - single point of truth, single point of failure.
Delta v16 (subset register Chapter 0.3): Serial & Batch traceability report (jejak ujung-ke-ujung jadi laporan bawaan); perbaikan valuasi retur batch (current avg rate); permission & validasi fixes di sekitar bundle; presisi conversion_factor (Chapter B) juga menyentuh baris bundle.
Catatan scope BMP:
- Adopsi penuh: Serial No, Batch, dan pola Bundle - distributor F&B/farmasi/kimia butuh expiry+FEFO; elektronik butuh serial+warranty; keduanya profil UMKM Indonesia yang nyata.
- Adopsi (keputusan desain): expiry blocking **kondisional** - default ON hanya untuk item has_shelf_life = True (skincare/makanan) dengan override role ber-audit-trail; auto-select FEFO default hanya untuk item tersebut. **Parfum (EDP/EDC): has_shelf_life = False** - expiry date dicetak di label (compliance BPOM) tetapi tidak pernah memblokir transaksi, dan pengambilan stok memakai FIFO/manual (batch tua = aset matured, bukan barang basi). batch_id auto-series default, manual hanya opsi.
- Adaptasi: batch-wise valuation = opsional, default tetap moving average level item - sederhana dulu, valuasi per batch bagi yang benar-benar butuh (mis. harga beli lot sangat fluktuatif).
- Tunda: riwayat servis per serial (service/warranty module) - fase 2; identitas dasar sudah cukup untuk MVP.
Chapter D - Ledger & Valuasi
Mesin apa: jantung fisik ERP - tempat kuantitas menjadi nilai. Bila Chapter B adalah lalu lintas dan Chapter C identitas, chapter ini adalah buku besarnya: setiap gerakan menulis Stock Ledger Entry yang immutabel, dan metode valuasi memutuskan "berapa nilai barang yang keluar ini". Dari sinilah COGS, stock-in-hand, dan akhirnya laba kotor lahir - dan karena itu chapter ini adalah cermin fisik sempurna dari Core Ledger Accounting: SLE ↔ GL Entry, Repost ↔ Repost Accounting Ledger, Stock Closing ↔ Account Closing Balance.
Perilaku per doctype/konsep:
- Stock Ledger Entry (erpnext/stock/doctype/stock_ledger_entry/) - baris ledger fisik yang immutabel: item, warehouse, qty bertanda, valuation_rate, qty_after_transaction (saldo berjalan yang disimpan per baris), rujukan bundle serial/batch, voucher, posting date+time, dimensi inventori. Perilaku: tidak ada edit/hapus; cancel = SLE pembalik; saldo = qty_after_transaction baris terakhir per (item, warehouse). Urutan adalah segalanya: posting date + waktu + creation menentukan posisi antrean valuasi - menyisipkan transaksi bertanggal lampau berarti mengubah semua hitungan setelahnya.
- Metode valuasi - dua warga kelas satu + satu warisan: Moving Average (avg baru = (saldo×avg + masuk×rate) / total; keluar memakai avg terkini - stabil dan ramah volume kecil) dan FIFO (antrean lot masuk; keluar mengonsumsi lot tertua - jujur secara fisik untuk barang bermasa simpan). LIFO ada sebagai warisan ⚠️ (branch v16 menawarkan tiga opsi; Standard Cost hanya di develop) - ditolak di BMP karena PSAK/IFRS melarangnya. Metode bisa per item (override default Stock Settings); menggantinya di tengah riwayat = diskontinuitas satu arah.
- Repost Item Valuation - mesin perbaikan ledger fisik: antrean async yang lahir otomatis saat ada transaksi backdated/perubahan rate; menghitung ulang qty_after_transaction + valuation_rate dari titik terdampak ke depan, lalu ikut mem-posting ulang GL perpetual ⚠️. Status Queued/InProgress/Completed/Failed; idempoten (perbaikan v16 untuk return multi-baris moving average). Pola async yang sama dengan Repost Accounting Ledger dan keluarga background job Framework.
- Stock Reconciliation - penyesuaian hitung fisik: menetapkan qty/rate aktual per (item, warehouse), selisihnya menjadi SLE penyesuaian ke akun stock adjustment. Perilaku: jalan untuk stok awal dan cycle count; item berserial wajib menyerahkan daftar serial, berbatch wajib cocok dengan batch yang ada; mengubah rate di sini mengubah valuasi keluaran masa depan (dan bisa memicu repost ⚠️).
- Landed Cost Voucher - menyebar biaya tambahan (ongkir, bea, asuransi) ke valuasi barang diterima: mereferensi Purchase Receipt - dan di v16 juga stock entry & subcontracting receipt - dengan child biaya (akun beban + amount) dan basis sebar (Qty / Amount / Weight ⚠️). Submit = valuasi item naik + GL memindah beban ke stock-in-hand. Bila barangnya sudah terlanjur keluar, biaya mendarat di akun beban/COGS, bukan valuasi ⚠️ - kebijakan yang harus dipahami, bukan bug.
- Stock Closing Entry (v16) - penutupan stok per periode: menghitung snapshot qty+nilai per (item, warehouse) dan menulisnya ke Stock Closing Balance; Period Closing Voucher kini memvalidasi stock closing sebelum menutup buku akuntansi - dua ledger ditutup bersama atau tidak sama sekali.
- Stock Closing Balance (v16) - snapshot fisik: sumber performa report as-of (saldo tanggal lampau dibaca dari snapshot + delta, bukan jutaan SLE) dan seed valuasi batch setelah closing; stok periode tertutup dibekukan - transaksi backdated ke periode closed diblokir.
Lintas domain: SLE ditulis semua transaksi Chapter B + invoice ber-update_stock (Accounting B); valuasi memberi angka ke COGS & stock-in-hand di GL (Accounting A); repost menyentuh dua ledger sekaligus; landed cost membaca Buying (PR) dan akun beban; stock closing menggerbangi Period Closing Voucher (Accounting I); dimensi inventori (Chapter F) tersimpan di SLE; batch valuation (Chapter C) adalah antrean FIFO per batch.
Edge case:
- Backdated receipt setelah barang terlanjur keluar = antrean FIFO berubah seluruhnya → repost besar; semakin panjang jendela keterlambatan pencatatan, semakin mahal perbaikannya.
- Moving average dengan stok negatif = matematika rusak (pembagian saldo nol/negatif → avg tak bermakna) - alasan teknis terkuat mengapa BMP memblokir stok negatif mutlak.
- Rate reconciliation di item berriwayat = keluaran masa depan dinilai baru, COGS lampau tetap - diskontinuitas yang sah secara sistem tapi wajib dikomunikasikan ke akuntan.
- Landed cost terlambat pada barang yang sudah dijual = biaya tak bisa masuk valuasi lagi; mendarat di beban/COGS ⚠️ - "kenapa ongkir ini jadi beban langsung?" adalah pertanyaan yang lahir di sini.
- Konkurensi & timestamp identik pada SLE (perbaikan cancel v16 yang kita verifikasi) = bukti bahwa urutan ledger rapuh; validasi & locking harus server-side.
- Snapshot basi: repost yang menyentuh periode tertutup harus diblokir, bukan dibiarkan menimpa Stock Closing Balance - freeze adalah fitur integritas, bukan ketidaknyamanan.
- Retur & rate: barang kembali masuk antrean dengan rate kebijakan (current avg untuk return keluar batch-wise per perbaikan v16) - pilihan kebijakan valuasi retur yang mengubah laba per transaksi.
Delta v16 (subset register Chapter 0.3): Stock Closing Entry/Balance + validasi stok di Period Closing Voucher + freeze periode tertutup + seed valuasi batch dari snapshot; idempotensi repost (return moving average multi-baris); perbaikan cancel SLE same-timestamp; landed cost diperluas ke stock entry & subcontracting receipt.
Catatan scope BMP:
- Adopsi penuh: SLE immutabel dengan saldo berjalan, repost async (dengan status yang terlihat oleh user - konsisten prinsip async jujur kita), Stock Reconciliation, Landed Cost Voucher, Stock Closing + freeze + snapshot (materialized view SQLite - keputusan Chapter A Accounting kini punya kembar fisik).
- Keputusan desain BMP: default valuasi = Moving Average (stabil untuk lot kecil & harga berfluktuasi harian); FIFO opsional per item untuk bisnis yang harga lotnya benar-benar menentukan margin (mis. komoditas); LIFO tidak ditawarkan sama sekali.
- Adaptasi: basis sebar landed cost = Qty & Amount saja di fase 1 (Weight menyusul bersama modul manufaktur lanjutan ⚠️→keputusan).
- Keputusan desain BMP: tutup buku BMP = satu aksi, dua ledger - stock closing dan accounting closing divalidasi bersama; tidak ada periode yang tertutup setengah.
Chapter E - Stock Reports
Mesin apa: mata dan telinga gudang - serta jembatan audit ke Accounting. Laporan stok bukanlah entitas yang menyimpan data sendiri; mereka adalah proyeksi on-the-fly dari Stock Ledger Entry (SLE) dan Bin, atau pembacaan atas snapshot Stock Closing Balance. Prinsipnya sama dengan Financial Reports di Accounting: sumber kebenaran tunggal adalah ledger, laporan hanyalah cara kita melihatnya. Laporan di sini menjawab tiga pertanyaan fundamental operasional: "Apa yang saya punya sekarang?", "Apa yang akan saya punya besok?", dan "Apakah nilai fisik saya cocok dengan nilai buku?"
Perilaku per laporan (di erpnext/stock/report/):
- Stock Balance - laporan paling sering dibuka. Menampilkan Qty, Valuation Rate, dan Total Value per Item/Warehouse pada tanggal tertentu (as-of date). Perilaku: di v16, laporan ini membaca Stock Closing Balance untuk tanggal lampau lalu menjumlahkan delta SLE setelahnya, alih-alih menghitung jutaan baris SLE dari awal. Ini yang membuat report bisa dibuka dalam hitungan detik walau data sudah bertahun-tahun. Filter: Item Group, Warehouse, Brand, dimensi inventori.
- Stock Ledger - rekaman kronologis pergerakan: filter dari SLE mentah berdasarkan tanggal, item, warehouse, dan voucher. Perilaku: ini adalah ultimate audit trail fisik. Bila ada pertanyaan "kenapa stok barang X tiba-tiba hilang 5 pcs hari Selasa?", jawaban mutlaknya ada di laporan ini, menelusuri baris SLE pembalik (cancel) atau penyesuaian (reconciliation).
- Stock Ageing - menjawab "berapa lama barang ini mengendap?". Mengelompokkan stok (terutama batch) ke dalam bucket umur (0-30, 31-60, 90+ hari). Perilaku: krusial untuk barang ber-expiry (menemukan batch yang harus didiskon/dibuang segera) dan identifikasi dead stock (barang yang dibeli tapi tidak laku).
- Projected Quantity - pandangan ke masa depan. Rumusnya: Actual Qty (Bin) + Planned Receipts (PO/PR/MR) - Planned Issues (SO/DN). Perilaku: alat utama inventory planner. Menjawab "apakah stok kita cukup untuk memenuhi pesanan bulan depan?".
- Item Shortage - turunan dari Projected Quantity; memfilter hanya item yang Projected Qty < 0 atau Projected Qty < Reorder Level. Perilaku: daftar belanja otomatis. Bila auto-reorder (Chapter B) gagal atau tidak dikonfigurasi, laporan ini adalah safety net manual.
- Serial and Batch Traceability (v16) - jejak ujung-ke-ujung identitas. Memasukkan satu Serial No atau Batch ID, laporan ini memuntahkan seluruh riwayat: kapan dibeli (dari supplier mana, harga berapa), dipindah ke mana, dan kapan dijual (ke customer mana). Perilaku: senjata utama untuk product recall atau investigasi garansi.
- Stock and Account Value Reconciliation - jembatan audit paling kritis. Membandingkan total nilai valuasi di Stock Ledger (fisik) dengan saldo akun Stock-in-Hand di GL (Accounting Chapter A). Perilaku: keduanya harus sama persis hingga ke sen terakhir. Bila tidak, berarti ada kesalahan fatal (misal: ada Journal Entry manual yang menyentuh akun Stock-in-Hand, atau ada SLE yang gagal menulis GL).
Lintas domain: Membaca SLE & Bin (Chapter D & A); membaca PO/SO untuk proyeksi (Buying/Selling); membaca GL untuk rekonsiliasi (Accounting Chapter A); Ageing membaca Batch/Serial (Chapter C).
Edge case:
- "Stock-Account Mismatch": saat laporan rekonsiliasi menunjukkan selisih. Ini adalah mimpi buruk akuntan. Penyebab paling umum: admin membuat Journal Entry manual langsung ke akun Stock-in-Hand (melanggar aturan bahwa akun stok hanya boleh ditulis oleh transaksi stok).
- Projected Quantity berbohong: jika sales tidak membuat SO, atau purchasing tidak membuat PO, projected qty akan sama dengan actual qty. Laporan ini hanya sejujur kedisiplinan input order di hulu.
- Stock Balance lambat: sebelum adanya Stock Closing Balance (v16), menarik Stock Balance untuk 3 tahun lalu memaksa sistem menghitung semua SLE dari hari pertama company berdiri. Dengan snapshot, ini teratasi.
- Ageing pada Moving Average: karena barang masuk dan keluar bercampur (tidak ada identitas lot yang ketat seperti FIFO murni), menghitung "umur" rata-rata stok menjadi aproksimasi matematis, bukan kebenaran fisik absolut ⚠️.
Delta v16 (subset register Chapter 0.3): Serial and batch traceability report diresmikan sebagai laporan bawaan; optimasi performa report lewat pembacaan snapshot Stock Closing Balance.
Catatan scope BMP:
- Adopsi penuh: Stock Balance, Stock Ledger, Projected Quantity, dan Stock and Account Value Reconciliation. Laporan rekonsiliasi ini wajib ada di dashboard admin gudang/akuntan sebagai "alarm harian" untuk mencegah kebocoran nilai.
- Adaptasi UI: Stock Ageing dan Item Shortage di BMP tidak hanya disajikan sebagai grid tabel yang membosankan, tapi diintegrasikan ke Landing Page (My Attention). Item yang masuk shortage atau ageing > 90 hari langsung muncul sebagai notifikasi actionable di dashboard user terkait.
- Keputusan Desain: Semua laporan stok di BMP wajib bisa di-export ke Excel/CSV dengan satu klik, karena realitas UMKM adalah mereka masih sering melakukan tweak atau analisis lanjutan di spreadsheet.
Chapter F - Stock Settings & Inventory Dimension
Mesin apa: ruang policy modul stok - bab penutup yang tidak menggerakkan barang apa pun, tapi menentukan boleh tidaknya dan bagaimana semua gerakan di Chapter B-E terjadi. Pola yang sama dengan Accounts Settings di Accounting: single doctype policy + satu pendamping dimensi. Dan seperti kembarannya di sisi akuntansi, chapter ini adalah wujud modul Administration BMP untuk domain stok.
Perilaku per doctype:
- Stock Settings (erpnext/stock/doctype/stock_settings/) - single doctype policy; kelompok perilakunya:
- Valuasi & integritas: default valuation_method (tiga opsi di branch v16: FIFO / Moving Average / LIFO-warisan ⚠️; Standard Cost hanya di develop) - dipakai item baru yang tidak men-set metode sendiri; stock frozen till date + role pengecualian (kembar fisik dari frozen date Accounting) ⚠️; dan toggle paling berbahaya di seluruh modul: allow negative stock.
- Serial & batch: auto-create batch via naming series; auto-select batch untuk keluaran (FEFO hanya item has_shelf_life = True; FIFO/manual untuk parfum ⚠️); blokir batch kedaluwarsa **kondisional** per flag has_shelf_life (parfum = compliance saja, tanpa blokir); penegakan bundle vs field lama ⚠️.
- Allowance: default over-delivery/over-receipt (bisa di-override per item) - pagar kuantitas bagi PR/DN.
- Reservasi & penempatan: toggle stock reservation; toggle putaway rule ⚠️. Perilaku kunci yang sama dengan kembarannya: mengubah policy tidak retroaktif - SLE lama tetap seperti saat ditulis; item baru mewarisi default baru, item lama membawa salinannya sendiri.
- Inventory Dimension - kembar fisik Accounting Dimension: dimensi custom (Rack, Lokasi, Project, Salesman ⚠️) yang menambah field ke transaksi stok + SLE (implementasi via custom field ⚠️); menjadi filter & sumbu grup di Stock Reports (Chapter E). Perilaku penting: dimensi tidak memecah valuasi - valuasi tetap per (item, warehouse, batch); dimensi murni sumbu pelaporan ⚠️. v16: cek mandatory dipindah server-side - wajib isi tak bisa di-bypass client.
Lintas domain: policy di sini menggerbangi semua penulis SLE (Chapter B-D): frozen date memblokir posting fisik; allowance membatasi PR/DN; toggle expiry & FEFO mengendalikan perilaku Chapter C; default valuation memberi metode ke item baru (Chapter D); dimensi tersimpan di SLE dan dibaca Stock Reports (Chapter E); stock frozen + valuation default adalah cermin fisik dari Accounts Settings (Accounting Chapter I).
Edge case:
- Allow negative stock = ON adalah satu-satunya setting yang sanggup merusak matematika valuasi (moving average atas saldo negatif tak bermakna) - alasan ia tidak ditawarkan sama sekali di BMP (keputusan Chapter A & D).
- Mengganti default valuation method tidak menyentuh item lama - pola warisan yang sama dengan akun default Item Group; admin bingung "kenapa item ini masih FIFO?".
- Stock frozen date dipasang saat gudang masih punya receipt tertahan = blokir mendadak; tanpa role pengecualian, operasional berhenti.
- Menyalakan auto-select FEFO di tengah operasi mengubah batch mana yang keluar - kontinuitas COGS bergeser; keputusan kebijakan, bukan klik biasa.
- Dimensi yang dibuat belakangan tidak mengisi SLE lama - laporan per dimensi harus sadar batas waktunya (pola identik Accounting).
- Mandatory dimensi server-side (v16) = integrasi/script lama yang menulis transaksi lewat API tanpa dimensi kini gagal - integritas menang, tapi migrasi butuh komunikasi ⚠️.
Delta v16 (subset register Chapter 0.3): mandatory check Inventory Dimension dipindah server-side; literal valuation_method tiga opsi (Standard Cost develop-only); validasi stock closing sebelum period closing (Chapter D) menutup lingkaran policy periode.
Catatan scope BMP:
- Adopsi penuh: pola single doctype policy per domain; stock frozen + role pengecualian; reservasi toggle. **Expiry & FEFO kondisional:** blokir + FEFO auto-select hanya untuk item has_shelf_life = True; parfum (EDP/EDC) default has_shelf_life = False - expiry = compliance field, pengambilan FIFO/manual.
- Keputusan desain BMP (ditegaskan): toggle allow negative stock dihapus dari permukaan - bukan default off, tapi tidak ada; opsi berbahaya yang tidak pernah benar untuk UMKM tidak layak diberi tombol.
- Adaptasi: Inventory Dimension = kolom terdefinisi kode per instalasi (Rack/Lokasi/Project sebagai set awal, terversioning) - konsisten dengan keputusan Accounting Dimension.
- Adaptasi: allowance default 0 per company, override eksplisit per item - pagar ketat dulu, kelonggaran diminta sadar.
Chapter F tertutup - dokumen ERPNext_v16 - Stock (Versi BMP) selesai: Chapter 0 + A-F, dari master hingga policy, dengan simetri penuh terhadap Accounting (SLE↔GL, Repost↔Repost, Stock Closing↔Account Closing, frozen date kembar, dimensi kembar).
Posisi BMP setelah dua modul inti: ledger ganda (nilai + fisik) immutabel dengan snapshot closing; valuasi Moving Average default + FIFO opsional, LIFO ditolak; stok negatif diblokir mutlak; FEFO + expiry blocking untuk barang bermasa simpan; landed cost & rekonsiliasi stok-akuntansi sebagai alarm integritas. Fondasi untuk distributor/manufaktur ringan sudah berdiri.
Sesuai roadmap, berikutnya Buying & Selling (siklus operasional inti). Mulai dengan file terpisah ERPNext_v16 - Buying_Selling.docx dan Chapter 0 cross-check-nya?