0% encontró este documento útil (0 votos)
35 vistas10 páginas

Sistema de Tintas para Pedidos y Gestión

El documento describe un sistema de gestión de pedidos y control de inventario de tintas para el Colegio Andrés Bello Pampa, implementado en Google Apps Script. Incluye funciones para crear pedidos, aprobarlos, gestionar inventario y verificar el stock crítico, además de enviar notificaciones por correo electrónico. El sistema permite la interacción a través de un menú en Google Sheets, facilitando la administración de los recursos de tintas.

Cargado por

Christian Guzman
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como TXT, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
35 vistas10 páginas

Sistema de Tintas para Pedidos y Gestión

El documento describe un sistema de gestión de pedidos y control de inventario de tintas para el Colegio Andrés Bello Pampa, implementado en Google Apps Script. Incluye funciones para crear pedidos, aprobarlos, gestionar inventario y verificar el stock crítico, además de enviar notificaciones por correo electrónico. El sistema permite la interacción a través de un menú en Google Sheets, facilitando la administración de los recursos de tintas.

Cargado por

Christian Guzman
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como TXT, PDF, TXT o lee en línea desde Scribd

// == SISTEMA DE TINTAS - COLEGIO ANDRÉS BELLO PAMPA ==

// == VERSIÓN ADAPTADA AL FORMATO REAL ==

const PDF_EXPORT_FOLDER_ID = "1gPNl4g4WQVG7mDRxOs8DZRztVpiJcvun";

function onOpen() {
const ui = [Link]();
[Link]('Sistema de Tintas')
.addItem('📝 Nuevo Pedido', 'mostrarFormularioPedido')
.addItem('✅ Aprobar Pedidos', 'mostrarAprobador')
.addItem('📦 Gestión de Inventario', 'mostrarFormularioInventario')
.addItem('📊 Panel de Control', 'mostrarPanelControl')
.addSeparator()
.addItem('⚠️ Verificar Stock Crítico', 'verificarStockCritico')
.addToUi();
}

// ========== FUNCIONES PARA FORMULARIO DE PEDIDO ==========

function mostrarFormularioPedido() {
const html = [Link]('FormularioPedido')
.setWidth(650)
.setHeight(700);
[Link]().showModalDialog(html, "Nuevo Pedido de Tintas");
}

function getModelosParaPedido() {
const ss = [Link]();
const hojaTintas = [Link]("Tintas");
if (!hojaTintas) return [];

const datos = [Link](2, 1, [Link]()-1,


9).getValues();
const modelos = [];

[Link](fila => {
if (fila[1]) { // Si hay marca (columna B)
const modeloCompleto = `${fila[1]} - ${fila[4]} - ${fila[5]}`; // Marca -
Número - Color
[Link]({
modeloCompleto: modeloCompleto,
impresora: fila[2] || "", // Columna C: Impresora
existencia: fila[7] || 0, // Columna H: Existencia
ubicacion: fila[3] || "", // Columna D: Ubicación
stockMaximo: fila[6] || 4 // Columna G: Stock MAXIMO
});
}
});

return modelos;
}

function procesarPedido(datos) {
try {
const ss = [Link]();
const hojaPedidos = [Link]("Pedidos");
const hojaAprobaciones = [Link]("Aprobaciones");

if (!hojaPedidos || !hojaAprobaciones) {
throw new Error("No se encontraron las hojas 'Pedidos' o 'Aprobaciones'");
}

const idPedido = generarIdPedido();


const fecha = new Date();
const resumen = `${[Link]} (${[Link]}) x ${[Link]}`;

// Registrar en hoja Pedidos


[Link]([
idPedido,
fecha,
[Link],
resumen,
[Link] || ""
]);

// Registrar en hoja Aprobaciones


[Link]([
idPedido,
fecha,
[Link],
resumen,
"Pendiente", // Estado
"" // Aprobador
]);

// Generar PDF y enviar email


const pdf = generarPDFPedido(idPedido, [Link], [{
modelo: [Link],
impresora: [Link],
cantidad: [Link]
}], fecha, [Link]);

enviarEmailPedido(idPedido, [Link], resumen, [Link], pdf);

return {
success: true,
message: "Pedido enviado correctamente. Se ha notificado al equipo."
};
} catch (e) {
[Link]("Error en procesarPedido: " + [Link]());
return {
success: false,
message: "Error al procesar el pedido: " + [Link]
};
}
}

// ========== FUNCIONES PARA APROBACIÓN DE PEDIDOS ==========

function mostrarAprobador() {
const html = [Link]('AprobacionUI')
.setWidth(500)
.setHeight(600);
[Link]().showModalDialog(html, "Aprobar Pedidos de Tintas");
}

function obtenerPedidosPendientes() {
const ss = [Link]();
const hojaAprobaciones = [Link]("Aprobaciones");
if (!hojaAprobaciones) return [];

const datos = [Link](2, 1, [Link]()-1,


6).getValues();
const pedidos = [];

[Link](fila => {
if (fila[4] === "Pendiente") { // Estado en columna E
[Link]({
id: fila[0], // Columna A: ID
fecha: [Link](fila[1], [Link](),
"dd/MM/yyyy HH:mm"),
solicitante: fila[2], // Columna C: Solicitante
detalles: fila[3], // Columna D: Detalles
estado: fila[4] // Columna E: Estado
});
}
});

return pedidos;
}

function aprobarPedidoPorID(idPedido) {
try {
const ss = [Link]();
const hojaAprobaciones = [Link]("Aprobaciones");
const hojaTintas = [Link]("Tintas");
const usuario = [Link]().getEmail();

if (!hojaAprobaciones || !hojaTintas) {
throw new Error("No se encontraron las hojas necesarias");
}

// Buscar y actualizar pedido


const datosAprob = [Link](2, 1,
[Link]()-1, 6).getValues();
let filaActualizada = -1;

for (let i = 0; i < [Link]; i++) {


if (datosAprob[i][0] === idPedido) {
filaActualizada = i + 2; // +2 porque empieza desde fila 2
[Link](filaActualizada, 5).setValue("Aprobado");
[Link](filaActualizada, 6).setValue(usuario);

// Procesar cada item del pedido (pueden ser varios separados por <br>)
const detalles = datosAprob[i][3].toString();
const items = [Link]('<br>');

[Link](item => {
if ([Link]()) {
// Extraer cantidad (último número después de 'x')
const matchCantidad = [Link](/x (\d+)$/);
const cantidad = matchCantidad ? parseInt(matchCantidad[1]) : 1;

// Extraer modelo completo (todo antes del '(')


const modeloCompleto = [Link](' (')[0].trim();
const [marca, numero, color] = [Link](' - ');
// Actualizar inventario en hoja Tintas
const datosTintas = [Link](2, 1, [Link]()-
1, 9).getValues();
for (let j = 0; j < [Link]; j++) {
if (datosTintas[j][1] === marca && datosTintas[j][4] == numero &&
datosTintas[j][5] === color) {
const stockActual = parseInt(datosTintas[j][7]) || 0;
const nuevoStock = stockActual - cantidad;

// Actualizar existencia y reponer


[Link](j + 2, 8).setValue(nuevoStock < 0 ? 0 :
nuevoStock);

// Calcular si hay que reponer (sin usar fórmula)


const stockMaximo = parseInt(datosTintas[j][6]) || 4;
const reponer = nuevoStock < stockMaximo * 0.3 ? "SI" : "NO";
[Link](j + 2, 9).setValue(reponer);
break;
}
}
}
});

break;
}
}

if (filaActualizada === -1) {


throw new Error("No se encontró el pedido con ID: " + idPedido);
}

return {
success: true,
message: `Pedido ${idPedido} aprobado correctamente. Inventario actualizado.`
};
} catch (e) {
[Link]("Error en aprobarPedidoPorID: " + [Link]());
return {
success: false,
message: "Error al aprobar el pedido: " + [Link]
};
}
}

// ========== FUNCIONES PARA INVENTARIO ==========

function mostrarFormularioInventario() {
const html = [Link]('FormularioInventario')
.setWidth(550)
.setHeight(700);
[Link]().showModalDialog(html, "Gestión de Inventario");
}

function getModelosDesdeInventario() {
const ss = [Link]();
const hojaTintas = [Link]("Tintas");
if (!hojaTintas) return [];

const datos = [Link](2, 1, [Link]()-1,


9).getValues();
const modelos = [];

[Link](fila => {
if (fila[1]) { // Si hay marca (columna B)
const modeloCompleto = `${fila[1]} - ${fila[4]} - ${fila[5]}`; // Marca -
Número - Color
[Link]({
modeloCompleto: modeloCompleto,
impresora: fila[2] || "", // Columna C: Impresora
existencia: fila[7] || 0, // Columna H: Existencia
stockMaximo: fila[6] || 4, // Columna G: Stock MAXIMO
ubicacion: fila[3] || "" // Columna D: Ubicación
});
}
});

return modelos;
}

function agregarAlInventario(formData) {
try {
const ss = [Link]();
const hojaTintas = [Link]("Tintas");

if (!hojaTintas) {
throw new Error("No se encontró la hoja 'Tintas'");
}

// Extraer marca, número y color del modelo completo


const [marca, numero, color] = [Link](' - ');

// Verificar si el modelo ya existe


const datos = [Link](2, 1, [Link]()-1,
9).getValues();
let filaExistente = -1;

for (let i = 0; i < [Link]; i++) {


if (datos[i][1] === marca && datos[i][4] == numero && datos[i][5] === color)
{
filaExistente = i + 2; // +2 porque empieza desde fila 2
break;
}
}

if (filaExistente > 0) {
// Actualizar modelo existente
const stockActual = parseInt(datos[filaExistente-2][7]) || 0;
const nuevoStock = stockActual + parseInt([Link]);

[Link](filaExistente, 8).setValue(nuevoStock); // Existencia (H)


[Link](filaExistente, 4).setValue([Link]); //
Ubicación (D)
[Link](filaExistente, 7).setValue([Link]); //
Stock Máximo (G)

// Calcular si hay que reponer (sin usar fórmula)


const reponer = nuevoStock < [Link] * 0.3 ? "SI" : "NO";
[Link](filaExistente, 9).setValue(reponer);
} else {
// Agregar nuevo modelo
const nuevaFila = [
[Link](), // N° (A)
marca, // Marca (B)
[Link], // Impresora (C)
[Link], // Ubicación (D)
numero, // Numero (E)
color, // Color (F)
[Link], // Stock MAXIMO (G)
[Link], // Existencia (H)
[Link] < [Link] * 0.3 ? "SI" : "NO" // Reponer (I)
];

[Link](nuevaFila);
}

return {
success: true,
message: "Inventario actualizado correctamente"
};
} catch (e) {
[Link]("Error en agregarAlInventario: " + [Link]());
return {
success: false,
message: "Error al actualizar inventario: " + [Link]
};
}
}

// ========== FUNCIONES PARA PANEL DE CONTROL ==========

function mostrarPanelControl() {
const html = [Link]('PanelControl')
.setWidth(800)
.setHeight(600);
[Link]().showModalDialog(html, "Panel de Control de Stock");
}

function actualizarListaCriticos() {
const ss = [Link]();
const hojaTintas = [Link]("Tintas");
if (!hojaTintas) return [];

const datos = [Link](2, 1, [Link]()-1,


9).getValues();
const itemsCriticos = [];

[Link](fila => {
const existencia = parseInt(fila[7]) || 0;
const stockMaximo = parseInt(fila[6]) || 4;

if (existencia < stockMaximo * 0.6) { // Menos del 60% del stock máximo
const modeloCompleto = `${fila[1]} - ${fila[4]} - ${fila[5]}`;
[Link]({
modelo: modeloCompleto,
impresora: fila[2] || "",
existencia: existencia,
ubicacion: fila[3] || "",
nivel: existencia < stockMaximo * 0.3 ? "CRÍTICO" : "BAJO"
});
}
});

return itemsCriticos;
}

function verificarStockCritico() {
const itemsCriticos = actualizarListaCriticos();
if ([Link] === 0) {
return {
count: 0,
message: "No hay items con stock crítico"
};
}

// Enviar notificación por email


const destinatarios = "cabpampa2016@[Link], coordimedia@[Link]"; //
Ajusta los destinatarios según sea necesario
const mensaje = "Los siguientes items tienen stock crítico:\n\n" +
[Link](item =>
`- ${[Link]} (${[Link]}): ${[Link]} unidades ($
{[Link]})`
).join("\n") +
"\n\nPor favor, realizar pedido de reposición.";

[Link]({
to: destinatarios,
subject: "⚠️ Alerta: Stock Crítico de Tintas",
body: mensaje
});

return {
count: [Link],
message: `Se envió alerta sobre ${[Link]} items con stock
crítico`
};
}

function enviarNotificacion(destinatarios, mensajePersonalizado) {


try {
const itemsCriticos = actualizarListaCriticos();
if ([Link] === 0) {
return {
success: false,
count: 0,
message: "No hay items con stock crítico para notificar"
};
}

const mensaje = mensajePersonalizado + "\n\n" +


[Link](item =>
`- ${[Link]} (${[Link]}): ${[Link]} unidades ($
{[Link]})`
).join("\n");

[Link]({
to: destinatarios,
subject: "⚠️ Alerta: Stock Crítico de Tintas",
body: mensaje
});

return {
success: true,
count: [Link],
message: `Notificación enviada sobre ${[Link]} items`
};
} catch (e) {
[Link]("Error en enviarNotificacion: " + [Link]());
return {
success: false,
message: "Error al enviar notificación: " + [Link]
};
}
}

// ========== FUNCIONES AUXILIARES ==========

function generarIdPedido() {
return "PED-" + [Link](new Date(), [Link](),
"yyyyMMdd-HHmmss");
}

function generarPDFPedido(id, nombre, items, fecha, obs) {


try {
const doc = [Link](`Pedido ${id}`); // Nombre temporal del
Documento Google
const body = [Link]();

try {
const logoUrl = "[Link]
%20ANDRES%20BELLO%20CIR%[Link]";
const response = [Link](logoUrl);
const blob = [Link]();
[Link](blob)
.setWidth(100)
.setHeight(100)
.setAlignment([Link]);
} catch (e) {
[Link]("No se pudo cargar el logo: " + [Link]);
// Continuar aunque el logo no se cargue
}

[Link]("COLEGIO ANDRÉS BELLO - PAMPA")


.setHeading([Link].HEADING1)
.setAlignment([Link])
.setBold(true);

[Link](`\nFecha: ${[Link](fecha,
[Link](), "dd/MM/yyyy HH:mm")}`);
[Link](`Solicitante: ${nombre}`);
[Link]("\nSOLICITUD DE MATERIALES\
n").setHeading([Link].HEADING2);

const tabla = [Link]();


const header = [Link]();
["Modelo", "Cantidad", "Impresora"].forEach(text => {
[Link](text).setBackgroundColor("#dddddd").setBold(true);
});

[Link](i => {
const row = [Link]();
[Link]([Link]);
[Link]([Link]());
[Link]([Link]);
});

[Link](`\nObservaciones: ${obs || "Ninguna"}`).setItalic(true);


[Link](`\n\n\nAtentamente,\n\n_____________________________\n$
{nombre}`);
[Link]("\n\n\n\n\nAprobado por:\n\n_____________________________\
nNombre y firma")
.setSpacingBefore(150);

[Link]();
[Link](4000); // Esperar a que se genere el documento

const pdfBlob = [Link]("application/pdf");


[Link]([Link]() + ".pdf"); // Asignar nombre al archivo PDF

// --- Guardar PDF en la carpeta especificada ---


if (PDF_EXPORT_FOLDER_ID && PDF_EXPORT_FOLDER_ID !== "ID_DE_TU_CARPETA_AQUI") {
try {
const folder = [Link](PDF_EXPORT_FOLDER_ID);
[Link](pdfBlob);
[Link](`PDF '${[Link]()}' guardado en la carpeta con ID: $
{PDF_EXPORT_FOLDER_ID}`);
} catch (e) {
[Link]("Error al guardar PDF en la carpeta especificada: " +
[Link]() +
". Verifique que el ID de la carpeta sea correcto y que tenga
permisos de escritura.");
// El PDF aún se adjuntará al correo electrónico.
}
} else if (PDF_EXPORT_FOLDER_ID === "ID_DE_TU_CARPETA_AQUI") {
[Link]("El ID de la carpeta para exportar PDFs no ha sido
configurado. El PDF solo se adjuntará al correo.");
}
// --- Fin de la sección de guardado en carpeta ---

[Link]([Link]()).setTrashed(true); // Eliminar el documento


temporal de Google Docs
return pdfBlob; // Devolver el blob del PDF para adjuntarlo al correo

} catch (e) {
[Link]("Error en generarPDFPedido: " + [Link]());
// Si hay un error creando el PDF, intentar eliminar el documento si se creó
if (doc && [Link]()) {
try {
[Link]([Link]()).setTrashed(true);
} catch (cleanUpError) {
[Link]("Error al intentar limpiar el documento temporal: " +
[Link]());
}
}
throw e; // Re-lanzar el error para que sea manejado por la función que llama
}
}

function enviarEmailPedido(id, nombre, resumen, obs, pdf) {


try {
const destinatarios = [
"cabpampa2016@[Link]",
"jordanbarcelo@[Link]",
"joelrivas@[Link]",
"coordimedia@[Link]"
].join(",");

const fechaStr = [Link](new Date(), [Link](),


"dd/MM/yyyy HH:mm");
const body = `Nuevo pedido de tintas registrado:

Solicitante: ${nombre}
Fecha: ${fechaStr}

Detalles:
${resumen}

Observaciones: ${obs || "Ninguna"}

--
Sistema Automático de Pedidos
Colegio Andrés Bello - Pampa`;

[Link]({
to: destinatarios,
subject: `📨 Pedido de Tintas: ${id}`,
body: body,
attachments: [pdf], // El PDF se adjunta aquí
name: "Sistema de Pedidos"
});
} catch (e) {
[Link]("Error en enviarEmailPedido: " + [Link]());
throw e; // Re-lanzar el error
}
}

También podría gustarte