Coverage for modules\trucking\api.py: 32%
221 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-06 17:01 +0800
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-06 17:01 +0800
1from ninja import Router
2from typing import List, Optional
3from uuid import UUID
4from core.auth import JWTAuth
5from core.permissions import require_tenant, require_admin
6from core.exceptions import NotFoundException
7from .schemas import (
8 VehicleIn, VehicleOut,
9 DriverIn, DriverOut,
10 FuelCardIn, FuelCardOut,
11 FuelLogIn, FuelLogOut,
12 MaintenanceLogIn, MaintenanceLogOut,
13 TripIn, TripOut,
14 DeliveryIn, DeliveryOut, DeliveryUpdateProof,
15)
16from .models import Vehicle, Driver, FuelCard, FuelLog, MaintenanceLog, Trip, Delivery
18router = Router(tags=["Trucking"])
20# ── Vehicles ──────────────────────────────────────────────────────────────────
21@router.get("/vehicles", auth=JWTAuth(), response={200: List[VehicleOut]})
22def list_vehicles(request, status: Optional[str] = None):
23 tenant_id = require_tenant(request)
24 qs = Vehicle.objects.filter(tenant_id=tenant_id)
25 if status:
26 qs = qs.filter(status=status)
27 return list(qs)
30@router.post("/vehicles", auth=JWTAuth(), response={201: VehicleOut})
31@require_admin
32def create_vehicle(request, payload: VehicleIn):
33 tenant_id = require_tenant(request)
34 vehicle = Vehicle.objects.create(tenant_id=tenant_id, **payload.dict())
35 return 201, vehicle
38@router.get("/vehicles/{vehicle_id}", auth=JWTAuth(), response={200: VehicleOut})
39def get_vehicle(request, vehicle_id: UUID):
40 tenant_id = require_tenant(request)
41 vehicle = Vehicle.objects.filter(id=vehicle_id, tenant_id=tenant_id).first()
42 if not vehicle:
43 raise NotFoundException("Vehicle not found.")
44 return vehicle
47@router.put("/vehicles/{vehicle_id}", auth=JWTAuth(), response={200: VehicleOut})
48@require_admin
49def update_vehicle(request, vehicle_id: UUID, payload: VehicleIn):
50 tenant_id = require_tenant(request)
51 vehicle = Vehicle.objects.filter(id=vehicle_id, tenant_id=tenant_id).first()
52 if not vehicle:
53 raise NotFoundException("Vehicle not found.")
54 for key, value in payload.dict().items():
55 setattr(vehicle, key, value)
56 vehicle.save()
57 return vehicle
60@router.delete("/vehicles/{vehicle_id}", auth=JWTAuth())
61@require_admin
62def delete_vehicle(request, vehicle_id: UUID):
63 tenant_id = require_tenant(request)
64 vehicle = Vehicle.objects.filter(id=vehicle_id, tenant_id=tenant_id).first()
65 if not vehicle:
66 raise NotFoundException("Vehicle not found.")
67 vehicle.delete()
68 return {"success": True}
71# ── Drivers ────────────────────────────────────────────────────────────────────
72@router.get("/drivers", auth=JWTAuth(), response={200: List[DriverOut]})
73def list_drivers(request, status: Optional[str] = None):
74 tenant_id = require_tenant(request)
75 qs = Driver.objects.filter(tenant_id=tenant_id)
76 if status:
77 qs = qs.filter(status=status)
78 return list(qs)
81@router.post("/drivers", auth=JWTAuth(), response={201: DriverOut})
82@require_admin
83def create_driver(request, payload: DriverIn):
84 tenant_id = require_tenant(request)
85 driver = Driver.objects.create(tenant_id=tenant_id, **payload.dict())
86 return 201, driver
89@router.get("/drivers/{driver_id}", auth=JWTAuth(), response={200: DriverOut})
90def get_driver(request, driver_id: UUID):
91 tenant_id = require_tenant(request)
92 driver = Driver.objects.filter(id=driver_id, tenant_id=tenant_id).first()
93 if not driver:
94 raise NotFoundException("Driver not found.")
95 return driver
98@router.put("/drivers/{driver_id}", auth=JWTAuth(), response={200: DriverOut})
99def update_driver(request, driver_id: UUID, payload: DriverIn):
100 tenant_id = require_tenant(request)
101 driver = Driver.objects.filter(id=driver_id, tenant_id=tenant_id).first()
102 if not driver:
103 raise NotFoundException("Driver not found.")
104 for key, value in payload.dict().items():
105 setattr(driver, key, value)
106 driver.save()
107 return driver
110# ── Fuel Cards ────────────────────────────────────────────────────────────────
111@router.get("/fuel-cards", auth=JWTAuth(), response={200: List[FuelCardOut]})
112def list_fuel_cards(request):
113 tenant_id = require_tenant(request)
114 return list(FuelCard.objects.filter(tenant_id=tenant_id))
117@router.post("/fuel-cards", auth=JWTAuth(), response={201: FuelCardOut})
118@require_admin
119def create_fuel_card(request, payload: FuelCardIn):
120 tenant_id = require_tenant(request)
121 card = FuelCard.objects.create(tenant_id=tenant_id, **payload.dict())
122 return 201, card
125@router.delete("/fuel-cards/{card_id}", auth=JWTAuth())
126def delete_fuel_card(request, card_id: UUID):
127 tenant_id = require_tenant(request)
128 card = FuelCard.objects.filter(id=card_id, tenant_id=tenant_id).first()
129 if not card:
130 raise NotFoundException("Fuel card not found.")
131 card.delete()
132 return {"success": True}
135# ── Fuel Logs ─────────────────────────────────────────────────────────────────
136@router.get("/fuel-logs", auth=JWTAuth(), response={200: List[FuelLogOut]})
137def list_fuel_logs(request, vehicle_id: Optional[UUID] = None):
138 tenant_id = require_tenant(request)
139 qs = FuelLog.objects.filter(tenant_id=tenant_id)
140 if vehicle_id:
141 qs = qs.filter(vehicle_id=vehicle_id)
142 return list(qs.select_related("vehicle", "driver"))
145@router.post("/fuel-logs", auth=JWTAuth(), response={201: FuelLogOut})
146@require_admin
147def create_fuel_log(request, payload: FuelLogIn):
148 tenant_id = require_tenant(request)
149 log = FuelLog.objects.create(tenant_id=tenant_id, **payload.dict())
150 return 201, log
153# ── Maintenance Logs ───────────────────────────────────────────────────────────
154@router.get("/maintenance-logs", auth=JWTAuth(), response={200: List[MaintenanceLogOut]})
155def list_maintenance_logs(request, vehicle_id: Optional[UUID] = None):
156 tenant_id = require_tenant(request)
157 qs = MaintenanceLog.objects.filter(tenant_id=tenant_id)
158 if vehicle_id:
159 qs = qs.filter(vehicle_id=vehicle_id)
160 return list(qs.select_related("vehicle"))
163@router.post("/maintenance-logs", auth=JWTAuth(), response={201: MaintenanceLogOut})
164@require_admin
165def create_maintenance_log(request, payload: MaintenanceLogIn):
166 tenant_id = require_tenant(request)
167 log = MaintenanceLog.objects.create(tenant_id=tenant_id, **payload.dict())
168 return 201, log
171@router.put("/maintenance-logs/{log_id}", auth=JWTAuth(), response={200: MaintenanceLogOut})
172def update_maintenance_log(request, log_id: UUID, payload: MaintenanceLogIn):
173 tenant_id = require_tenant(request)
174 log = MaintenanceLog.objects.filter(id=log_id, tenant_id=tenant_id).first()
175 if not log:
176 raise NotFoundException("Maintenance log not found.")
177 for key, value in payload.dict().items():
178 setattr(log, key, value)
179 log.save()
180 return log
183# ── Trips ─────────────────────────────────────────────────────────────────────
184@router.get("/trips", auth=JWTAuth(), response={200: List[TripOut]})
185def list_trips(request, status: Optional[str] = None, driver_id: Optional[UUID] = None):
186 tenant_id = require_tenant(request)
187 qs = Trip.objects.filter(tenant_id=tenant_id)
188 if status:
189 qs = qs.filter(status=status)
190 if driver_id:
191 qs = qs.filter(driver_id=driver_id)
192 return list(qs.select_related("vehicle", "driver"))
195@router.post("/trips", auth=JWTAuth(), response={201: TripOut})
196@require_admin
197def create_trip(request, payload: TripIn):
198 tenant_id = require_tenant(request)
199 trip = Trip.objects.create(tenant_id=tenant_id, **payload.dict())
200 return 201, trip
203@router.get("/trips/{trip_id}", auth=JWTAuth(), response={200: TripOut})
204def get_trip(request, trip_id: UUID):
205 tenant_id = require_tenant(request)
206 trip = Trip.objects.filter(id=trip_id, tenant_id=tenant_id).first()
207 if not trip:
208 raise NotFoundException("Trip not found.")
209 return trip
212@router.put("/trips/{trip_id}", auth=JWTAuth(), response={200: TripOut})
213def update_trip(request, trip_id: UUID, payload: TripIn):
214 tenant_id = require_tenant(request)
215 trip = Trip.objects.filter(id=trip_id, tenant_id=tenant_id).first()
216 if not trip:
217 raise NotFoundException("Trip not found.")
218 for key, value in payload.dict().items():
219 setattr(trip, key, value)
220 trip.save()
221 return trip
224# ── Deliveries ─────────────────────────────────────────────────────────────────
225@router.get("/deliveries", auth=JWTAuth(), response={200: List[DeliveryOut]})
226def list_deliveries(request, trip_id: Optional[UUID] = None):
227 tenant_id = require_tenant(request)
228 qs = Delivery.objects.filter(tenant_id=tenant_id)
229 if trip_id:
230 qs = qs.filter(trip_id=trip_id)
231 return qs
234@router.post("/deliveries", auth=JWTAuth(), response={201: DeliveryOut})
235def create_delivery(request, payload: DeliveryIn):
236 tenant_id = require_tenant(request)
237 data = payload.dict()
238 trip_id = data.pop("trip_id", None)
239 if trip_id:
240 data["trip_id"] = trip_id
241 delivery = Delivery.objects.create(tenant_id=tenant_id, **data)
242 return delivery
245@router.get("/deliveries/{delivery_id}", auth=JWTAuth(), response={200: DeliveryOut})
246def get_delivery(request, delivery_id: UUID):
247 tenant_id = require_tenant(request)
248 delivery = Delivery.objects.filter(id=delivery_id, tenant_id=tenant_id).first()
249 if not delivery:
250 raise NotFoundException("Delivery not found.")
251 return delivery
254@router.put("/deliveries/{delivery_id}", auth=JWTAuth(), response={200: DeliveryOut})
255def update_delivery(request, delivery_id: UUID, payload: DeliveryIn):
256 tenant_id = require_tenant(request)
257 delivery = Delivery.objects.filter(id=delivery_id, tenant_id=tenant_id).first()
258 if not delivery:
259 raise NotFoundException("Delivery not found.")
260 data = payload.dict()
261 trip_id = data.pop("trip_id", None)
262 if trip_id is not None:
263 delivery.trip_id = trip_id
264 for key, value in data.items():
265 if key != "trip_id":
266 setattr(delivery, key, value)
267 delivery.save()
268 return delivery
271@router.patch("/deliveries/{delivery_id}/proof", auth=JWTAuth(), response={200: DeliveryOut})
272def update_delivery_proof(request, delivery_id: UUID, payload: DeliveryUpdateProof):
273 tenant_id = require_tenant(request)
274 delivery = Delivery.objects.filter(id=delivery_id, tenant_id=tenant_id).first()
275 if not delivery:
276 raise NotFoundException("Delivery not found.")
277 for key, value in payload.dict(exclude_unset=True).items():
278 setattr(delivery, key, value)
279 delivery.save()
280 return delivery
283@router.delete("/deliveries/{delivery_id}", auth=JWTAuth(), response={204: None})
284def delete_delivery(request, delivery_id: UUID):
285 tenant_id = require_tenant(request)
286 delivery = Delivery.objects.filter(id=delivery_id, tenant_id=tenant_id).first()
287 if not delivery:
288 raise NotFoundException("Delivery not found.")
289 delivery.delete()
290 return None