Files
CKB/app/Http/Controllers/WarehouseManagement/MutationsController.php

278 lines
10 KiB
PHP

<?php
namespace App\Http\Controllers\WarehouseManagement;
use App\Http\Controllers\Controller;
use App\Models\Mutation;
use App\Models\MutationDetail;
use App\Models\Product;
use App\Models\Dealer;
use App\Enums\MutationStatus;
use App\Models\Menu;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Yajra\DataTables\DataTables;
class MutationsController extends Controller
{
public function index(Request $request)
{
$menu = Menu::where('link','mutations.index')->first();
if ($request->ajax()) {
$data = Mutation::with(['fromDealer', 'toDealer', 'requestedBy', 'approvedBy', 'receivedBy'])
->select('mutations.*');
// Filter berdasarkan dealer jika user bukan admin
if (auth()->user()->dealer_id) {
$data->where(function($query) {
$query->where('from_dealer_id', auth()->user()->dealer_id)
->orWhere('to_dealer_id', auth()->user()->dealer_id);
});
}
return DataTables::of($data)
->addIndexColumn()
->addColumn('mutation_number', function($row) {
return $row->mutation_number;
})
->addColumn('from_dealer', function($row) {
return $row->fromDealer->name ?? '-';
})
->addColumn('to_dealer', function($row) {
return $row->toDealer->name ?? '-';
})
->addColumn('requested_by', function($row) {
return $row->requestedBy->name ?? '-';
})
->addColumn('status', function($row) {
$statusColor = $row->status_color;
$statusLabel = $row->status_label;
return "<span class=\"kt-badge kt-badge--{$statusColor} kt-badge--dot\"></span>&nbsp;<span class=\"kt-font-bold kt-font-{$statusColor}\">{$statusLabel}</span>";
})
->addColumn('total_items', function($row) {
return number_format($row->total_items, 0);
})
->addColumn('created_at', function($row) {
return $row->created_at->format('d/m/Y H:i');
})
->addColumn('action', function($row) {
return view('warehouse_management.mutations._action', compact('row'))->render();
})
->rawColumns(['status', 'action'])
->make(true);
}
return view('warehouse_management.mutations.index', compact('menu'));
}
public function create()
{
$menu = Menu::where('link','mutations.create')->first();
$dealers = Dealer::all();
$products = Product::with('stocks')->get();
return view('warehouse_management.mutations.create', compact('menu', 'dealers', 'products'));
}
public function store(Request $request)
{
$request->validate([
'from_dealer_id' => 'required|exists:dealers,id',
'to_dealer_id' => 'required|exists:dealers,id|different:from_dealer_id',
'notes' => 'nullable|string',
'products' => 'required|array|min:1',
'products.*.product_id' => 'required|exists:products,id',
'products.*.quantity_requested' => 'required|numeric|min:0.01'
]);
DB::beginTransaction();
try {
// Buat mutation record dengan status SENT (langsung terkirim ke dealer tujuan)
$mutation = Mutation::create([
'from_dealer_id' => $request->from_dealer_id,
'to_dealer_id' => $request->to_dealer_id,
'status' => 'sent',
'requested_by' => auth()->id(),
'notes' => $request->notes
]);
// Buat mutation details
foreach ($request->products as $productData) {
MutationDetail::create([
'mutation_id' => $mutation->id,
'product_id' => $productData['product_id'],
'quantity_requested' => $productData['quantity_requested'],
'notes' => $productData['notes'] ?? null
]);
}
DB::commit();
return redirect()->route('mutations.index')
->with('success', 'Mutasi berhasil dibuat dan terkirim ke dealer tujuan');
} catch (\Exception $e) {
DB::rollback();
return back()->withErrors(['error' => 'Gagal membuat mutasi: ' . $e->getMessage()]);
}
}
public function show(Mutation $mutation)
{
$mutation->load(['fromDealer', 'toDealer', 'requestedBy', 'approvedBy', 'receivedBy', 'mutationDetails.product']);
return view('warehouse_management.mutations.show', compact('mutation'));
}
public function receive(Mutation $mutation)
{
if (!$mutation->canBeReceived()) {
return back()->withErrors(['error' => 'Mutasi tidak dapat diterima dalam status saat ini']);
}
try {
$mutation->receive(auth()->id());
return redirect()->route('mutations.index')
->with('success', 'Mutasi berhasil diterima dan menunggu persetujuan pengirim');
} catch (\Exception $e) {
return back()->withErrors(['error' => 'Gagal menerima mutasi: ' . $e->getMessage()]);
}
}
public function approve(Request $request, Mutation $mutation)
{
$request->validate([
'notes' => 'nullable|string',
'details' => 'required|array',
'details.*.quantity_approved' => 'required|numeric|min:0'
]);
if (!$mutation->canBeApproved()) {
return back()->withErrors(['error' => 'Mutasi tidak dapat disetujui dalam status saat ini']);
}
DB::beginTransaction();
try {
// Update mutation details dengan quantity approved
foreach ($request->details as $detailId => $detailData) {
$mutationDetail = MutationDetail::findOrFail($detailId);
$mutationDetail->update([
'quantity_approved' => $detailData['quantity_approved']
]);
}
// Approve mutation
$mutation->approve(auth()->id(), $request->notes);
DB::commit();
return redirect()->route('mutations.index')
->with('success', 'Mutasi berhasil disetujui');
} catch (\Exception $e) {
DB::rollback();
return back()->withErrors(['error' => 'Gagal menyetujui mutasi: ' . $e->getMessage()]);
}
}
public function reject(Request $request, Mutation $mutation)
{
$request->validate([
'rejection_reason' => 'required|string'
]);
if (!$mutation->canBeApproved()) {
return back()->withErrors(['error' => 'Mutasi tidak dapat ditolak dalam status saat ini']);
}
try {
$mutation->reject(auth()->id(), $request->rejection_reason);
return redirect()->route('mutations.index')
->with('success', 'Mutasi berhasil ditolak');
} catch (\Exception $e) {
return back()->withErrors(['error' => 'Gagal menolak mutasi: ' . $e->getMessage()]);
}
}
public function complete(Mutation $mutation)
{
if (!$mutation->canBeCompleted()) {
return back()->withErrors(['error' => 'Mutasi tidak dapat diselesaikan dalam status saat ini']);
}
try {
$mutation->complete();
return redirect()->route('mutations.index')
->with('success', 'Mutasi berhasil diselesaikan dan stock telah dipindahkan');
} catch (\Exception $e) {
return back()->withErrors(['error' => 'Gagal menyelesaikan mutasi: ' . $e->getMessage()]);
}
}
public function cancel(Mutation $mutation)
{
if (!$mutation->canBeCancelled()) {
return back()->withErrors(['error' => 'Mutasi tidak dapat dibatalkan dalam status saat ini']);
}
try {
$mutation->update(['status' => MutationStatus::CANCELLED]);
return redirect()->route('mutations.index')
->with('success', 'Mutasi berhasil dibatalkan');
} catch (\Exception $e) {
return back()->withErrors(['error' => 'Gagal membatalkan mutasi: ' . $e->getMessage()]);
}
}
// API untuk mendapatkan detail mutasi untuk approval
public function getDetails(Mutation $mutation)
{
$mutation->load(['mutationDetails.product', 'fromDealer']);
$details = $mutation->mutationDetails->map(function($detail) use ($mutation) {
$availableStock = $detail->product->getStockByDealer($mutation->from_dealer_id);
return [
'id' => $detail->id,
'product' => [
'id' => $detail->product->id,
'name' => $detail->product->name
],
'quantity_requested' => $detail->quantity_requested,
'quantity_approved' => $detail->quantity_approved,
'available_stock' => $availableStock
];
});
return response()->json([
'mutation' => [
'id' => $mutation->id,
'mutation_number' => $mutation->mutation_number,
'from_dealer' => $mutation->fromDealer->name,
'to_dealer' => $mutation->toDealer->name
],
'details' => $details
]);
}
// API untuk mendapatkan stock produk di dealer tertentu
public function getProductStock(Request $request)
{
$dealerId = $request->dealer_id;
$productId = $request->product_id;
$product = Product::findOrFail($productId);
$stock = $product->getStockByDealer($dealerId);
return response()->json([
'product_name' => $product->name,
'current_stock' => $stock
]);
}
}