user()->canAccessCliente($cliente->id)) { return response()->json(['message' => 'No autorizado'], 403); } $balanzas = $cliente->balanzas() ->orderByDesc('periodo_fin') ->get(); return response()->json($balanzas); } public function store(Request $request, Cliente $cliente): JsonResponse { if (!$request->user()->canAccessCliente($cliente->id)) { return response()->json(['message' => 'No autorizado'], 403); } $request->validate([ 'archivo' => 'required|file|mimes:pdf,xlsx,xls,csv|max:10240', 'periodo_inicio' => 'required|date', 'periodo_fin' => 'required|date|after_or_equal:periodo_inicio', ]); $file = $request->file('archivo'); $path = $file->store('balanzas/' . $cliente->id, 'local'); $balanza = Balanza::create([ 'cliente_id' => $cliente->id, 'periodo_inicio' => $request->periodo_inicio, 'periodo_fin' => $request->periodo_fin, 'archivo_original' => $path, 'sistema_origen' => 'pendiente', 'status' => 'pendiente', ]); // Procesar archivo en background o inmediatamente try { $this->procesarBalanza($balanza, $path); } catch (\Exception $e) { $balanza->update([ 'status' => 'error', 'error_mensaje' => $e->getMessage(), ]); } return response()->json($balanza, 201); } public function show(Request $request, Balanza $balanza): JsonResponse { if (!$request->user()->canAccessCliente($balanza->cliente_id)) { return response()->json(['message' => 'No autorizado'], 403); } return response()->json($balanza->load(['cuentas', 'cliente'])); } public function cuentas(Request $request, Balanza $balanza): JsonResponse { if (!$request->user()->canAccessCliente($balanza->cliente_id)) { return response()->json(['message' => 'No autorizado'], 403); } $cuentas = $balanza->cuentas() ->with(['categoriaContable', 'reporteContable', 'cuentaPadre']) ->orderBy('codigo') ->get(); return response()->json($cuentas); } public function updateExclusiones(Request $request, Balanza $balanza): JsonResponse { if (!$request->user()->canAccessCliente($balanza->cliente_id)) { return response()->json(['message' => 'No autorizado'], 403); } $request->validate([ 'exclusiones' => 'required|array', 'exclusiones.*' => 'integer|exists:cuentas,id', ]); // Marcar todas como incluidas primero $balanza->cuentas()->update(['excluida' => false]); // Marcar las seleccionadas como excluidas $balanza->cuentas() ->whereIn('id', $request->exclusiones) ->update(['excluida' => true]); // Recalcular saldos de cuentas padre $cuentasPadre = $balanza->cuentasPadre()->get(); foreach ($cuentasPadre as $cuentaPadre) { $cuentaPadre->recalcularSaldoDesdeHijos(); } return response()->json(['message' => 'Exclusiones actualizadas']); } private function procesarBalanza(Balanza $balanza, string $path): void { $balanza->update(['status' => 'procesando']); $fullPath = Storage::disk('local')->path($path); // Detectar sistema origen $resultado = $this->detector->detectar($fullPath); $balanza->update(['sistema_origen' => $resultado['sistema']]); // Parsear y guardar cuentas $cuentas = $resultado['parser']->parsear($fullPath); foreach ($cuentas as $cuentaData) { $balanza->cuentas()->create($cuentaData); } // Clasificar cuentas $this->clasificador->clasificar($balanza); $balanza->update(['status' => 'completado']); } }