Primer version funcional

This commit is contained in:
Administrador Ibiza
2025-12-29 23:37:11 -06:00
commit 5289fd4133
294 changed files with 111418 additions and 0 deletions

269
views/import/index.php Executable file
View File

@@ -0,0 +1,269 @@
<div class="row mb-4">
<div class="col-12">
<h2><i class="bi bi-file-earmark-arrow-up"></i> Importación de Datos</h2>
<p class="text-muted">Importación masiva de datos al sistema (formato CSV)</p>
</div>
</div>
<div class="row">
<div class="col-md-8 offset-md-2">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Seleccionar Tipo de Importación</h5>
</div>
<div class="card-body">
<div class="mb-4">
<label class="form-label">¿Qué deseas importar?</label>
<select id="importTypeSelector" class="form-select form-select-lg">
<option value="">-- Selecciona una opción --</option>
<option value="houses">Casas</option>
<option value="payments">Pagos de Agua</option>
<option value="expenses">Gastos</option>
<option value="concept_payments">Pagos por Concepto Especial</option>
</select>
</div>
<div id="importArea" style="display: none;">
<form id="importForm">
<input type="hidden" name="type" id="importTypeInput">
<div id="dynamicFields"></div>
<div class="mb-3">
<label class="form-label">Archivo CSV</label>
<input type="file" class="form-control" name="file" accept=".csv" required>
</div>
<div id="formatInfo" class="alert alert-info small mb-3"></div>
<div class="d-flex gap-2">
<button type="button" id="downloadTemplateBtn" class="btn btn-outline-primary">
<i class="bi bi-download"></i> Descargar Plantilla
</button>
<button type="submit" class="btn btn-primary">
<i class="bi bi-upload"></i> Importar
</button>
</div>
</form>
</div>
<div id="placeholder" class="text-center py-5 text-muted">
<i class="bi bi-arrow-up-circle display-4"></i>
<p class="mt-3">Selecciona un tipo de importación para continuar</p>
</div>
</div>
</div>
</div>
</div>
<div class="card mt-4">
<div class="card-header">
<h5 class="card-title mb-0"><i class="bi bi-clock-history"></i> Historial de Importaciones</h5>
</div>
<div class="card-body">
<table class="table table-sm">
<thead>
<tr>
<th>Fecha</th>
<th>Tipo</th>
<th>Registros</th>
<th>Estado</th>
<th>Usuario</th>
</tr>
</thead>
<tbody id="importHistory">
<tr><td colspan="5" class="text-muted">No hay importaciones recientes</td></tr>
</tbody>
</table>
</div>
</div>
<?php
$conceptsJson = json_encode($concepts);
?>
<script>
const conceptsData = <?= $conceptsJson ?>;
const importTypes = {
houses: {
title: 'Importar Casas',
description: 'Importa o actualiza la información de las casas del condominio.',
fieldsHtml: '',
format: 'number,status,consumption_only,owner_name,owner_email,owner_phone',
examples: '001,activa,0,Juan Pérez,juan@email.com,555-1234\n002,activa,1,Maria García,maria@email.com,555-5678\n003,deshabitada,0,,,',
template: 'number,status,consumption_only,owner_name,owner_email,owner_phone\n001,activa,0,Juan Pérez,juan@email.com,555-1234\n002,activa,1,Maria García,maria@email.com,555-5678\n003,deshabitada,0,,,',
filename: 'plantilla_importar_casas.csv'
},
payments: {
title: 'Importar Pagos de Agua',
description: 'Importa pagos mensuales de agua para las casas.',
fieldsHtml: '',
format: 'year,house_number,month,amount,payment_date,payment_method,notes',
examples: '2025,001,Enero,250.00,2025-01-15,Efectivo,Pago mensual\n2025,002,Enero,250.00,2025-01-16,Transferencia,Pago transferencia\n2024,003,Diciembre,250.00,2024-12-20,Efectivo,Pago diciembre 2024',
template: 'year,house_number,month,amount,payment_date,payment_method,notes\n2025,001,Enero,250.00,2025-01-15,Efectivo,Pago mensual\n2025,002,Enero,250.00,2025-01-16,Transferencia,Pago transferencia\n2024,003,Diciembre,250.00,2024-12-20,Efectivo,Pago diciembre 2024',
filename: 'plantilla_importar_pagos_agua.csv'
},
expenses: {
title: 'Importar Gastos',
description: 'Importa gastos del condominio.',
fieldsHtml: '',
format: 'description,amount,expense_date,category,notes',
examples: 'Mantenimiento jardín,150.00,2025-01-10,Mantenimiento,Pago jardinero\nCompra insumos,75.50,2025-01-12,Insumos,Tornillos y clavos',
template: 'description,amount,expense_date,category,notes\nMantenimiento jardín,150.00,2025-01-10,Mantenimiento,Pago jardinero\nCompra insumos,75.50,2025-01-12,Insumos,Tornillos y clavos',
filename: 'plantilla_importar_gastos.csv'
},
concept_payments: {
title: 'Importar Pagos por Concepto Especial',
description: 'Importa pagos para uno o varios conceptos especiales.',
format: 'concept_id,house_number,amount,payment_date,notes',
examples: '1,001,200.00,2025-01-15,Pago cuota\n1,002,200.00,2025-01-16,Pago cuota\n2,003,300.00,2025-01-17,Pago fondo',
template: 'concept_id,house_number,amount,payment_date,notes\n1,001,200.00,2025-01-15,Pago cuota\n1,002,200.00,2025-01-16,Pago cuota\n2,003,300.00,2025-01-17,Pago fondo',
filename: 'plantilla_importar_pagos_concepto.csv'
}
};
document.getElementById('importTypeSelector').addEventListener('change', function() {
const type = this.value;
const importArea = document.getElementById('importArea');
const placeholder = document.getElementById('placeholder');
const dynamicFields = document.getElementById('dynamicFields');
const formatInfo = document.getElementById('formatInfo');
if (!type) {
importArea.style.display = 'none';
placeholder.style.display = 'block';
return;
}
const config = importTypes[type];
document.getElementById('importTypeInput').value = type;
let fieldsHtml = '';
let conceptHelp = '';
if (type === 'concept_payments') {
conceptHelp = '<div class="alert alert-secondary mb-3"><strong>Conceptos disponibles (ID - Nombre):</strong><br>';
conceptsData.forEach(function(c, index) {
const separator = index === conceptsData.length - 1 ? '' : ' | ';
conceptHelp += '<code>' + c.id + ' - ' + c.name + '</code>' + separator;
});
conceptHelp += '</div>';
fieldsHtml += conceptHelp;
}
dynamicFields.innerHTML = fieldsHtml;
formatInfo.innerHTML = '<strong>Formato esperado:</strong><br>' + config.format + '<br><br>' +
'<em>Ejemplo:</em><br><code>' + config.examples.replace(/\n/g, '<br>') + '</code><br><br>' +
'<small class="text-muted">' + config.description + '</small>';
importArea.style.display = 'block';
placeholder.style.display = 'none';
});
document.getElementById('downloadTemplateBtn').addEventListener('click', function() {
const type = document.getElementById('importTypeSelector').value;
if (type) {
downloadTemplate(type);
}
});
function downloadTemplate(type) {
const config = importTypes[type];
const csv = config.template;
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = config.filename;
link.click();
}
document.getElementById('importForm').addEventListener('submit', importData);
function importData(e) {
e.preventDefault();
const form = e.target;
const formData = new FormData(form);
const file = formData.get('file');
if (!file) {
Swal.fire('Error', 'Debe seleccionar un archivo', 'error');
return;
}
Swal.fire({
title: 'Importando...',
text: 'Procesando archivo, por favor espere',
allowOutsideClick: false,
didOpen: () => Swal.showLoading()
});
fetch('/dashboard.php?page=import_actions&action=import', {
method: 'POST',
body: formData
})
.then(r => r.json())
.then(data => {
if (data.success) {
Swal.fire('Éxito', data.message, 'success').then(() => location.reload());
} else {
Swal.fire('Error', data.message || 'Error al importar', 'error');
}
})
.catch(error => {
Swal.fire('Error', 'Error de conexión', 'error');
});
}
function loadImportHistory() {
fetch('/dashboard.php?page=import_actions&action=history')
.then(r => r.json())
.then(data => {
if (data.success && data.data.length > 0) {
const tbody = document.getElementById('importHistory');
tbody.innerHTML = '';
data.data.forEach(log => {
const row = document.createElement('tr');
// Extraer tipo y registros de los detalles
const details = log.details || '';
let type = 'Desconocido';
let records = '0';
if (details.includes('Importación houses')) {
type = 'Casas';
} else if (details.includes('Importación payments')) {
type = 'Pagos de Agua';
} else if (details.includes('Importación expenses')) {
type = 'Gastos';
} else if (details.includes('Importación concept_payments')) {
type = 'Pagos por Concepto';
}
const recordsMatch = details.match(/(\d+)\s+registros/);
if (recordsMatch) {
records = recordsMatch[1];
}
row.innerHTML = `
<td>${new Date(log.created_at).toLocaleString('es-MX')}</td>
<td>${type}</td>
<td>${records}</td>
<td><span class="badge bg-success">Exitoso</span></td>
<td>${log.username || 'N/A'}</td>
`;
tbody.appendChild(row);
});
}
})
.catch(error => {
console.error('Error al cargar historial:', error);
});
}
// Cargar historial al iniciar la página
document.addEventListener('DOMContentLoaded', loadImportHistory);
</script>