mirror of
https://github.com/cheveguerra/Colaborando.git
synced 2026-08-19 00:46:37 +00:00
feat: gestion de mantenimiento/penalizaciones y documentacion doc/
This commit is contained in:
+151
-49
@@ -1021,11 +1021,7 @@
|
||||
if (p.estatus === 'Completado' && p.trimestreCompletado === trimestreActual) {
|
||||
completedContainer.append(crearTarjetaProyecto(p, true));
|
||||
} else if (p.estatus === 'En proceso') {
|
||||
// Solo se muestra en el resumen si no ha pagado mantenimiento en este trimestre
|
||||
console.log("[ResumenUI] Proyecto:", p.nombreProyecto, "estatus:", p.estatus, "mantoPagadoEnTrimestre:", p.mantenimientoPagadoEnTrimestre, "trimestreActual:", trimestreActual);
|
||||
if (p.mantenimientoPagadoEnTrimestre !== trimestreActual) {
|
||||
activeContainer.append(crearTarjetaProyecto(p, false));
|
||||
}
|
||||
activeContainer.append(crearTarjetaProyecto(p, false));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1073,21 +1069,32 @@
|
||||
const completadoClass = esCompletado ? 'completado' : '';
|
||||
|
||||
const quincena = parseInt($('#etQuincena').val()) || 1;
|
||||
const trimestreActual = Math.floor((quincena - 1) / 6) + 1;
|
||||
const esFinTrimestre = (quincena % 6 === 0);
|
||||
const yaResolvioManto = (proyecto.mantenimiento !== 'N/A' && proyecto.mantenimientoPagadoEnTrimestre === trimestreActual);
|
||||
|
||||
let actionButtonsHtml = '';
|
||||
if (!esCompletado && esFinTrimestre) {
|
||||
if (!esCompletado && esFinTrimestre && proyecto.dificultad !== 'Incidencia' && proyecto.mantenimiento !== 'N/A') {
|
||||
const showPay = proyecto.mantenimiento !== 'N/A';
|
||||
const buttonManto = showPay ?
|
||||
`<button class="nav-button btn-pagar-mantenimiento" data-id="${proyecto.id}" style="background-color: #28a745; border: none; font-size: 11px; padding: 5px 8px; cursor: pointer; flex: 1;">Pagar Manto</button>` : '';
|
||||
|
||||
const penalizacionFormateada = formatCurrency(proyecto.penalizacion || 0);
|
||||
const buttonDesechar = `<button class="nav-button btn-desechar-proyecto" data-id="${proyecto.id}" style="background-color: #dc3545; border: none; font-size: 11px; padding: 5px 8px; cursor: pointer; flex: 1;">Desechar (${penalizacionFormateada})</button>`;
|
||||
|
||||
let buttonManto = '';
|
||||
let buttonPenalizar = '';
|
||||
|
||||
if (yaResolvioManto) {
|
||||
buttonManto = showPay ?
|
||||
`<button disabled class="nav-button btn-pagar-mantenimiento" data-id="${proyecto.id}" style="background-color: #6c757d; border: none; font-size: 11px; padding: 5px 8px; cursor: not-allowed; flex: 1;">Pagar Manto</button>` : '';
|
||||
buttonPenalizar = `<button disabled class="nav-button btn-penalizar-proyecto" data-id="${proyecto.id}" style="background-color: #6c757d; border: none; font-size: 11px; padding: 5px 8px; cursor: not-allowed; flex: 1;">Penalizar</button>`;
|
||||
} else {
|
||||
buttonManto = showPay ?
|
||||
`<button class="nav-button btn-pagar-mantenimiento" data-id="${proyecto.id}" style="background-color: #28a745; border: none; font-size: 11px; padding: 5px 8px; cursor: pointer; flex: 1;">Pagar Manto</button>` : '';
|
||||
buttonPenalizar = `<button class="nav-button btn-penalizar-proyecto" data-id="${proyecto.id}" style="background-color: #dc3545; border: none; font-size: 11px; padding: 5px 8px; cursor: pointer; flex: 1;">Penalizar</button>`;
|
||||
}
|
||||
|
||||
actionButtonsHtml = `
|
||||
<div class="project-actions" style="display: flex; gap: 8px; margin-top: 10px; width: 100%;">
|
||||
${buttonManto}
|
||||
${buttonDesechar}
|
||||
${buttonPenalizar}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -1105,7 +1112,7 @@
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="project-card ${completadoClass}" style="flex-basis: 48%; display: flex; flex-direction: column; justify-content: space-between; min-height: 180px; box-sizing: border-box; padding: 10px;">
|
||||
<div class="project-card ${completadoClass}" style="flex-basis: 48%; display: flex; flex-direction: column; justify-content: space-between; min-height: 180px; box-sizing: border-box; padding: 10px; ${yaResolvioManto ? 'background-color: #e9e9e9; opacity: 0.75; border-color: #aaa;' : ''}">
|
||||
<div>
|
||||
<div class="project-header" style="font-weight: bold; margin-bottom: 5px; text-align: center; border-bottom: 1px solid gray; padding-bottom: 4px;">${proyecto.nombreProyecto.toUpperCase()}</div>
|
||||
<div class="project-body" style="padding: 5px; text-align: center;">
|
||||
@@ -1423,7 +1430,7 @@
|
||||
guardaSesion();
|
||||
}
|
||||
}
|
||||
} else if (accionQuincenal === 'Descartar un proyecto (se va al limbo con todo y activos)') {
|
||||
} else if (accionQuincenal === 'Descartar un proyecto') {
|
||||
const proyectosEnProceso = proyectos.filter(p => p.estatus === 'En proceso');
|
||||
if (proyectosEnProceso.length === 0) {
|
||||
alert('No hay proyectos en proceso para descartar.');
|
||||
@@ -1433,11 +1440,16 @@
|
||||
let dialogContent = $('<div><p>Selecciona el proyecto a descartar:</p></div>');
|
||||
let projectList = $('<form>');
|
||||
proyectosEnProceso.forEach((p) => {
|
||||
let labelText = p.nombreProyecto;
|
||||
if (p.dificultad === 'Incidencia') {
|
||||
const shortDesc = p.descripcion.length > 35 ? p.descripcion.substring(0, 35) + '...' : p.descripcion;
|
||||
labelText = `${p.nombreProyecto} (${shortDesc})`;
|
||||
}
|
||||
projectList.append(
|
||||
`<div style="display: flex; align-items: flex-start; margin: 8px 0; text-align: left;">
|
||||
<input type="radio" id="proj-discard-${p.id}" name="proyectoADescartar" value="${p.id}" style="margin-top: 2px; flex-shrink: 0;">
|
||||
<label for="proj-discard-${p.id}" style="margin-left: 8px; cursor: pointer;">
|
||||
${p.nombreProyecto} <span style="color: #555; font-size: 0.9em;">(${p.departamento})</span>
|
||||
${labelText}
|
||||
</label>
|
||||
</div>`
|
||||
);
|
||||
@@ -1477,6 +1489,74 @@
|
||||
$(this).dialog('destroy').remove();
|
||||
},
|
||||
});
|
||||
} else if (accionQuincenal === 'Omitir un mantenimiento') {
|
||||
const esFinTrimestre = (quincenaActual % 6 === 0);
|
||||
const trimestreActual = Math.floor((quincenaActual - 1) / 6) + 1;
|
||||
if (!esFinTrimestre) {
|
||||
alert('Esta Habilidad Especial solo se puede utilizar en el cierre de un trimestre (quincenas 6, 12, 18 y 24).');
|
||||
return;
|
||||
}
|
||||
const proyectosPendientes = proyectos.filter(p =>
|
||||
p.estatus === 'En proceso' &&
|
||||
p.mantenimiento !== 'N/A' &&
|
||||
p.mantenimientoPagadoEnTrimestre !== trimestreActual
|
||||
);
|
||||
|
||||
if (proyectosPendientes.length === 0) {
|
||||
alert('No hay proyectos activos con mantenimiento pendiente en este trimestre.');
|
||||
return;
|
||||
}
|
||||
|
||||
let dialogContent = $('<div><p>Selecciona el proyecto al que deseas omitir el costo de mantenimiento:</p></div>');
|
||||
let projectList = $('<form>');
|
||||
proyectosPendientes.forEach((p) => {
|
||||
projectList.append(
|
||||
`<div style="display: flex; align-items: flex-start; margin: 8px 0; text-align: left;">
|
||||
<input type="radio" id="proj-omit-${p.id}" name="proyectoAOmitir" value="${p.id}" style="margin-top: 2px; flex-shrink: 0;">
|
||||
<label for="proj-omit-${p.id}" style="margin-left: 8px; cursor: pointer;">
|
||||
${p.nombreProyecto} <span style="color: #555; font-size: 0.9em;">(Costo: ${p.mantenimiento})</span>
|
||||
</label>
|
||||
</div>`
|
||||
);
|
||||
});
|
||||
dialogContent.append(projectList);
|
||||
|
||||
dialogContent.dialog({
|
||||
modal: true,
|
||||
title: 'Omitir Mantenimiento - Logística y Sistemas',
|
||||
width: 450,
|
||||
buttons: {
|
||||
"Omitir Costo": function() {
|
||||
const selectedProjectId = $('input[name="proyectoAOmitir"]:checked').val();
|
||||
if (!selectedProjectId) {
|
||||
alert('Por favor, selecciona un proyecto.');
|
||||
return;
|
||||
}
|
||||
const proyectoAOmitir = proyectos.find(p => p.id == selectedProjectId);
|
||||
if (proyectoAOmitir) {
|
||||
proyectoAOmitir.mantenimientoPagadoEnTrimestre = trimestreActual;
|
||||
setArrayValue(elArreglo, 'accionEspecialLastUsed', quincenaActual);
|
||||
actualizarResumenUI();
|
||||
actualizarTarjetaDepto('LogisticaYSistemas');
|
||||
var audio = new Audio('./Microwave_bell.wav');
|
||||
audio.play();
|
||||
alert(`¡El mantenimiento del proyecto "${proyectoAOmitir.nombreProyecto}" ha sido omitido con éxito sin costo!`);
|
||||
if (typeof guardaSesion === 'function') {
|
||||
guardaSesion();
|
||||
} else if (typeof guardarDatosEnServidor === 'function') {
|
||||
guardarDatosEnServidor();
|
||||
}
|
||||
}
|
||||
$(this).dialog('close');
|
||||
},
|
||||
"Cancelar": function() {
|
||||
$(this).dialog('close');
|
||||
}
|
||||
},
|
||||
close: function() {
|
||||
$(this).dialog('destroy').remove();
|
||||
}
|
||||
});
|
||||
} else if (accionQuincenal === 'Ordenar los próximos 5 proyectos') {
|
||||
alert('Esta habilidad especial solo se puede utilizar desde el tablero de juego.');
|
||||
return;
|
||||
@@ -1539,11 +1619,35 @@
|
||||
const recT = parseInt(findArrayValue(deptoArr, 'recursoTiempo')) || 0;
|
||||
const recP = parseInt(findArrayValue(deptoArr, 'recursoPersonal')) || 0;
|
||||
const recC = parseInt(findArrayValue(deptoArr, 'recursoCapital')) || 0;
|
||||
|
||||
// Get department skills discounts
|
||||
const habTiempo = findArrayValue(deptoArr, 'habilidadTiempo');
|
||||
const habPersonal = findArrayValue(deptoArr, 'habilidadPersonal');
|
||||
const habCapital = findArrayValue(deptoArr, 'habilidadCapital');
|
||||
|
||||
const discountT = (habTiempo && habTiempo !== 0) ? parseInt(findArrayValue(arreglosCapacitaciones[habTiempo], 'beneficio')) || 0 : 0;
|
||||
const discountP = (habPersonal && habPersonal !== 0) ? parseInt(findArrayValue(arreglosCapacitaciones[habPersonal], 'beneficio')) || 0 : 0;
|
||||
const discountC = (habCapital && habCapital !== 0) ? parseInt(findArrayValue(arreglosCapacitaciones[habCapital], 'beneficio')) || 0 : 0;
|
||||
|
||||
const finalCostT = Math.max(0, cost.t + discountT);
|
||||
const finalCostP = Math.max(0, cost.p + discountP);
|
||||
const finalCostC = Math.max(0, cost.c + discountC);
|
||||
|
||||
console.log("[PagarManto] Recursos depto actual:", {
|
||||
recT,
|
||||
recP,
|
||||
recC
|
||||
});
|
||||
console.log("[PagarManto] Descuentos:", {
|
||||
discountT,
|
||||
discountP,
|
||||
discountC
|
||||
});
|
||||
console.log("[PagarManto] Costo final con descuento:", {
|
||||
finalCostT,
|
||||
finalCostP,
|
||||
finalCostC
|
||||
});
|
||||
|
||||
// Verificar si LogisticaYSistemas tiene comprada y disponible su habilidad especial
|
||||
let logisticaHabilidadEspecialDisponible = false;
|
||||
@@ -1602,7 +1706,7 @@
|
||||
},
|
||||
"Pagar Normal (Con costo)": function() {
|
||||
$(this).dialog('close');
|
||||
ejecutarPagoNormal(proyecto, cost, deptoArr, recT, recP, recC, deptoNombre, trimestreActual, quincena);
|
||||
ejecutarPagoNormal(proyecto, deptoArr, recT, recP, recC, deptoNombre, trimestreActual, finalCostT, finalCostP, finalCostC);
|
||||
},
|
||||
"Cancelar": function() {
|
||||
$(this).dialog('close');
|
||||
@@ -1610,26 +1714,28 @@
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ejecutarPagoNormal(proyecto, cost, deptoArr, recT, recP, recC, deptoNombre, trimestreActual, quincena);
|
||||
ejecutarPagoNormal(proyecto, deptoArr, recT, recP, recC, deptoNombre, trimestreActual, finalCostT, finalCostP, finalCostC);
|
||||
}
|
||||
}
|
||||
|
||||
function ejecutarPagoNormal(proyecto, cost, deptoArr, recT, recP, recC, deptoNombre, trimestreActual, quincena) {
|
||||
if (recT < cost.t || recP < cost.p || recC < cost.c) {
|
||||
function ejecutarPagoNormal(proyecto, deptoArr, recT, recP, recC, deptoNombre, trimestreActual, finalCostT, finalCostP, finalCostC) {
|
||||
if (recT < finalCostT || recP < finalCostP || recC < finalCostC) {
|
||||
alert(`El departamento seleccionado (${deptoNombre}) NO tiene suficientes recursos para pagar el mantenimiento del proyecto "${proyecto.nombreProyecto}".\n` +
|
||||
`Costo requerido: Tiempo: ${cost.t}, Personal: ${cost.p}, Capital: ${cost.c}\n` +
|
||||
`Costo requerido (con descuento): ${finalCostT}T ${finalCostP}P ${finalCostC}C\n` +
|
||||
`Recursos actuales: Tiempo: ${recT}, Personal: ${recP}, Capital: ${recC}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmMessage = `¿Estás seguro de que deseas pagar el mantenimiento de <b>${proyecto.mantenimiento}</b> para el proyecto "${proyecto.nombreProyecto}" desde el departamento <b>${deptoNombre}</b>?`;
|
||||
const confirmMessage = `¿Estás seguro de que deseas pagar el mantenimiento para el proyecto "${proyecto.nombreProyecto}" desde el departamento <b>${deptoNombre}</b>?<br>` +
|
||||
`Costo original: <b>${proyecto.mantenimiento}</b><br>` +
|
||||
`Costo final con descuento aplicado: <b>${finalCostT}T ${finalCostP}P ${finalCostC}C</b>`;
|
||||
|
||||
confirm(confirmMessage, function(confirmed) {
|
||||
if (confirmed) {
|
||||
// Deduct resources
|
||||
setArrayValue(deptoArr, 'recursoTiempo', recT - cost.t);
|
||||
setArrayValue(deptoArr, 'recursoPersonal', recP - cost.p);
|
||||
setArrayValue(deptoArr, 'recursoCapital', recC - cost.c);
|
||||
setArrayValue(deptoArr, 'recursoTiempo', recT - finalCostT);
|
||||
setArrayValue(deptoArr, 'recursoPersonal', recP - finalCostP);
|
||||
setArrayValue(deptoArr, 'recursoCapital', recC - finalCostC);
|
||||
|
||||
// Mark project maintenance as paid in the current trimester
|
||||
proyecto.mantenimientoPagadoEnTrimestre = trimestreActual;
|
||||
@@ -1655,7 +1761,7 @@
|
||||
}, 450);
|
||||
}
|
||||
|
||||
function desecharProyecto(projectId) {
|
||||
function penalizarProyecto(projectId) {
|
||||
const proyecto = proyectos.find(p => p.id == projectId);
|
||||
if (!proyecto) {
|
||||
alert('No se encontró el proyecto.');
|
||||
@@ -1686,41 +1792,40 @@
|
||||
}
|
||||
|
||||
if (operacionesHabilidadEspecialDisponible) {
|
||||
const dialogMsg = `El departamento de Operaciones tiene disponible la Habilidad Especial para descartar un proyecto sin costo.<br><br>` +
|
||||
`¿Deseas usar la Habilidad Especial para desechar el proyecto "${proyecto.nombreProyecto}" <b>sin pagar la penalización</b>,<br>` +
|
||||
`o prefieres realizar un desecho normal pagando la penalización de <b>${penalizacionFormateada}</b>?`;
|
||||
const dialogMsg = `El departamento de Operaciones tiene disponible la Habilidad Especial para exentar de penalización a un proyecto.<br><br>` +
|
||||
`¿Deseas usar la Habilidad Especial para el proyecto "${proyecto.nombreProyecto}" <b>sin pagar la penalización</b>,<br>` +
|
||||
`o prefieres pagar la penalización de <b>${penalizacionFormateada}</b> de manera normal?`;
|
||||
|
||||
$('<div></div>')
|
||||
.html(dialogMsg)
|
||||
.dialog({
|
||||
modal: true,
|
||||
title: 'Desechar Proyecto - Operaciones',
|
||||
title: 'Penalizar Proyecto - Operaciones',
|
||||
width: 480,
|
||||
buttons: {
|
||||
"Usar Habilidad (Sin costo)": function() {
|
||||
$(this).dialog('close');
|
||||
// Mark project as discarded
|
||||
proyecto.estatus = 'descartado';
|
||||
// Mantiene el estatus 'En proceso' pero marca el mantenimiento resuelto en este trimestre
|
||||
proyecto.mantenimientoPagadoEnTrimestre = trimestreActual;
|
||||
|
||||
// Update skill cooldown
|
||||
// Cooldown de la habilidad
|
||||
setArrayValue(elArregloOperaciones, 'accionEspecialLastUsed', quincena);
|
||||
|
||||
// Save and update UI
|
||||
actualizarResumenUI();
|
||||
actualizarTarjetaDepto('Operaciones'); // Refresca la tarjeta del depto en la UI
|
||||
actualizarTarjetaDepto('Operaciones');
|
||||
|
||||
var audio = new Audio('./Microwave_bell.wav');
|
||||
audio.play();
|
||||
|
||||
alert(`El proyecto "${proyecto.nombreProyecto}" ha sido desechado sin costo utilizando la Habilidad Especial de Operaciones.`);
|
||||
alert(`El proyecto "${proyecto.nombreProyecto}" ha sido exentado de la penalización utilizando la Habilidad Especial de Operaciones.`);
|
||||
|
||||
if (typeof guardarDatosEnServidor === 'function') {
|
||||
guardarDatosEnServidor();
|
||||
}
|
||||
},
|
||||
"Desechar Normal (Con costo)": function() {
|
||||
"Pagar Penalización (Con costo)": function() {
|
||||
$(this).dialog('close');
|
||||
ejecutarDesechoNormal(proyecto, penalizacionVal, penalizacionFormateada, trimestreActual);
|
||||
ejecutarPenalizacionNormal(proyecto, penalizacionVal, penalizacionFormateada, trimestreActual);
|
||||
},
|
||||
"Cancelar": function() {
|
||||
$(this).dialog('close');
|
||||
@@ -1728,46 +1833,43 @@
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ejecutarDesechoNormal(proyecto, penalizacionVal, penalizacionFormateada, trimestreActual);
|
||||
ejecutarPenalizacionNormal(proyecto, penalizacionVal, penalizacionFormateada, trimestreActual);
|
||||
}
|
||||
}
|
||||
|
||||
function ejecutarDesechoNormal(proyecto, penalizacionVal, penalizacionFormateada, trimestreActual) {
|
||||
// Validar que el acumulado general acumulado hasta el trimestre actual sea suficiente
|
||||
function ejecutarPenalizacionNormal(proyecto, penalizacionVal, penalizacionFormateada, trimestreActual) {
|
||||
let acumuladoDisponible = 0;
|
||||
for (let t = 1; t <= trimestreActual; t++) {
|
||||
acumuladoDisponible += (arregloGeneral['trimestre' + t] || 0);
|
||||
}
|
||||
|
||||
if (acumuladoDisponible < penalizacionVal) {
|
||||
alert(`No puedes desechar el proyecto "${proyecto.nombreProyecto}" porque no hay acumulado general suficiente.\n` +
|
||||
alert(`No puedes penalizar el proyecto "${proyecto.nombreProyecto}" porque no hay acumulado general suficiente.\n` +
|
||||
`Penalización requerida: ${penalizacionFormateada}\n` +
|
||||
`Acumulado general disponible: ${formatCurrency(acumuladoDisponible)}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmMessage = `¿Estás seguro de que deseas desechar el proyecto "${proyecto.nombreProyecto}"?<br>` +
|
||||
const confirmMessage = `¿Estás seguro de que quieres pagar la penalización del proyecto "${proyecto.nombreProyecto}"?<br>` +
|
||||
`Se aplicará una penalización de <b>${penalizacionFormateada}</b> del acumulado general.`;
|
||||
|
||||
confirm(confirmMessage, function(confirmed) {
|
||||
if (confirmed) {
|
||||
// Subtract from current trimester's earnings
|
||||
const trimestreKey = 'trimestre' + trimestreActual;
|
||||
if (typeof arregloGeneral[trimestreKey] === 'undefined') {
|
||||
arregloGeneral[trimestreKey] = 0;
|
||||
}
|
||||
arregloGeneral[trimestreKey] -= penalizacionVal;
|
||||
|
||||
// Mark project as discarded
|
||||
proyecto.estatus = 'descartado';
|
||||
// Mantiene el estatus 'En proceso' pero marca el mantenimiento resuelto en este trimestre
|
||||
proyecto.mantenimientoPagadoEnTrimestre = trimestreActual;
|
||||
|
||||
// Save and update UI
|
||||
actualizarResumenUI();
|
||||
|
||||
var audio = new Audio('./Microwave_bell.wav');
|
||||
audio.play();
|
||||
|
||||
alert(`El proyecto "${proyecto.nombreProyecto}" ha sido desechado. Se aplicó una penalización de ${penalizacionFormateada}.`);
|
||||
alert(`El proyecto "${proyecto.nombreProyecto}" ha sido penalizado. Se aplicó una penalización de ${penalizacionFormateada}.`);
|
||||
|
||||
if (typeof guardarDatosEnServidor === 'function') {
|
||||
guardarDatosEnServidor();
|
||||
@@ -1776,15 +1878,15 @@
|
||||
}, 450);
|
||||
}
|
||||
|
||||
// Delegación de eventos para los botones de pagar y desechar
|
||||
// Delegación de eventos para los botones de pagar y penalizar
|
||||
$(document).on('click', '.btn-pagar-mantenimiento', function() {
|
||||
const id = $(this).data('id');
|
||||
pagarMantenimientoProyecto(id);
|
||||
});
|
||||
|
||||
$(document).on('click', '.btn-desechar-proyecto', function() {
|
||||
$(document).on('click', '.btn-penalizar-proyecto', function() {
|
||||
const id = $(this).data('id');
|
||||
desecharProyecto(id);
|
||||
penalizarProyecto(id);
|
||||
});
|
||||
|
||||
// El listener para el click en .departamentos ya está en jsCode.js y funcionará ahora
|
||||
|
||||
Reference in New Issue
Block a user