9.4 KiB
03 — Ride Lifecycle
Status: Done Depends On: 02 — Stock Ledger
Goal
Turn the currently-unused DeliveryRun model into a fully working ride — a session that turns a
vehicle into a temporary warehouse. Covers driver profile management, ride creation, state
transitions, cancellation workflow, and the vehicle breakdown scenario.
Data Model Changes
New Entity — DriverProfile
Linked one-to-one with User. Holds compliance data only — no login or auth fields.
| Column | Type | Notes |
|---|---|---|
| Id | Guid | PK |
| FK_UserId | Guid | FK → User (unique) |
| LicenseNumber | string | |
| LicenseExpiry | DateTime | |
| MedicalClearanceExpiry | DateTime | |
| IsActive | bool | Can be suspended without deleting |
| CreatedAt | DateTime |
No license class tied to vehicle type — not required.
Compliance check rule: Driver is compliant if IsActive = true, plus any checks that are enabled in DriverComplianceConfig. By default all checks are disabled. When enabled: LicenseExpiry > today AND/OR MedicalClearanceExpiry > today.
Updated Entity — Vehicle
Add one nullable field:
| Column | Type | Notes |
|---|---|---|
| FK_DefaultWarehouseId | Guid? | If set, ride always loads from this warehouse. If null, warehouse is selected at ride creation. |
Updated Entity — DeliveryRun (becomes Ride)
Add the following fields to the existing DeliveryRun:
| Column | Type | Notes |
|---|---|---|
| RideType | RideType | Delivery or Sale |
| Status | RideStatus | State machine below |
| FK_DriverProfileId | Guid | FK → DriverProfile |
| FK_SourceWarehouseId | Guid | Where stock is loaded from. Must be the same warehouse stock returns to on cancel/close. |
| StartedAt | DateTime? | Set when Draft → Open |
| ActivatedAt | DateTime? | Set when Open → Active |
| ClosedAt | DateTime? | Set when Active → Closed |
New Enum — RideType
public enum RideType { Delivery, Sale }
New Enum — RideStatus
public enum RideStatus { Draft, Open, Active, CancelRequested, Cancelled, Closed }
New Entity — RideCancelRequest
| Column | Type | Notes |
|---|---|---|
| Id | Guid | PK |
| FK_RideId | Guid | FK → DeliveryRun |
| TypeCode | CancelRequestType | Reason category |
| Reason | string | Free text explanation |
| RequestedBy | Guid | UserId |
| RequestedAt | DateTime | |
| Status | CancelRequestStatus | Pending, Approved, Rejected |
| ApprovedBy | Guid? | UserId of approver |
| ApprovedAt | DateTime? |
New Enum — CancelRequestType
public enum CancelRequestType
{
VehicleBreakdown,
DriverUnavailable,
RouteIssue,
Other
}
New Entity — DriverComplianceConfig
Single-row configuration table. Controls which compliance checks are enforced at ride open time.
| Column | Type | Notes |
|---|---|---|
| Id | Guid | PK |
| EnforceLicenseExpiry | bool | Default false. If true, ride open is blocked if driver license is expired. |
| EnforceMedicalClearance | bool | Default false. If true, ride open is blocked if medical clearance is expired. |
| UpdatedAt | DateTime | |
| UpdatedBy | Guid | UserId of last person who changed the config. |
Managed via a dedicated config endpoint. Only one row ever exists.
New Enum — CancelRequestStatus
public enum CancelRequestStatus { Pending, Approved, Rejected }
State Machine
Draft → Open → Active → Closed
↘ CancelRequested → Cancelled
| Transition | Trigger | What it does |
|---|---|---|
| Draft → Open | POST /api/rides/{id}/open |
Validates driver compliance. Checks vehicle has no other Open/Active ride. Sets StartedAt. |
| Open → Active | POST /api/rides/{id}/activate |
Called by loading event confirmation (Step 04). Sets ActivatedAt. |
| Active → Closed | POST /api/rides/{id}/close |
Called after ride reconciliation (Step 07). All stock must be returned to source warehouse. Sets ClosedAt. |
| Active → CancelRequested | POST /api/rides/{id}/cancel-request |
Driver submits cancel request with type code and reason. Ride status changes to CancelRequested. |
| CancelRequested → Cancelled | POST /api/rides/cancel-requests/{id}/approve |
Supervisor approves. Ride status changes to Cancelled. All stock must return to source warehouse before cancel completes. |
| CancelRequested → Active | POST /api/rides/cancel-requests/{id}/reject |
Supervisor rejects. Ride returns to Active. |
Order assignment window: Orders can be assigned to a Delivery ride from Draft until the ride transitions to Open. No new orders after Open.
Warehouse Configuration
Per vehicle. A vehicle either has FK_DefaultWarehouseId set (always loads from that warehouse)
or it is null (warehouse is selected manually at ride creation time).
Loading and unloading warehouse are always the same — stock must return to its source warehouse before a ride can close or cancel.
Vehicle Breakdown Scenario
- Driver submits cancel request with
TypeCode = VehicleBreakdown. - Ride status moves to
CancelRequested. - Supervisor views a breakdown assist endpoint that lists all currently
Activerides with their current vehicle stock levels and the stranded ride's open orders — supervisor decides manually which ride takes over which orders. - Supervisor approves the cancel request.
- Ride moves to
Cancelledonce all stock is confirmed returned to source warehouse.
The system surfaces the information — it does not automatically reassign orders.
API Endpoints
Base route: /api/rides
| Method | Route | Description |
|---|---|---|
| POST | / |
Create ride (Draft). If vehicle has default warehouse, pre-populate it. |
| GET | / |
List rides (paginated). Filter by status, type, driverProfileId, vehicleId. |
| GET | /{id} |
Get ride detail |
| PUT | /{id}/driver |
Assign or change driver profile |
| POST | /{id}/open |
Transition Draft → Open |
| POST | /{id}/activate |
Transition Open → Active (called by loading event) |
| POST | /{id}/close |
Transition Active → Closed (called by reconciliation) |
| POST | /{id}/cancel-request |
Submit cancel request (Active rides only) |
| GET | /cancel-requests |
List cancel requests (paginated). Filter by status. |
| POST | /cancel-requests/{id}/approve |
Approve cancel request |
| POST | /cancel-requests/{id}/reject |
Reject cancel request |
| GET | /breakdown-assist/{rideId} |
Lists active rides with stock + stranded ride's open orders |
DriverComplianceConfig
Base route: /api/driver-compliance-config
| Method | Route | Description |
|---|---|---|
| GET | / |
Get current config |
| PUT | / |
Update config flags |
DriverProfile
Base route: /api/driver-profiles
| Method | Route | Description |
|---|---|---|
| POST | / |
Create driver profile (links to a User) |
| GET | / |
List driver profiles (paginated) |
| GET | /{id} |
Get driver profile detail |
| PUT | /{id} |
Update compliance fields |
| DELETE | /{id} |
Soft deactivate |
Business Rules
- A ride cannot transition Draft → Open if
IsActive = falseon the driver profile. IfEnforceLicenseExpiry = true, also blocks if license is expired. IfEnforceMedicalClearance = true, also blocks if medical clearance is expired. - A vehicle cannot be on more than one Open or Active ride at a time.
- Orders can only be assigned to a Delivery ride while it is in Draft status.
- Loading and unloading warehouse must be the same — enforced at close/cancel.
- A CancelRequested ride cannot close or activate until the request is resolved.
- All cancel requests must have a TypeCode — free text reason alone is not sufficient.
- All list endpoints return paginated responses.
Open Questions
- Cancel request type codes — ✅ Resolved.
VehicleBreakdown,DriverUnavailable,RouteIssue,Other. - Breakdown assist endpoint — ✅ Resolved. System shows info only, supervisor decides manually. No auto-reassignment.
- Driver compliance — ✅ Resolved. Checks are configurable via
DriverComplianceConfig.IsActiveis always enforced. License and medical checks are optional flags, default off.
Implementation Notes
Build order:
- ✅ Enums —
RideType,RideStatus,CancelRequestType,CancelRequestStatus→ added toModels/Enums.cs - ✅ Entities —
DriverProfile,DriverComplianceConfig,RideCancelRequest(new); updatedVehicle(+FK_DefaultWarehouseId), updatedDeliveryRun(+RideType,Status,FK_DriverProfileId,FK_SourceWarehouseId,StartedAt,ActivatedAt,ClosedAt) - ✅
AppDBContext— new DbSets + Fluent config - ✅ Migrations —
RideLifecycle(schema),RidePermissions(10 new permissions seeded across all roles) - ✅ DTOs —
DriverProfileDTOs.cs,DriverComplianceConfigDTOs.cs,RideDTOs.cs - ✅ Repositories —
IDriverProfileRepository,IDriverComplianceConfigRepository,IRideRepository,IRideCancelRequestRepository - ✅ Services —
DriverProfileService,DriverComplianceConfigService,RideService - ✅ Controllers —
DriverProfileController,DriverComplianceConfigController,RideController - ✅ DI registration in
Program.cs
Feature folders: Features/Ride/, Features/DriverProfile/, Features/DriverComplianceConfig/
Pending: dotnet ef database update to apply migrations to DB. Tests for the three new services.