Fish Control v9.0

Sistema de Inventario Zoocampo

API Conectada ✓
Zoocampo DB: elcerrit_zfc

Total Peces

0

Tanques Activos

0

Tipos de Tanque

0

Movimientos Hoy

0

Inventario por Tipo

Tanques con Mayor Inventario

Actividad Reciente

`; showModal(content); } // Función para ejecutar movimiento desde el modal function executeMovement(event) { event.preventDefault(); const formData = new FormData(event.target); const fromTankId = parseInt(formData.get('fromTankId')); const toTankId = parseInt(formData.get('toTankId')); const cantidad = parseInt(formData.get('cantidad')); const motivo = formData.get('motivo'); const observaciones = formData.get('observaciones') || ''; if (fromTankId === toTankId) { alert('El tanque origen y destino no pueden ser el mismo'); return; } const fromTank = tanquesData.find(t => t.id === fromTankId); const toTank = tanquesData.find(t => t.id === toTankId); if (!fromTank || !toTank) { alert('Error: No se encontraron los tanques seleccionados'); return; } if (cantidad > fromTank.inventario) { alert(`No hay suficientes peces. Disponible: ${fromTank.inventario.toLocaleString()}`); return; } if (cantidad <= 0) { alert('La cantidad debe ser mayor a 0'); return; } // Realizar el movimiento fromTank.inventario -= cantidad; toTank.inventario += cantidad; // Actualizar fechas fromTank.fecha_actualizacion = new Date().toLocaleDateString('es-ES'); toTank.fecha_actualizacion = new Date().toLocaleDateString('es-ES'); // Registrar movimiento const movimiento = { id: Date.now(), fecha: new Date().toISOString(), tanque_origen: fromTank.nombre, tanque_destino: toTank.nombre, tipo: motivo, cantidad: cantidad, lote: fromTank.lote || toTank.lote || '', observaciones: observaciones }; movimientosData.push(movimiento); // Registrar actividad const detalles = `${cantidad.toLocaleString()} peces de ${fromTank.nombre} → ${toTank.nombre}. Motivo: ${motivo}${observaciones ? '. ' + observaciones : ''}`; addActivity('actualizado', 'Movimiento de peces realizado', detalles); // Sincronizar y actualizar UI syncWithAPI(); renderTanques(tanquesData); renderMovimientos(); updateDashboard(); hideModal(); alert(`✓ Movimiento exitoso: ${cantidad.toLocaleString()} peces movidos de ${fromTank.nombre} a ${toTank.nombre}`); } // Exportar datos function exportData() { const csvData = []; csvData.push(['Tanque', 'Tipo', 'Lote', 'Inventario', 'Kg_Dia', 'Fecha_Siembra', 'Estado', 'Ultima_Actualizacion']); tanquesData.forEach(tanque => { let inventario = 0; let kilosDia = 0; let fechaSiembra = ''; let numeroLote = ''; if (tanque.tipo_tanque === 'Incubación') { inventario = tanque.inventario_inc || 0; kilosDia = tanque.kilos_dia_inc || 0; fechaSiembra = tanque.fecha_siembra_inc || ''; numeroLote = tanque.numero_lote_inc || ''; } else if (tanque.tipo_tanque === 'Canaletas Alevinos') { inventario = tanque.inventario_ale || 0; kilosDia = tanque.kilos_dia_ale || 0; fechaSiembra = tanque.fecha_siembra_ale || ''; numeroLote = tanque.numero_lote_ale || ''; } else if (tanque.tipo_tanque === 'Pre Levante') { inventario = tanque.inventario_prel || 0; kilosDia = tanque.kilos_dia_prel || 0; fechaSiembra = tanque.fecha_siembra_prel || ''; numeroLote = tanque.lote_prel || ''; } else if (tanque.tipo_tanque === 'Tanques Alevinos') { inventario = tanque.inventario_ale || 0; kilosDia = tanque.kilos_dia_ale || 0; fechaSiembra = tanque.fecha_siembra_ale || ''; numeroLote = tanque.numero_lote_ale || ''; } else if (tanque.tipo_tanque === 'Levante') { inventario = tanque.inventario_lev || 0; kilosDia = tanque.kilos_dia_lev || 0; fechaSiembra = tanque.fecha_siembra_lev || ''; numeroLote = tanque.numero_lote_lev || ''; } else if (tanque.tipo_tanque === 'Baterías') { inventario = tanque.inventario_bat || 0; kilosDia = tanque.kilos_dia_bat || 0; fechaSiembra = tanque.fecha_siembra_bat || ''; numeroLote = tanque.lote_bat || ''; } else if (tanque.tipo_tanque === 'Finalización') { inventario = tanque.inventario_fin || 0; kilosDia = tanque.kilos_dia_fin || 0; fechaSiembra = tanque.fecha_siembra_fin || ''; numeroLote = tanque.numero_lote_fin || ''; } else if (tanque.tipo_tanque === 'Engorde') { inventario = tanque.inventario_eng || 0; kilosDia = tanque.kilos_dia_eng || 0; fechaSiembra = tanque.fecha_siembra_eng || ''; numeroLote = tanque.lote_eng || ''; } csvData.push([ tanque.tanque_revision, tanque.tipo_tanque, numeroLote, inventario, kilosDia, fechaSiembra, tanque.estado, tanque.fecha_actualizacion ]); }); const csvContent = csvData.map(row => row.join(',')).join('\n'); const blob = new Blob([csvContent], { type: 'text/csv' }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `zoocampo_inventario_${new Date().toISOString().split('T')[0]}.csv`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); } // Funciones de utilidad function showError(message) { const errorDiv = document.createElement('div'); errorDiv.className = 'fixed top-4 right-4 bg-red-500 text-white px-6 py-3 rounded-lg z-50'; errorDiv.textContent = message; document.body.appendChild(errorDiv); setTimeout(() => { document.body.removeChild(errorDiv); }, 5000); } function showSuccess(message) { const successDiv = document.createElement('div'); successDiv.className = 'fixed top-4 right-4 bg-green-500 text-white px-6 py-3 rounded-lg z-50'; successDiv.textContent = message; document.body.appendChild(successDiv); setTimeout(() => { document.body.removeChild(successDiv); }, 3000); } // Función para limpiar todos los datos (para pruebas) function clearAllData() { if (confirm('¿Está seguro de que desea eliminar TODOS los datos? Esta acción no se puede deshacer.')) { localStorage.removeItem(STORAGE_KEY); localStorage.removeItem('zoocampo_remote_db'); localStorage.removeItem(DB_SYNC_KEY); tanquesData = []; actividadReciente = []; // Recargar datos iniciales loadZoocampoData(); alert('Todos los datos han sido eliminados y se han cargado los datos iniciales de Zoocampo.'); } } // Funciones de prueba para simular cambios remotos function simulateRemoteChange() { // Simular que otro usuario modificó un tanque const randomTank = tanquesData[Math.floor(Math.random() * tanquesData.length)]; if (!randomTank) return; const currentInventario = getInventario(randomTank, randomTank.tipo_tanque); const newInventario = currentInventario + Math.floor(Math.random() * 1000) - 500; // +/- 500 peces // Crear copia modificada const modifiedTank = { ...randomTank }; updateInventario(modifiedTank, modifiedTank.tipo_tanque, Math.max(0, newInventario)); // Simular datos remotos const modifiedData = tanquesData.map(t => t.id === modifiedTank.id ? modifiedTank : t); const syncData = { timestamp: Date.now(), data: { tanquesData: modifiedData, actividadReciente: [...actividadReciente, { id: Date.now(), tipo: 'actualizado', descripcion: `Cambio remoto en ${randomTank.tanque_revision}`, detalles: `Inventario: ${currentInventario.toLocaleString()} → ${Math.max(0, newInventario).toLocaleString()}`, fecha: new Date().toLocaleDateString('es-ES'), timestamp: new Date().toISOString() }] } }; localStorage.setItem('zoocampo_remote_db', JSON.stringify(syncData)); localStorage.setItem(DB_SYNC_KEY, syncData.timestamp.toString()); updateConnectionStatus('Cambio remoto detectado'); console.log('✓ Cambio remoto simulado para:', randomTank.tanque_revision); // Verificar cambios después de un momento setTimeout(checkForRemoteChanges, 2000); } function testDatabaseSync() { const content = `

Pruebas de Base de Datos

Estado Actual:

• Tanques locales: ${tanquesData.length}

• Última sincronización: ${new Date(lastSyncTime).toLocaleString()}

• BD remota: ${localStorage.getItem('zoocampo_remote_db') ? 'Activa' : 'No inicializada'}

Instrucciones de Prueba:

  1. 1. Abra esta app en dos pestañas diferentes
  2. 2. Modifique un tanque en una pestaña
  3. 3. Cambie a la otra pestaña
  4. 4. Los cambios aparecerán automáticamente
`; showModal(content); } // Información sobre funcionamiento sin conexión function showOfflineInfo() { const content = `

Funcionamiento Sin Conexión

Fish Control v9.0 funciona completamente SIN INTERNET

Todos los datos se guardan localmente en su dispositivo

En el Campo (Sin Señal Celular):

Almacenamiento Local:

Sincronización:

Recomendación: Exporte los datos regularmente y mantenga copias de respaldo en diferentes dispositivos.

`; showModal(content); }