- Laravel 11 backend with API REST - React 18 + TypeScript + Vite frontend - Multi-parser architecture for accounting systems (CONTPAQi, Aspel, SAP) - 27+ financial metrics calculation - PDF report generation with Browsershot - Complete documentation (10 documents) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
150 lines
4.6 KiB
PHP
150 lines
4.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Parsers;
|
|
|
|
use Spatie\PdfToText\Pdf;
|
|
|
|
class ContpaqiParser implements ParserInterface
|
|
{
|
|
public function getSistema(): string
|
|
{
|
|
return 'contpaqi';
|
|
}
|
|
|
|
public function puedeManej(string $filePath): bool
|
|
{
|
|
$extension = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
|
|
|
|
if ($extension !== 'pdf') {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
$text = Pdf::getText($filePath);
|
|
|
|
// Buscar patrones característicos de CONTPAQi
|
|
$patronesContpaqi = [
|
|
'/CONTPAQ/i',
|
|
'/Balanza de Comprobaci[óo]n/i',
|
|
'/\d{3}-\d{3}-\d{3}/', // Patrón de código de cuenta CONTPAQi
|
|
'/Saldo\s+Inicial.*Debe.*Haber.*Saldo\s+Final/is',
|
|
];
|
|
|
|
$coincidencias = 0;
|
|
foreach ($patronesContpaqi as $patron) {
|
|
if (preg_match($patron, $text)) {
|
|
$coincidencias++;
|
|
}
|
|
}
|
|
|
|
return $coincidencias >= 2;
|
|
} catch (\Exception $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function parsear(string $filePath): array
|
|
{
|
|
$text = Pdf::getText($filePath);
|
|
$lineas = explode("\n", $text);
|
|
$cuentas = [];
|
|
|
|
foreach ($lineas as $linea) {
|
|
$cuenta = $this->parsearLinea($linea);
|
|
if ($cuenta) {
|
|
$cuentas[] = $cuenta;
|
|
}
|
|
}
|
|
|
|
// Establecer relaciones padre-hijo
|
|
$this->establecerJerarquia($cuentas);
|
|
|
|
return $cuentas;
|
|
}
|
|
|
|
private function parsearLinea(string $linea): ?array
|
|
{
|
|
// Patrón para líneas de cuenta CONTPAQi
|
|
// Formato típico: "001-100-000 ACTIVO CIRCULANTE 1,234.56 0.00 500.00 200.00 1,534.56 0.00"
|
|
$patron = '/^(\d{3}-\d{3}-\d{3})\s+(.+?)\s+([\d,]+\.?\d*)\s+([\d,]+\.?\d*)\s+([\d,]+\.?\d*)\s+([\d,]+\.?\d*)\s+([\d,]+\.?\d*)\s+([\d,]+\.?\d*)$/';
|
|
|
|
if (!preg_match($patron, trim($linea), $matches)) {
|
|
return null;
|
|
}
|
|
|
|
$codigo = $matches[1];
|
|
$nombre = trim($matches[2]);
|
|
|
|
// Determinar nivel basado en el código
|
|
$nivel = $this->determinarNivel($codigo);
|
|
|
|
// Determinar si es cuenta padre (termina en -000-000 o -XXX-000)
|
|
$esCuentaPadre = preg_match('/-000-000$/', $codigo) || preg_match('/-\d{3}-000$/', $codigo);
|
|
|
|
return [
|
|
'codigo' => $codigo,
|
|
'nombre' => $nombre,
|
|
'nivel' => $nivel,
|
|
'saldo_inicial_deudor' => $this->parsearNumero($matches[3]),
|
|
'saldo_inicial_acreedor' => $this->parsearNumero($matches[4]),
|
|
'cargos' => $this->parsearNumero($matches[5]),
|
|
'abonos' => $this->parsearNumero($matches[6]),
|
|
'saldo_final_deudor' => $this->parsearNumero($matches[7]),
|
|
'saldo_final_acreedor' => $this->parsearNumero($matches[8]),
|
|
'es_cuenta_padre' => $esCuentaPadre,
|
|
];
|
|
}
|
|
|
|
private function determinarNivel(string $codigo): int
|
|
{
|
|
// En CONTPAQi el nivel se puede inferir del patrón de código
|
|
// XXX-000-000 = Nivel 1 (cuenta mayor)
|
|
// XXX-XXX-000 = Nivel 2 (subcuenta)
|
|
// XXX-XXX-XXX = Nivel 3 (detalle)
|
|
|
|
if (preg_match('/-000-000$/', $codigo)) {
|
|
return 1;
|
|
}
|
|
if (preg_match('/-\d{3}-000$/', $codigo)) {
|
|
return 2;
|
|
}
|
|
return 3;
|
|
}
|
|
|
|
private function parsearNumero(string $valor): float
|
|
{
|
|
// Remover comas y convertir a float
|
|
return (float) str_replace(',', '', $valor);
|
|
}
|
|
|
|
private function establecerJerarquia(array &$cuentas): void
|
|
{
|
|
// Crear índice por código
|
|
$indice = [];
|
|
foreach ($cuentas as $i => $cuenta) {
|
|
$indice[$cuenta['codigo']] = $i;
|
|
}
|
|
|
|
// Establecer padres basado en el código
|
|
foreach ($cuentas as $i => &$cuenta) {
|
|
$codigo = $cuenta['codigo'];
|
|
$partes = explode('-', $codigo);
|
|
|
|
// Buscar cuenta padre
|
|
if ($partes[2] !== '000') {
|
|
// Buscar padre de nivel 2 (XXX-XXX-000)
|
|
$codigoPadre = $partes[0] . '-' . $partes[1] . '-000';
|
|
if (isset($indice[$codigoPadre])) {
|
|
$cuenta['cuenta_padre_codigo'] = $codigoPadre;
|
|
}
|
|
} elseif ($partes[1] !== '000') {
|
|
// Buscar padre de nivel 1 (XXX-000-000)
|
|
$codigoPadre = $partes[0] . '-000-000';
|
|
if (isset($indice[$codigoPadre])) {
|
|
$cuenta['cuenta_padre_codigo'] = $codigoPadre;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|