Total Aset (Saldo)
Rp 0
Total Hutang / Kredit
Rp 0
Kekayaan Bersih (Net Worth)
Rp 0
Daftar Akun Keuangan
Kelola semua bank, e-wallet, dan instrumen hutang fleksibel Anda
Sistem Keuangan Mandiri & Fleksibel
Total Aset (Saldo)
Total Hutang / Kredit
Kekayaan Bersih (Net Worth)
Pengeluaran rata-rata per hari pada bulan ini.
Dihitung berdasarkan jumlah hari berjalan bulan ini.
Kelola semua bank, e-wallet, dan instrumen hutang fleksibel Anda
Lihat, saring, urutkan, edit, atau hapus log seluruh transaksi finansial Anda
| Tanggal ▼ | Kategori & Deskripsi | Aliran Akun | Jumlah | Aksi |
|---|
Kelola instrumen bank, dompet digital, atau limit kredit tagihan Anda.
Centang widget opsional yang ingin diaktifkan di panel depan.
Hubungkan data keuangan Anda ke Spreadsheet. Sinkronisasi cerdas Delta Sync melacak perubahan data secara instan!
/* KODE APPS SCRIPT FLEXIBLE INCREMENTAL SYNC V2 */
function doGet() {
var userProperties = PropertiesService.getScriptProperties();
var appData = userProperties.getProperty('FINANCELY_PRO_DATA_V2');
if (!appData) {
appData = JSON.stringify({ accounts: [], transactions: [], widgets: { summary: true, avg: true, planner: true } });
}
return ContentService.createTextOutput(appData)
.setMimeType(ContentService.MimeType.JSON);
}
function doPost(e) {
try {
var data = JSON.parse(e.postData.contents);
var userProperties = PropertiesService.getScriptProperties();
userProperties.setProperty('FINANCELY_PRO_DATA_V2', JSON.stringify(data));
writeToSheet(data);
return ContentService.createTextOutput(JSON.stringify({ status: "success" }))
.setMimeType(ContentService.MimeType.JSON);
} catch(err) {
return ContentService.createTextOutput(JSON.stringify({ status: "error", message: err.toString() }))
.setMimeType(ContentService.MimeType.JSON);
}
}
function writeToSheet(data) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
if(!ss) return;
// Sheet Akun (Sinkronisasi Baris Cerdas)
syncSheetGeneric(ss, "Akun", ["ID", "Nama Akun", "Tipe", "Saldo/Hutang", "Jumlah Tagihan Terkait"], data.accounts, function(acc) {
var billCount = acc.installments ? acc.installments.length : 0;
return [acc.id, acc.name, acc.type, acc.balance, billCount];
});
// Sheet Transaksi (Sinkronisasi Baris Cerdas)
syncSheetGeneric(ss, "Transaksi", ["ID", "Tanggal (DD-MM-YYYY)", "Tipe", "Kategori/Cicilan", "Sumber Akun", "Akun Tujuan", "Nominal Pokok", "Biaya Admin"], data.transactions, function(tx) {
return [tx.id, tx.date, tx.type, tx.category, tx.accountName || "-", tx.targetAccountName || "-", tx.amount, tx.fee || 0];
});
// Urai installments / sub-cicilan dari seluruh akun kredit
var flatInstallments = [];
data.accounts.forEach(function(acc) {
if (acc.type === 'CREDIT' && acc.installments) {
acc.installments.forEach(function(inst) {
flatInstallments.push({
id: inst.id,
accId: acc.id,
accName: acc.name,
name: inst.name,
amount: inst.amount,
dueDate: inst.dueDate,
tenor: inst.tenor,
lastPaidMonth: inst.lastPaidMonth || ""
});
});
}
});
// Sheet Cicilan Aktif (Sinkronisasi Baris Cerdas)
syncSheetGeneric(ss, "Cicilan", ["ID Cicilan", "ID Akun Kredit", "Nama Akun Kredit", "Nama Cicilan", "Nominal Bulanan", "Tanggal Jatuh Tempo", "Sisa Tenor", "Terakhir Dibayar (Bulan)"], flatInstallments, function(inst) {
return [inst.id, inst.accId, inst.accName, inst.name, inst.amount, inst.dueDate, inst.tenor, inst.lastPaidMonth || "-"];
});
}
function syncSheetGeneric(ss, sheetName, headers, items, rowMapper) {
var sheet = ss.getSheetByName(sheetName) || ss.insertSheet(sheetName);
// Buat header jika kosong
if (sheet.getLastRow() === 0) {
sheet.appendRow(headers);
}
var lastRow = sheet.getLastRow();
var existingValues = lastRow > 1 ? sheet.getRange(2, 1, lastRow - 1, sheet.getLastColumn()).getValues() : [];
// Map ID baris di Google Sheets ke nomor baris fisiknya (Kolom 1 adalah ID unik)
var sheetIdMap = {};
for (var i = 0; i < existingValues.length; i++) {
var rowId = String(existingValues[i][0]);
if (rowId) {
sheetIdMap[rowId] = i + 2; // Baris riil di sheet (dimulai dari indeks 2)
}
}
var incomingIds = {};
// Upsert (Insert Baru atau Edit yang Sudah Ada)
items.forEach(function(item) {
var rowData = rowMapper(item);
var itemId = String(rowData[0]);
incomingIds[itemId] = true;
var existingRowNumber = sheetIdMap[itemId];
if (existingRowNumber) {
// Edit baris yang ada di Google Sheet
sheet.getRange(existingRowNumber, 1, 1, rowData.length).setValues([rowData]);
} else {
// Sisipkan baris baru di bawah
sheet.appendRow(rowData);
}
});
// Hapus baris yang sudah tidak ada lagi di aplikasi
var rowsToDelete = [];
for (var id in sheetIdMap) {
if (!incomingIds[id]) {
rowsToDelete.push(sheetIdMap[id]);
}
}
rowsToDelete.sort(function(a, b) { return b - a; });
rowsToDelete.forEach(function(rowNum) {
sheet.deleteRow(rowNum);
});
}
Apakah Anda yakin ingin melakukan aksi ini?