Files
ERP_Distribution/Distribution/docs/06_sale_ride.md
T
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

4.8 KiB

06 — Sale Ride Execution

Status: Planning 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.CreateAsync already exists and handles both FulfillImmediately paths.
  • What Step 06 adds: vehicle stock validation and deduction on immediate handover, and the Payment recording endpoint.
  • Sale.PaymentStatus and the Payment model were added in pre-planning.
  • Pricing is already resolved via PriceListService inside SaleService.

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:

  1. Stock check — for each item, verify vehicle has enough stock (StockBalance at LocationType.Vehicle with LocationId = VehicleId). Reject the whole sale if any item is short.
  2. Stock deduction — debit vehicle balance per item, create one StockMovement per item:
    • MovementType.Sale
    • FromLocationId = VehicleId, FromLocationType = Vehicle
    • ReferenceId = OrderId

VehicleId is resolved from Sale.FK_RideId → DeliveryRun.FK_VehicleId.

SaleService constructor additions required:

  • IStockRepository — for stock balance check and debit
  • IRideRepository — to look up the ride and resolve FK_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:

  1. Validate sale exists and is not Cancelled.
  2. Add Payment record (FK_SaleId, FK_StoreId from sale, Amount, Method, CollectedAt, CollectedBy).
  3. Sum all payments for this sale. Recalculate Sale.PaymentStatus:
    • sum == 0 → Pending
    • sum < Sale.TotalAmount → PartiallyPaid
    • sum >= Sale.TotalAmount → Paid
  4. Update Sale.PaymentStatus in 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

  1. Driver cannot sell more than vehicle stock for immediate handover — entire sale rejected if any item is short (no partial sale creation).
  2. Every sale creates an order, even when all items are handed over immediately.
  3. FulfillImmediately = true:
    • Vehicle stock debited immediately per item.
    • Order → Fulfilled, Sale → Completed at creation time (already in SaleService).
    • QuantityFulfilled = Quantity on all order items (already in SaleService).
  4. FulfillImmediately = false:
    • No stock movement at sale time.
    • Order → Pending, Sale → Pending at creation time.
  5. Payment can be recorded at any time — sale does not need to be Completed first.
  6. Overpayment is allowed (sum > TotalAmount) — PaymentStatus stays Paid.
  7. A Cancelled sale cannot receive new payments.

Implementation Build Order

  1. Migration — Payment table, Sale.PaymentStatus, OrderItem.QuantityFulfilled
  2. Extend SaleService.CreateAsync — stock check + stock movement on FulfillImmediately
  3. DTOs — PaymentDTOs.cs in Features/Payment/
  4. Repository — IPaymentRepository, PaymentRepository
  5. Service — PaymentService
  6. Controller — PaymentController
  7. 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