Anggap ada sebuah gudang snack di belakang toko. Di dalam gudang itu ada beberapa rak. Di rak-rak tersebut tersusun berbagai macam produk: ciki, minuman, mie instan, dan lain-lain. Di setiap rak ditempel kertas kecil yang berisi catatan stok terakhir, misalnya:
“Rak A: Ciki 200 pcs”.

Selain kertas di rak, ada satu buku harian gudang. Setiap kali ada barang masuk atau keluar, petugas gudang menulis catatan di buku itu, misalnya:
“Masuk 50 pcs Ciki ke Rak A” atau “Keluar 10 pcs Ciki dari Rak A karena dijual”.

Dengan dua hal sederhana itu, kertas di rak dan buku harian, gudang bisa tahu dua hal sekaligus: stok terakhir dan riwayat pergerakan barang. Odoo 18 melakukan hal yang mirip, hanya saja dalam bentuk data di database, bukan kertas dan buku.
stock.quant: Catatan Stok Terakhir di Setiap Rak
Saat form produk dibuka di Odoo, di sana terlihat On Hand Quantity, misalnya “On Hand: 200”. Angka ini berasal dari tabel yang bernama stock.quant.
Tabel stock.quant bisa dianggap seperti kumpulan kertas yang ditempel di rak. Satu baris di stock.quant adalah catatan stok terakhir untuk satu produk di satu lokasi. Jika Produk 1 disimpan di lokasi gudang utama dan jumlahnya 200, maka ada satu baris di stock.quant yang kira-kira berarti: “Produk 1, Lokasi Gudang, Quantity 200”.
Hal penting di sini: stock.quant hanya menyimpan kondisi stok terbaru. Jika sebelumnya stok 0, lalu pernah jadi 100, kemudian diubah menjadi 200, maka stock.quant hanya menyimpan angka akhirnya, yaitu 200. Riwayat perubahan “0 → 100 → 200” tidak ada di stock.quant. Sejarah seperti itu dicatat di tempat lain.
Contoh Stok Opname: Dari 0 ke 100 ke 200
Ambil contoh Produk 1. Pada awalnya stok masih 0, sehingga belum ada catatan quant untuk produk ini di lokasi tersebut. Lalu tim gudang melakukan stok opname dan menemukan bahwa barang di gudang sebenarnya sudah ada 100 unit. Angka 100 dimasukkan ke Odoo sebagai stok hasil pengecekan.
Beberapa waktu kemudian dilakukan pengecekan ulang, dan ternyata stok yang benar adalah 200, bukan 100. Angka di Odoo kemudian diperbarui menjadi 200.

Di tampilan Odoo, pada akhirnya On Hand untuk Produk 1 akan terlihat sebagai 200. Namun ketika data stock.quant dilihat langsung di database, bisa muncul lebih dari satu baris yang terkait produk ini. Satu baris berisi stok di lokasi gudang utama dengan quantity 200. Baris lain dapat muncul untuk lokasi lain yang terlihat “aneh”, misalnya lokasi penyesuaian.

Di sinilah muncul konsep lokasi virtual. Secara fisik, gudang hanya merasa punya satu lokasi utama. Namun di database Odoo terdapat lokasi tambahan yang bersifat virtual dan digunakan untuk menampung selisih atau penyesuaian stok. Lokasi ini tidak berupa rak sungguhan, tetapi tetap ada sebagai data.
Lokasi Virtual: Rak Bayangan untuk Penyesuaian
Odoo mempunyai prinsip bahwa setiap perubahan stok adalah perpindahan barang dari satu lokasi ke lokasi lain. Saat stok opname dilakukan, Odoo tidak sekadar mengganti angka dari 0 ke 100 atau dari 100 ke 200. Sistem menganggap ada barang yang berpindah dari suatu lokasi ke lokasi lain.

Ketika stok di lokasi WH/Stock (gudang utama) berubah dari 0 menjadi 100, Odoo menganggap barang itu datang dari sebuah lokasi penyesuaian menuju lokasi gudang. Lokasi penyesuaian ini bisa dibayangkan sebagai rak bayangan yang hanya ada di dalam sistem. Ketika stok berubah lagi dari 100 menjadi 200, Odoo menganggap ada tambahan 100 unit yang kembali pindah dari lokasi penyesuaian ke lokasi gudang.
Karena alasan itu, di database sering terlihat dua lokasi yang berhubungan dengan stok suatu produk. Lokasi pertama adalah lokasi gudang yang benar-benar ada, misalnya WH/Stock. Lokasi kedua adalah lokasi virtual seperti Inventory Adjustment yang digunakan untuk mengelola selisih. Di tampilan sehari-hari, pengguna biasanya lebih fokus ke lokasi nyata, sementara lokasi virtual bekerja di latar belakang untuk menjaga pencatatan tetap seimbang.
Riwayat Stok Disimpan di stock.move dan stock.move.line
Jika stock.quant hanya menyimpan angka stok terakhir, muncul pertanyaan: di mana riwayat perpindahan barang disimpan? Bagaimana bisa diketahui bahwa stok sebelumnya 0, lalu 100, lalu 200?
Riwayat pergerakan itu disimpan di dua tabel lain: stock.move dan stock.move.line. Jika stock.quant diibaratkan kertas di rak, maka stock.move dan stock.move.line adalah buku harian gudang.
Tabel stock.move dapat dianggap sebagai “judul” perpindahan barang. Satu baris di stock.move menjelaskan bahwa sejumlah unit dari suatu produk dipindahkan dari satu lokasi ke lokasi lain. Informasi yang disimpan antara lain jenis produk, lokasi asal, lokasi tujuan, jumlah yang direncanakan, dan referensi dokumen yang terkait, seperti penjualan, pembelian, atau penyesuaian stok. Saat penyesuaian stok mengubah angka, misalnya dari 100 menjadi 200, akan dibuat stock.move baru untuk mencatat perpindahan selisihnya.
Sementara itu, stock.move.line berfungsi sebagai detail dari stock.move. Jika stock.move adalah judul bab, maka stock.move.line adalah paragraf isi. Di sini disimpan berapa banyak barang yang benar-benar dipindahkan (field qty_done), dari lokasi mana ke mana secara faktual, serta informasi tambahan seperti nomor lot atau serial jika dipakai. Setiap baris stock.move.line terhubung ke stock.move yang bersangkutan.
Dengan kombinasi kedua tabel ini, Odoo bisa menceritakan riwayat stok secara lengkap. Dari sana dapat dilacak barang datang dari mana, kapan berpindah, menuju lokasi mana, dan terkait transaksi apa. Jadi, untuk melihat sejarah stok, fokus utamanya adalah stock.move dan stock.move.line, bukan stock.quant.
Dua Cara Penyesuaian Stok di UI Odoo: On Hand vs Physical Inventory
Di antarmuka Odoo, terdapat dua cara yang umum digunakan untuk melakukan stok opname atau penyesuaian stok.
Cara pertama adalah melalui halaman produk. Dari daftar produk, salah satu produk dapat dibuka, kemudian digunakan tombol On Hand atau Update Quantity. Di sana bisa langsung diisi jumlah stok yang diinginkan. Setelah disimpan, Odoo akan menghitung selisih antara stok lama dan angka baru. Selisih tersebut kemudian diubah menjadi record stock.move dan stock.move.line, dan akhirnya stock.quant diperbarui sehingga On Hand menampilkan angka terbaru. Pada cara ini, perhatian pengguna biasanya lebih tertuju pada “stok sekarang mau diset berapa”, bukan pada nilai selisih.
Cara kedua adalah melalui menu Inventory → Adjustment → Physical Inventory. Di sini alur kerjanya sedikit berbeda. Sistem terlebih dahulu menampilkan stok yang tercatat di Odoo saat ini. Petugas gudang kemudian mengisi stok hasil hitung fisik di lapangan. Sebelum perubahan benar-benar diterapkan, sistem menghitung selisih antara angka di sistem dan angka hasil hitung fisik. Selisih ini ditampilkan di kolom khusus yang di belakang layar bersumber dari field inventory_diff_quantity.
Kolom selisih tersebut sangat membantu, karena membuat stok opname menjadi lebih jelas. Barang-barang yang selisihnya besar bisa langsung terlihat dan, jika perlu, dicek ulang sebelum tombol Apply ditekan. Setelah disetujui, penyesuaian ini akan menghasilkan record stock.move dan stock.move.line untuk selisih tersebut, dan stock.quant diperbarui agar stok di sistem sama dengan stok fisik di gudang.
Ringkasan

Secara singkat, cara kerja stok di Odoo 18 dapat dibayangkan seperti gambar di atas.
Tabel stock.quant berperan sebagai snapshot stok: berapa jumlah barang saat ini di setiap lokasi.
Di sisi lain, tabel stock.move dan stock.move.line berperan sebagai detail perpindahan: dari mana barang datang, ke mana pergi, dan kapan perpindahan itu terjadi.
Sedangkan dalam proses penyesuaian stok atau stok opname secara manual, Odoo memakai lokasi virtual sebagai lokasi bayangan untuk menampung catatan perpindahan
Dengan memahami hubungan antara stock.quant, stock.move, dan stock.move.line, cara kerja stok di Odoo 18 menjadi lebih mudah dibayangkan
