shipment
This commit is contained in:
157
app/Http/Controllers/Admin/ShipmentController.php
Normal file
157
app/Http/Controllers/Admin/ShipmentController.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Shipment;
|
||||
use App\Models\ShipmentItem;
|
||||
use App\Models\Order;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class ShipmentController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show shipment page (Create Shipment + Shipment List)
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// 1) Get all used order IDs
|
||||
$usedOrderIds = ShipmentItem::pluck('order_id')->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_ttl_kg' => $order->ttl_kg,
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', "Shipment $newShipmentId created successfully!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Show shipment details (for modal popup)
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$shipment = Shipment::findOrFail($id);
|
||||
|
||||
// Load full order data from orders table
|
||||
$orders = Order::whereIn('id',
|
||||
ShipmentItem::where('shipment_id', $id)->pluck('order_id')
|
||||
)->get();
|
||||
|
||||
return response()->json([
|
||||
'shipment' => $shipment,
|
||||
'orders' => $orders
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Update Shipment status from action button
|
||||
*/
|
||||
public function updateStatus(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'shipment_id' => 'required|exists:shipments,id',
|
||||
'status' => 'required|string'
|
||||
]);
|
||||
|
||||
$shipment = Shipment::findOrFail($request->shipment_id);
|
||||
$shipment->status = $request->status;
|
||||
$shipment->save();
|
||||
|
||||
return redirect()->back()->with('success', "Shipment status updated to {$shipment->statusLabel()}");
|
||||
}
|
||||
|
||||
}
|
||||
80
app/Models/Shipment.php
Normal file
80
app/Models/Shipment.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Shipment extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'shipment_id',
|
||||
'origin',
|
||||
'destination',
|
||||
'total_ctn',
|
||||
'total_qty',
|
||||
'total_ttl_qty',
|
||||
'total_amount',
|
||||
'total_cbm',
|
||||
'total_ttl_cbm',
|
||||
'total_kg',
|
||||
'total_ttl_kg',
|
||||
'status',
|
||||
'shipment_date',
|
||||
'meta',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'meta' => 'array',
|
||||
'shipment_date' => 'date',
|
||||
];
|
||||
|
||||
// ---------------------------
|
||||
// RELATIONSHIPS
|
||||
// ---------------------------
|
||||
|
||||
public function items()
|
||||
{
|
||||
return $this->hasMany(ShipmentItem::class);
|
||||
}
|
||||
|
||||
public function orders()
|
||||
{
|
||||
return $this->belongsToMany(Order::class, 'shipment_items', 'shipment_id', 'order_id');
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// STATUS CONSTANTS
|
||||
// ---------------------------
|
||||
|
||||
const STATUS_PENDING = 'pending';
|
||||
const STATUS_IN_TRANSIT = 'in_transit';
|
||||
const STATUS_DISPATCHED = 'dispatched';
|
||||
const STATUS_DELIVERED = 'delivered';
|
||||
|
||||
public static function statusOptions()
|
||||
{
|
||||
return [
|
||||
self::STATUS_PENDING => 'Pending',
|
||||
self::STATUS_IN_TRANSIT => 'In Transit',
|
||||
self::STATUS_DISPATCHED => 'Dispatched',
|
||||
self::STATUS_DELIVERED => 'Delivered',
|
||||
];
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// HELPERS
|
||||
// ---------------------------
|
||||
|
||||
public function totalOrdersCount()
|
||||
{
|
||||
return $this->items()->count();
|
||||
}
|
||||
|
||||
public function statusLabel()
|
||||
{
|
||||
return self::statusOptions()[$this->status] ?? ucfirst($this->status);
|
||||
}
|
||||
}
|
||||
46
app/Models/ShipmentItem.php
Normal file
46
app/Models/ShipmentItem.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ShipmentItem extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'shipment_id',
|
||||
'order_id',
|
||||
'order_ctn',
|
||||
'order_qty',
|
||||
'order_ttl_qty',
|
||||
'order_ttl_amount',
|
||||
'order_ttl_kg',
|
||||
];
|
||||
|
||||
// ---------------------------
|
||||
// RELATIONSHIPS
|
||||
// ---------------------------
|
||||
|
||||
public function shipment()
|
||||
{
|
||||
return $this->belongsTo(Shipment::class);
|
||||
}
|
||||
|
||||
public function order()
|
||||
{
|
||||
return $this->belongsTo(Order::class);
|
||||
}
|
||||
|
||||
// Helper: return order data with fallback to snapshot
|
||||
public function getDisplayQty()
|
||||
{
|
||||
return $this->order->qty ?? $this->order_qty;
|
||||
}
|
||||
|
||||
public function getDisplayAmount()
|
||||
{
|
||||
return $this->order->ttl_amount ?? $this->order_ttl_amount;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user