Memeriksa koneksi Google Sheets…
0 / 0 selesai
Belum ada aktivitas hari ini

Cari Tugas

Tambah Aktivitas ke Timeline Harian

Tersimpan untuk tanggal:

Tugas Rutin (Harian & Bulanan)

0 tugas rutin tersimpan
Statistik Mingguan / Bulanan
Selesai per Hari
Selesai per Kategori
Ekspor Data

Unduh salinan seluruh data (dari cache yang sedang dimuat) sebagai file CSV.

Pengaturan Sinkronisasi Google Sheets (Database Utama)

Aplikasi ini membaca dan menulis langsung ke Google Sheets. URL Web App sudah ditanam sebagai bawaan (default) di dalam kode, jadi aplikasi ini otomatis terhubung di perangkat mana pun tanpa perlu tempel URL lagi. Ubah kolom di bawah hanya jika Anda ingin memakai spreadsheet lain.

Penting: karena tab "💰 Kas" menyimpan data baru (sheet "Kas"), pastikan Apps Script di spreadsheet Anda sudah memakai kode terbaru di bawah ini (deploy ulang / "Manage deployments" → naikkan versi baru) agar data Kas ikut tersimpan.

  1. Buka Google Sheets baru (boleh kosong).
  2. Menu Extensions → Apps Script.
  3. Hapus kode default, tempel kode di bawah ini.
  4. Klik Deploy → New deployment → Web app. Pilih "Execute as: Me" dan "Who has access: Anyone". (Kalau sudah pernah deploy sebelumnya, pilih "Manage deployments" → edit → naikkan versi baru.)
  5. Salin URL Web App, tempel ke kolom di bawah, klik Simpan (opsional — hanya jika ganti spreadsheet).
function doGet(e) { var ss = SpreadsheetApp.getActiveSpreadsheet(); var type = (e.parameter && e.parameter.type) || 'tasks'; if (type === 'templates') { var sheet = ss.getSheetByName('Templates'); if (!sheet || sheet.getLastRow() < 2) return jsonOutput([]); var rows = sheet.getDataRange().getValues(); var data = []; for (var i = 1; i < rows.length; i++) { data.push({ id: rows[i][0], name: rows[i][1], category: rows[i][2], recurrence: rows[i][3] }); } return jsonOutput(data); } if (type === 'cash') { var sheet = ss.getSheetByName('Kas'); if (!sheet || sheet.getLastRow() < 2) return jsonOutput([]); var rows = sheet.getDataRange().getValues(); var data = []; for (var i = 1; i < rows.length; i++) { var r = rows[i]; data.push({ id: r[0], date: fmtDate(r[1]), txType: r[2], name: r[3], category: r[4], amount: r[5], fundId: r[6], note: r[7] }); } return jsonOutput(data); } var sheet = ss.getSheetByName('Data'); if (!sheet || sheet.getLastRow() < 2) return jsonOutput([]); var rows = sheet.getDataRange().getValues(); var data = []; for (var i = 1; i < rows.length; i++) { var r = rows[i]; data.push({ id: r[0], date: fmtDate(r[1]), time: r[2], name: r[3], category: r[4], priority: r[5], deadline: fmtDate(r[6]), status: r[7], elapsed: r[8], timerStart: r[9], note: r[10] }); } return jsonOutput(data); } function fmtDate(v) { if (v instanceof Date) return Utilities.formatDate(v, "GMT+8", "yyyy-MM-dd"); return v; } function jsonOutput(obj) { return ContentService.createTextOutput(JSON.stringify(obj)).setMimeType(ContentService.MimeType.JSON); } function doPost(e) { var data = JSON.parse(e.postData.contents); var ss = SpreadsheetApp.getActiveSpreadsheet(); if (data.type === 'template') { var sheet = ss.getSheetByName('Templates') || ss.insertSheet('Templates'); if (sheet.getLastRow() === 0) sheet.appendRow(['ID', 'Nama', 'Kategori', 'Recurrence']); var rows = sheet.getDataRange().getValues(); var rowIndex = -1; for (var i = 1; i < rows.length; i++) { if (rows[i][0] === data.id) { rowIndex = i + 1; break; } } if (data.action === 'delete') { if (rowIndex > -1) sheet.deleteRow(rowIndex); return jsonOutput({ result: 'deleted' }); } var rowData = [data.id, data.name, data.category, data.recurrence]; if (rowIndex > -1) sheet.getRange(rowIndex, 1, 1, rowData.length).setValues([rowData]); else sheet.appendRow(rowData); return jsonOutput({ result: 'ok' }); } if (data.type === 'cash') { var sheet = ss.getSheetByName('Kas') || ss.insertSheet('Kas'); if (sheet.getLastRow() === 0) { sheet.appendRow(['ID','Tanggal','Tipe','Nama/Keterangan','Kategori','Jumlah','FundID','Catatan','Diperbarui']); } var rows = sheet.getDataRange().getValues(); var rowIndex = -1; for (var i = 1; i < rows.length; i++) { if (rows[i][0] === data.id) { rowIndex = i + 1; break; } } if (data.action === 'delete') { if (rowIndex > -1) sheet.deleteRow(rowIndex); return jsonOutput({ result: 'deleted' }); } var rowData = [data.id, data.date, data.txType, data.name, data.category || '', data.amount, data.fundId || '', data.note || '', new Date()]; if (rowIndex > -1) sheet.getRange(rowIndex, 1, 1, rowData.length).setValues([rowData]); else sheet.appendRow(rowData); return jsonOutput({ result: 'ok' }); } var sheet = ss.getSheetByName('Data') || ss.insertSheet('Data'); if (sheet.getLastRow() === 0) { sheet.appendRow(['ID','Tanggal','Waktu','Nama Tugas','Kategori','Prioritas','Tenggat','Status','Elapsed (detik)','Timer Mulai','Catatan','Diperbarui']); } var rows = sheet.getDataRange().getValues(); var rowIndex = -1; for (var i = 1; i < rows.length; i++) { if (rows[i][0] === data.id) { rowIndex = i + 1; break; } } if (data.action === 'delete') { if (rowIndex > -1) sheet.deleteRow(rowIndex); return jsonOutput({ result: 'deleted' }); } var rowData = [data.id, data.date, data.time, data.name, data.category, data.priority, data.deadline, data.status, data.elapsed || 0, data.timerStart || '', data.note, new Date()]; if (rowIndex > -1) sheet.getRange(rowIndex, 1, 1, rowData.length).setValues([rowData]); else sheet.appendRow(rowData); return jsonOutput({ result: 'ok' }); }

URL bawaan sudah otomatis dipakai di semua perangkat. Kolom ini hanya untuk override manual, dan tetap tersimpan di penyimpanan browser perangkat ini bila diubah.

Riwayat Pekerjaan Selesai

Semua tugas yang sudah ditandai "Selesai", dikelompokkan per tanggal pengerjaan.

Total Uang Masuk
Rp0
Total Pengeluaran
Rp0
Sisa Saldo
Rp0
+ Catat Uang Masuk

Contoh: dana yang diberikan Tn. Rudi untuk belanja bahan bangunan.

− Catat Pengeluaran

Pilih sumber dana agar saldonya otomatis berkurang.

Rekap per Sumber Dana

Klik nama sumber atau pengeluaran untuk lihat rincian.

Semua Transaksi Kas