function jsonOutput(obj) {
return ContentService.createTextOutput(JSON.stringify(obj)).setMimeType(ContentService.MimeType.JSON);
}
function parseJsonCell(value, fallback) {
if (!value) return fallback;
try { return JSON.parse(value); } catch (e) { return fallback; }
}
function dateToIso(value) {
if (!value) return "";
if (Object.prototype.toString.call(value) === "[object Date]" && !isNaN(value)) {
return Utilities.formatDate(value, Session.getScriptTimeZone(), "yyyy-MM-dd");
}
return String(value);
}
function normalizeGoal(g) {
return {
id: String(g.id || ("goal_" + new Date().getTime())),
type: "goal",
name: g.name || "Cíl",
target: g.target || "",
initial: g.initial || "",
monthly: g.monthly || "",
startDate: dateToIso(g.startDate),
extras: g.extras || {}
};
}
function readLatestRaw(ss) {
const rawSheet = ss.getSheetByName("Hrubá data (Záloha)");
if (!rawSheet || rawSheet.getLastRow() < 2) return null;
const parsed = parseJsonCell(rawSheet.getRange(2, 2).getValue(), null);
if (Array.isArray(parsed)) return { tree: parsed, goals: parsed };
if (parsed && typeof parsed === "object") return parsed;
return null;
}
function readOverviewGoals(ss) {
const sheet = ss.getSheetByName("Přehled cílů");
if (!sheet || sheet.getLastRow() < 2) return [];
const values = sheet.getDataRange().getValues();
const goals = [];
for (let i = 1; i < values.length; i++) {
const r = values[i];
if (r.join("") === "") continue;
goals.push(normalizeGoal({
id: r[1],
type: r[2],
name: r[3],
target: r[4],
initial: r[5],
monthly: r[6],
startDate: r[7],
extras: parseJsonCell(r[8], {})
}));
}
return goals;
}
function mergeGoalsIntoTree(tree, overviewGoals) {
const byId = {};
overviewGoals.forEach(function(g) { byId[String(g.id)] = g; });
const seen = {};
function walk(items) {
return (items || []).map(function(item) {
if (!item) return item;
if (item.type === "folder") {
item.children = walk(item.children || []);
return item;
}
const replacement = byId[String(item.id)];
if (replacement) {
seen[String(item.id)] = true;
return Object.assign({}, item, replacement, { extras: replacement.extras || item.extras || {} });
}
return item;
});
}
const merged = walk(Array.isArray(tree) ? tree : []);
overviewGoals.forEach(function(g) {
if (!seen[String(g.id)]) merged.push(g);
});
return merged;
}
function doGet() {
try {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const raw = readLatestRaw(ss);
const overviewGoals = readOverviewGoals(ss);
let data = [];
if (raw && raw.tree) data = raw.tree;
else if (raw && raw.goals) data = raw.goals;
if (overviewGoals.length > 0) {
data = data.length > 0 ? mergeGoalsIntoTree(data, overviewGoals) : overviewGoals;
}
return jsonOutput({ status: "success", data: data, hasData: data.length > 0 });
} catch (error) {
return jsonOutput({ status: "error", message: error.toString() });
}
}
function doPost(e) {
try {
let payload;
if (e && e.postData && e.postData.contents) {
payload = JSON.parse(e.postData.contents);
} else {
return jsonOutput({ status: "error", message: "Chybí data" });
}
if (payload.action !== 'sync_sporici_cile') {
return jsonOutput({ status: "error", message: "Neznámá akce" });
}
const goals = Array.isArray(payload.data) ? payload.data : [];
const tree = Array.isArray(payload.tree) ? payload.tree : goals;
const timestamp = payload.timestamp;
const ss = SpreadsheetApp.getActiveSpreadsheet();
const existingOverview = ss.getSheetByName("Přehled cílů");
if (goals.length === 0 && existingOverview && existingOverview.getLastRow() > 1) {
return jsonOutput({ status: "skipped", message: "Prázdný zápis přeskočen kvůli ochraně existujících dat" });
}
// 1. Hrubá data (Záloha)
let rawSheet = ss.getSheetByName("Hrubá data (Záloha)");
if (!rawSheet) {
rawSheet = ss.insertSheet("Hrubá data (Záloha)");
rawSheet.appendRow(["Čas uložení", "Kompletní JSON data"]);
rawSheet.getRange("A1:B1").setFontWeight("bold");
}
rawSheet.insertRowAfter(1);
rawSheet.getRange(2, 1, 1, 2).setValues([[timestamp, JSON.stringify({ tree: tree, goals: goals })]]);
if (rawSheet.getMaxRows() > 50) rawSheet.deleteRows(51, rawSheet.getMaxRows() - 50);
// 2. Přehled cílů
let overviewSheet = ss.getSheetByName("Přehled cílů");
if (!overviewSheet) overviewSheet = ss.insertSheet("Přehled cílů");
overviewSheet.clearContents();
overviewSheet.appendRow(["Poslední Sync", "ID", "Typ", "Název", "Cílová částka", "Počáteční vklad", "Měsíčně", "Začátek spoření", "Mimořádné vklady JSON"]);
overviewSheet.getRange("A1:I1").setFontWeight("bold").setBackground("#f3f4f6");
if (goals && goals.length > 0) {
const rows = goals.map(g => [timestamp, g.id, g.type || "goal", g.name, g.target || 0, g.initial || 0, g.monthly || 0, g.startDate || "", JSON.stringify(g.extras || {})]);
overviewSheet.getRange(2, 1, rows.length, 9).setValues(rows);
}
return jsonOutput({ status: "success", message: "OK" });
} catch (error) {
return jsonOutput({ status: "error", message: error.toString() });
}
}
Kopírovat kód