# 09 — Fraud Controls & Audit Trail **Status**: Not Started **Depends On**: 08 — Sales Agent Controls --- ## Goal A dedicated, immutable audit log plus automated anomaly detection over everything built in steps 01–08. Covers [mainPlan.md §18](./mainPlan.md#18-compliance--audit) (`logAuditEvent`, `checkLicenseCompliance`, `generateComplianceReport`) and the fraud-relevant piece of §15 (`sendRouteDeviationAlert`) and §12 (return-without-prior-sale detection). --- ## Background / Context Every prior step in this plan references "logged" or "flagged" behavior (override logs, loading discrepancies, reconciliation variances) — this step is where that logging actually becomes a queryable, immutable audit trail instead of scattered per-feature notes. --- ## Data Model Changes ### Audit Log Entry `UserId`, `Action`, `EntityType`, `EntityId`, `OldValue`/`NewValue` (JSON snapshot), `CreatedAt`, `SourceIp`/`RequestId` if available. Append-only — no update or delete path exposed anywhere. ### Anomaly Flag `Type` (`ReturnWithoutSale`, `RouteDeviation`, `HighVarianceRate`, `CashShortfall`), `EntityType`/ `EntityId`, `RaisedAt`, `Status` (`Open`, `Reviewed`, `Dismissed`). ### Return Approval Record Reuses the `approveReturn` flow from [18_returns_claims.md](./18_returns_claims.md) — this step adds the automated pre-check (`ReturnWithoutSale`) that runs before a human approver sees it. --- ## API Endpoints | Function reference | Endpoint intent | |---|---| | `logAuditEvent(userId, action, entityType, entityId)` (§18) | Internal — called by every mutating service, not directly exposed | | `checkLicenseCompliance(entityId, licenseType)` (§18) | `GET /api/compliance/{entityType}/{entityId}/license-check` | | `generateComplianceReport(period)` (§18) | `GET /api/compliance/report?period=...` | | `sendRouteDeviationAlert(routeId, salesRepId)` (§15) | Not implemented. The underlying data now exists — `POST /api/routes/{id}/visits/check-in` ([11_route_sales_management.md](./11_route_sales_management.md)) computes and stores `IsDeviationFlagged` on each `RouteVisitLog` row (Haversine distance vs. the store's registered lat/long) — but nothing reads that flag to raise an alert/anomaly flag yet. | | — | `GET /api/audit-log?entityType=...&entityId=...` | | — | `GET /api/anomaly-flags?status=Open` | --- ## Automated Checks & Flags - Returns without a matching prior sale (`ReturnWithoutSale`). - Sales/deliveries outside the geofenced route (`RouteDeviation`) — the GPS check-in/out data this depends on now exists ([11_route_sales_management.md](./11_route_sales_management.md)'s `RouteVisitLog.IsDeviationFlagged`), but no anomaly flag or alert reads it yet. - Agents with consistently high variance rates across closed rides (`HighVarianceRate`) — reads `Variance Record` history from [07_ride_reconciliation.md](./07_ride_reconciliation.md). - Cash collected below invoiced amount (`CashShortfall`) — reads `Payment Record` from [06_sale_ride.md](./06_sale_ride.md). --- ## Business Rules - Every state-changing action across every feature must call `logAuditEvent` — this is the one cross-cutting requirement every other numbered step inherits once this lands. - Return approval requires supervisor sign-off before stock is credited back ([18_returns_claims.md](./18_returns_claims.md)), regardless of whether an anomaly flag fired. --- ## Fraud / Control Considerations - Anomaly reports are surfaced to area managers on a scheduled basis — batch job or on-demand endpoint, not real-time push, in v1. --- ## Open Questions - Should `logAuditEvent` be wired in as a cross-cutting concern (e.g. an EF `SaveChanges` interceptor) rather than an explicit call in every service, to guarantee nothing is missed? - What licenses does `checkLicenseCompliance` actually need to validate for this business — is regulated goods handling in scope at all, or is this function speculative until a real requirement shows up? --- ## Implementation Notes > Fill this section in during planning discussion before writing any code.