toArray(); // 2) Load available orders (not used in any shipment) $availableOrders = Order::whereNotIn('id', $usedOrderIds)->get(); // 3) Load all shipments for listing $shipments = Shipment::latest()->get(); // Return your file: resources/views/admin/shipment.blade.php return view('admin.shipments', compact('availableOrders', 'shipments')); } /** * Store new shipment */ public function store(Request $request) { $request->validate([ 'origin' => 'required|string', 'destination' => 'required|string', 'shipment_date' => 'required|date', 'order_ids' => 'required|array|min:1', ]); // ----------------------------- // PREVENT DUPLICATE ORDERS // ----------------------------- foreach ($request->order_ids as $id) { if (ShipmentItem::where('order_id', $id)->exists()) { return back()->with('error', "Order ID $id is already assigned to a shipment."); } } // ----------------------------- // GENERATE UNIQUE SHIPMENT ID // ----------------------------- $year = date('y'); $prefix = "SHIP-$year-"; $lastShipment = Shipment::latest('id')->first(); $nextNumber = $lastShipment ? intval(substr($lastShipment->shipment_id, -8)) + 1 : 1; $newShipmentId = $prefix . str_pad($nextNumber, 8, '0', STR_PAD_LEFT); // ----------------------------- // CALCULATE TOTALS // ----------------------------- $orders = Order::whereIn('id', $request->order_ids)->get(); $total_ctn = $orders->sum('ctn'); $total_qty = $orders->sum('qty'); $total_ttl_qty = $orders->sum('ttl_qty'); $total_amount = $orders->sum('ttl_amount'); $total_cbm = $orders->sum('cbm'); $total_ttl_cbm = $orders->sum('ttl_cbm'); $total_kg = $orders->sum('kg'); $total_ttl_kg = $orders->sum('ttl_kg'); // ----------------------------- // CREATE SHIPMENT //------------------------------- $shipment = Shipment::create([ 'shipment_id' => $newShipmentId, 'origin' => $request->origin, 'destination' => $request->destination, 'status' => Shipment::STATUS_PENDING, 'shipment_date' => $request->shipment_date, 'total_ctn' => $total_ctn, 'total_qty' => $total_qty, 'total_ttl_qty' => $total_ttl_qty, 'total_amount' => $total_amount, 'total_cbm' => $total_cbm, 'total_ttl_cbm' => $total_ttl_cbm, 'total_kg' => $total_kg, 'total_ttl_kg' => $total_ttl_kg, ]); // ----------------------------- // INSERT SHIPMENT ITEMS // ----------------------------- foreach ($orders as $order) { ShipmentItem::create([ 'shipment_id' => $shipment->id, 'order_id' => $order->id, 'order_ctn' => $order->ctn, 'order_qty' => $order->qty, 'order_ttl_qty' => $order->ttl_qty, 'order_ttl_amount' => $order->ttl_amount, 'order_cbm' => $order->cbm, 'order_ttl_cbm' => $order->ttl_cbm, 'order_kg' => $order->kg, 'order_ttl_kg' => $order->ttl_kg, ]); } return redirect()->back()->with('success', "Shipment $newShipmentId created successfully!"); } public function edit($id) { $shipment = Shipment::with('orders')->findOrFail($id); return view('admin.shipments.show', [ 'shipment' => $shipment, 'orders' => $shipment->orders, 'isViewMode' => false // This is the edit mode ]); } public function show($id) { $mode = request()->get('mode', 'view'); $shipment = Shipment::with(['items.order'])->findOrFail($id); // Get orders from shipment items $orders = collect(); foreach ($shipment->items as $item) { if ($item->order) { $orders->push($item->order); } } // Get orders not assigned to any shipment (available orders) $usedOrderIds = ShipmentItem::pluck('order_id')->toArray(); $availableOrders = Order::whereNotIn('id', $usedOrderIds)->get(); return view('admin.view-shipment', [ 'shipment' => $shipment, 'orders' => $orders, 'mode' => $mode, 'availableOrders' => $availableOrders ]); } public function addOrders(Request $request, $id) { $request->validate([ 'order_ids' => 'required|array|min:1' ]); $shipment = Shipment::findOrFail($id); $orderIds = $request->order_ids; DB::beginTransaction(); try { $orders = Order::whereIn('id', $orderIds)->get(); $addedOrders = []; foreach ($orders as $order) { // Prevent duplicates if (ShipmentItem::where('shipment_id', $shipment->id)->where('order_id', $order->id)->exists()) { continue; } ShipmentItem::create([ 'shipment_id' => $shipment->id, 'order_id' => $order->id, 'order_ctn' => $order->ctn, 'order_qty' => $order->qty, 'order_ttl_qty' => $order->ttl_qty, 'order_ttl_amount' => $order->ttl_amount, 'order_cbm' => $order->cbm, 'order_ttl_cbm' => $order->ttl_cbm, 'order_kg' => $order->kg, 'order_ttl_kg' => $order->ttl_kg, ]); $addedOrders[] = $order; } // Recalculate totals $this->recalculateShipmentTotals($shipment->id); DB::commit(); $shipment->refresh(); $shipment->load('items.order'); // Get updated orders list $updatedOrders = collect(); foreach ($shipment->items as $item) { if ($item->order) { $updatedOrders->push($item->order); } } return response()->json([ 'success' => true, 'message' => 'Orders added to shipment successfully.', 'shipment' => $shipment, 'orders' => $addedOrders ]); } catch (\Exception $e) { DB::rollBack(); return response()->json([ 'success' => false, 'message' => 'Failed to add orders: ' . $e->getMessage() ], 500); } } /** * Update Shipment status from action button */ public function updateStatus(Request $request) { $request->validate([ 'shipment_id' => 'required|exists:shipments,id', 'status' => 'required|string' ]); // 1) Update shipment status $shipment = Shipment::findOrFail($request->shipment_id); $shipment->status = $request->status; $shipment->save(); // 2) Update ALL related orders' status foreach ($shipment->orders as $order) { $order->status = $shipment->status; // status is string: pending, in_transit, dispatched, delivered $order->save(); } return redirect()->back()->with( 'success', "Shipment status updated to {$shipment->statusLabel()} and related orders updated." ); } /** * Update shipment details */ public function update(Request $request, $id) { $shipment = Shipment::findOrFail($id); $data = $request->validate([ 'origin' => 'required|string', 'destination' => 'required|string', 'shipment_date' => 'required|date', 'status' => 'required|string', ]); $shipment->update($data); // If it's an AJAX request, return JSON response if ($request->ajax() || $request->wantsJson()) { return response()->json([ 'success' => true, 'message' => 'Shipment updated successfully.' ]); } return redirect()->back()->with('success', 'Shipment updated successfully.'); } /** * Delete shipment permanently */ public function destroy($id, Request $request) { $shipment = Shipment::findOrFail($id); // Delete shipment items ShipmentItem::where('shipment_id', $shipment->id)->delete(); // Delete shipment itself $shipment->delete(); // If it's an AJAX request, return JSON response if ($request->ajax() || $request->wantsJson()) { return response()->json([ 'success' => true, 'message' => 'Shipment deleted successfully.' ]); } return redirect()->route('admin.shipments') ->with('success', 'Shipment deleted successfully.'); } public function removeOrder(Request $request) { $request->validate([ 'shipment_id' => 'required|exists:shipments,id', 'order_id' => 'required|exists:orders,id' ]); $shipmentId = $request->shipment_id; $orderId = $request->order_id; // Get order data before deletion $order = Order::findOrFail($orderId); // Delete pivot entry ShipmentItem::where('shipment_id', $shipmentId) ->where('order_id', $orderId) ->delete(); // Recalculate totals $shipment = $this->recalculateShipmentTotals($shipmentId); if ($request->ajax() || $request->wantsJson()) { return response()->json([ 'success' => true, 'message' => 'Order removed successfully.', 'order' => $order, 'shipment' => $shipment ]); } return back()->with('success', 'Order removed successfully.'); } private function recalculateShipmentTotals($shipmentId) { $shipment = Shipment::with('items')->findOrFail($shipmentId); // Use the shipment items to calculate totals $shipment->total_ctn = $shipment->items->sum('order_ctn'); $shipment->total_qty = $shipment->items->sum('order_qty'); $shipment->total_ttl_qty = $shipment->items->sum('order_ttl_qty'); $shipment->total_amount = $shipment->items->sum('order_ttl_amount'); $shipment->total_cbm = $shipment->items->sum('order_cbm'); $shipment->total_ttl_cbm = $shipment->items->sum('order_ttl_cbm'); $shipment->total_kg = $shipment->items->sum('order_kg'); $shipment->total_ttl_kg = $shipment->items->sum('order_ttl_kg'); $shipment->save(); $shipment->refresh(); // Refresh to get updated data return $shipment; } // Helper method to get available orders for a shipment public function getAvailableOrders($shipmentId) { $shipment = Shipment::findOrFail($shipmentId); // Get all used order IDs $usedOrderIds = ShipmentItem::pluck('order_id')->toArray(); // Remove orders that are already in this shipment $shipmentOrderIds = $shipment->items->pluck('order_id')->toArray(); $availableOrderIds = array_diff($usedOrderIds, $shipmentOrderIds); $availableOrders = Order::whereNotIn('id', $availableOrderIds)->get(); return response()->json([ 'success' => true, 'availableOrders' => $availableOrders ]); } }