# Utilidades y Funciones Helper

## Utils.php

Ubicación: `app/Commands/Utils.php`

### Funciones de Formateo

#### formatNumber($number, $precision = 2, $down = false)
Formatea números para XML CFDI.

```php
// Uso normal (redondeo)
Utils::formatNumber(16.666, 2);  // "16.67"

// Truncar (sin redondeo)
Utils::formatNumber(16.666, 2, true);  // "16.66"

// Para tasas (6 decimales)
Utils::formatNumber(0.16, 6);  // "0.160000"
```

**Características**:
- Elimina comas, símbolos $ y comillas
- Opción de truncar en lugar de redondear
- Formato sin separador de miles
- Punto decimal

#### truncarDosDecimales($numero)
Trunca a 2 decimales sin redondear.

```php
Utils::truncarDosDecimales(16.666);  // 16.66
```

#### deleteExtraSpaces($string)
Elimina espacios extras y normaliza texto.

```php
Utils::deleteExtraSpaces("Texto   con    espacios");  // "Texto con espacios"
```

---

### Funciones de Certificados

#### validateCerVsKeyFile($cerFile, $keyFile)
Valida que el certificado y la llave privada coincidan.

```php
$isValid = Utils::validateCerVsKeyFile("cert.pem", "key.pem");
```

**Proceso**:
1. Extrae módulo del certificado con OpenSSL
2. Extrae módulo de la llave privada con OpenSSL
3. Compara los hashes MD5
4. Retorna true si coinciden

#### derToPem($der_data, $type='CERTIFICATE')
Convierte certificado DER a formato PEM.

```php
$pem = Utils::derToPem($derData, 'CERTIFICATE');
```

---

### Funciones de Facturación

#### obtenerDatosFactura($id_factura, $id_proveedor)
Obtiene todos los datos de una factura desde la base de datos.

**Retorna**:
- Datos generales de factura
- IdDoc (serie, folio, UUID)
- Totales
- Conceptos con impuestos
- Impuestos generales (resumen)
- Facturas relacionadas
- Complementos (nómina, pagos, comercio, carta porte, etc.)

**Procesamiento**:
1. Query SQL con joins de todas las tablas relacionadas
2. Carga de conceptos con relaciones (impuestos, alumno, unidad, clave)
3. Agregación de impuestos por tipo y tasa
4. Procesamiento de complementos específicos
5. Cálculos de nómina (si aplica)
6. Carga de pagos con impuestos por documento relacionado

```php
$datosFactura = Utils::obtenerDatosFactura(123, 456);

// Estructura retornada:
$datosFactura->id_factura
$datosFactura->serie
$datosFactura->folio
$datosFactura->uUID
$datosFactura->conceptos = [...]
$datosFactura->impuestosTrasladados = [...]
$datosFactura->impuestosRetenidos = [...]
$datosFactura->comercio (si existe)
$datosFactura->cartaPorte (si existe)
$datosFactura->nomina (si existe)
$datosFactura->pagos (si existe)
// etc.
```

**Uso principal**: Regeneración de XML para reimpresión o cancelación

---

### Funciones de Nómina

Dentro de `obtenerDatosFactura()` se procesa la nómina si `tipoDeComprobante == 'N'`:

**Cálculos**:
- Total de percepciones (gravadas + exentas)
- Total de deducciones
- Total de otros pagos
- Clasificación de percepciones (sueldos, separación, jubilación)
- Clasificación de deducciones (ISR, otras)
- Antigüedad en formato ISO 8601 (semanas)

**Ejemplo de antigüedad**:
```php
$datetime1 = new DateTime('2020-01-01');
$datetime2 = new DateTime('2024-01-15');
$interval = $datetime1->diff($datetime2);
$antiguedad = $interval->format('%R%a');
$antiguedad = str_replace('+', '', $antiguedad);
$antiguedad = $antiguedad / 7;
$antiguedad = floor($antiguedad);
$receptorN['Antigüedad'] = "P" . $antiguedad . "W";  // P208W
```

---

### Funciones de Encriptación

#### encryptData($data)
Encripta datos usando AES.

```php
$encrypted = Utils::encryptData("texto a encriptar");
```

#### decryptData($data)
Desencripta datos.

```php
$decrypted = Utils::decryptData($encrypted);
```

**Clave**: Definida en `$ENCRYPTION_KEY = "SergioEvaAlePau01"`

---

### Funciones Auxiliares

#### randomPass($length = 12)
Genera contraseña aleatoria.

```php
$password = Utils::randomPass(16);
```

#### genUuid()
Genera UUID v4.

```php
$uuid = Utils::genUuid();  // "550e8400-e29b-41d4-a716-446655440000"
```

#### saveBase64File($data, $filename)
Guarda archivo desde base64.

```php
Utils::saveBase64File($base64Data, '/path/to/file.pdf');
```

#### headers($xmlString, $soapAction, $token)
Genera headers para peticiones SOAP.

```php
$headers = Utils::headers($xmlString, "http://action", $token);
```

#### xmlToArray($xml)
Convierte XML a array.

```php
$array = Utils::xmlToArray($xmlString);
```

---

### Funciones Específicas de Carta Porte

#### getIDCCP($proveedor)
Genera ID único para Carta Porte.

```php
$idccp = Utils::getIDCCP($proveedor);  // "CCC20240115001"
```

**Formato**: CCC + YYYYMMDD + Consecutivo

---

## XMLUtils.php

Ubicación: `app/Commands/XMLUtils.php`

### Funciones Principales

#### createXML32($dataFactura)
Genera XML CFDI versión 3.2 (deprecada).

**Uso**: Solo para facturación histórica o proveedores legacy.

#### createXML33($dataFactura, $proveedor)
Genera XML CFDI versión 3.3.

**Diferencias con 4.0**:
- No requiere Exportación
- No requiere RegimenFiscalReceptor
- No requiere DomicilioFiscalReceptor
- No requiere ObjetoImp en conceptos
- Complemento Pagos 1.0 en lugar de 2.0
- Comercio Exterior 1.1 en lugar de 2.0
- Carta Porte 2.0 en lugar de 3.0/3.1

#### createXML40($dataFactura, $proveedor)
Genera XML CFDI versión 4.0 (actual).

Ver documento: `02-XML-GENERATION-V40.md`

---

### Funciones de Sello Digital

#### obtenerSello($xml, $keyPath)
Genera el sello digital del comprobante.

**Proceso**:
1. Obtiene cadena original usando XSLT
2. Firma con RSA-SHA256 usando la llave privada
3. Codifica en base64
4. Retorna sello

```php
$sello = XMLUtils::obtenerSello($xml, '/path/to/key.pem');
```

**XSLT utilizado**: Transformación oficial del SAT para generar cadena original.

---

### Procesamiento de Complementos

Cada complemento tiene su propia lógica de construcción dentro de `createXML40()`:

**Pagos 2.0**:
```php
if(isset($dataFactura->pagos) && count($dataFactura->pagos) > 0){
    $nodoPagos = $nodoComplemento->addChild('pago20:Pagos', true, ['Version' => '2.0']);
    // Procesar cada pago
    // Agregar nodo Totales
}
```

**Carta Porte**:
```php
if(isset($dataFactura->cartaPorte) && $dataFactura->cartaPorte != null){
    $nodoCartaPorte = $nodoComplemento->addChild("cartaporte$cartaVersion:CartaPorte", ...);
    // Procesar ubicaciones, mercancías, autotransporte, figuras
}
```

**Nómina**:
```php
if(isset($dataFactura->nomina)){
    $nodoNomina = $nodoComplemento->addChild('nomina12:Nomina', ...);
    // Procesar emisor, receptor, percepciones, deducciones, otros pagos
}
```

**Comercio Exterior**:
```php
if(isset($dataFactura->comercio) && $dataFactura->comercio != null){
    $nodoComercioE = $nodoComplemento->addChild("cce$versionComercionXSD:ComercioExterior", ...);
    // Procesar emisor, receptor, destinatario, mercancías
}
```

---

## NumeroLetra.php

Ubicación: `app/Commands/NumeroLetra.php`

### Función Principal

#### convertir($numero, $moneda = "PESOS")
Convierte número a texto para importes.

```php
NumeroLetra::convertir(1234.56, "PESOS");
// "MIL DOSCIENTOS TREINTA Y CUATRO PESOS 56/100 M.N."

NumeroLetra::convertir(1234.56, "DOLARES");
// "MIL DOSCIENTOS TREINTA Y CUATRO DOLARES 56/100 USD"
```

**Uso**: Representación en letra del total de la factura en PDFs.

---

## Constantes y Configuración

### Constants.php

Ubicación: `app/Commands/Constants.php`

Contiene constantes del sistema:
```php
class Constants {
    const FINKOK_URL_PROD = "https://facturacion.finkok.com/servicios/soap/stamp.wsdl";
    const FINKOK_URL_DEMO = "https://demo-facturacion.finkok.com/servicios/soap/stamp.wsdl";
    // etc.
}
```

---

## Buenas Prácticas

### Formateo de Números
- Siempre usar `Utils::formatNumber()` para montos en XML
- Usar 2 decimales para importes
- Usar 6 decimales para tasas
- Considerar truncar vs redondear según requisitos fiscales

### Validación de Certificados
- Validar certificado y llave antes de facturar
- Verificar vigencia del certificado
- Guardar certificado en formato PEM

### Manejo de Datos de Factura
- Usar `obtenerDatosFactura()` para regeneración
- No modificar directamente los objetos retornados
- Validar existencia de complementos antes de acceder

### Encriptación
- Encriptar datos sensibles (contraseñas, claves privadas)
- No hardcodear claves de encriptación
- Usar variables de entorno para claves

### Generación de XML
- Siempre validar datos antes de generar XML
- Usar namespaces correctos según versión y complementos
- Validar XSD si es posible antes de timbrar

### Logs y Debugging
- Registrar todos los errores de timbrado
- Guardar XMLs generados temporalmente
- Limpiar archivos temporales periódicamente
