caja menor y libro diario

This commit is contained in:
2026-09-03 16:03:14 -05:00
commit 1d3e4e9dd0
17 changed files with 1910 additions and 0 deletions
+193
View File
@@ -0,0 +1,193 @@
/*************************************************
* APP LUISA - API con Google Apps Script
*
* Esta API conecta el frontend con Google Sheets.
*
* ESTRUCTURA DE CADA HOJA:
* Columna A: Fecha
* Columna B: Tipo (Ingreso / Gasto)
* Columna C: Descripción
* Columna D: Monto
* Columna E: ID ← identificador del registro en SQLite
*
* La primera fila debe ser el encabezado.
*
* ACCIONES (parámetro "accion" en el body del POST):
* - "nuevo" o "actualizar": crea la fila si el ID no existe,
* o actualiza la fila que tenga ese ID
* - "borrar": elimina la fila que tenga ese ID
*************************************************/
const SPREADSHEET_ID = "PEGA_AQUI_EL_ID_DE_TU_HOJA"; // ⚠️ Reemplazar con tu ID
// ============ LEER DATOS (GET) ============
// Uso: URL?hoja=CajaMenor
function doGet(e) {
const nombreHoja = e.parameter.hoja || "CajaMenor";
const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
const hoja = ss.getSheetByName(nombreHoja);
if (!hoja) {
return responderJSON({ error: "La hoja '" + nombreHoja + "' no existe" });
}
const datos = hoja.getDataRange().getValues();
const encabezados = datos[0];
const filas = datos.slice(1);
// Convertir a array de objetos
const resultado = filas.map(fila => {
const objeto = {};
encabezados.forEach((encabezado, i) => {
objeto[encabezado] = fila[i];
});
return objeto;
});
return responderJSON({ datos: resultado });
}
// ============ GUARDAR / ACTUALIZAR / BORRAR (POST) ============
// Body: { hoja, accion, id, datos: { Fecha, Tipo, Descripción, Monto } }
function doPost(e) {
try {
// Log de depuración: grabar en una hoja llamada "Logs" para ver si doPost se ejecuta
logDebug("doPost iniciado. postData: " + JSON.stringify(e.postData));
let body;
if (e.postData && e.postData.contents) {
try {
body = JSON.parse(e.postData.contents);
logDebug("Parseado postData.contents como JSON");
} catch (parseErr) {
logDebug("FALLÓ parse JSON. Tipo: " + (e.postData.type || "N/A") + " | Contenido: " + e.postData.contents);
body = e.parameter || {};
}
} else if (e.postData && e.postData.type === "application/json") {
body = JSON.parse(e.parameter);
} else {
logDebug("No había postData. Params: " + JSON.stringify(e.parameter || {}));
body = e.parameter || {};
}
const nombreHoja = body.hoja || "CajaMenor";
const accion = body.accion || "nuevo";
const id = String(body.id || "");
const datos = body.datos || {};
logDebug("Parsed body: hoja=" + nombreHoja + " accion=" + accion + " id=" + id);
const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
const hoja = ss.getSheetByName(nombreHoja);
if (!hoja) {
return responderJSON({ error: "La hoja '" + nombreHoja + "' no existe" });
}
const encabezados = hoja.getRange(1, 1, 1, hoja.getLastColumn()).getValues()[0];
let indiceId = indiceColumnaId(encabezados);
// Si no existe la columna "Id", la creamos al final para no dañar datos
if (indiceId === -1) {
hoja.getRange(1, encabezados.length + 1).setValue("Id");
encabezados.push("Id");
indiceId = encabezados.length - 1;
logDebug("La columna 'Id' no existía en " + nombreHoja + ". Se creó en la columna " + (indiceId + 1));
}
// Buscar si ya existe una fila con ese ID en la columna de ID
const filaExistente = buscarFilaPorId(hoja, id, indiceId);
logDebug("buscarFilaPorId(" + id + ") => fila " + filaExistente);
if (accion === "borrar") {
if (filaExistente > 0) {
hoja.deleteRow(filaExistente);
logDebug("Borrado fila " + filaExistente + " id=" + id);
return responderJSON({ ok: true, mensaje: "Registro borrado" });
}
logDebug("Nada que borrar id=" + id + ". IDs en hoja: " + listarIds(hoja, indiceId));
return responderJSON({ ok: true, mensaje: "Nada que borrar" });
}
if (filaExistente > 0) {
// ACTUALIZAR
encabezados.forEach((encabezado, i) => {
if (i === indiceId) {
hoja.getRange(filaExistente, i + 1).setValue(id);
} else if (datos[encabezado] !== undefined) {
hoja.getRange(filaExistente, i + 1).setValue(datos[encabezado]);
}
});
return responderJSON({ ok: true, mensaje: "Registro actualizado" });
}
// NUEVO
const nuevaFila = encabezados.map((encabezado, i) => {
if (i === indiceId) return id;
return datos[encabezado] || "";
});
hoja.appendRow(nuevaFila);
return responderJSON({ ok: true, mensaje: "Registro guardado correctamente" });
} catch (error) {
logDebug("ERROR doPost: " + error.message);
return responderJSON({ error: error.message });
}
}
// ============ LOG DE DEPURACIÓN ============
function logDebug(mensaje) {
try {
const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
let hojaLog = ss.getSheetByName("Logs");
if (!hojaLog) {
hojaLog = ss.insertSheet("Logs");
hojaLog.appendRow(["Fecha", "Mensaje"]);
}
hojaLog.appendRow([new Date(), mensaje]);
} catch (e) {
// Si no puede logear, ignora
}
}
// ============ BUSCAR COLUMNA ID ============
// Encuentra el índice de la columna cuyo encabezado sea "id" sin importar mayúsculas.
// Si no existe, devuelve -1 (el doPost se encarga de crearla al final).
function indiceColumnaId(encabezados) {
for (let i = 0; i < encabezados.length; i++) {
if (String(encabezados[i]).trim().toLowerCase() === "id") return i;
}
return -1;
}
// ============ BUSCAR FILA POR ID ============
// Busca el ID en la columna ID indicada. Devuelve el número de fila o 0 si no existe.
function buscarFilaPorId(hoja, id, indiceColumnaId) {
if (!id || indiceColumnaId === null || indiceColumnaId < 0) return 0;
const ultimaFila = hoja.getLastRow();
if (ultimaFila < 2) return 0; // solo encabezados
const columnaId = hoja.getRange(2, indiceColumnaId + 1, ultimaFila - 1, 1).getValues();
for (let i = 0; i < columnaId.length; i++) {
if (String(columnaId[i][0]).trim() === id.trim()) {
return i + 2; // +2: fila 1 es encabezado, índice base 1
}
}
return 0;
}
// ============ LISTAR IDs (para depurar) ============
function listarIds(hoja, indiceColumnaId) {
const ultimaFila = hoja.getLastRow();
if (ultimaFila < 2) return "(hoja sin datos)";
const valores = hoja.getRange(2, indiceColumnaId + 1, ultimaFila - 1, 1).getValues();
return "[" + valores.map(v => "'" + String(v[0]) + "'").join(", ") + "]";
}
// ============ FUNCIÓN AUXILIAR ============
function responderJSON(objeto) {
return ContentService
.createTextOutput(JSON.stringify(objeto))
.setMimeType(ContentService.MimeType.JSON);
}