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'; } }