- 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>
163 lines
5.3 KiB
PHP
163 lines
5.3 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Cliente;
|
|
use App\Models\Reporte;
|
|
use App\Services\CalculadorMetricas;
|
|
use App\Services\GeneradorPdf;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
|
|
|
class ReporteController extends Controller
|
|
{
|
|
public function __construct(
|
|
private CalculadorMetricas $calculador,
|
|
private GeneradorPdf $generadorPdf,
|
|
) {}
|
|
|
|
public function index(Request $request, Cliente $cliente): JsonResponse
|
|
{
|
|
if (!$request->user()->canAccessCliente($cliente->id)) {
|
|
return response()->json(['message' => 'No autorizado'], 403);
|
|
}
|
|
|
|
$reportes = $cliente->reportes()
|
|
->orderByDesc('periodo_fin')
|
|
->get();
|
|
|
|
return response()->json($reportes);
|
|
}
|
|
|
|
public function store(Request $request, Cliente $cliente): JsonResponse
|
|
{
|
|
if (!$request->user()->canAccessCliente($cliente->id)) {
|
|
return response()->json(['message' => 'No autorizado'], 403);
|
|
}
|
|
|
|
$validated = $request->validate([
|
|
'nombre' => 'required|string|max:255',
|
|
'balanza_ids' => 'required|array|min:1',
|
|
'balanza_ids.*' => 'exists:balanzas,id',
|
|
]);
|
|
|
|
// Verificar que las balanzas pertenecen al cliente
|
|
$balanzasValidas = $cliente->balanzas()
|
|
->whereIn('id', $validated['balanza_ids'])
|
|
->where('status', 'completado')
|
|
->get();
|
|
|
|
if ($balanzasValidas->count() !== count($validated['balanza_ids'])) {
|
|
return response()->json([
|
|
'message' => 'Algunas balanzas no son válidas o no están procesadas'
|
|
], 422);
|
|
}
|
|
|
|
// Determinar tipo de periodo
|
|
$periodoTipo = $this->determinarTipoPeriodo($balanzasValidas);
|
|
|
|
$reporte = Reporte::create([
|
|
'cliente_id' => $cliente->id,
|
|
'nombre' => $validated['nombre'],
|
|
'periodo_tipo' => $periodoTipo,
|
|
'periodo_inicio' => $balanzasValidas->min('periodo_inicio'),
|
|
'periodo_fin' => $balanzasValidas->max('periodo_fin'),
|
|
'status' => 'procesando',
|
|
]);
|
|
|
|
$reporte->balanzas()->attach($validated['balanza_ids']);
|
|
|
|
// Calcular métricas
|
|
try {
|
|
$dataCalculada = $this->calculador->calcular($reporte);
|
|
$reporte->update([
|
|
'data_calculada' => $dataCalculada,
|
|
'fecha_generacion' => now(),
|
|
'status' => 'completado',
|
|
]);
|
|
} catch (\Exception $e) {
|
|
$reporte->update(['status' => 'error']);
|
|
return response()->json(['message' => $e->getMessage()], 500);
|
|
}
|
|
|
|
return response()->json($reporte, 201);
|
|
}
|
|
|
|
public function show(Request $request, Reporte $reporte): JsonResponse
|
|
{
|
|
if (!$request->user()->canAccessCliente($reporte->cliente_id)) {
|
|
return response()->json(['message' => 'No autorizado'], 403);
|
|
}
|
|
|
|
return response()->json($reporte->load(['cliente', 'balanzas']));
|
|
}
|
|
|
|
public function pdf(Request $request, Reporte $reporte): BinaryFileResponse|JsonResponse
|
|
{
|
|
if (!$request->user()->canAccessCliente($reporte->cliente_id)) {
|
|
return response()->json(['message' => 'No autorizado'], 403);
|
|
}
|
|
|
|
// Verificar permisos de empleado si aplica
|
|
$user = $request->user();
|
|
if ($user->isEmpleado()) {
|
|
$permiso = $user->permisosEmpleado()
|
|
->where('cliente_id', $reporte->cliente_id)
|
|
->first();
|
|
|
|
if (!$permiso || !$permiso->tienePermiso('exportar_pdf')) {
|
|
return response()->json(['message' => 'No tiene permiso para exportar PDF'], 403);
|
|
}
|
|
}
|
|
|
|
if (!$reporte->pdf_path || !file_exists(storage_path('app/' . $reporte->pdf_path))) {
|
|
// Generar PDF
|
|
$pdfPath = $this->generadorPdf->generar($reporte);
|
|
$reporte->update(['pdf_path' => $pdfPath]);
|
|
}
|
|
|
|
return response()->download(
|
|
storage_path('app/' . $reporte->pdf_path),
|
|
$reporte->nombre . '.pdf'
|
|
);
|
|
}
|
|
|
|
public function destroy(Request $request, Reporte $reporte): JsonResponse
|
|
{
|
|
if (!$request->user()->isAdmin() && !$request->user()->isAnalista()) {
|
|
return response()->json(['message' => 'No autorizado'], 403);
|
|
}
|
|
|
|
$reporte->delete();
|
|
|
|
return response()->json(['message' => 'Reporte eliminado']);
|
|
}
|
|
|
|
private function determinarTipoPeriodo($balanzas): string
|
|
{
|
|
if ($balanzas->count() < 2) {
|
|
$balanza = $balanzas->first();
|
|
$dias = $balanza->periodo_inicio->diffInDays($balanza->periodo_fin);
|
|
|
|
if ($dias <= 35) return 'mensual';
|
|
if ($dias <= 100) return 'trimestral';
|
|
return 'anual';
|
|
}
|
|
|
|
// Calcular diferencia promedio entre periodos
|
|
$diferencias = [];
|
|
$sorted = $balanzas->sortBy('periodo_inicio')->values();
|
|
|
|
for ($i = 1; $i < $sorted->count(); $i++) {
|
|
$diferencias[] = $sorted[$i - 1]->periodo_fin->diffInDays($sorted[$i]->periodo_inicio);
|
|
}
|
|
|
|
$promedio = array_sum($diferencias) / count($diferencias);
|
|
|
|
if ($promedio <= 35) return 'mensual';
|
|
if ($promedio <= 100) return 'trimestral';
|
|
return 'anual';
|
|
}
|
|
}
|