md file and gap fix
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
namespace Distribution.Features.Route;
|
||||
|
||||
public record CreateRouteRequest(string RouteName, string StartPoint, string EndPoint, decimal Length);
|
||||
public record CreateRouteRequest(Guid RegionId, string RouteName, string StartPoint, string EndPoint, decimal Length);
|
||||
|
||||
public record UpdateRouteRequest(string RouteName, string StartPoint, string EndPoint, decimal Length);
|
||||
public record UpdateRouteRequest(Guid RegionId, string RouteName, string StartPoint, string EndPoint, decimal Length);
|
||||
|
||||
public record RouteResponse(Guid Id, string RouteName, string StartPoint, string EndPoint, decimal Length, bool IsActive, DateTime CreatedAt);
|
||||
public record RouteResponse(Guid Id, Guid RegionId, string RouteName, string StartPoint, string EndPoint, decimal Length, bool IsActive, DateTime CreatedAt);
|
||||
|
||||
@@ -7,20 +7,21 @@ public class RouteService(IRouteRepository repo, IUnitOfWork uow)
|
||||
public async Task<List<RouteResponse>> GetAllAsync() =>
|
||||
(await repo.GetAllAsync())
|
||||
.Where(r => r.IsActive)
|
||||
.Select(r => new RouteResponse(r.Id, r.RouteName, r.StartPoint, r.EndPoint, r.Length, r.IsActive, r.CreatedAt))
|
||||
.Select(r => new RouteResponse(r.Id, r.FK_RegionId, r.RouteName, r.StartPoint, r.EndPoint, r.Length, r.IsActive, r.CreatedAt))
|
||||
.ToList();
|
||||
|
||||
public async Task<RouteResponse?> GetByIdAsync(Guid id)
|
||||
{
|
||||
var route = await repo.GetByIdAsync(id);
|
||||
if (route is null || !route.IsActive) return null;
|
||||
return new RouteResponse(route.Id, route.RouteName, route.StartPoint, route.EndPoint, route.Length, route.IsActive, route.CreatedAt);
|
||||
return new RouteResponse(route.Id, route.FK_RegionId, route.RouteName, route.StartPoint, route.EndPoint, route.Length, route.IsActive, route.CreatedAt);
|
||||
}
|
||||
|
||||
public async Task<RouteResponse> CreateAsync(CreateRouteRequest request, CancellationToken ct = default)
|
||||
{
|
||||
var route = new Models.Route
|
||||
{
|
||||
FK_RegionId = request.RegionId,
|
||||
RouteName = request.RouteName,
|
||||
StartPoint = request.StartPoint,
|
||||
EndPoint = request.EndPoint,
|
||||
@@ -37,13 +38,14 @@ public class RouteService(IRouteRepository repo, IUnitOfWork uow)
|
||||
await uow.RollbackAsync(ct);
|
||||
throw;
|
||||
}
|
||||
return new RouteResponse(route.Id, route.RouteName, route.StartPoint, route.EndPoint, route.Length, route.IsActive, route.CreatedAt);
|
||||
return new RouteResponse(route.Id, route.FK_RegionId, route.RouteName, route.StartPoint, route.EndPoint, route.Length, route.IsActive, route.CreatedAt);
|
||||
}
|
||||
|
||||
public async Task<RouteResponse?> UpdateAsync(Guid id, UpdateRouteRequest request, CancellationToken ct = default)
|
||||
{
|
||||
var route = await repo.GetByIdAsync(id);
|
||||
if (route is null || !route.IsActive) return null;
|
||||
route.FK_RegionId = request.RegionId;
|
||||
route.RouteName = request.RouteName;
|
||||
route.StartPoint = request.StartPoint;
|
||||
route.EndPoint = request.EndPoint;
|
||||
@@ -59,7 +61,7 @@ public class RouteService(IRouteRepository repo, IUnitOfWork uow)
|
||||
await uow.RollbackAsync(ct);
|
||||
throw;
|
||||
}
|
||||
return new RouteResponse(route.Id, route.RouteName, route.StartPoint, route.EndPoint, route.Length, route.IsActive, route.CreatedAt);
|
||||
return new RouteResponse(route.Id, route.FK_RegionId, route.RouteName, route.StartPoint, route.EndPoint, route.Length, route.IsActive, route.CreatedAt);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteAsync(Guid id, CancellationToken ct = default)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
namespace Distribution.Features.Store;
|
||||
|
||||
public record CreateStoreRequest(
|
||||
Guid RouteId,
|
||||
string OwnerName,
|
||||
string StoreName,
|
||||
string StoreAddress,
|
||||
@@ -10,6 +11,7 @@ public record CreateStoreRequest(
|
||||
decimal CreditAmount);
|
||||
|
||||
public record UpdateStoreRequest(
|
||||
Guid RouteId,
|
||||
string OwnerName,
|
||||
string StoreName,
|
||||
string StoreAddress,
|
||||
@@ -20,6 +22,7 @@ public record UpdateStoreRequest(
|
||||
|
||||
public record StoreResponse(
|
||||
Guid Id,
|
||||
Guid RouteId,
|
||||
string OwnerName,
|
||||
string StoreName,
|
||||
string StoreAddress,
|
||||
|
||||
@@ -7,9 +7,6 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe
|
||||
{
|
||||
private const int GlobalScopeRoleLevel = 50;
|
||||
|
||||
private static StoreResponse ToResponse(Models.Store s) =>
|
||||
new(s.Id, s.OwnerName, s.StoreName, s.StoreAddress, s.Latitude, s.Longitude, s.OneTimeAmount, s.CreditAmount, s.IsActive, s.CreatedAt);
|
||||
|
||||
private bool HasGlobalScope => currentUser.RoleLevel >= GlobalScopeRoleLevel;
|
||||
|
||||
private async Task<bool> HasStoreScopeAsync(Guid storeId)
|
||||
@@ -26,7 +23,7 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe
|
||||
var accessibleStoreIds = (await scopeAccess.GetAccessibleStoreIdsAsync(currentUser.UserId.Value)).ToHashSet();
|
||||
stores = stores.Where(s => accessibleStoreIds.Contains(s.Id)).ToList();
|
||||
}
|
||||
return stores.Select(ToResponse).ToList();
|
||||
return stores.Select(s => new StoreResponse(s.Id, s.FK_RouteId, s.OwnerName, s.StoreName, s.StoreAddress, s.Latitude, s.Longitude, s.OneTimeAmount, s.CreditAmount, s.IsActive, s.CreatedAt)).ToList();
|
||||
}
|
||||
|
||||
public async Task<StoreResponse?> GetByIdAsync(Guid id)
|
||||
@@ -34,13 +31,14 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe
|
||||
var store = await repo.GetByIdAsync(id);
|
||||
if (store is null || !store.IsActive) return null;
|
||||
if (!await HasStoreScopeAsync(id)) return null;
|
||||
return ToResponse(store);
|
||||
return new StoreResponse(store.Id, store.FK_RouteId, store.OwnerName, store.StoreName, store.StoreAddress, store.Latitude, store.Longitude, store.OneTimeAmount, store.CreditAmount, store.IsActive, store.CreatedAt);
|
||||
}
|
||||
|
||||
public async Task<StoreResponse> CreateAsync(CreateStoreRequest request, CancellationToken ct = default)
|
||||
{
|
||||
var store = new Models.Store
|
||||
{
|
||||
FK_RouteId = request.RouteId,
|
||||
OwnerName = request.OwnerName,
|
||||
StoreName = request.StoreName,
|
||||
StoreAddress = request.StoreAddress,
|
||||
@@ -60,7 +58,7 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe
|
||||
await uow.RollbackAsync(ct);
|
||||
throw;
|
||||
}
|
||||
return ToResponse(store);
|
||||
return new StoreResponse(store.Id, store.FK_RouteId, store.OwnerName, store.StoreName, store.StoreAddress, store.Latitude, store.Longitude, store.OneTimeAmount, store.CreditAmount, store.IsActive, store.CreatedAt);
|
||||
}
|
||||
|
||||
public async Task<StoreResponse?> UpdateAsync(Guid id, UpdateStoreRequest request, CancellationToken ct = default)
|
||||
@@ -68,6 +66,7 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe
|
||||
var store = await repo.GetByIdAsync(id);
|
||||
if (store is null || !store.IsActive) return null;
|
||||
if (!await HasStoreScopeAsync(id)) return null;
|
||||
store.FK_RouteId = request.RouteId;
|
||||
store.OwnerName = request.OwnerName;
|
||||
store.StoreName = request.StoreName;
|
||||
store.StoreAddress = request.StoreAddress;
|
||||
@@ -86,7 +85,7 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe
|
||||
await uow.RollbackAsync(ct);
|
||||
throw;
|
||||
}
|
||||
return ToResponse(store);
|
||||
return new StoreResponse(store.Id, store.FK_RouteId, store.OwnerName, store.StoreName, store.StoreAddress, store.Latitude, store.Longitude, store.OneTimeAmount, store.CreditAmount, store.IsActive, store.CreatedAt);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteAsync(Guid id, CancellationToken ct = default)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Server=localhost;Port=3306;Database=distribution_db;User=root;Password=root"
|
||||
"DefaultConnection": "Server=187.127.102.190;Port=3306;Database=distribution_db;User=kalana;Password=kalana@hexdive"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# 00 — Existing Gap Fixes
|
||||
|
||||
**Status**: Not Started
|
||||
**Depends On**: —
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Gaps to Fix
|
||||
|
||||
### 1. Route DTO — Missing RegionId
|
||||
|
||||
|
||||
|
||||
### 2. Store DTO — Missing RouteId
|
||||
|
||||
|
||||
|
||||
### 3. Role Controller — Missing Authorization
|
||||
|
||||
|
||||
|
||||
### 4. Secrets & Configuration Management
|
||||
|
||||
|
||||
|
||||
### 5. Input Validation
|
||||
|
||||
|
||||
|
||||
### 6. Item Model — Missing Properties
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Scope (What This Step Does NOT Include)
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Business Rules
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
> Fill this section in during planning discussion before writing any code.
|
||||
@@ -0,0 +1,52 @@
|
||||
# 01 — Item Catalog
|
||||
|
||||
**Status**: Not Started
|
||||
**Depends On**: 00 — Existing Gap Fixes
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Background / Context
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Data Model Changes
|
||||
|
||||
### Item
|
||||
|
||||
|
||||
|
||||
### Related Changes to Existing Models
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Business Rules
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
> Fill this section in during planning discussion before writing any code.
|
||||
@@ -0,0 +1,62 @@
|
||||
# 02 — Stock Ledger
|
||||
|
||||
**Status**: Not Started
|
||||
**Depends On**: 01 — Item Catalog
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Background / Context
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Data Model Changes
|
||||
|
||||
### Stock Balance (per location)
|
||||
|
||||
|
||||
|
||||
### Stock Movement Transaction
|
||||
|
||||
|
||||
|
||||
### Location Abstraction (Warehouse vs Vehicle)
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Business Rules
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Fraud / Control Considerations
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
> Fill this section in during planning discussion before writing any code.
|
||||
@@ -0,0 +1,62 @@
|
||||
# 03 — Ride Lifecycle
|
||||
|
||||
**Status**: Not Started
|
||||
**Depends On**: 02 — Stock Ledger
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Background / Context
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Data Model Changes
|
||||
|
||||
### Ride (extending DeliveryRun)
|
||||
|
||||
|
||||
|
||||
### Ride State Machine
|
||||
|
||||
|
||||
|
||||
### Ride Type (Delivery vs Sale)
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Business Rules
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Fraud / Control Considerations
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
> Fill this section in during planning discussion before writing any code.
|
||||
@@ -0,0 +1,62 @@
|
||||
# 04 — Loading Event
|
||||
|
||||
**Status**: Not Started
|
||||
**Depends On**: 03 — Ride Lifecycle
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Background / Context
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Data Model Changes
|
||||
|
||||
### Loading Sheet
|
||||
|
||||
|
||||
|
||||
### Loading Line Item
|
||||
|
||||
|
||||
|
||||
### Two-Party Confirmation
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Business Rules
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Fraud / Control Considerations
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
> Fill this section in during planning discussion before writing any code.
|
||||
@@ -0,0 +1,62 @@
|
||||
# 05 — Delivery Ride Execution
|
||||
|
||||
**Status**: Not Started
|
||||
**Depends On**: 04 — Loading Event
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Background / Context
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Data Model Changes
|
||||
|
||||
### Delivery Task (per order on ride)
|
||||
|
||||
|
||||
|
||||
### Delivery Outcome
|
||||
|
||||
|
||||
|
||||
### Customer Confirmation
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Business Rules
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Fraud / Control Considerations
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
> Fill this section in during planning discussion before writing any code.
|
||||
@@ -0,0 +1,62 @@
|
||||
# 06 — Sale Ride Execution
|
||||
|
||||
**Status**: Not Started
|
||||
**Depends On**: 04 — Loading Event
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Background / Context
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Data Model Changes
|
||||
|
||||
### Field Invoice
|
||||
|
||||
|
||||
|
||||
### Field Invoice Line Item
|
||||
|
||||
|
||||
|
||||
### Payment Record
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Business Rules
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Fraud / Control Considerations
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
> Fill this section in during planning discussion before writing any code.
|
||||
@@ -0,0 +1,68 @@
|
||||
# 07 — Ride Close & Reconciliation
|
||||
|
||||
**Status**: Not Started
|
||||
**Depends On**: 05 — Delivery Ride Execution, 06 — Sale Ride Execution
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Background / Context
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Data Model Changes
|
||||
|
||||
### Ride Close Summary
|
||||
|
||||
|
||||
|
||||
### Variance Record
|
||||
|
||||
|
||||
|
||||
### Supervisor Approval
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## The Reconciliation Equation
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Business Rules
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Fraud / Control Considerations
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
> Fill this section in during planning discussion before writing any code.
|
||||
@@ -0,0 +1,62 @@
|
||||
# 08 — Sales Agent Controls
|
||||
|
||||
**Status**: Not Started
|
||||
**Depends On**: 07 — Ride Close & Reconciliation
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Background / Context
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Data Model Changes
|
||||
|
||||
### Agent Limit Configuration (per role level)
|
||||
|
||||
|
||||
|
||||
### Override Log
|
||||
|
||||
|
||||
|
||||
### Customer Credit Profile
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Business Rules
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Fraud / Control Considerations
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
> Fill this section in during planning discussion before writing any code.
|
||||
@@ -0,0 +1,68 @@
|
||||
# 09 — Fraud Controls & Audit Trail
|
||||
|
||||
**Status**: Not Started
|
||||
**Depends On**: 08 — Sales Agent Controls
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Background / Context
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Data Model Changes
|
||||
|
||||
### Audit Log Entry
|
||||
|
||||
|
||||
|
||||
### Anomaly Flag
|
||||
|
||||
|
||||
|
||||
### Return Approval Record
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Automated Checks & Flags
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Business Rules
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Fraud / Control Considerations
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
> Fill this section in during planning discussion before writing any code.
|
||||
@@ -0,0 +1,128 @@
|
||||
# ERP Distribution — Master Plan
|
||||
|
||||
## What Already Exists
|
||||
|
||||
| Area | Status | Notes |
|
||||
|---|---|---|
|
||||
| JWT Authentication | Done | Token generation, claims, 120-min expiry |
|
||||
| Permission-based Authorization | Done | Dynamic policy provider, role → permission mapping |
|
||||
| User Management | Done | CRUD, role assignment, scope assignment (region/route/store) |
|
||||
| Role Management | Done (read-only) | 6 roles seeded, no create/update via API |
|
||||
| Region / Route / Vehicle CRUD | Done | Full API surface with permission checks |
|
||||
| Store / Warehouse CRUD | Done | Full API surface with permission checks |
|
||||
| Order CRUD | Done | Scope-aware (user only sees their assigned stores) |
|
||||
| Unit of Work + Repositories | Done | Transaction support throughout |
|
||||
| Soft Deletes | Done | IsActive pattern on all entities |
|
||||
|
||||
## Known Gaps in Existing System (Pre-Work)
|
||||
|
||||
These are not new features — they are fixable holes in what's already built.
|
||||
They must be resolved before new feature steps begin because later steps depend on correct foundations.
|
||||
|
||||
| Gap | Impact |
|
||||
|---|---|
|
||||
| Route DTOs missing RegionId | Can't link routes to regions via API |
|
||||
| Store DTOs missing RouteId | Can't link stores to routes via API |
|
||||
| Role endpoints have no [Authorize] | Anyone can read roles without authentication |
|
||||
| JWT key & DB credentials in appsettings.json | Security risk in any shared/production environment |
|
||||
| No input validation on any DTO | Bad data can enter the system silently |
|
||||
| Item model is empty (no properties) | OrderItems reference items that have no data |
|
||||
|
||||
---
|
||||
|
||||
## Feature Steps
|
||||
|
||||
| # | Feature | Status | Depends On | Detail File |
|
||||
|---|---|---|---|---|
|
||||
| 00 | Existing Gap Fixes | Not Started | — | [00_existing_gaps.md](./00_existing_gaps.md) |
|
||||
| 01 | Item Catalog | Not Started | 00 | [01_item_catalog.md](./01_item_catalog.md) |
|
||||
| 02 | Stock Ledger | Not Started | 01 | [02_stock_ledger.md](./02_stock_ledger.md) |
|
||||
| 03 | Ride Lifecycle | Not Started | 02 | [03_ride_lifecycle.md](./03_ride_lifecycle.md) |
|
||||
| 04 | Loading Event | Not Started | 03 | [04_loading_event.md](./04_loading_event.md) |
|
||||
| 05 | Delivery Ride Execution | Not Started | 04 | [05_delivery_ride.md](./05_delivery_ride.md) |
|
||||
| 06 | Sale Ride Execution | Not Started | 04 | [06_sale_ride.md](./06_sale_ride.md) |
|
||||
| 07 | Ride Close & Reconciliation | Not Started | 05, 06 | [07_ride_reconciliation.md](./07_ride_reconciliation.md) |
|
||||
| 08 | Sales Agent Controls | Not Started | 07 | [08_sales_agent_controls.md](./08_sales_agent_controls.md) |
|
||||
| 09 | Fraud Controls & Audit Trail | Not Started | 08 | [09_fraud_controls.md](./09_fraud_controls.md) |
|
||||
|
||||
---
|
||||
|
||||
## Feature Summaries
|
||||
|
||||
### 00 — Existing Gap Fixes
|
||||
Fix all known holes in the current codebase before new features are layered on top.
|
||||
Includes DTO field gaps, missing authorization on role endpoints, secrets management,
|
||||
and adding input validation across all existing endpoints.
|
||||
**Gate**: All existing API endpoints behave correctly and securely before step 01 begins.
|
||||
|
||||
### 01 — Item Catalog
|
||||
Define the Item entity with real properties (name, SKU, unit of measure, category, price tiers).
|
||||
Items are the unit of stock — nothing in the stock ledger makes sense without them.
|
||||
This step also covers the Item CRUD API and how items relate to orders.
|
||||
**Gate**: An item can be created, updated, and referenced in an order with quantity and price.
|
||||
|
||||
### 02 — Stock Ledger
|
||||
Introduce the concept of stock per location. Every warehouse (fixed or vehicle) holds a quantity
|
||||
of each item. Stock is never edited directly — all changes happen through movement transactions
|
||||
(load, sale, return, damage, adjustment). The current balance is always the running sum.
|
||||
This is the single most important architectural decision for the whole system.
|
||||
**Gate**: Stock balance for any item at any location can be queried. Every change is a traceable transaction.
|
||||
|
||||
### 03 — Ride Lifecycle
|
||||
A ride is a session that turns a vehicle into a temporary warehouse. It has a state machine:
|
||||
Draft → Open → Active → Closed. A vehicle can only be on one active ride at a time.
|
||||
Two ride types exist: Delivery (pre-orders) and Sale (van sales). The ride tracks who is driving,
|
||||
which route, which vehicle, and when it started and ended.
|
||||
**Gate**: A ride can be created, opened, and closed. A vehicle's stock ledger is created when a ride opens.
|
||||
|
||||
### 04 — Loading Event
|
||||
When a ride opens, stock must be physically loaded from a source warehouse onto the vehicle.
|
||||
This is a two-party event: warehouse manager confirms what was dispatched, driver confirms what was received.
|
||||
Discrepancies between the two are flagged immediately and require resolution before the ride goes active.
|
||||
The loading event creates stock movement transactions that debit the warehouse and credit the vehicle.
|
||||
**Gate**: Stock moves from warehouse to vehicle via a confirmed loading event. Any discrepancy is logged.
|
||||
|
||||
### 05 — Delivery Ride Execution
|
||||
On a delivery ride, the driver fulfills pre-confirmed orders. Each order becomes a delivery task.
|
||||
The driver marks each as delivered, partially delivered, or failed. Partially delivered orders
|
||||
generate a credit note for the undelivered quantity. Customer confirmation (signature or OTP)
|
||||
is required to mark a delivery complete. Stock movements are created per delivery event.
|
||||
**Gate**: A delivery ride can execute all orders on its route and produce a delivery report.
|
||||
|
||||
### 06 — Sale Ride Execution
|
||||
On a sale ride, the driver visits customers and creates invoices on the spot. No pre-orders exist.
|
||||
The driver selects items, quantities, and prices (within approved discount limits), and the invoice
|
||||
is generated immediately. Payment is collected (cash or credit) at the point of sale.
|
||||
The vehicle's stock ledger decreases with each sale. The driver cannot sell more than what is loaded.
|
||||
**Gate**: A sale ride can generate invoices in the field. Stock on vehicle decreases with each sale.
|
||||
|
||||
### 07 — Ride Close & Reconciliation
|
||||
When a ride ends, the system performs a full reconciliation. The equation must balance:
|
||||
Opening Stock − Sales − Damages + Returns = Closing Stock (physical count)
|
||||
Cash Collected + Credit Extended = Total Sales − Returns
|
||||
Any variance must be approved by a supervisor with a reason code before the ride can close.
|
||||
Unresolved variances are logged as discrepancies against the agent's record.
|
||||
**Gate**: A ride cannot close with an unexplained variance. All discrepancies are permanently recorded.
|
||||
|
||||
### 08 — Sales Agent Controls
|
||||
Agents operate within boundaries set by their role level. This step wires role levels to business rules:
|
||||
discount limits per agent level, credit limits per customer, maximum single-transaction value,
|
||||
and approval chains for exceptions. Supervisors can grant one-time overrides logged against their account.
|
||||
**Gate**: An agent cannot exceed their limits without a logged, authorized override.
|
||||
|
||||
### 09 — Fraud Controls & Audit Trail
|
||||
A dedicated audit log records every sensitive event (who, what, when, from where).
|
||||
Automated checks flag: returns without a matching prior sale, sales outside the geofenced route,
|
||||
agents with consistently high variance rates, and cash collected below invoiced amounts.
|
||||
Return approval requires supervisor sign-off before stock is credited. Anomaly reports are
|
||||
surfaced to area managers on a scheduled basis.
|
||||
**Gate**: Every sensitive event is logged immutably. Automated flags surface to the right level of management.
|
||||
|
||||
---
|
||||
|
||||
## Rules
|
||||
|
||||
1. A feature file must be filled in and reviewed before implementation begins on that step.
|
||||
2. Status must be updated in this file whenever a step transitions.
|
||||
3. If implementation reveals a design change, the feature file is updated and this plan is revised.
|
||||
4. Steps must be done in dependency order — no skipping.
|
||||
Reference in New Issue
Block a user