4.8 KiB
06 — Sale Ride Execution
Status: Done Depends On: 04 — Loading Event
Goal
Allow a sales agent on an active Sale ride to create sales in the field. Every sale creates an order. Immediate handover deducts vehicle stock and closes the order immediately. Non-immediate handover leaves the order pending for a future delivery ride.
Background / Context
SaleService.CreateAsyncalready exists and handles bothFulfillImmediatelypaths.- What Step 06 adds: vehicle stock validation and deduction on immediate handover, and the Payment recording endpoint.
Sale.PaymentStatusand thePaymentmodel were added in pre-planning.- Pricing is already resolved via
PriceListServiceinsideSaleService.
Data Model Changes
No new entities. Everything needed was added in pre-planning:
| What | Where | Status |
|---|---|---|
Payment entity |
Models/Payment.cs |
✅ Done |
PaymentStatus enum |
Models/Enums.cs |
✅ Done |
PaymentMethod enum |
Models/Enums.cs |
✅ Done |
Sale.PaymentStatus |
Models/Sale.cs |
✅ Done |
OrderItem.QuantityFulfilled |
Models/OrderItem.cs |
✅ Done |
Migration needed for: Payment table, Sale.PaymentStatus column, OrderItem.QuantityFulfilled column.
Changes to Existing SaleService
SaleService.CreateAsync needs two additions when FulfillImmediately = true and RideId is set:
- Stock check — for each item, verify vehicle has enough stock (
StockBalanceatLocationType.VehiclewithLocationId = VehicleId). Reject the whole sale if any item is short. - Stock deduction — debit vehicle balance per item, create one
StockMovementper item:MovementType.SaleFromLocationId = VehicleId,FromLocationType = VehicleReferenceId = OrderId
VehicleId is resolved from Sale.FK_RideId → DeliveryRun.FK_VehicleId.
SaleService constructor additions required:
IStockRepository— for stock balance check and debitIRideRepository— to look up the ride and resolveFK_VehicleId
When FulfillImmediately = false, no stock movement — order stays Pending for a delivery ride.
New Feature — Payment
Feature folder: Features/Payment/
API Endpoints
| Method | Route | Who | Description |
|---|---|---|---|
| POST | /api/sales/{id}/payments |
Driver/Agent | Record a payment against a sale |
| GET | /api/sales/{id}/payments |
Any | List all payments for a sale |
Payment Creation Logic
On POST /api/sales/{id}/payments:
- Validate sale exists and is not
Cancelled. - Add
Paymentrecord (FK_SaleId,FK_StoreIdfrom sale,Amount,Method,CollectedAt,CollectedBy). - Sum all payments for this sale. Recalculate
Sale.PaymentStatus:sum == 0→Pendingsum < Sale.TotalAmount→PartiallyPaidsum >= Sale.TotalAmount→Paid
- Update
Sale.PaymentStatusin the same transaction.
DTOs
public record CreatePaymentRequest(decimal Amount, PaymentMethod Method);
public record PaymentResponse(Guid Id, Guid SaleId, Guid StoreId, decimal Amount,
PaymentMethod Method, DateTime CollectedAt, Guid CollectedBy);
Business Rules
- Driver cannot sell more than vehicle stock for immediate handover — entire sale rejected if any item is short (no partial sale creation).
- Every sale creates an order, even when all items are handed over immediately.
FulfillImmediately = true:- Vehicle stock debited immediately per item.
- Order →
Fulfilled, Sale →Completedat creation time (already in SaleService). QuantityFulfilled = Quantityon all order items (already in SaleService).
FulfillImmediately = false:- No stock movement at sale time.
- Order →
Pending, Sale →Pendingat creation time.
- Payment can be recorded at any time — sale does not need to be
Completedfirst. - Overpayment is allowed (
sum > TotalAmount) —PaymentStatusstaysPaid. - A
Cancelledsale cannot receive new payments.
Implementation Build Order
- Migration —
Paymenttable,Sale.PaymentStatus,OrderItem.QuantityFulfilled - Extend
SaleService.CreateAsync— stock check + stock movement onFulfillImmediately - DTOs —
PaymentDTOs.csinFeatures/Payment/ - Repository —
IPaymentRepository,PaymentRepository - Service —
PaymentService - Controller —
PaymentController - DI registration in
Program.cs
Feature folder: Features/Payment/ (new), Features/Sale/ (extended)
Open Questions — Resolved
- ✅ Payment timing — can be recorded at any time, not locked to sale completion
- ✅ Overpayment — allowed, status stays Paid
- ✅ Stock check scope — entire sale rejected if any item is short, no partial creation
- ✅ Non-immediate handover — no stock movement at sale time, order picked up by delivery ride