233 lines
8.9 KiB
PHP
233 lines
8.9 KiB
PHP
<?php
|
|
|
|
namespace Modules\Tms\Http\Controllers;
|
|
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Routing\Controller;
|
|
use Modules\Tms\Http\Controllers\Concerns\AuthorizesTms;
|
|
use Modules\Tms\Models\RoutePlan;
|
|
use Modules\Tms\Models\Shipment;
|
|
use Modules\Tms\Services\TmsRouteService;
|
|
use Modules\Tms\Utils\TmsUtil;
|
|
use Yajra\DataTables\Facades\DataTables;
|
|
|
|
class RouteController extends Controller
|
|
{
|
|
use AuthorizesTms;
|
|
|
|
public function __construct(
|
|
protected TmsUtil $tmsUtil,
|
|
protected TmsRouteService $routeService
|
|
) {}
|
|
|
|
public function index(Request $request)
|
|
{
|
|
$this->authorizeTmsAccess('tms.routes.view');
|
|
$business_id = $this->tmsUtil->getBusinessId();
|
|
$statuses = $this->routeService->routeStatuses();
|
|
|
|
if ($request->ajax()) {
|
|
$query = RoutePlan::where('business_id', $business_id)
|
|
->with(['driver:id,name', 'vehicle:id,plate_number'])
|
|
->withCount('stops')
|
|
->orderByDesc('route_date');
|
|
|
|
return DataTables::of($query)
|
|
->editColumn('route_date', fn ($row) => $row->route_date?->format('Y-m-d'))
|
|
->editColumn('status', fn ($row) => $statuses[$row->status] ?? $row->status)
|
|
->addColumn('driver_name', fn ($row) => $row->driver?->name ?? '—')
|
|
->addColumn('vehicle_plate', fn ($row) => $row->vehicle?->plate_number ?? '—')
|
|
->addColumn('action', function ($row) {
|
|
$html = '<div class="btn-group">';
|
|
$html .= '<a href="'.action([self::class, 'show'], [$row->id]).'" class="btn btn-xs btn-info"><i class="fa fa-eye"></i></a>';
|
|
if (auth()->user()->can('tms.routes.update')) {
|
|
$html .= ' <a href="'.action([self::class, 'edit'], [$row->id]).'" class="btn btn-xs btn-primary"><i class="glyphicon glyphicon-edit"></i></a>';
|
|
}
|
|
if (auth()->user()->can('tms.routes.delete')) {
|
|
$html .= ' <button type="button" data-href="'.action([self::class, 'destroy'], [$row->id]).'" class="btn btn-xs btn-danger delete_tms_route"><i class="glyphicon glyphicon-trash"></i></button>';
|
|
}
|
|
$html .= '</div>';
|
|
|
|
return $html;
|
|
})
|
|
->rawColumns(['action'])
|
|
->make(true);
|
|
}
|
|
|
|
return view('tms::routes.index', compact('statuses'));
|
|
}
|
|
|
|
public function create()
|
|
{
|
|
$this->authorizeTmsAccess('tms.routes.create');
|
|
$business_id = $this->tmsUtil->getBusinessId();
|
|
|
|
$pendingShipments = Shipment::where('business_id', $business_id)
|
|
->whereIn('status', ['draft', 'scheduled'])
|
|
->whereNull('route_id')
|
|
->orderBy('scheduled_at')
|
|
->get(['id', 'ref_no', 'destination_address', 'destination_latitude', 'destination_longitude']);
|
|
|
|
return view('tms::routes.create', [
|
|
'drivers' => $this->tmsUtil->getDriversDropdown($business_id),
|
|
'vehicles' => $this->tmsUtil->getVehiclesDropdown($business_id),
|
|
'locations' => $this->tmsUtil->getLocationsDropdown($business_id),
|
|
'pending_shipments' => $pendingShipments,
|
|
'statuses' => $this->routeService->routeStatuses(),
|
|
]);
|
|
}
|
|
|
|
public function store(Request $request)
|
|
{
|
|
$this->authorizeTmsAccess('tms.routes.create');
|
|
$business_id = $this->tmsUtil->getBusinessId();
|
|
|
|
$data = $request->validate([
|
|
'route_date' => 'required|date',
|
|
'driver_id' => 'nullable|exists:tms_drivers,id',
|
|
'vehicle_id' => 'nullable|exists:tms_vehicles,id',
|
|
'origin_location_id' => 'nullable|exists:business_locations,id',
|
|
'origin_latitude' => 'nullable|numeric|between:-90,90',
|
|
'origin_longitude' => 'nullable|numeric|between:-180,180',
|
|
'notes' => 'nullable|string',
|
|
'shipment_ids' => 'nullable|array',
|
|
'shipment_ids.*' => 'exists:tms_shipments,id',
|
|
]);
|
|
|
|
$route = $this->routeService->createRoute($business_id, [
|
|
'route_date' => $data['route_date'],
|
|
'driver_id' => $data['driver_id'] ?? null,
|
|
'vehicle_id' => $data['vehicle_id'] ?? null,
|
|
'origin_location_id' => $data['origin_location_id'] ?? null,
|
|
'origin_latitude' => $data['origin_latitude'] ?? null,
|
|
'origin_longitude' => $data['origin_longitude'] ?? null,
|
|
'notes' => $data['notes'] ?? null,
|
|
'status' => 'draft',
|
|
]);
|
|
|
|
if (! empty($data['shipment_ids'])) {
|
|
$this->routeService->addShipmentsToRoute($route, $data['shipment_ids']);
|
|
}
|
|
|
|
if ($request->boolean('optimize_now') && $route->stops()->exists()) {
|
|
$this->routeService->optimizeStops($route);
|
|
}
|
|
|
|
return redirect()
|
|
->action([self::class, 'show'], [$route->id])
|
|
->with('status', __('tms::lang.route_created'));
|
|
}
|
|
|
|
public function show($id)
|
|
{
|
|
$this->authorizeTmsAccess('tms.routes.view');
|
|
$route = $this->findRoute($id);
|
|
$route->load(['stops.shipment', 'driver', 'vehicle', 'originLocation']);
|
|
|
|
$mapStops = $route->stops->map(function ($s) {
|
|
if (! $s->latitude || ! $s->longitude) {
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'lat' => (float) $s->latitude,
|
|
'lng' => (float) $s->longitude,
|
|
'label' => $s->shipment?->ref_no ?? '#'.$s->stop_order,
|
|
];
|
|
})->filter()->values();
|
|
|
|
return view('tms::routes.show', [
|
|
'route' => $route,
|
|
'statuses' => $this->routeService->routeStatuses(),
|
|
'mapStops' => $mapStops,
|
|
'mapOrigin' => [
|
|
'lat' => $route->origin_latitude ? (float) $route->origin_latitude : null,
|
|
'lng' => $route->origin_longitude ? (float) $route->origin_longitude : null,
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function edit($id)
|
|
{
|
|
$this->authorizeTmsAccess('tms.routes.update');
|
|
$business_id = $this->tmsUtil->getBusinessId();
|
|
$route = $this->findRoute($id);
|
|
|
|
$pendingShipments = Shipment::where('business_id', $business_id)
|
|
->whereIn('status', ['draft', 'scheduled'])
|
|
->where(function ($q) use ($route) {
|
|
$q->whereNull('route_id')->orWhere('route_id', $route->id);
|
|
})
|
|
->orderBy('scheduled_at')
|
|
->get(['id', 'ref_no', 'destination_address', 'route_id']);
|
|
|
|
$selectedIds = $route->stops()->pluck('shipment_id')->filter()->all();
|
|
|
|
return view('tms::routes.edit', [
|
|
'route' => $route,
|
|
'drivers' => $this->tmsUtil->getDriversDropdown($business_id),
|
|
'vehicles' => $this->tmsUtil->getVehiclesDropdown($business_id),
|
|
'locations' => $this->tmsUtil->getLocationsDropdown($business_id),
|
|
'pending_shipments' => $pendingShipments,
|
|
'selected_ids' => $selectedIds,
|
|
'statuses' => $this->routeService->routeStatuses(),
|
|
]);
|
|
}
|
|
|
|
public function update(Request $request, $id)
|
|
{
|
|
$this->authorizeTmsAccess('tms.routes.update');
|
|
$route = $this->findRoute($id);
|
|
|
|
$data = $request->validate([
|
|
'route_date' => 'required|date',
|
|
'driver_id' => 'nullable|exists:tms_drivers,id',
|
|
'vehicle_id' => 'nullable|exists:tms_vehicles,id',
|
|
'origin_location_id' => 'nullable|exists:business_locations,id',
|
|
'origin_latitude' => 'nullable|numeric|between:-90,90',
|
|
'origin_longitude' => 'nullable|numeric|between:-180,180',
|
|
'notes' => 'nullable|string',
|
|
'status' => 'nullable|string|max:50',
|
|
]);
|
|
|
|
$route->update($data);
|
|
|
|
return redirect()
|
|
->action([self::class, 'show'], [$route->id])
|
|
->with('status', __('tms::lang.route_updated'));
|
|
}
|
|
|
|
public function optimize($id)
|
|
{
|
|
$this->authorizeTmsAccess('tms.routes.update');
|
|
$route = $this->findRoute($id);
|
|
$this->routeService->optimizeStops($route);
|
|
|
|
return redirect()
|
|
->action([self::class, 'show'], [$route->id])
|
|
->with('status', __('tms::lang.route_optimized'));
|
|
}
|
|
|
|
public function destroy(Request $request, $id)
|
|
{
|
|
$this->authorizeTmsAccess('tms.routes.delete');
|
|
$route = $this->findRoute($id);
|
|
|
|
Shipment::where('route_id', $route->id)->update(['route_id' => null]);
|
|
$route->stops()->delete();
|
|
$route->delete();
|
|
|
|
return $this->tmsUtil->jsonOrRedirect(
|
|
$request,
|
|
true,
|
|
__('tms::lang.route_deleted'),
|
|
action([self::class, 'index'])
|
|
);
|
|
}
|
|
|
|
protected function findRoute($id): RoutePlan
|
|
{
|
|
return RoutePlan::where('business_id', $this->tmsUtil->getBusinessId())->findOrFail($id);
|
|
}
|
|
}
|