227 lines
9.8 KiB
PHP
227 lines
9.8 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\WarehouseManagement;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\StockLog;
|
|
use App\Models\Menu;
|
|
use App\Models\Dealer;
|
|
use App\Models\Product;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Yajra\DataTables\DataTables;
|
|
|
|
class StockAuditController extends Controller
|
|
{
|
|
public function index(Request $request)
|
|
{
|
|
$menu = Menu::where('link', 'stock-audit.index')->first();
|
|
$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 "<span class=\"font-weight-bold {$class}\">{$changeType->label()}</span>";
|
|
})
|
|
->addColumn('quantity_change', function($row) {
|
|
$change = $row->quantity_change;
|
|
if ($change > 0) {
|
|
return "<span class=\"text-success\">+{$change}</span>";
|
|
} elseif ($change < 0) {
|
|
return "<span class=\"text-danger\">{$change}</span>";
|
|
} else {
|
|
return "<span class=\"text-muted\">0</span>";
|
|
}
|
|
})
|
|
->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') {
|
|
return "Opname";
|
|
} 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 = '<button type="button" class="btn btn-info btn-sm" onclick="showAuditDetail('.$row->id.')">
|
|
Detail
|
|
</button>';
|
|
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);
|
|
}
|
|
}
|
|
}
|