Primer commit del sistema avantika sin cambios

This commit is contained in:
2026-01-06 19:42:24 -06:00
commit 3ae4be5957
7127 changed files with 440072 additions and 0 deletions

100
php8-migration/.env.example Normal file
View File

@@ -0,0 +1,100 @@
# Ejemplo de archivo .env para sistema multi-empresa
# COPIAR ESTE CONTENIDO A UN ARCHIVO .env EN LA RAÍZ DEL PROYECTO
# =============================================================================
# CONFIGURACIÓN DE BASES DE DATOS MULTI-EMPRESA
# =============================================================================
# NOTA: No incluir información sensible en el código
# Todas las credenciales deben estar en este archivo .env
# Base de datos Master (para autenticación y catálogos globales)
# Basado en estructura real: avantikads_nmgen
DB_MASTER_HOST=localhost
DB_MASTER_DATABASE=avantikads_nmgen
DB_MASTER_USER=root
DB_MASTER_PASSWORD=venomp
# Configuración general de conexión
DB_HOST=localhost
DB_PORT=3306
DB_CHARSET=utf8mb4
# Prefijo para bases de datos de empresas
# Patón real: avantikads_nm{empresaId}
DB_EMPRESA_PREFIX=avantikads_nm
DB_EMPRESA_USER=root
DB_EMPRESA_PASSWORD=venomp
# =============================================================================
# CONFIGURACIÓN DEL SISTEMA
# =============================================================================
# Rutas del sistema
DOC_ROOT=/var/www/html/ventas
WEB_ROOT=http://localhost
# Configuración SMTP (correos)
SMTP_HOST=smtp.gmail.com
SMTP_USER=tu_correo@gmail.com
SMTP_PASS=tu_password_app
SMTP_PORT=587
# Configuración PAC (Facturación electrónica)
USER_PAC=tu_usuario_pac
PW_PAC=tu_password_pac
# Configuración de paginación
ITEMS_PER_PAGE=20
# Rango de años válidos
MIN_YEAR=2025
MAX_YEAR=2030
# Modo de depuración (true/false)
DEBUG_MODE=false
# =============================================================================
# CONFIGURACIÓN ADICIONAL
# =============================================================================
# Configuración de sesión
SESSION_LIFETIME=3600
SESSION_PATH=/
SESSION_DOMAIN=
# Configuración de seguridad
ENCRYPTION_KEY=tu_clave_encriptacion_32_chars
JWT_SECRET=tu_jwt_secret_key_aqui
# Configuración de archivos
MAX_FILE_SIZE=10485760
ALLOWED_FILE_TYPES=pdf,xml,csv,xlsx
# Configuración de respaldos
BACKUP_PATH=/var/backups/ventas
AUTO_BACKUP=true
BACKUP_RETENTION_DAYS=30
# =============================================================================
# VARIABLES ESPECÍFICAS DEL NEGOCIO
# =============================================================================
# Tasas de impuestos (pueden venir de BD pero tener valores por defecto)
IVA_RATE=0.16
ISR_RATE=0.10
# Configuración de facturación
SERIE_FACTURA=A
SERIE_NOTA_CREDITO=B
FOLIO_INICIAL=1
# Información bancaria por defecto (actualizar según empresa)
BANK_NAME=Banamex
BANK_ACCOUNT=224996
BANK_CLABE=002100017902249960
BANK_BRANCH=0179
# Contacto por defecto
CONTACT_PHONE=(961) 10 5 58 20
CONTACT_EMAIL=contacto@empresa.com
CONTACT_WEB=www.empresa.com

View File

@@ -0,0 +1,136 @@
# Análisis de Base de Datos - Sistema Multi-Empresa
## Estructura de Bases de Datos Identificada
### Base de Datos Principal: `avantikads_nmgen`
**Propósito:** Base de datos master/global del sistema
**Tablas clave:**
- `empresa` - Catálogo de empresas (empresaId como PK)
- `usuario` - Usuarios del sistema con empresaId asociado
- Configuración global y shared resources
### Base de Datos de Empresa: `avantikads_nm15`
**Propósito:** Datos específicos de empresa con ID 15
**Contenido:** Tablas operativas como:
- `_prodSinInv` - Productos sin inventario
- Todas las tablas transaccionales de la empresa 15
## Arquitectura Multi-Empresa Confirmada
### Estructura Validada
```
avantikads_nmgen (Master DB)
├── empresa (empresaId, activo, datos generales)
├── usuario (usuarioId, empresaId, datos usuario)
└── [Tablas globales]
avantikads_nm{empresaId} (Empresa-specific DB)
├── _prodSinInv
├── productos
├── pedidos
├── ventas
└── [Todas las tablas operativas]
```
### Flujo de Autenticación y Selección
1. **Usuario inicia sesión** → Conexión a `avantikads_nmgen`
2. **Validación de credenciales** en tabla `usuario`
3. **Obtención de empresaId** del registro del usuario
4. **Conexión dinámica** a `avantikads_nm{empresaId}`
5. **Operaciones en BD de la empresa específica**
## Implicaciones para la Migración
### 1. Base de Datos Master Obligatoria
- **Mantener `avantikads_nmgen` como master**
- **Función**: Autenticación y routing de empresas
- **Acceso**: Todos los usuarios se conectan primero aquí
### 2. Configuración Dinámica por empresaId
- **Patón confirmado**: `SQL_DATABASE2 . empresaId`
- **Ejemplo real**: `avantikads_nm15` para empresaId = 15
- **Necesidad**: Routing automático basado en usuario
### 3. Archivos de Configuración Adicionales
#### `/base_datos/` - Esquemas de Referencia
- `avantikads_nmgen.sql` - Estructura master
- `avantikads_nm15.sql` - Ejemplo estructura empresa
- **Uso**: Validar estructura durante migración
## Actualizaciones al Plan de Migración
### Configuración .env - Ajustes Necesarios
#### Variables de Conexión Master
```env
# Base de datos Master (global para autenticación)
DB_MASTER_HOST=localhost
DB_MASTER_DATABASE=avantikads_nmgen
DB_MASTER_USER=admin_user
DB_MASTER_PASSWORD=secure_master_password
# Prefijo para bases de datos de empresas
DB_EMPRESA_PREFIX=avantikads_nm
```
#### Lógica de Conexión por Empresa
```php
// Función actualizada en ejemplo-env-config.php
public static function getDatabaseConfig($empresaId) {
$masterConfig = [
'database' => $_ENV['DB_MASTER_DATABASE'] . '_' . $empresaId
];
// Validar que exista la base de datos
if (!self::validateDatabaseExists($masterConfig['database'])) {
throw new Exception("Base de datos no existe: " . $masterConfig['database']);
}
return $masterConfig;
}
```
### Validaciones de Migración
#### 1. Verificación de Estructura
- Comparar esquemas actuales con archivos en `/base_datos/`
- Validar que todas las tablas requeridas existan
- Verificar integridad de datos por empresa
#### 2. Testing Multi-Empresa
- Crear usuarios de prueba para diferentes empresas
- Validar routing correcto de conexiones
- Probar aislamiento de datos entre empresas
#### 3. Migración Gradual
- Iniciar con empresa 15 (tenemos su esquema)
- Validar funcionamiento completo
- Extender a otras empresas
## Archivos de Migración Adicionales
### `/base_datos/` - Incluir en plan
- **Mantener como referencia** durante migración
- **Usar para validación** de estructura post-migración
- **Documentar esquema** actual para comparación
### Scripts de Migración (Sugeridos)
- `migrar_master_db.php` - Migración base master
- `validar_esquemas.php` - Validación contra archivos SQL
- `test_multi_empresa.php` - Testing routing de conexiones
## Impacto en Estimación de Tiempo
### Tiempo Adicional Requerido
- **Validación de esquemas:** +5-10 horas
- **Testing multi-empresa:** +10-15 horas
- **Migración de datos:** +8-12 horas
- **Total adicional:** +23-37 horas
### Estimación Actualizada: 113-162 horas
## Recomendación
**La estructura `/base_datos/` confirma la arquitectura multi-empresa.**
El plan actualizado debe incluir validación específica contra estos esquemas para asegurar migración correcta.

View File

@@ -0,0 +1,99 @@
# Análisis de Sistema para Migración a PHP 8
## Información General del Sistema
- **Directorio Principal**: /var/www/html/ventas
- **Versión PHP Actual**: 8.3.6
- **Tipo de Sistema**: Sistema de ventas/inventario
## Estructura Identificada
### Archivos PHP Principales (excluyendo templates_c y tcpdf)
- `index.php` - Archivo de entrada principal
- `libraries.php` - Carga de clases y librerías
- Múltiples archivos en directorios:
- `ajax/` - Endpoints AJAX
- `classes/` - Clases del sistema
- `pdf/` - Generación de PDFs
- `properties/` - Configuración
### Librerías Detectadas
- Smarty (motor de plantillas)
- NuSOAP (webservices)
- PHPMailer
- TCPDF
- FPDI
- Clases personalizadas del sistema
## Potenciales Problemas de Compatibilidad con PHP 8
### 1. Funciones Obsoletas/Eliminadas
- `mysql_*` functions (si existen)
- `ereg*` functions
- `each()` function
- `create_function()`
- Magic quotes (si están siendo usadas)
### 2. Cambios en Manejo de Errores
- Concatenación con null
- Operadores de comparación no estricta
- Orden de evaluación de operandos
### 3. Cambios en Tipado
- Declaraciones de tipo más estrictas
- Retorno por referencia
- Parámetros por referencia
### 4. Extensiones
- mcrypt (eliminada en PHP 8)
- mysql (eliminada)
- mssql (eliminada)
## Archivos Críticos a Analizar
### Nivel 1 - Core del Sistema
1. `index.php` - Punto de entrada
2. `libraries.php` - Carga de dependencias
3. `properties/config.php` - Configuración
4. `init.php` - Inicialización
### Nivel 2 - Clases Principales
- `classes/db.class.php` - Conexión a BD
- `classes/user.class.php` - Gestión de usuarios
- `classes/producto.class.php` - Gestión de productos
- `classes/pedido.class.php` - Gestión de pedidos
### Nivel 3 - Endpoints AJAX
- Todos los archivos en `ajax/`
### Nivel 4 - Generación de Reportes
- Archivos en `pdf/`
- Archivos en `reportes/`
## Plan de Migración Propuesto
### Fase 1: Análisis Detallado
1. Escanear todos los archivos PHP en busca de:
- Funciones obsoletas
- Sintaxis incompatible
- Extensiones eliminadas
### Fase 2: Creación de Backups
1. Backup completo del sistema
2. Versionado de archivos modificados
### Fase 3: Migración Gradual
1. Actualizar configuración PHP
2. Reemplazar funciones obsoletas
3. Actualizar sintaxis
4. Probar funcionalidad
### Fase 4: Validación
1. Pruebas unitarias
2. Pruebas de integración
3. Pruebas de sistema completo
## Recomendaciones Iniciales
1. NO hacer modificaciones sin aprobación previa
2. Crear un entorno de prueba aislado
3. Hacer backup completo antes de cualquier cambio
4. Documentar todos los cambios realizados

View File

@@ -0,0 +1,97 @@
# Lista de Archivos Críticos para Migración PHP 8 y Configuración .env
## Nivel CRÍTICO - Modificación Obligatoria
### 1. Archivos de Configuración
- `config.php` - ELIMINAR y reemplazar por configuración .env
- `.env` - CREAR con todas las credenciales seguras
- `init.php` - Actualizar para cargar configuración .env
### 2. Base de Datos y Conexiones
- `classes/db.class.php` - REESCRIBIR completamente con mysqli y multi-empresa
- `classes/util.class.php` - Actualizar DBSelect() y configuración
- `libraries.php` - Actualizar includes de configuración
### 3. Autenticación y Sesiones
- `index.php` - Actualizar lógica de sesión y empresaId
- `login.php` - Implementar determinación de empresaId desde usuario
- Cualquier archivo que maneje `$_SESSION['empresaId']` (100+ archivos)
## Nivel ALTO - Funciones Obsoletas
### Funciones Eliminadas en PHP 8
- `classes/util.class.php:575` - ereg_replace()
- `ajax/cuentas-pagar.php:18` - split()
- `ajax/evaluar-pedidos.php:160,360` - split()
- `classes/comprobante.class.php:350` - split()
- `pdf/domdf.php:227` - split()
### each() function (7 archivos)
- `pdf/include/style.cls.php:790`
- `pdf/fpdf.php:1565`
- `pdf/fpdi.php:435`
- `classes/class.phpmailer.php:1645`
- `classes/class.smtp.php:388,417`
- `classes/comprobante.class.php:1465`
### create_function() (4 archivos)
- `pdf/include/frame_reflower.cls.php:233`
- `pdf/include/text_frame_reflower.cls.php:380,388,416`
## Nivel MEDIO - Sintaxis Incompatible
### Llaves para acceso a strings/arrays (100+ archivos)
Principalmente en:
- `tcpdf/barcodes.php` - 50+ ocurrencias
- `classes/json.class.php` - 20+ ocurrencias
- Múltiples archivos en `tcpdf/`, `pdf/`, `libs/`
### list() assignments (98+ ocurrencias)
- `pdf/include/gd_adapter.cls.php` - Múltiples líneas
- `pdf/include/functions.inc.php:725`
- Múltiples archivos PDF
## Archivos por Directorio (Priorizados)
### / (Raíz)
- `index.php` - Entrada principal
- `config.php` - Eliminar
- `libraries.php` - Actualizar
- `init.php` - Actualizar
### /classes/
- `db.class.php` - Reescribir completamente
- `util.class.php` - Actualizar DBSelect()
- `json.class.php` - Sintaxis llaves
- `class.phpmailer.php` - each() function
- `comprobante.class.php` - split(), each()
- `class.smtp.php` - each() function
### /ajax/
- `cuentas-pagar.php` - split() function
- `evaluar-pedidos.php` - split() function
- Todos los archivos con $_SESSION['empresaId'] (50+)
### /modules/
- Todos los archivos con $_SESSION['empresaId'] (100+)
- Archivos de facturación, inventario, usuarios, etc.
### /pdf/
- `fpdf.php` - each() function
- `domdf.php` - split() function
- `fpdi.php` - each() function
- Todos los archivos con sintaxis de llaves
### /tcpdf/
- `barcodes.php` - 50+ sintaxis de llaves
- Múltiples archivos con sintaxis incompatible
## Total Estimado
- **Archivos críticos:** 10-15
- **Archivos con funciones obsoletas:** 20+
- **Archivos con sintaxis incompatible:** 100+
- **Archivos con $_SESSION['empresaId']:** 100+
## Nota Importante
Esta lista representa el mínimo de archivos que deben ser modificados.
Durante el proceso de migración pueden descubrirse archivos adicionales que requieran cambios.

View File

@@ -0,0 +1,195 @@
<?php
/**
* Ejemplo de migración de db.class.php de MySQL a MySQLi
*
* Este archivo muestra cómo migrar las funciones mysql_* obsoletas
* a mysqli_* compatible con PHP 8
*/
// Versión original con mysql_* (NO COMPATIBLE CON PHP 8)
/*
class DB {
private $connection;
function Open($host, $user, $pass, $db) {
$this->connection = mysql_connect($host, $user, $pass);
mysql_select_db($db, $this->connection);
}
function Query($sql) {
return mysql_query($sql, $this->connection);
}
function FetchAssoc($result) {
return mysql_fetch_assoc($result);
}
function NumRows($result) {
return mysql_num_rows($result);
}
}
*/
// Versión actualizada con mysqli_* (COMPATIBLE CON PHP 8)
class DB {
private $connection;
/**
* Abre conexión a la base de datos
* @param string $host Host de la base de datos
* @param string $user Usuario de la base de datos
* @param string $pass Contraseña de la base de datos
* @param string $db Nombre de la base de datos
* @return boolean
*/
function Open($host, $user, $pass, $db) {
$this->connection = mysqli_connect($host, $user, $pass, $db);
if (!$this->connection) {
// Manejo de error compatible
die("Error de conexión: " . mysqli_connect_error());
return false;
}
// Establecer charset para compatibilidad UTF-8
mysqli_set_charset($this->connection, "utf8");
return true;
}
/**
* Ejecuta una consulta SQL
* @param string $sql Consulta SQL
* @return mysqli_result|false
*/
function Query($sql) {
$result = mysqli_query($this->connection, $sql);
if ($result === false) {
// Manejo de error compatible
error_log("Error en consulta: " . mysqli_error($this->connection));
error_log("SQL: " . $sql);
}
return $result;
}
/**
* Obtiene una fila como arreglo asociativo
* @param mysqli_result $result Resultado de consulta
* @return array|null
*/
function FetchAssoc($result) {
return mysqli_fetch_assoc($result);
}
/**
* Obtiene el número de filas de un resultado
* @param mysqli_result $result Resultado de consulta
* @return int
*/
function NumRows($result) {
return mysqli_num_rows($result);
}
/**
* Obtiene un valor específico de una fila
* @param mysqli_result $result Resultado de consulta
* @param int $row Número de fila
* @param mixed $field Campo (nombre o índice)
* @return mixed
*/
function Result($result, $row, $field = 0) {
mysqli_data_seek($result, $row);
$row_data = mysqli_fetch_array($result);
return is_numeric($field) ? $row_data[$field] : $row_data[$field];
}
/**
* Obtiene el último ID insertado
* @return int
*/
function InsertId() {
return mysqli_insert_id($this->connection);
}
/**
* Obtiene el número de filas afectadas
* @return int
*/
function AffectedRows() {
return mysqli_affected_rows($this->connection);
}
/**
* Libera memoria del resultado
* @param mysqli_result $result Resultado a liberar
*/
function FreeResult($result) {
mysqli_free_result($result);
}
/**
* Escapa caracteres especiales para prevenir SQL injection
* @param string $string String a escapar
* @return string
*/
function Escape($string) {
return mysqli_real_escape_string($this->connection, $string);
}
/**
* Cierra la conexión a la base de datos
*/
function Close() {
if ($this->connection) {
mysqli_close($this->connection);
}
}
/**
* Obtiene el último error de MySQL
* @return string
*/
function GetError() {
return mysqli_error($this->connection);
}
/**
* Inicia una transacción
*/
function BeginTransaction() {
mysqli_begin_transaction($this->connection);
}
/**
* Confirma una transacción
*/
function Commit() {
mysqli_commit($this->connection);
}
/**
* Revierte una transacción
*/
function Rollback() {
mysqli_rollback($this->connection);
}
// Destructor para asegurar cierre de conexión
public function __destruct() {
$this->Close();
}
}
// Ejemplo de uso (similar al original)
/*
$db = new DB();
$db->Open("localhost", "usuario", "password", "basededatos");
$result = $db->Query("SELECT * FROM usuarios");
while ($row = $db->FetchAssoc($result)) {
echo $row['nombre'] . "\n";
}
$db->FreeResult($result);
*/

View File

@@ -0,0 +1,257 @@
<?php
/**
* Ejemplo de configuración segura con archivo .env
* Compatible con PHP 8 y arquitectura multi-empresa
*/
// Cargar variables de entorno desde .env
function loadEnv($path = '.env') {
if (!file_exists($path)) {
throw new Exception("Archivo .env no encontrado en: $path");
}
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (strpos(trim($line), '#') === 0) continue;
if (strpos($line, '=') !== false) {
list($key, $value) = explode('=', $line, 2);
$key = trim($key);
$value = trim($value);
// Remover comillas si existen
$value = trim($value, '"\'');
// Establecer como variable de entorno
$_ENV[$key] = $value;
putenv("$key=$value");
}
}
}
// Cargar configuración desde .env
try {
loadEnv(__DIR__ . '/.env');
} catch (Exception $e) {
die("Error cargando configuración: " . $e->getMessage());
}
/**
* Clase de configuración centralizada
*/
class Config {
/**
* Obtiene configuración de base de datos por empresaId
* @param int $empresaId ID de la empresa
* @return array Configuración de BD para la empresa
*/
public static function getDatabaseConfig($empresaId) {
// Configuración base desde .env
$baseConfig = [
'host' => $_ENV['DB_HOST'] ?? 'localhost',
'port' => $_ENV['DB_PORT'] ?? '3306',
'charset' => $_ENV['DB_CHARSET'] ?? 'utf8mb4'
];
// Mapeo dinámico de empresas basado en estructura real del sistema
// Patón:avantikads_nm{empresaId} donde empresaId viene del usuario
$prefix = $_ENV['DB_EMPRESA_PREFIX'] ?? 'avantikads_nm';
$database = $prefix . $empresaId;
// Validar que exista la base de datos
if (!self::validateDatabaseExists($database)) {
throw new Exception("Base de datos no existe para empresaId: $empresaId (DB: $database)");
}
$empresas = [
$empresaId => [
'database' => $database,
'user' => $_ENV['DB_EMPRESA_USER'] ?? $_ENV['DB_USER'],
'password' => $_ENV['DB_EMPRESA_PASSWORD'] ?? $_ENV['DB_PASSWORD']
]
];
// Validar que exista configuración para la empresa
if (!isset($empresas[$empresaId])) {
throw new Exception("No existe configuración para empresaId: $empresaId");
}
return array_merge($baseConfig, $empresas[$empresaId]);
}
/**
* Valida que exista la base de datos para una empresa
* @param string $database Nombre de la base de datos
* @return bool
*/
public static function validateDatabaseExists($database) {
try {
$host = $_ENV['DB_HOST'] ?? 'localhost';
$user = $_ENV['DB_USER'] ?? 'root';
$password = $_ENV['DB_PASSWORD'] ?? '';
// Conexión sin especificar BD para verificar existencia
$mysqli = new mysqli($host, $user, $password);
if ($mysqli->connect_error) {
return false;
}
// Verificar si la base de datos existe
$result = $mysqli->query("SHOW DATABASES LIKE '$database'");
$exists = $result->num_rows > 0;
$mysqli->close();
return $exists;
} catch (Exception $e) {
error_log("Error validando BD $database: " . $e->getMessage());
return false;
}
}
/**
* Obtiene empresaId del usuario actual
* @param int $userId ID del usuario
* @return int|null empresaId del usuario
*/
public static function getEmpresaIdByUserId($userId) {
// Conexión a base de datos master para obtener empresaId del usuario
$masterConfig = [
'host' => $_ENV['DB_MASTER_HOST'] ?? $_ENV['DB_HOST'],
'database' => $_ENV['DB_MASTER_DATABASE'] ?? 'avantikads_nmgen',
'user' => $_ENV['DB_MASTER_USER'] ?? $_ENV['DB_USER'],
'password' => $_ENV['DB_MASTER_PASSWORD'] ?? $_ENV['DB_PASSWORD']
];
try {
$mysqli = new mysqli(
$masterConfig['host'],
$masterConfig['user'],
$masterConfig['password'],
$masterConfig['database']
);
if ($mysqli->connect_error) {
throw new Exception("Error conexión master DB: " . $mysqli->connect_error);
}
$stmt = $mysqli->prepare("SELECT empresaId FROM usuario WHERE usuarioId = ? LIMIT 1");
$stmt->bind_param("i", $userId);
$stmt->execute();
$result = $stmt->get_result();
if ($row = $result->fetch_assoc()) {
return (int)$row['empresaId'];
}
return null;
} catch (Exception $e) {
error_log("Error obteniendo empresaId: " . $e->getMessage());
return null;
}
}
/**
* Obtiene configuración general del sistema
* @return array Configuración general
*/
public static function getSystemConfig() {
return [
'doc_root' => $_ENV['DOC_ROOT'] ?? '/var/www/html/ventas',
'web_root' => $_ENV['WEB_ROOT'] ?? 'http://localhost',
'smtp_host' => $_ENV['SMTP_HOST'] ?? '',
'smtp_user' => $_ENV['SMTP_USER'] ?? '',
'smtp_port' => $_ENV['SMTP_PORT'] ?? '587',
'items_per_page' => $_ENV['ITEMS_PER_PAGE'] ?? '20',
'min_year' => $_ENV['MIN_YEAR'] ?? '2025',
'max_year' => $_ENV['MAX_YEAR'] ?? '2030',
'debug_mode' => $_ENV['DEBUG_MODE'] ?? 'false'
];
}
}
/**
* Clase de base de datos mejorada con soporte multi-empresa
*/
class DatabaseManager {
private $connections = [];
private $currentEmpresaId = null;
/**
* Obtiene conexión para empresa específica
* @param int $empresaId ID de la empresa
* @return mysqli
*/
public function getConnection($empresaId) {
if (!isset($this->connections[$empresaId])) {
$config = Config::getDatabaseConfig($empresaId);
$mysqli = new mysqli(
$config['host'] . ':' . $config['port'],
$config['user'],
$config['password'],
$config['database']
);
if ($mysqli->connect_error) {
throw new Exception("Error conexión empresa $empresaId: " . $mysqli->connect_error);
}
$mysqli->set_charset($config['charset']);
$this->connections[$empresaId] = $mysqli;
}
return $this->connections[$empresaId];
}
/**
* Establece empresaId actual basado en usuario en sesión
* @param int $userId ID del usuario en sesión
*/
public function setEmpresaByUser($userId) {
$this->currentEmpresaId = Config::getEmpresaIdByUserId($userId);
if (!$this->currentEmpresaId) {
throw new Exception("No se pudo determinar empresaId para usuario: $userId");
}
}
/**
* Obtiene conexión para empresa actual
* @return mysqli
*/
public function getCurrentConnection() {
if (!$this->currentEmpresaId) {
throw new Exception("No hay empresaId establecido");
}
return $this->getConnection($this->currentEmpresaId);
}
}
// Ejemplo de inicialización
/*
try {
// Cargar configuración
$dbManager = new DatabaseManager();
// Establecer empresa basado en usuario en sesión
if (isset($_SESSION['userId'])) {
$dbManager->setEmpresaByUser($_SESSION['userId']);
}
// Obtener conexión para la empresa del usuario
$db = $dbManager->getCurrentConnection();
// Ejecutar consulta
$result = $db->query("SELECT * FROM productos");
} catch (Exception $e) {
error_log("Error en inicialización: " . $e->getMessage());
die("Error del sistema");
}
*/
?>

View File

@@ -0,0 +1,120 @@
# Plan de Migración PHP 8 - Archivos y Cambios Específicos
## Requisitos Adicionales del Cliente
### Configuración Segura con .env
- **Migrar toda configuración sensible a archivo .env**
- **Eliminar credenciales de bases de datos del código**
- **Usar empresaId del usuario para determinar conexión**
### Arquitectura Multi-Empresa
- **Los usuarios tienen campo empresaId en su registro**
- **La conexión a BD debe seleccionarse dinámicamente por empresaId**
- **No debe existir información de bases de datos fuera del .env**
## Archivos Creados para la Migración
### 1. `/php8-migration/analisis-sistema.md`
- Análisis general del sistema
- Identificación de estructura y componentes
- Evaluación de librerías utilizadas
### 2. `/php8-migration/reporte-problemas.md`
- Reporte detallado de incompatibilidades
- Priorización de problemas
- Estimación de tiempos y riesgos
### 3. `/php8-migration/ejemplo-db-mysqli.php`
- Ejemplo de migración de MySQL a MySQLi
- Código compatible con PHP 8
### 4. `/php8-migration/ejemplo-env-config.php`
- Ejemplo de configuración con archivo .env
- Gestión multi-empresa basada en usuario
## Cambios Críticos Requeridos
### Nivel URGENTE - Configuración y Base de Datos
#### 1. Migrar a Configuración .env
**Archivo:** `config.php` (eliminar completamente)
- **Eliminar todas las credenciales del código**
- **Crear archivo .env con variables seguras**
- **Implementar clase Config para gestión centralizada**
- **Impacto:** Seguridad y mantenibilidad
#### 2. Implementar Conexión Multi-Empresa
**Archivos:** `classes/db.class.php`, `classes/util.class.php`
- **Reemplazar 13 funciones mysql_* por mysqli***
- **Implementar gestión de empresas basada en empresaId del usuario**
- **Crear DatabaseManager para conexiones dinámicas**
- **Impacto:** Sistema completo sin base de datos
#### 3. Actualizar Lógica de Selección de BD
**Archivos:** 100+ archivos con `$_SESSION['empresaId']`
- **Reemplazar `SQL_DATABASE2.$empresaId` por conexión dinámica**
- **Usar DatabaseManager para obtener conexión específica**
- **Impacto:** Funcionalidad multi-empresa completa
### Nivel ALTO - Funciones Eliminadas
1. **`classes/util.class.php:575`** - `ereg_replace()``preg_replace()`
2. **`ajax/cuentas-pagar.php:18`** - `split()``explode()`
3. **`ajax/evaluar-pedidos.php:160,360`** - `split()``explode()`
4. **7 archivos con `each()`** - Reemplazar por `foreach`
5. **4 archivos con `create_function()`** - Reemplazar por funciones anónimas
### Nivel MEDIO - Sintaxis
1. **100+ ocurrencias de `$string{index}`**`$string[index]`
2. **98+ ocurrencias de `list()` incorrecto** → sintaxis moderna
3. **Múltiples asignaciones por referencia** → revisión y limpieza
## Estructura de Archivos de Migración
```
php8-migration/
├── analisis-sistema.md # Análisis general
├── reporte-problemas.md # Problemas identificados
├── ejemplo-db-mysqli.php # Ejemplo de migración DB
├── ejemplo-env-config.php # Ejemplo configuración .env
├── .env.example # Plantilla archivo .env
├── plan-ejecucion.md # Plan actualizado (este archivo)
└── archivos-criticos.txt # Lista de archivos críticos
```
## Recomendaciones de Seguridad
### Antes de Cualquier Cambio:
1.**Backup completo del sistema**
2.**Crear entorno de prueba aislado**
3.**Documentar versión actual funcional**
4.**Lista de todos los usuarios y procesos activos**
### Durante la Migración:
1.**Cambiar un módulo a la vez**
2.**Probar cada cambio inmediatamente**
3.**Mantener log detallado de modificaciones**
4.**No aplicar cambios a producción sin pruebas**
### Después de Cada Cambio:
1.**Ejecutar pruebas unitarias**
2.**Verificar funcionalidad completa**
3.**Comprobar rendimiento**
4.**Validar que no hay regresiones**
## Próximos Pasos Sugeridos
1. **Aprobación del plan:** Revisar y aprobar este análisis
2. **Preparación del entorno:** Configurar entorno de pruebas
3. **Backup completo:** Realizar backup integral del sistema
4. **Migración fase 1:** Actualizar capa de base de datos (MySQL → MySQLi)
5. **Migración fase 2:** Reemplazar funciones eliminadas
6. **Migración fase 3:** Actualizar sintaxis
7. **Pruebas finales:** Validación completa del sistema
## Estado Actual
-**NO COMPATIBLE** con PHP 8
- ⚠️ **Requiere migración completa** antes de actualizar PHP
- 🔴 **Alto riesgo** de pérdida total de funcionalidad
- 📋 **Plan detallado** disponible para ejecución
**ADVERTENCIA:** No actualizar la versión de PHP sin completar todos los cambios de migración.

View File

@@ -0,0 +1,109 @@
# Reporte Detallado de Problemas de Compatibilidad con PHP 8
## Resumen Ejecutivo
El sistema actual **NO es compatible con PHP 8** debido a múltiples problemas críticos que requieren actualización inmediata.
## Problemas Críticos Identificados
### 1. Funciones MySQL Obsoletas (Nivel CRÍTICO)
**Archivo:** `classes/db.class.php`
- **Líneas afectadas:** 87, 88, 103, 107, 117, 133, 147, 154, 165, 178, 189, 203, 212
- **Funciones a reemplazar:**
- `mysql_connect()``mysqli_connect()` o `PDO::__construct()`
- `mysql_select_db()``mysqli_select_db()` o parte de PDO DSN
- `mysql_query()``mysqli_query()` o `PDO::query()`
- `mysql_fetch_assoc()``mysqli_fetch_assoc()` o `PDO::fetch()`
- `mysql_num_rows()``mysqli_num_rows()` o método PDO equivalente
- `mysql_result()``mysqli_data_seek()` + `mysqli_fetch_row()`
- `mysql_insert_id()``mysqli_insert_id()` o `PDO::lastInsertId()`
- `mysql_affected_rows()``mysqli_affected_rows()`
- `mysql_free_result()``mysqli_free_result()`
- `mysql_fetch_array()``mysqli_fetch_array()`
### 2. Funciones Eliminadas en PHP 8 (Nivel ALTO)
#### ereg_replace()
**Archivo:** `classes/util.class.php:575`
- **Reemplazo:** `preg_replace('/patron/', 'reemplazo', $string)`
#### each() function
**Archivos afectados (7 total):**
- `pdf/include/style.cls.php:790`
- `pdf/fpdf.php:1565`
- `pdf/fpdi.php:435`
- `classes/class.phpmailer.php:1645`
- `classes/class.smtp.php:388,417`
- `classes/comprobante.class.php:1465`
- **Reemplazo:** Estructuras `foreach` o `reset() + key() + current()`
#### create_function()
**Archivos afectados (4 total):**
- `pdf/include/frame_reflower.cls.php:233`
- `pdf/include/text_frame_reflower.cls.php:380,388,416`
- **Reemplazo:** Funciones anónimas o closures
#### split() function
**Archivos afectados:**
- `pdf/domdf.php:227`
- `classes/comprobante.class.php:350`
- `ajax/evaluar-pedidos.php:160,360`
- `ajax/cuentas-pagar.php:18`
- **Reemplazo:** `explode()` (para separadores simples) o `preg_split()` (para expresiones regulares)
### 3. Sintaxis Incompatible (Nivel MEDIO)
#### Llaves para acceso a strings/arrays
**Patrón:** `$string{index}``$string[index]`
**Archivos con más incidencias:**
- `tcpdf/barcodes.php` (50+ ocurrencias)
- `classes/json.class.php` (20+ ocurrencias)
- Múltiples archivos en tcpdf/, pdf/, libs/
#### Asignaciones list() con elementos vacíos
**Ejemplos problemáticos:**
```php
// Incompatible
list($x1,,$x2) = $array;
list(,$y2,,,,$y1) = $array;
// Compatible
list($x1, $_, $x2) = $array;
list($_, $y2, $_, $_, $_, $y1) = $array;
// O mejor aún: usar array_slice() o destructuring moderno
```
## Plan de Acción Priorizado
### Fase 1: Urgente (Funciones MySQL)
1. **Crear nueva clase de base de datos compatible con mysqli/PDO**
2. **Actualizar todas las llamadas a funciones mysql_***
3. **Probar exhaustivamente la conexión y consultas**
### Fase 2: Funciones Eliminadas
1. **Reemplazar ereg_replace() → preg_replace()**
2. **Convertir each() → foreach()**
3. **Actualizar create_function() → funciones anónimas**
4. **Cambiar split() → explode() / preg_split()**
### Fase 3: Sintaxis
1. **Actualizar sintaxis de llaves**
2. **Corregir asignaciones list()**
3. **Revisar asignaciones por referencia**
## Estimación de Tiempo
- **Fase 1 (MySQL):** 20-30 horas
- **Fase 2 (Funciones):** 15-20 horas
- **Fase 3 (Sintaxis):** 10-15 horas
- **Pruebas:** 15-20 horas
- **Total:** 60-85 horas
## Riesgos
- **Alto:** Corrupción de datos durante migración MySQL
- **Medio:** Pérdida de funcionalidad en reportes PDF
- **Bajo:** Problemas de rendimiento temporales
## Recomendaciones
1. **NO migrar sin actualizar completamente**
2. **Crear entorno de prueba aislado**
3. **Backup completo antes de cambios**
4. **Migración incremental por módulos**