ultimatepos/Modules/Tms/Http/Controllers/RouteController.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);
}
}