Skip to content

Latest commit

 

History

History
674 lines (548 loc) · 17.3 KB

File metadata and controls

674 lines (548 loc) · 17.3 KB

Guía de Ficheros - Los Toldos ERP

📂 Estructura de Archivos y Descripción


🔧 SERVICIOS (lib/services/)

1. auth_service.dart - Autenticación

Propósito: Gestionar login y validación de credenciales

Responsabilidades:

  • Validar usuario y PIN contra base de datos
  • Mantener token de sesión activa
  • Cerrar sesión de usuario

Métodos Principales:

autenticar(nombre, pin) → Usuario | nullConsulta tabla "usuarios" en SupabaseValida que PIN coincida
  • Retorna objeto Usuario si es válido

cerrarSesion() → Future<void>
  • Limpia sesión actual
  • Elimina token de memoria

obtenerUsuarioActual() → Usuario?Obtiene usuario en sesión sin DB

Dependencias:

  • supabase_flutter
  • usuario_model.dart

Estado Manejado:

  • Usuario autenticado actual
  • Token de sesión

2. dashboard_service.dart - Datos en Tiempo Real ⭐

Propósito: Core del sistema realtime - Sincroniza datos con PostgreSQL en vivo

Responsabilidades Críticas:

  • Gestionar StreamControllers para datos reactivos
  • Configurar listeners con Supabase Realtime
  • Detectar cambios en inventario y movimientos
  • Disparar callbacks para animaciones

Métodos Principales:

obtenerInventarioStream() → Stream<List<Map>>
  • Retorna stream actualizado en tiempo real
  • Emite cuando hay cambios en BDUsado en HomeScreen + StockScreen

obtenerAlmacenes() → Future<List<Map>>
  • Lista todos los almacenes disponibles
  • Cache local para optimización

obtenerMovimientos() → Future<List<Map>>
  • Historial de movimientos de stock
  • Filtra por fecha y usuario

initializeRealtimeListeners() → void
  ⚠️ CRÍTICO: Se llama UNA SOLA VEZ en main.dart
  • _subscribeToAlmacenesChanges()
  • _subscribeToMovimientosChanges()
  • Configura WebSocket listeners

dispose() → voidLimpia todos los streams
  • Cancela suscripciones
  • IMPORTANTE: Llamar en onDispose

Callbacks para Animaciones:

onNewStockChange: Function() ?Se dispara cuando actualiza inventario
  • Anima tarjeta de Stock en HomeScreen

onNewMovimiento: Function() ?Se dispara cuando hay nuevo movimiento
  • Anima tarjeta de Movimientos en HomeScreen

Listeners Internos:

_subscribeToAlmacenesChanges()
  └─ Escucha: INSERT, UPDATE en tabla "almacenes"
  └─ Acción: Actualiza _inventarioStreamController
  └─ Efecto: Dispara onNewStockChange callback

_subscribeToMovimientosChanges()
  └─ Escucha: INSERT en tabla "movimientos"
  └─ Acción: Agrega nuevo movimiento a stream
  └─ Efecto: Dispara onNewMovimiento callback

Mapeo de Datos:

Inventario {
  id: int,
  almacen_id: int,
  producto: string,
  cantidad: int,
  cantidad_minima: int,
  cantidad_maxima: int,
  precio_unitario: decimal,
  fecha_actualizacion: timestamp
}

Movimiento {
  id: int,
  tipo: 'entrada' | 'salida' | 'transferencia',
  cantidad: int,
  almacen_origen_id: int,
  almacen_destino_id: int,
  usuario_id: int,
  fecha: timestamp,
  motivo: string,
  estado: 'pendiente' | 'completado' | 'cancelado'
}

Dependencias:

  • supabase_flutter
  • dart:async (StreamControllers)

3. session_service.dart - Sesión Local

Propósito: Persistir y recuperar información de sesión del usuario

Responsabilidades:

  • Guardar datos de usuario en dispositivo
  • Recuperar sesión guardada al abrir app
  • Limpiar sesión al logout

Métodos Principales:

guardarSesion(usuario) → Future<void>
  • Almacena Usuario en SharedPreferencesEncripta si es posible

obtenerSesion() → Future<Usuario?>
  • Lee Usuario de SharedPreferencesRetorna null si no existe

limpiarSesion() → Future<void>
  • Elimina datos de SharedPreferencesLimpia caché local

obtenerUsuarioActual() → Usuario?Lee usuario en memoria (sin I/O)
  • Rápido para checks frecuentes

Almacenamiento:

  • ✅ SharedPreferences (local)
  • ⚠️ NO encriptado actualmente
  • 🔒 Considerar Keychain/KeyStore en producción

Dependencias:

  • shared_preferences
  • usuario_model.dart

4. supabase_service.dart - Conexión BD

Propósito: Wrapper para operaciones Supabase

Responsabilidades:

  • Gestionar cliente Supabase singleton
  • Métodos helpers para CRUD
  • Manejo de errores de conexión

Métodos Principales:

getClient() → SupabaseClientSingleton del cliente Supabase

query(table) → PostgrestQueryBuilderBuilder para consultas

consultarInventario() → Future<List<Map>>
  • SELECT * FROM inventarios

insertarMovimiento(datos) → Future<void>
  • INSERT INTO movimientos

actualizarInventario(id, datos) → Future<void>
  • UPDATE inventarios SET...

Dependencias:

  • supabase_flutter

5. stock_transfer_service.dart - Transferencias

Propósito: Operaciones específicas de transferencia de inventario

Responsabilidades:

  • Validar transferencias
  • Actualizar almacenes origen y destino
  • Crear registro en movimientos

Métodos Principales:

transferir(origen, destino, cantidad, motivo)
  • Valida disponibilidad
  • Resta de origen, suma a destino
  • Registra en movimientos

Dependencias:

  • supabase_flutter
  • dashboard_service.dart

📱 PANTALLAS (lib/screens/)

1. login_screen.dart - Autenticación UI

Propósito: Interfaz de ingreso de usuario

Componentes:

  • TextField para usuario
  • TextField para PIN (con toggle visibilidad)
  • Botón Login
  • SnackBar para errores

Flujo:

Usuario ingresa credenciales
  ↓
Presiona botón Login
  ↓
authService.autenticar(usuario, pin)
  ↓
¿Validado?
  ├─ SÍ: SessionService.guardarSesion()
  │       Navigator → MainLayout
  └─ NO: SnackBar error

Estado:

_usuario: String           // Username ingresado
_pin: String              // PIN ingresado
_mostrarPin: bool         // Toggle visibilidad
_cargando: bool           // Loading durante validación

2. home_screen.dart - Dashboard Principal ⭐

Propósito: Panel principal con datos en tiempo real

Características: ✅ Dos tarjetas lado a lado (Row layout) ✅ Animaciones con glow effect verde ✅ Lista de actividades expandible ✅ Actualización en tiempo real con StreamBuilder

Layout:

┌─────────────────────────────────┐
│     HomeScreen Container        │
├─────────────────┬───────────────┤
│  Stock General  │ Movimientos   │
│   (izquierda)   │    Hoy        │
│                 │  (derecha)    │
├─────────────────────────────────┤
│     Activity List (expandible)  │
│  - últimos 10 movimientos       │
│  - AnimatedOpacity              │
│  - SlideTransition              │
└─────────────────────────────────┘

Animaciones:

AnimationController
  ├─ Duración: 1 segundo
  ├─ Curve: Curves.easeInOut
  └─ Usado para: FadeTransition + glow

onNewStockChange callback
  └─ Dispara: animationController.forward()
  └─ Efecto: Tarjeta izquierda destella

onNewMovimiento callback
  └─ Dispara: animationController.forward()
  └─ Efecto: Tarjeta derecha destella

Datos Mostrados:

  • Stock General: Suma de cantidades en inventario
  • Movimientos Hoy: Conteo de movimientos en fecha actual
  • Actividades: Usuario, Tipo, Cantidad, Hora

Estado:

_animationController: AnimationController
_inventarioStream: Stream<List<Map>>
_movimientosStream: Stream<List<Map>>
_expanded: bool                     // Lista expandida?

3. stock_screen.dart - Gestor de Inventario ⭐ ORIGINAL

Propósito: Pantalla principal de gestión de stock

Funcionalidades: ✅ Vista Grid/List de productos ✅ Filtro por almacén ✅ Búsqueda de productos ✅ Transferencia entre almacenes ✅ Edición de cantidades ✅ Indicadores visuales de stock

Componentes:

AppBar
  ├─ Título: "Stock"
  ├─ Toggle Grid/List button
  └─ Filtro almacén selector

StreamBuilder<List<Map>>
  ├─ data: Grid/List de productos
  ├─ loading: Shimmer loader
  └─ error: Error widget

Grid/List View
  ├─ ProductCard/ProductRow
  ├─ Actions: Edit, Transfer
  └─ Indicadores de estado

Vista Grid:

Tarjetas de productos en cuadrícula
┌──────────┐ ┌──────────┐
│ Producto │ │ Producto │
│ Cantidad │ │ Cantidad │
│ Estado   │ │ Estado   │
└──────────┘ └──────────┘

Vista List:

Filas con más detalles
┌─────────────────────────────────────┐
│ Prod | Cant | Almacén | Acciones   │
├─────────────────────────────────────┤
│ Item 1 | 125 | Almacén 1| Edit/Xfer│

Indicadores de Stock:

cantidad >= cantidad_maxima * 0.8VERDE ✅
cantidad >= cantidad_minima        → NARANJA ⚠️
cantidad < cantidad_minima         → ROJO

Diálogos:

EditarCantidadDialog
  ├─ Ingresa nueva cantidad
  ├─ Valida > 0
  └─ Actualiza en BD

TransferenciaDialog (stock_transfer_sheet.dart)
  ├─ Almacén origen
  ├─ Almacén destino
  ├─ Cantidad
  └─ Motivo

Estado:

_vistaGrid: bool                    // Grid o List?
_filtroAlmacenId: int?             // Almacén seleccionado
_inventarioStream: Stream           // Datos en vivo
_ultimoInventario: List<Map>?      // Cache
_almacenes: List<Map>              // Almacenes disponibles

4. main_layout.dart - Contenedor Principal

Propósito: Navegación entre pantallas principales

Estructura:

Scaffold
  ├─ appBar (opcional)
  ├─ body: IndexedStack
  │  ├─ HomeScreen (index 0)
  │  └─ StockScreen (index 1)
  └─ bottomNavigationBar
     ├─ "Panel" icon (índice 0)
     └─ "Stock" icon (índice 1)

Características: ✅ Navegación persistent (mantiene estado) ✅ IndexedStack para no perder datos ✅ BottomNavigationBar con 2 tabs

Estado:

_selectedIndex: int = 0  // Tab seleccionado

Navegación:

BottomNavigationBar tap (index)
  └─ setState(() { _selectedIndex = index; })
  └─ IndexedStack cambia a ese widget

5. screen_Home.dart (OBSOLETO)

⚠️ Archivo antiguo no usado - ignorar


🎨 WIDGETS PERSONALIZADOS (lib/widgets/)

1. dashboard_card.dart - Tarjeta de Métrica

Propósito: Componente reutilizable para mostrar KPIs

Props:

titulo: String           // "Stock General"
valor: String           // "1,245"
color: Color            // #1B4332 (verde)
icono: IconData          // Icons.inventory_2
animado: bool?          // Mostrar animación?

Efectos Visuales:

  • Sombra verde glow
  • FadeTransition suave
  • Responsive sizing

2. stock_transfer_sheet.dart - Modal de Transferencia

Propósito: Bottom Sheet para transferencias de inventario

Campos:

  • Selector almacén origen
  • Selector almacén destino
  • TextField cantidad
  • TextField motivo

Validaciones: ✓ Cantidad > 0 ✓ Almacenes diferentes ✓ Stock disponible

Acción:

Usuario completa form
  ↓
Validar datos
  ↓
Crear registro en "movimientos"
  ↓
Actualizar "almacenes" origen/destino
  ↓
Cerrar dialog + mostrar success

3. realtime_animated_widget.dart - Wrapper Realtime

Propósito: Widget reutilizable que anima cuando llegan datos en tiempo real

Props:

channel: String          // 'almacenes'
tableName: String        // 'almacenes'
child: Widget            // Widget a animar

Funcionalidad:

  • Suscribe a cambios en tabla
  • Dispara animación glow
  • Refresca datos

4. animated_count.dart - Contador Animado

Propósito: Animar transición de números

Props:

value: int              // Valor final
duration: Duration      // Duración animación

Efecto:

Valor anterior: 100
Valor nuevo: 125
  ↓
Anima suavemente de 100 → 125

5. custom_navbar.dart - Barra Navegación (NO USADO)

Propósito: Widget de navegación personalizada

Estado: ⚠️ NO ACTUALMENTE EN USO

  • Se mantiene para referencia futura
  • System actual usa BottomNavigationBar en main_layout.dart

📊 MODELOS (lib/models/)

usuario_model.dart - Modelo de Usuario

Estructura:

class Usuario {
  final int id;
  final String nombre;
  final String rango;      // 'Admin', 'Owner', 'Empleado'
  final bool activo;
  final DateTime? fechaCreacion;
  
  // Getters útiles
  bool esAdmin() → nombre == 'Admin'
  bool esOwner() → nombre == 'Owner'
}

📝 main.dart - Punto de Entrada

Responsabilidad: Inicialización y configuración global

Flujo:

main()
  ├─ Supabase.initialize(url, anonKey)
  ├─ DashboardService.initializeRealtimeListeners() ⚠️
  ├─ Verificar sesión guardada
  ├─ runApp(MyApp)
  └─ Cargar LoginScreen o MainLayout

MyApp build()
  ├─ MaterialApp setup
  ├─ Tema global
  ├─ Rutas
  └─ Home screen según autenticación

Configuración Importante:

Supabase.initialize(
  url: 'https://xxxx.supabase.co',      // Tu URL
  anonKey: 'eyJ0eXAi...',               // Tu key
)

// CRÍTICO: Inicializar listeners aquí
DashboardService dashService = DashboardService();
dashService.initializeRealtimeListeners();

🔄 FLUJO DE DATOS GLOBAL

┌─────────────────────────────────────────────────────────┐
│                     INICIO APP                          │
│  main.dart → Supabase.initialize()                      │
│          → DashboardService.initializeRealtimeListeners()│
└────────────────────┬────────────────────────────────────┘
                     │
                ┌────▼─────────────────┐
                │  ¿Usuario logged in? │
                └────┬─────────┬───────┘
                     │         │
            Sí ◄─────┘         └────► No
            │                        │
            ▼                        ▼
        MainLayout              LoginScreen
         │                        │
    ┌────┼────┐              Ingresa Usuario+PIN
    │    │    │                │
Home│ Stock   │         AuthService.autenticar()
    │    │    │                │
    ▼    ▼    ▼         ¿Válido?
 Widgets│              ├─ SÍ: SessionService.guardarSesion()
 Stream │                    └─ Navega MainLayout
 Builder │              └─ NO: SnackBar error
        │
   REAL-TIME DATA
   PostgreSQL <─── Supabase Realtime (WebSocket)
        │
   DashboardService
        │
   StreamControllers
   inventarioStream
   movimientosStream
        │
    HomeScreen/StockScreen StreamBuilders
        │
   Actualiza UI + Animaciones

✅ RESUMEN ARCHIVO POR ARCHIVO

Archivo Tipo Líneas Propósito
main.dart Entrada ~50 Inicialización y rutas
Servicios
auth_service.dart Service ~100 Autenticación PIN
dashboard_service.dart Service ~400 Realtime core ⭐
session_service.dart Service ~80 Sesión local
supabase_service.dart Service ~100 Cliente Supabase
stock_transfer_service.dart Service ~150 Transferencias
Pantallas
login_screen.dart Screen ~180 Login UI
home_screen.dart Screen ~350 Dashboard principal
stock_screen.dart Screen ~1155 Gestor inventario
main_layout.dart Screen ~80 Navegación principal
Widgets
dashboard_card.dart Widget ~50 Tarjeta métrica
stock_transfer_sheet.dart Widget ~200 Modal transferencia
realtime_animated_widget.dart Widget ~150 Wrapper realtime
animated_count.dart Widget ~100 Contador animado
custom_navbar.dart Widget ~30 NavBar (no usado)
Modelos
usuario_model.dart Model ~40 Estructura usuario

🔧 REGLAS DE DESARROLLO

✅ HACER:

  • ✅ Usar StreamBuilder para datos dinámicos
  • ✅ Llamar dispose() en widgets stateful
  • ✅ Usar async/await para operaciones BD
  • ✅ Validar datos antes de guardar
  • ✅ Mostrar loading states
  • ✅ Capturar excepciones con try/catch

❌ NO HACER:

  • ❌ Modificar estado sin setState()
  • ❌ Olvidar dispose() de controllers
  • ❌ Hacer queries sin error handling
  • ❌ Almacenar datos sensibles en SharedPreferences sin encripción
  • ❌ Crear múltiples instancias de Supabase
  • ❌ Inicializar listeners más de una vez

Documento: Guía de Ficheros - Los Toldos ERP v1.0 Última actualización: 2024