'year-data', 'years' => $this->getAvailableYears(), 'exports' => $this->getRecentExports(), ]); } public function stats(Request $request): JsonResponse { $year = (int) $request->input('year'); if ($year < 2020 || $year > 2100) { return response()->json(['error' => 'Некорректный год'], 422); } $stats = $this->collectStats($year); return response()->json([ 'year' => $year, 'stats' => $stats, 'total' => array_sum($stats), ]); } public function export(Request $request): RedirectResponse { $year = (int) $request->input('year'); if ($year < 2020 || $year > 2100) { return redirect()->back()->with('danger', 'Некорректный год'); } ExportYearDataJob::dispatch($year, $request->user()->id); return redirect()->route('year-data.index') ->with('success', "Экспорт данных за {$year} год запущен. Вы получите уведомление о завершении."); } public function import(Request $request): RedirectResponse { $request->validate([ 'year' => 'required|integer|min:2020|max:2100', 'import_file' => 'required|file|mimes:zip', ]); $year = (int) $request->input('year'); $clearExisting = $request->boolean('clear_existing'); // Сохраняем загруженный файл $file = $request->file('import_file'); $path = 'import/year_data/' . Str::uuid() . '.zip'; Storage::disk('public')->put($path, $file->getContent()); $fullPath = storage_path('app/public/' . $path); ImportYearDataJob::dispatch($fullPath, $year, $request->user()->id, $clearExisting); $message = "Импорт данных за {$year} год запущен."; if ($clearExisting) { $message .= " Существующие данные будут очищены."; } $message .= " Вы получите уведомление о завершении."; return redirect()->route('year-data.index')->with('success', $message); } public function download(File $file) { if (!$file->path || !Storage::disk('public')->exists($file->path)) { abort(404, 'Файл не найден'); } return Storage::disk('public')->download($file->path, $file->original_name); } private function getAvailableYears(): array { $years = []; $orderYears = Order::withoutGlobalScopes()->withTrashed() ->selectRaw('DISTINCT year') ->pluck('year') ->toArray(); $productYears = Product::withoutGlobalScopes()->withTrashed() ->selectRaw('DISTINCT year') ->pluck('year') ->toArray(); $mafOrderYears = MafOrder::withoutGlobalScopes()->withTrashed() ->selectRaw('DISTINCT year') ->pluck('year') ->toArray(); $contractYears = Contract::selectRaw('DISTINCT year') ->pluck('year') ->toArray(); $ttnYears = Ttn::selectRaw('DISTINCT year') ->pluck('year') ->toArray(); $years = array_unique(array_merge($orderYears, $productYears, $mafOrderYears, $contractYears, $ttnYears)); rsort($years); return $years; } private function getRecentExports(): \Illuminate\Database\Eloquent\Collection { return File::where('original_name', 'like', 'year_data_%') ->where('mime_type', 'application/zip') ->orderBy('created_at', 'desc') ->limit(10) ->get(); } private function collectStats(int $year): array { $orderIds = Order::withoutGlobalScopes()->withTrashed()->where('year', $year)->pluck('id'); $productIds = Product::withoutGlobalScopes()->withTrashed()->where('year', $year)->pluck('id'); $mafOrderIds = MafOrder::withoutGlobalScopes()->withTrashed()->where('year', $year)->pluck('id'); $productSkuIds = ProductSKU::withoutGlobalScopes()->withTrashed()->where('year', $year)->pluck('id'); $reclamationCount = Reclamation::whereIn('order_id', $orderIds)->count(); $fileIds = $this->collectFileIds($year, $orderIds, $productIds, $productSkuIds); return [ 'Заказы (Orders)' => $orderIds->count(), 'Заказы МАФ (MafOrders)' => $mafOrderIds->count(), 'Продукты (Products)' => $productIds->count(), 'SKU продуктов (ProductSKU)' => $productSkuIds->count(), 'Рекламации (Reclamations)' => $reclamationCount, 'Расписания (Schedules)' => Schedule::whereIn('order_id', $orderIds)->count(), 'ТТН (Ttns)' => Ttn::where('year', $year)->count(), 'Контракты (Contracts)' => Contract::where('year', $year)->count(), 'Файлы (Files)' => $fileIds->count(), ]; } private function collectFileIds(int $year, $orderIds, $productIds, $productSkuIds): \Illuminate\Support\Collection { $fileIds = collect(); $fileIds = $fileIds->merge( DB::table('order_photo')->whereIn('order_id', $orderIds)->pluck('file_id') ); $fileIds = $fileIds->merge( DB::table('order_document')->whereIn('order_id', $orderIds)->pluck('file_id') ); $fileIds = $fileIds->merge( DB::table('order_statement')->whereIn('order_id', $orderIds)->pluck('file_id') ); $reclamationIds = Reclamation::whereIn('order_id', $orderIds)->pluck('id'); $fileIds = $fileIds->merge( DB::table('reclamation_photo_before')->whereIn('reclamation_id', $reclamationIds)->pluck('file_id') ); $fileIds = $fileIds->merge( DB::table('reclamation_photo_after')->whereIn('reclamation_id', $reclamationIds)->pluck('file_id') ); $fileIds = $fileIds->merge( DB::table('reclamation_document')->whereIn('reclamation_id', $reclamationIds)->pluck('file_id') ); $fileIds = $fileIds->merge( DB::table('reclamation_act')->whereIn('reclamation_id', $reclamationIds)->pluck('file_id') ); $fileIds = $fileIds->merge( Product::withoutGlobalScopes()->withTrashed() ->whereIn('id', $productIds) ->whereNotNull('certificate_id') ->pluck('certificate_id') ); $fileIds = $fileIds->merge( ProductSKU::withoutGlobalScopes()->withTrashed() ->whereIn('id', $productSkuIds) ->whereNotNull('passport_id') ->pluck('passport_id') ); $fileIds = $fileIds->merge( Ttn::where('year', $year)->whereNotNull('file_id')->pluck('file_id') ); return $fileIds->unique(); } }