Files
Kalana B. Thilakarathna 6d8b6b3e59 Add DeliveryTask, Payment, and Sale features
Implemented DeliveryTask, Payment, and Sale entities with full CRUD, business logic, and API endpoints. Updated Order/OrderItem for sale linkage and fulfillment tracking. Registered new DbSets and DI services. Revised documentation for delivery, sale, and payment workflows.
2026-08-24 21:34:57 +05:30

9.9 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.


Current Business Understanding

  • A Ride is one operational trip for one vehicle and one driver on one route for now.
  • RideType distinguishes business behavior:
    • Delivery ride: used to fulfill existing orders.
    • Sale ride: used by sales agents in the field to create sales, and every sale creates an order.
  • Sale and Order are different:
    • Sale is the commercial transaction created by the sales agent.
    • Order is the fulfillment document created from every sale.
    • Item lines are maintained once in OrderItem; Sale does not keep a separate item-line table.
  • Sale statuses use sales language such as Completed.
  • Order statuses use logistics language such as Fulfilled.

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 }

Meaning of statuses:

  • Draft: ride exists but operational work has not started.
  • Open: ride is prepared and ready for loading/preparation work.
  • Active: ride is live in the field.
  • CancelRequested: active ride is waiting for cancellation approval or rejection.
  • Cancelled: ride ended through approved cancellation.
  • Closed: ride ended normally.

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

  1. Driver submits cancel request with TypeCode = VehicleBreakdown.
  2. Ride status moves to CancelRequested.
  3. Supervisor views a breakdown assist endpoint that lists all currently Active rides with their current vehicle stock levels and the stranded ride's open orders. Supervisor decides manually which ride takes over which orders.
  4. Supervisor approves the cancel request.
  5. Ride moves to Cancelled once 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 plus 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

  1. A ride cannot transition Draft -> Open if IsActive = false on the driver profile. If EnforceLicenseExpiry = true, also block if license is expired. If EnforceMedicalClearance = true, also block if medical clearance is expired.
  2. A vehicle cannot be on more than one Open or Active ride at a time.
  3. Orders can only be assigned to a Delivery ride while it is in Draft status.
  4. Loading and unloading warehouse must be the same - enforced at close/cancel.
  5. A CancelRequested ride cannot close or activate until the request is resolved.
  6. All cancel requests must have a TypeCode - free text reason alone is not sufficient.
  7. All list endpoints return paginated responses.
  8. A ride uses a single route for now.

Open Questions

  1. Cancel request type codes - resolved. VehicleBreakdown, DriverUnavailable, RouteIssue, Other.
  2. Breakdown assist endpoint - resolved. System shows info only, supervisor decides manually. No auto-reassignment.
  3. Driver compliance - resolved. Checks are configurable via DriverComplianceConfig. IsActive is always enforced. License and medical checks are optional flags, default off.
  4. Multi-route ride assignment - deferred for now. Current model stays single-route.

Implementation Notes

Build order:

  1. Enums - RideType, RideStatus, CancelRequestType, CancelRequestStatus
  2. Entities - DriverProfile, DriverComplianceConfig, RideCancelRequest
  3. AppDBContext - new DbSets plus Fluent config
  4. Migrations - ride lifecycle schema and permissions
  5. DTOs - driver profile, compliance config, ride
  6. Repositories - ride and ride cancel request support
  7. Services - driver profile, compliance config, ride
  8. Controllers - driver profile, compliance config, ride
  9. DI registration in Program.cs

Feature folders: Features/Ride/, Features/DriverProfile/, Features/DriverComplianceConfig/