Auth::id(), 'request_data' => $request->all() ]); try { DB::beginTransaction(); $validated = $request->validate([ 'account_number' => ['required', 'string', 'max:50'], 'report_date' => ['required', 'date_format:Y-m-d'], ]); // Convert date to Ymd format for period $period = Carbon::createFromFormat('Y-m-d', $validated['report_date'])->format('Ymd'); // Add user tracking data $reportData = [ 'account_number' => $validated['account_number'], 'period' => $period, 'report_date' => $validated['report_date'], 'user_id' => Auth::id(), 'created_by' => Auth::id(), 'ip_address' => $request->ip(), 'user_agent' => $request->userAgent(), 'status' => 'pending', ]; // Create the report request log $reportRequest = ClosingBalanceReportLog::create($reportData); // Dispatch the job to generate the report GenerateClosingBalanceReportJob::dispatch( $validated['account_number'], $period, $reportRequest->id ); $reportRequest->update([ 'status' => 'processing', 'updated_by' => Auth::id() ]); DB::commit(); Log::info('Permintaan laporan closing balance berhasil dibuat', [ 'report_id' => $reportRequest->id, 'account_number' => $validated['account_number'], 'period' => $period ]); return redirect()->route('laporan-closing-balance.index') ->with('success', 'Permintaan laporan closing balance berhasil dibuat dan sedang diproses.'); } catch (Exception $e) { DB::rollback(); Log::error('Error saat membuat permintaan laporan closing balance', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return redirect()->back() ->withInput() ->with('error', 'Terjadi kesalahan saat membuat permintaan laporan: ' . $e->getMessage()); } } /** * Menampilkan form untuk membuat permintaan laporan baru * * @return \Illuminate\View\View */ public function create() { Log::info('Menampilkan form pembuatan laporan closing balance'); return view('webstatement::laporan-closing-balance.create'); } /** * Menampilkan detail permintaan laporan * * @param ClosingBalanceReportLog $closingBalanceReport * @return \Illuminate\View\View */ public function show(ClosingBalanceReportLog $closingBalanceReport) { Log::info('Menampilkan detail laporan closing balance', [ 'report_id' => $closingBalanceReport->id ]); $closingBalanceReport->load(['user', 'creator', 'authorizer']); return view('webstatement::laporan-closing-balance.show', compact('closingBalanceReport')); } /** * Authorize permintaan laporan * * @param Request $request * @param ClosingBalanceReportLog $closingBalanceReport * @return \Illuminate\Http\RedirectResponse */ public function authorize(Request $request, ClosingBalanceReportLog $closingBalanceReport) { Log::info('Authorize laporan closing balance', [ 'report_id' => $closingBalanceReport->id, 'user_id' => Auth::id() ]); try { DB::beginTransaction(); $request->validate([ 'authorization_status' => ['required', Rule::in(['approved', 'rejected'])], 'remarks' => ['nullable', 'string', 'max:255'], ]); // Update authorization status $closingBalanceReport->update([ 'authorization_status' => $request->authorization_status, 'authorized_by' => Auth::id(), 'authorized_at' => now(), 'remarks' => $request->remarks, 'updated_by' => Auth::id() ]); DB::commit(); $statusText = $request->authorization_status === 'approved' ? 'disetujui' : 'ditolak'; Log::info('Laporan closing balance berhasil diauthorize', [ 'report_id' => $closingBalanceReport->id, 'status' => $request->authorization_status ]); return redirect()->route('laporan-closing-balance.show', $closingBalanceReport->id) ->with('success', "Permintaan laporan closing balance berhasil {$statusText}."); } catch (Exception $e) { DB::rollback(); Log::error('Error saat authorize laporan', [ 'report_id' => $closingBalanceReport->id, 'error' => $e->getMessage() ]); return back()->with('error', 'Terjadi kesalahan saat authorize laporan.'); } } /** * Menyediakan data untuk datatables * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function dataForDatatables(Request $request) { Log::info('Mengambil data untuk datatables laporan closing balance', [ 'filters' => $request->all() ]); try { // Retrieve data from the database $query = ClosingBalanceReportLog::query(); // Apply search filter if provided (handle JSON search parameters) if ($request->has('search') && !empty($request->get('search'))) { $search = $request->get('search'); // Check if search is JSON format if (is_string($search) && json_decode($search, true) !== null) { $searchParams = json_decode($search, true); // Apply account number filter if (!empty($searchParams['account_number'])) { $query->where('account_number', 'LIKE', "%{$searchParams['account_number']}%"); } // Apply date range filter if (!empty($searchParams['start_date'])) { $startPeriod = Carbon::createFromFormat('Y-m-d', $searchParams['start_date'])->format('Ymd'); $query->where('period', '>=', $startPeriod); } if (!empty($searchParams['end_date'])) { $endPeriod = Carbon::createFromFormat('Y-m-d', $searchParams['end_date'])->format('Ymd'); $query->where('period', '<=', $endPeriod); } } else { // Handle regular string search (fallback) $query->where(function ($q) use ($search) { $q->where('account_number', 'LIKE', "%$search%") ->orWhere('period', 'LIKE', "%$search%") ->orWhere('status', 'LIKE', "%$search%") ->orWhere('authorization_status', 'LIKE', "%$search%"); }); } } // Apply individual parameter filters (for backward compatibility) if ($request->has('account_number') && !empty($request->get('account_number'))) { $query->where('account_number', 'LIKE', "%{$request->get('account_number')}%"); } if ($request->has('start_date') && !empty($request->get('start_date'))) { $startPeriod = Carbon::createFromFormat('Y-m-d', $request->get('start_date'))->format('Ymd'); $query->where('period', '>=', $startPeriod); } if ($request->has('end_date') && !empty($request->get('end_date'))) { $endPeriod = Carbon::createFromFormat('Y-m-d', $request->get('end_date'))->format('Ymd'); $query->where('period', '<=', $endPeriod); } // Apply column filters if provided if ($request->has('filters') && !empty($request->get('filters'))) { $filters = json_decode($request->get('filters'), true); foreach ($filters as $filter) { if (!empty($filter['value'])) { if ($filter['column'] === 'status') { $query->where('status', $filter['value']); } else if ($filter['column'] === 'authorization_status') { $query->where('authorization_status', $filter['value']); } else if ($filter['column'] === 'account_number') { $query->where('account_number', 'LIKE', "%{$filter['value']}%"); } } } } // Apply sorting if provided if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) { $order = $request->get('sortOrder'); $column = $request->get('sortField'); // Map frontend column names to database column names if needed $columnMap = [ 'account_number' => 'account_number', 'period' => 'period', 'status' => 'status', ]; $dbColumn = $columnMap[$column] ?? $column; $query->orderBy($dbColumn, $order); } else { // Default sorting $query->latest('created_at'); } // Get the total count of records $totalRecords = $query->count(); // Apply pagination if provided if ($request->has('page') && $request->has('size')) { $page = $request->get('page'); $size = $request->get('size'); $offset = ($page - 1) * $size; $query->skip($offset)->take($size); } // Get the filtered count of records $filteredRecords = $query->count(); // Eager load relationships $query->with(['user', 'authorizer']); // Get the data for the current page $data = $query->get()->map(function ($item) { $processingHours = $item->status === 'processing' ? $item->updated_at->diffInHours(now()) : 0; $isProcessingTimeout = $item->status === 'processing' && $processingHours >= 1; return [ 'id' => $item->id, 'account_number' => $item->account_number, 'period' => $item->period, 'report_date' => Carbon::createFromFormat('Ymd', $item->period)->format('Y-m-d'), 'status' => $item->status, 'status_display' => $item->status . ($isProcessingTimeout ? ' (Timeout)' : ''), 'processing_hours' => $processingHours, 'is_processing_timeout' => $isProcessingTimeout, 'authorization_status' => $item->authorization_status, 'is_downloaded' => $item->is_downloaded, 'created_at' => $item->created_at->format('Y-m-d H:i:s'), 'created_by' => $item->user->name ?? 'N/A', 'authorized_by' => $item->authorizer ? $item->authorizer->name : null, 'authorized_at' => $item->authorized_at ? $item->authorized_at->format('Y-m-d H:i:s') : null, 'file_path' => $item->file_path, 'record_count' => $item->record_count, 'can_retry' => in_array($item->status, ['failed', 'pending']) || $isProcessingTimeout || ($item->status === 'completed' && !$item->file_path), ]; }); // Calculate the page count $pageCount = ceil($filteredRecords / ($request->get('size') ?: 1)); $currentPage = $request->get('page') ?: 1; Log::info('Data laporan closing balance berhasil diambil', [ 'total_records' => $totalRecords, 'filtered_records' => $filteredRecords ]); return response()->json([ 'draw' => $request->get('draw'), 'recordsTotal' => $totalRecords, 'recordsFiltered' => $filteredRecords, 'pageCount' => $pageCount, 'page' => $currentPage, 'totalCount' => $totalRecords, 'data' => $data, ]); } catch (Exception $e) { Log::error('Error saat mengambil data datatables', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return response()->json([ 'error' => 'Terjadi kesalahan saat mengambil data laporan', 'message' => $e->getMessage() ], 500); } } /** * Hapus permintaan laporan * * @param ClosingBalanceReportLog $closingBalanceReport * @return \Illuminate\Http\JsonResponse */ public function destroy(ClosingBalanceReportLog $closingBalanceReport) { Log::info('Menghapus laporan closing balance', [ 'report_id' => $closingBalanceReport->id ]); try { DB::beginTransaction(); // Delete the file if exists if ($closingBalanceReport->file_path && Storage::exists($closingBalanceReport->file_path)) { Storage::delete($closingBalanceReport->file_path); } // Delete the report request $closingBalanceReport->delete(); DB::commit(); Log::info('Laporan closing balance berhasil dihapus', [ 'report_id' => $closingBalanceReport->id ]); return response()->json([ 'message' => 'Laporan closing balance berhasil dihapus.', ]); } catch (Exception $e) { DB::rollback(); Log::error('Error saat menghapus laporan', [ 'report_id' => $closingBalanceReport->id, 'error' => $e->getMessage() ]); return response()->json([ 'error' => 'Terjadi kesalahan saat menghapus laporan', 'message' => $e->getMessage() ], 500); } } /** * Retry generating laporan closing balance * * @param ClosingBalanceReportLog $closingBalanceReport * @return \Illuminate\Http\RedirectResponse */ public function retry(ClosingBalanceReportLog $closingBalanceReport) { Log::info('Retry laporan closing balance', [ 'report_id' => $closingBalanceReport->id ]); try { // Check if retry is allowed $allowedStatuses = ['failed', 'pending']; $isProcessingTooLong = $closingBalanceReport->status === 'processing' && $closingBalanceReport->updated_at->diffInHours(now()) >= 1; if (!in_array($closingBalanceReport->status, $allowedStatuses) && !$isProcessingTooLong) { return back()->with('error', 'Laporan hanya dapat diulang jika status failed, pending, atau processing lebih dari 1 jam.'); } DB::beginTransaction(); // If it was processing for too long, mark it as failed first if ($isProcessingTooLong) { $closingBalanceReport->update([ 'status' => 'failed', 'error_message' => 'Processing timeout - melebihi batas waktu 1 jam', 'updated_by' => Auth::id() ]); } // Reset the report status and clear previous data $closingBalanceReport->update([ 'status' => 'processing', 'error_message' => null, 'file_path' => null, 'file_size' => null, 'record_count' => null, 'updated_by' => Auth::id() ]); // Dispatch the job again GenerateClosingBalanceReportJob::dispatch( $closingBalanceReport->account_number, $closingBalanceReport->period, $closingBalanceReport->id ); DB::commit(); Log::info('Laporan closing balance berhasil diulang', [ 'report_id' => $closingBalanceReport->id ]); return back()->with('success', 'Job laporan closing balance berhasil diulang.'); } catch (Exception $e) { DB::rollback(); Log::error('Error saat retry laporan', [ 'report_id' => $closingBalanceReport->id, 'error' => $e->getMessage() ]); $closingBalanceReport->update([ 'status' => 'failed', 'error_message' => $e->getMessage(), 'updated_by' => Auth::id() ]); return back()->with('error', 'Gagal mengulang generate laporan: ' . $e->getMessage()); } } /** * Download laporan berdasarkan nomor rekening dan periode * * @param string $accountNumber * @param string $period * @return \Illuminate\Http\Response */ public function download($accountNumber, $period) { Log::info('Download laporan closing balance', [ 'account_number' => $accountNumber, 'period' => $period, 'user_id' => Auth::id() ]); try { // Cari laporan berdasarkan account number dan period $closingBalanceReport = ClosingBalanceReportLog::where('account_number', $accountNumber) ->where('period', $period) ->where('status', 'completed') ->whereNotNull('file_path') ->first(); if (!$closingBalanceReport) { Log::warning('Laporan tidak ditemukan atau belum selesai', [ 'account_number' => $accountNumber, 'period' => $period ]); return back()->with('error', 'Laporan tidak ditemukan atau belum selesai diproses.'); } DB::beginTransaction(); // Update download status $closingBalanceReport->update([ 'is_downloaded' => true, 'downloaded_at' => now(), 'updated_by' => Auth::id() ]); DB::commit(); // Download the file $filePath = $closingBalanceReport->file_path; if (Storage::exists($filePath)) { $fileName = "closing_balance_report_{$accountNumber}_{$period}.csv"; Log::info('File laporan berhasil didownload', [ 'account_number' => $accountNumber, 'period' => $period, 'file_path' => $filePath ]); return Storage::download($filePath, $fileName); } Log::error('File laporan tidak ditemukan di storage', [ 'account_number' => $accountNumber, 'period' => $period, 'file_path' => $filePath ]); return back()->with('error', 'File laporan tidak ditemukan.'); } catch (Exception $e) { DB::rollback(); Log::error('Error saat download laporan', [ 'account_number' => $accountNumber, 'period' => $period, 'error' => $e->getMessage() ]); return back()->with('error', 'Terjadi kesalahan saat mengunduh laporan: ' . $e->getMessage()); } } }