Kalkulator HPP Kopi & Bakery

Hitung biaya produksi secara presisi hingga satuan gram/mililiter, pantau rasio Food Cost standar kafe (25% - 32%), dan sinkronisasikan otomatis dengan Google Spreadsheet sebagai database cloud Anda.

0
Resep Menu
0
Master Bahan
0%
Rata-rata Food Cost
☕

Formulasi Resep & Biaya

Real-Time
Nama Bahan
Jumlah Pakai
Satuan
Subtotal
Aksi
Nama Kemasan
Jumlah Pakai
Satuan
Subtotal
Aksi
Gas oven kue / kompor
Mesin espresso, oven, chiller
Cuci alat, sanitasi bar & PDAM
Parkir belanja pasar / supplier
Transport belanja & antar pesanan
Buffer tenaga kerja / tak terduga
Persentase cadangan tumpahan / susut bahan (Standar: 3% - 10%)
💡 Tips: Jika resep dibuat untuk 1 batch berisi banyak porsi (misal 10 croissant), masukkan total pemakaian utilitas batch tersebut. Sistem otomatis membaginya per potong.
Total Overhead Batch: Rp 850 (Alokasi: Rp 850 / cup)
🔥 Gas: Rp 0 ⚡ Listrik: Rp 450 💧 Air: Rp 150 🅿️ Parkir: Rp 50 ⛽ Bensin: Rp 200 💼 Lainnya: Rp 0
40%
50%
60%
65%
70%
75%
Memudahkan kasir dan transaksi uang tunai/QRIS
Rekomendasi Harga Jual Menu
Keuntungan Bersih: +Rp 0 (Margin 65%)
HPP Per Porsi / Cup
Rp 0
Total HPP 1 Batch
Rp 0
Bahan Baku / Porsi
Rp 0
Kemasan / Porsi
Rp 0
Overhead & Utilitas / Porsi
Rp 0
⚖️
Food Cost: Ideal & Sehat
Rasio biaya bahan terhadap harga jual berada di kisaran sehat kafe.
Rasio Food Cost: 0% (Benchmark industri: 25% - 35%)
Struktur Proporsi Biaya HPP
Bahan Baku
Kemasan
Overhead
💡 Simulasi Harga Jual Berbagai Margin

Katalog Menu & Resep Tersimpan

Daftar seluruh formula HPP dan harga jual yang telah dihitung dan disimpan.

Master Database Bahan Baku & Kemasan

Daftar harga beli bahan dan konversi otomatis harga per gram, ml, atau pcs.

Nama Bahan / Kemasan Kategori Satuan Beli Total Harga Beli Harga Pokok Terkecil Aksi
Cloud Database Backend

Integrasi Google Spreadsheet

Hubungkan kalkulator ini dengan Google Spreadsheet milik Anda menggunakan Google Apps Script Web App gratis tanpa server berbayar.

Belum pernah disinkronisasi
Didapatkan setelah melakukan Deploy > New deployment > Web app di Google Apps Script spreadsheet Anda.

📋 Panduan Mudah Memasang Google Apps Script (2 Menit Selesai)

1

Buat Google Spreadsheet Baru

Buka tab baru dan buat Google Sheets di sheets.new. Beri judul spreadsheet Anda, misal: "Database HPP Kopi & Bakery".

2

Buka Menu Ekstensi > Apps Script

Di menu bar Google Sheets, klik menu Extensions (Ekstensi) > lalu klik Apps Script.

3

Salin & Tempel Kode Script Berikut ke File Code.gs

Hapus seluruh isi default file Code.gs di Apps Script, lalu klik tombol "Salin Seluruh Kode Script" di bawah dan paste ke sana:

/**
 * BACKEND GOOGLE APPS SCRIPT: KALKULATOR HPP KOPI & BAKERY
 */
const SHEET_NAMES = {
  INGREDIENTS: "BahanBaku",
  RECIPES: "ResepHPP",
  LOGS: "LogRiwayat"
};

function doGet(e) {
  try {
    const action = e.parameter.action || "getData";
    if (action === "ping") {
      return createJsonResponse({ status: "success", message: "Koneksi Google Spreadsheet Aktif!" });
    }
    if (action === "getData") {
      return createJsonResponse({ status: "success", data: getAllDatabase() });
    }
    return createJsonResponse({ status: "error", message: "Action tidak dikenali" });
  } catch (error) {
    return createJsonResponse({ status: "error", message: error.toString() });
  }
}

function doPost(e) {
  try {
    let payload = {};
    if (e.postData && e.postData.contents) {
      payload = JSON.parse(e.postData.contents);
    }
    const action = payload.action || "syncAll";
    if (action === "syncAll") {
      syncAllData(payload.ingredients || [], payload.recipes || []);
      return createJsonResponse({ status: "success", message: "Sinkronisasi Berhasil ke Google Sheets!" });
    }
    return createJsonResponse({ status: "error", message: "Action tidak dikenali" });
  } catch (error) {
    return createJsonResponse({ status: "error", message: error.toString() });
  }
}

function createJsonResponse(data) {
  return ContentService.createTextOutput(JSON.stringify(data)).setMimeType(ContentService.MimeType.JSON);
}

function getOrCreateSheet(sheetName, headers) {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  let sheet = ss.getSheetByName(sheetName);
  if (!sheet) {
    sheet = ss.insertSheet(sheetName);
    if (headers) {
      const headerRange = sheet.getRange(1, 1, 1, headers.length);
      headerRange.setValues([headers]);
      headerRange.setBackground("#3D2618").setFontColor("#FFFFFF").setFontWeight("bold").setHorizontalAlignment("center");
      sheet.setFrozenRows(1);
    }
  }
  return sheet;
}

function getAllDatabase() {
  const ingSheet = getOrCreateSheet(SHEET_NAMES.INGREDIENTS, ["ID", "Nama Bahan Baku / Kemasan", "Kategori", "Satuan", "Jumlah Beli", "Harga Beli (Rp)", "Harga Per Satuan (Rp)", "Catatan", "Terakhir Diperbarui"]);
  const ingData = ingSheet.getDataRange().getValues();
  const ingredients = [];
  for (let i = 1; i < ingData.length; i++) {
    const row = ingData[i];
    if (row[0] || row[1]) {
      ingredients.push({ id: String(row[0]), name: String(row[1]), category: String(row[2]), unit: String(row[3]), purchaseQty: Number(row[4]), purchasePrice: Number(row[5]), unitPrice: Number(row[6]), notes: String(row[7] || "") });
    }
  }

  const recSheet = getOrCreateSheet(SHEET_NAMES.RECIPES, ["ID Resep", "Nama Resep / Menu", "Kategori", "Hasil Batch", "Satuan Porsi", "Biaya Bahan Baku (Rp)", "Biaya Kemasan (Rp)", "Biaya Overhead (Rp)", "Total HPP Batch (Rp)", "HPP Per Porsi (Rp)", "Target Margin %", "Harga Jual Rekomendasi (Rp)", "Profit Per Porsi (Rp)", "Food Cost %", "Detail Komposisi (JSON)", "Terakhir Diperbarui"]);
  const recData = recSheet.getDataRange().getValues();
  const recipes = [];
  for (let i = 1; i < recData.length; i++) {
    const row = recData[i];
    if (row[0] || row[1]) {
      let details = { ingredients: [], packaging: [] };
      try { if (row[14]) details = JSON.parse(row[14]); } catch (e) {}
      recipes.push({ id: String(row[0]), name: String(row[1]), category: String(row[2]), batchYield: Number(row[3]), yieldUnit: String(row[4]), ingredientsTotal: Number(row[5]), packagingTotal: Number(row[6]), overheadTotal: Number(row[7]), totalBatchHpp: Number(row[8]), hppPerUnit: Number(row[9]), targetMargin: Number(row[10]), finalSellingPrice: Number(row[11]), profitPerUnit: Number(row[12]), foodCostRatio: Number(row[13]), ingredients: details.ingredients || [], packaging: details.packaging || [], gasCost: Number(details.gasCost) || 0, electricityCost: Number(details.electricityCost) || 0, waterCost: Number(details.waterCost) || 0, parkingCost: Number(details.parkingCost) || 0, petrolCost: Number(details.petrolCost) || 0, otherOverheadNominal: Number(details.otherOverheadNominal) || 0, overheadRate: details.overheadRate !== undefined ? details.overheadRate : 5 });
    }
  }
  return { ingredients, recipes };
}

function syncAllData(ingredients, recipes) {
  if (ingredients.length > 0) {
    const ingSheet = getOrCreateSheet(SHEET_NAMES.INGREDIENTS, ["ID", "Nama Bahan Baku / Kemasan", "Kategori", "Satuan", "Jumlah Beli", "Harga Beli (Rp)", "Harga Per Satuan (Rp)", "Catatan", "Terakhir Diperbarui"]);
    const lastRow = ingSheet.getLastRow();
    if (lastRow > 1) ingSheet.getRange(2, 1, lastRow - 1, 9).clearContent();
    const rows = ingredients.map(ing => [ing.id, ing.name, ing.category, ing.unit, ing.purchaseQty, ing.purchasePrice, ing.unitPrice, ing.notes || "", new Date().toLocaleString("id-ID")]);
    ingSheet.getRange(2, 1, rows.length, 9).setValues(rows);
  }
  if (recipes.length > 0) {
    const recSheet = getOrCreateSheet(SHEET_NAMES.RECIPES, ["ID Resep", "Nama Resep / Menu", "Kategori", "Hasil Batch", "Satuan Porsi", "Biaya Bahan Baku (Rp)", "Biaya Kemasan (Rp)", "Biaya Overhead (Rp)", "Total HPP Batch (Rp)", "HPP Per Porsi (Rp)", "Target Margin %", "Harga Jual Rekomendasi (Rp)", "Profit Per Porsi (Rp)", "Food Cost %", "Detail Komposisi (JSON)", "Terakhir Diperbarui"]);
    const lastRow = recSheet.getLastRow();
    if (lastRow > 1) recSheet.getRange(2, 1, lastRow - 1, 16).clearContent();
    const rows = recipes.map(r => [r.id, r.name, r.category, r.batchYield, r.yieldUnit, r.ingredientsTotal, r.packagingTotal, r.overheadTotal, r.totalBatchHpp, r.hppPerUnit, r.targetMargin, r.finalSellingPrice, r.profitPerUnit, r.foodCostRatio, JSON.stringify({ ingredients: r.ingredients, packaging: r.packaging, notes: r.notes, gasCost: r.gasCost, electricityCost: r.electricityCost, waterCost: r.waterCost, parkingCost: r.parkingCost, petrolCost: r.petrolCost, otherOverheadNominal: r.otherOverheadNominal, overheadRate: r.overheadRate }), new Date().toLocaleString("id-ID")]);
    recSheet.getRange(2, 1, rows.length, 16).setValues(rows);
  }
}
4

Deploy sebagai Web App

Di pojok kanan atas editor Apps Script, klik tombol Deploy (Terapkan) > pilih New deployment (Penerapan baru).
1. Klik ikon gerigi (Select type) lalu pilih Web app.
2. Execute as: pilih Me (email Anda).
3. Who has access: pilih Anyone (Siapa saja).
4. Klik tombol Deploy. Salin Web App URL yang dihasilkan (berakhiran /exec), lalu tempelkan ke kolom URL di atas!

Simulasi Sensitivitas Biaya & Break Even Point (BEP)

Lakukan proyeksi skenario jika harga bahan baku di pasar naik, serta hitung berapa gelas kopi/kue yang harus terjual per hari untuk menutup biaya sewa dan gaji karyawan.

📈

Simulasi Fluktuasi Bahan Utama

Kenaikan Harga Susu Segar (Fresh Milk) +0%
0% +50%
Kenaikan Harga Biji Kopi (Green Bean / Roast Bean) +0%
0% +50%
Kenaikan Biaya per Cup Kopi Susu:
+Rp 0 / cup

Jika bahan baku naik sebesar persentase di atas, Anda disarankan menaikkan harga jual atau menyesuaikan resep agar margin tetap aman di atas 60%.

🎯

Kalkulator Target Penjualan & BEP

⚡ Rincian Utilitas & Biaya Rutin Bulanan
Setiap kali angka di atas diubah, Total Biaya Operasional dan BEP di bawah diperbarui secara otomatis.
BEP (Impas Biaya) / Bulan
0 Cup
Target BEP / Hari
0 Cup/Hari
Target Jual + Profit / Bulan
0 Cup
Target Jual + Profit / Hari
0 Cup/Hari