Files
taller_automotriz/src/lib/google-sheets.ts
T
Beldor 7b3ce1ab82
Deploy / deploy (push) Successful in 4m2s
fix: google sheets append con A1 + INSERT_ROWS para evitar desplazamiento de columna
El rango A:N confundía a la API cuando había datos manuales en K/L (GASTOS/VARIOS),
haciendo que la nueva fila empezara en columna K en vez de A.
INSERT_ROWS siempre inserta fila nueva independiente del estado del sheet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 22:53:52 -03:00

103 lines
2.8 KiB
TypeScript

import crypto from "crypto";
const SHEET_ID = process.env.GOOGLE_SHEET_ID!;
const SHEET_TAB = "Hoja 1";
async function getAccessToken(): Promise<string> {
const clientEmail = process.env.GOOGLE_CLIENT_EMAIL!;
const privateKey = process.env.GOOGLE_PRIVATE_KEY!.replace(/\\n/g, "\n");
const now = Math.floor(Date.now() / 1000);
const header = Buffer.from(JSON.stringify({ alg: "RS256", typ: "JWT" })).toString("base64url");
const payload = Buffer.from(
JSON.stringify({
iss: clientEmail,
scope: "https://www.googleapis.com/auth/spreadsheets",
aud: "https://oauth2.googleapis.com/token",
exp: now + 3600,
iat: now,
})
).toString("base64url");
const sign = crypto.createSign("RSA-SHA256");
sign.update(`${header}.${payload}`);
const signature = sign.sign(privateKey, "base64url");
const jwt = `${header}.${payload}.${signature}`;
const res = await fetch("https://oauth2.googleapis.com/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
assertion: jwt,
}),
});
const data = await res.json() as { access_token: string };
return data.access_token;
}
export interface FilaTaller {
patente: string;
kilometros: number;
marca: string;
modelo: string;
anio: number;
nombre: string;
apellido: string;
email: string;
telefono: string;
fecha: Date;
trabajo: string;
comentario?: string | null;
}
export async function agregarFilaSheet(fila: FilaTaller) {
const token = await getAccessToken();
const fecha = fila.fecha.toLocaleDateString("es-AR", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
// K=GASTOS, L=VARIOS, M=M DE OBRA son manuales — se dejan vacíos
const valores = [
fila.patente,
fila.kilometros,
fila.marca,
fila.modelo,
fila.anio,
fila.nombre,
fila.apellido,
fecha,
fila.email,
fila.telefono,
"", // K: GASTOS
"", // L: VARIOS
"", // M: M DE OBRA
fila.comentario ?? "", // N: Comentarios
];
// Anclamos en A1 para que la API siempre busque desde el inicio de la tabla.
// INSERT_ROWS fuerza una fila nueva, evita que desplace los datos cuando hay
// celdas ocupadas en columnas manuales (GASTOS, VARIOS, etc.).
const range = encodeURIComponent(`${SHEET_TAB}!A1`);
const url = `https://sheets.googleapis.com/v4/spreadsheets/${SHEET_ID}/values/${range}:append?valueInputOption=USER_ENTERED&insertDataOption=INSERT_ROWS`;
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ values: [valores] }),
});
if (!res.ok) {
const err = await res.text();
throw new Error(`Sheets API error: ${err}`);
}
}