first(); abort_if(!Gate::allows('view', $menu), 403); $dealers = Dealer::all(); $products = Product::all(); if ($request->ajax()) { Log::info('Stock audit ajax request received', [ 'filters' => $request->only(['dealer', 'product', 'change_type', 'date']), 'user_id' => auth()->id(), 'user_dealer_id' => auth()->user()->dealer_id ]); $data = StockLog::query() ->with([ 'stock.product', 'stock.dealer', 'user.role', 'source' ]) ->leftJoin('stocks', 'stock_logs.stock_id', '=', 'stocks.id') ->leftJoin('products', 'stocks.product_id', '=', 'products.id') ->leftJoin('dealers', 'stocks.dealer_id', '=', 'dealers.id') ->leftJoin('users', 'stock_logs.user_id', '=', 'users.id') ->select('stock_logs.*'); // Filter berdasarkan dealer jika user bukan admin if (auth()->user()->dealer_id) { $data->whereHas('stock', function($query) { $query->where('dealer_id', auth()->user()->dealer_id); }); } // Apply filters from request if ($request->filled('dealer')) { $data->where('dealers.name', 'like', '%' . $request->dealer . '%'); } if ($request->filled('product')) { $data->where('products.name', 'like', '%' . $request->product . '%'); } if ($request->filled('change_type')) { $data->where('stock_logs.change_type', $request->change_type); } if ($request->filled('date')) { $data->whereDate('stock_logs.created_at', $request->date); } return DataTables::of($data) ->addIndexColumn() ->addColumn('product_name', function($row) { return $row->stock->product->name ?? '-'; }) ->addColumn('dealer_name', function($row) { return $row->stock->dealer->name ?? '-'; }) ->addColumn('change_type', function($row) { $changeType = $row->change_type; $class = match($changeType->value) { 'increase' => 'text-success', 'decrease' => 'text-danger', 'adjustment' => 'text-warning', 'no_change' => 'text-muted', default => 'text-dark' }; return "{$changeType->label()}"; }) ->addColumn('quantity_change', function($row) { $change = $row->quantity_change; if ($change > 0) { return "+{$change}"; } elseif ($change < 0) { return "{$change}"; } else { return "0"; } }) ->addColumn('stock_before_after', function($row) { return "{$row->previous_quantity} → {$row->new_quantity}"; }) ->addColumn('source_info', function($row) { if ($row->source_type === 'App\\Models\\Mutation') { $mutationNumber = $row->source ? $row->source->mutation_number : '-'; return "Mutasi: {$mutationNumber}"; } elseif ($row->source_type === 'App\\Models\\Opname') { $opname_id = $row->source ? $row->source->id : '-'; return "Opname: #{$opname_id}"; } elseif ($row->source_type === 'App\\Models\\Transaction') { $transaction_id = $row->source ? $row->source->id : '-'; return "Transaksi: #{$transaction_id}"; }else { return $row->source_type ?? '-'; } }) ->addColumn('user_name', function($row) { return $row->user->name ?? '-'; }) ->addColumn('created_at', function($row) { return $row->created_at->format('d M Y, H:i'); }) ->addColumn('action', function($row) { $buttons = ''; return $buttons; }) // Filtering ->filterColumn('product_name', function($query, $keyword) { $query->where('products.name', 'like', "%{$keyword}%"); }) ->filterColumn('dealer_name', function($query, $keyword) { $query->where('dealers.name', 'like', "%{$keyword}%"); }) ->filterColumn('change_type', function($query, $keyword) { $query->where('stock_logs.change_type', 'like', "%{$keyword}%"); }) ->filterColumn('source_info', function($query, $keyword) { $query->where(function($q) use ($keyword) { $q->where('stock_logs.source_type', 'like', "%{$keyword}%") ->orWhere('stock_logs.description', 'like', "%{$keyword}%"); }); }) ->filterColumn('user_name', function($query, $keyword) { $query->where('users.name', 'like', "%{$keyword}%"); }) ->filterColumn('created_at', function($query, $keyword) { $query->whereDate('stock_logs.created_at', 'like', "%{$keyword}%"); }) // Order column mapping ->orderColumn('product_name', function($query, $order) { return $query->orderBy('products.name', $order); }) ->orderColumn('dealer_name', function($query, $order) { return $query->orderBy('dealers.name', $order); }) ->orderColumn('user_name', function($query, $order) { return $query->orderBy('users.name', $order); }) ->orderColumn('created_at', function($query, $order) { return $query->orderBy('stock_logs.created_at', $order); }) ->orderColumn('quantity_change', function($query, $order) { return $query->orderBy('stock_logs.quantity_change', $order); }) ->orderColumn('stock_before_after', function($query, $order) { return $query->orderBy('stock_logs.previous_quantity', $order); }) ->orderColumn('change_type', function($query, $order) { return $query->orderBy('stock_logs.change_type', $order); }) ->orderColumn('source_info', function($query, $order) { return $query->orderBy('stock_logs.source_type', $order); }) ->rawColumns(['change_type', 'quantity_change', 'action']) ->make(true); } return view('warehouse_management.stock_audit.index', compact('menu', 'dealers', 'products')); } public function getDetail(StockLog $stockLog) { try { $stockLog->load([ 'stock.product', 'stock.dealer', 'user.role', 'source' ]); // Format data untuk response $stockLog->created_at_formatted = $stockLog->created_at->format('d M Y, H:i'); $stockLog->change_type_label = $stockLog->change_type->label(); // Detail source berdasarkan tipe $sourceDetail = null; if ($stockLog->source) { if ($stockLog->source_type === 'App\\Models\\Mutation') { $mutation = $stockLog->source; $mutation->load(['fromDealer', 'toDealer', 'requestedBy', 'approvedBy']); // Format approved_at date if exists if ($mutation->approved_at) { $mutation->approved_at_formatted = $mutation->approved_at->format('d M Y, H:i'); } $sourceDetail = [ 'type' => 'mutation', 'data' => $mutation ]; } elseif ($stockLog->source_type === 'App\\Models\\StockOpname') { $opname = $stockLog->source; $opname->load(['dealer', 'user']); $sourceDetail = [ 'type' => 'opname', 'data' => $opname ]; } } return response()->json([ 'success' => true, 'data' => $stockLog, 'source_detail' => $sourceDetail ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'Gagal memuat detail audit: ' . $e->getMessage() ], 500); } } }