Berikut adalah modul pembelajaran lengkap untuk membangun aplikasi Kasir (Point of Sale) sederhana berbasis JavaScript/Web, lengkap dengan persiapan, struktur kode, dan alur logika di balik layarnya.
Modul 1: Persiapan & Struktur Project
1. Tools yang Dibutuhkan
2. Struktur Folder & File
Buat folder baru bernama aplikasi-kasir dan siapkan 3 file utama:
index.html (Tampilan antarmuka)
style.css (Desain/tata letak)
app.js (Logika & pemrosesan data)
Modul 2: Desain Antarmuka (index.html & style.css)
Buat struktur HTML dengan dua area utama: Daftar Produk (kiri) dan Keranjang/Struk (kanan).
index.html
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Aplikasi Kasir Sederhana</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<!-- Bagian Produk -->
<div class="produk-section">
<h2>Daftar Produk</h2>
<div id="daftar-produk" class="grid-produk"></div>
</div>
<!-- Bagian Keranjang & Pembayaran -->
<div class="kasir-section">
<h2>Keranjang Belanja</h2>
<ul id="daftar-keranjang"></ul>
<hr>
<p><strong>Total: Rp <span id="total-harga">0</span></strong></p>
<div class="pembayaran">
<label>Bayar: Rp</label>
<input type="number" id="input-bayar" placeholder="Masukkan nominal">
<button onclick="prosesBayar()">Proses Transaksi</button>
</div>
<p id="pesan-kembalian"></p>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
style.css
body { font-family: Arial, sans-serif; margin: 20px; background: #f4f4f9; }
.container { display: flex; gap: 20px; }
.produk-section, .kasir-section { background: white; padding: 20px; border-radius: 8px; flex: 1; }
.grid-produk { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.item-produk { border: 1px solid #ddd; padding: 10px; text-align: center; border-radius: 5px; }
.item-produk button { background: #28a745; color: white; border: none; padding: 5px 10px; cursor: pointer; }
button { cursor: pointer; }
Modul 3: Logika Sistem & Pemrograman (app.js)
app.js
// Data Master Produk
const produkList = [
{ id: 1, nama: "Kopi Hitam", harga: 10000 },
{ id: 2, nama: "Roti Bakar", harga: 15000 },
{ id: 3, nama: "Es Teh Manis", harga: 5000 },
{ id: 4, nama: "Nasi Goreng", harga: 20000 }
];
// State Keranjang
let keranjang = [];
// 1. Tampilkan Daftar Produk ke HTML
function tampilkanProduk() {
const container = document.getElementById('daftar-produk');
container.innerHTML = '';
produkList.forEach(item => {
container.innerHTML += `
<div class="item-produk">
<h4>${item.nama}</h4>
<p>Rp ${item.harga.toLocaleString('id-ID')}</p>
<button onclick="tambahKeKeranjang(${item.id})">+ Tambah</button>
</div>
`;
});
}
// 2. Tambah Produk ke Keranjang
function tambahKeKeranjang(idProduk) {
const itemInKeranjang = keranjang.find(item => item.id === idProduk);
if (itemInKeranjang) {
itemInKeranjang.jumlah += 1;
} else {
const produkAwal = produkList.find(p => p.id === idProduk);
keranjang.push({ ...produkAwal, jumlah: 1 });
}
updateTampilanKeranjang();
}
// 3. Update Tampilan Keranjang & Total
function updateTampilanKeranjang() {
const listKeranjang = document.getElementById('daftar-keranjang');
const totalHargaEl = document.getElementById('total-harga');
listKeranjang.innerHTML = '';
let total = 0;
keranjang.forEach(item => {
const subtotal = item.harga * item.jumlah;
total += subtotal;
listKeranjang.innerHTML += `
<li>${item.nama} x ${item.jumlah} = Rp ${subtotal.toLocaleString('id-ID')}</li>
`;
});
totalHargaEl.innerText = total.toLocaleString('id-ID');
}
// 4. Hitung Pembayaran & Kembalian
function prosesBayar() {
const total = keranjang.reduce((acc, item) => acc + (item.harga * item.jumlah), 0);
const nominalBayar = Number(document.getElementById('input-bayar').value);
const pesanEl = document.getElementById('pesan-kembalian');
if (total === 0) {
pesanEl.innerText = "Keranjang masih kosong!";
return;
}
if (nominalBayar < total) {
pesanEl.innerText = "Uang tidak cukup!";
} else {
const kembalian = nominalBayar - total;
pesanEl.innerText = `Transaksi Berhasil! Kembalian: Rp ${kembalian.toLocaleString('id-ID')}`;
// Reset Kasir
keranjang = [];
document.getElementById('input-bayar').value = '';
updateTampilanKeranjang();
}
}
// Jalankan saat pertama kali dibuka
tampilkanProduk();
Modul 4: Penjelasan Alur Logika Sistem
1. Konsep State (Penyimpanan Sementara Data)
Array produkList: Berfungsi sebagai database lokal yang menyimpan master barang (ID, nama, harga).
Array keranjang: Berfungsi menampung transaksi aktif. Ketika tombol "+ Tambah" diklik, sistem mengecek apakah barang sudah ada di keranjang. Jika sudah ada, tambahkan nilai jumlah (+1). Jika belum ada, masukkan objek barang baru dengan jumlah = 1.
2. Alur Pengolahan Data (Data Flow)
Aksi Tambah: User klik tombol $\rightarrow$ Fungsi tambahKeKeranjang(id) $\rightarrow$ Update Array keranjang $\rightarrow$ Panggil updateTampilanKeranjang().
Kalkulasi Total: Fungsi updateTampilanKeranjang() melakukan kalkulasi subtotal per item ($\text{harga} \times \text{jumlah}$) lalu menjumlahkan seluruh item untuk menghasilkan Total.
3. Logika Validasi Pembayaran
Berikut adalah pembaruan kode app.js yang menyertakan fitur hapus/kurangi item dari keranjang serta penyimpanan riwayat transaksi otomatis menggunakan localStorage agar data tidak hilang saat halaman di-refresh.
1. Pembaruan Antarmuka (index.html)
Tambahkan area untuk menampilkan riwayat transaksi di bawah bagian keranjang pada index.html:
<!-- Tambahkan div ini di dalam .container atau di bagian bawah html -->
<div class="riwayat-section" style="margin-top: 20px; background: white; padding: 20px; border-radius: 8px;">
<h2>Riwayat Transaksi</h2>
<button onclick="hapusRiwayat()" style="background: #dc3545; color: white; border: none; padding: 5px 10px; margin-bottom: 10px;">Hapus Riwayat</button>
<ul id="daftar-riwayat"></ul>
</div>
2. Kode Logika Baru (app.js)
Gantikan seluruh isi file app.js Anda dengan kode berikut:
// Data Master Produk
const produkList = [
{ id: 1, nama: "Kopi Hitam", harga: 10000 },
{ id: 2, nama: "Roti Bakar", harga: 15000 },
{ id: 3, nama: "Es Teh Manis", harga: 5000 },
{ id: 4, nama: "Nasi Goreng", harga: 20000 }
];
// State Keranjang & Riwayat Transaksi
let keranjang = [];
// Ambil riwayat dari LocalStorage jika ada, jika tidak set array kosong
let riwayatTransaksi = JSON.parse(localStorage.getItem('riwayat_kasir')) || [];
// 1. Tampilkan Daftar Produk
function tampilkanProduk() {
const container = document.getElementById('daftar-produk');
container.innerHTML = '';
produkList.forEach(item => {
container.innerHTML += `
<div class="item-produk">
<h4>${item.nama}</h4>
<p>Rp ${item.harga.toLocaleString('id-ID')}</p>
<button onclick="tambahKeKeranjang(${item.id})">+ Tambah</button>
</div>
`;
});
}
// 2. Tambah Produk ke Keranjang
function tambahKeKeranjang(idProduk) {
const itemInKeranjang = keranjang.find(item => item.id === idProduk);
if (itemInKeranjang) {
itemInKeranjang.jumlah += 1;
} else {
const produkAwal = produkList.find(p => p.id === idProduk);
keranjang.push({ ...produkAwal, jumlah: 1 });
}
updateTampilanKeranjang();
}
// 3. Fitur Baru: Hapus / Kurangi Item dari Keranjang
function kurangiDariKeranjang(idProduk) {
const index = keranjang.findIndex(item => item.id === idProduk);
if (index !== -1) {
if (keranjang[index].jumlah > 1) {
keranjang[index].jumlah -= 1; // Kurangi jumlah jika > 1
} else {
keranjang.splice(index, 1); // Hapus dari keranjang jika jumlah tinggal 1
}
}
updateTampilanKeranjang();
}
// 4. Update Tampilan Keranjang
function updateTampilanKeranjang() {
const listKeranjang = document.getElementById('daftar-keranjang');
const totalHargaEl = document.getElementById('total-harga');
listKeranjang.innerHTML = '';
let total = 0;
keranjang.forEach(item => {
const subtotal = item.harga * item.jumlah;
total += subtotal;
listKeranjang.innerHTML += `
<li style="margin-bottom: 5px;">
${item.nama} x ${item.jumlah} = Rp ${subtotal.toLocaleString('id-ID')}
<button onclick="kurangiDariKeranjang(${item.id})" style="background:#dc3545; color:white; border:none; padding:2px 6px; border-radius:3px; margin-left:8px;">- Hapus</button>
</li>
`;
});
totalHargaEl.innerText = total.toLocaleString('id-ID');
}
// 5. Fitur Baru: Simpan & Tampilkan Riwayat
function simpanDanTampilkanRiwayat(transaksiBaru) {
// Tambahkan ke array riwayat
riwayatTransaksi.push(transaksiBaru);
// Simpan array riwayat ke LocalStorage (diubah ke format string JSON)
localStorage.setItem('riwayat_kasir', JSON.stringify(riwayatTransaksi));
tampilkanRiwayat();
}
function tampilkanRiwayat() {
const listRiwayat = document.getElementById('daftar-riwayat');
if (!listRiwayat) return;
listRiwayat.innerHTML = '';
if (riwayatTransaksi.length === 0) {
listRiwayat.innerHTML = '<li>Belum ada transaksi.</li>';
return;
}
riwayatTransaksi.forEach((tx, index) => {
listRiwayat.innerHTML += `
<li style="margin-bottom: 8px; border-bottom: 1px #eee solid; padding-bottom: 5px;">
<strong>[${tx.waktu}]</strong> Total: Rp ${tx.total.toLocaleString('id-ID')} | Bayar: Rp ${tx.bayar.toLocaleString('id-ID')} (Kembali: Rp ${tx.kembalian.toLocaleString('id-ID')})
</li>
`;
});
}
function hapusRiwayat() {
localStorage.removeItem('riwayat_kasir');
riwayatTransaksi = [];
tampilkanRiwayat();
}
// 6. Hitung Pembayaran
function prosesBayar() {
const total = keranjang.reduce((acc, item) => acc + (item.harga * item.jumlah), 0);
const nominalBayar = Number(document.getElementById('input-bayar').value);
const pesanEl = document.getElementById('pesan-kembalian');
if (total === 0) {
pesanEl.innerText = "Keranjang masih kosong!";
return;
}
if (nominalBayar < total) {
pesanEl.innerText = "Uang tidak cukup!";
} else {
const kembalian = nominalBayar - total;
pesanEl.innerText = `Transaksi Berhasil! Kembalian: Rp ${kembalian.toLocaleString('id-ID')}`;
// Buat Objek Transaksi
const transaksi = {
waktu: new Date().toLocaleString('id-ID'),
total: total,
bayar: nominalBayar,
kembalian: kembalian
};
// Simpan ke LocalStorage
simpanDanTampilkanRiwayat(transaksi);
// Reset Kasir
keranjang = [];
document.getElementById('input-bayar').value = '';
updateTampilanKeranjang();
}
}
// Inisialisasi awal saat halaman dimuat
tampilkanProduk();
tampilkanRiwayat();
3. Penjelasan Logika Fitur Baru
localStorage (Penyimpanan Browser): localStorage hanya menerima tipe data string. Oleh karena itu, kita gunakan JSON.stringify() saat menyimpannya dan JSON.parse() saat membaca kembali data objek ke bentuk Javascript Array.
Logika Pengurangan/Penghapusan Array (splice): Pada kurangiDariKeranjang(), sistem mengecek nilai jumlah. Jika item lebih dari 1, nilainya cukup dikurangi 1. Jika tersisa 1, kita gunakan splice(index, 1) untuk menghapus elemen tersebut secara penuh dari array keranjang.