frontend user-flow
This commit is contained in:
@@ -0,0 +1,175 @@
|
|||||||
|
# 20 · FRONTEND — Phase 1 (Inventory & Supply Chain)
|
||||||
|
|
||||||
|
> **Authoritative for:** frontend user-flows, the rules to follow the existing architecture, and the validation posture.
|
||||||
|
> **Navigation:** you arrived from `00-CORE.md`. The API contract this UI consumes is in `11-BACKEND-PHASE1.md`; business rules are in `10-BACKEND-PHASE1.md`. Record work in `Frontend/PROGRESS.md`.
|
||||||
|
> **Note:** the Frontend project is **already initialized** (Next.js, App Router, TypeScript). This document does **not** scaffold structure — it governs how to build within what exists.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Stack & architecture rules
|
||||||
|
|
||||||
|
| Concern | Rule |
|
||||||
|
|---|---|
|
||||||
|
| Framework | Next.js **App Router** + **TypeScript**. Work within the existing structure; do not restructure or introduce a competing router. |
|
||||||
|
| State / forms | **Plain React hooks** (`useState`, `useReducer`, custom hooks). No form/state library. |
|
||||||
|
| Validation | **Dependency-free** (hand-rolled helpers). See §3. |
|
||||||
|
| API access | A single **typed fetch client** (`lib/`) against `NEXT_PUBLIC_API_BASE_URL`; all calls go through it. No scattered `fetch()` in components. |
|
||||||
|
| Types | TS types in `types/` **mirror the API DTOs** in `11-BACKEND-PHASE1.md`. When the contract changes, update these first. |
|
||||||
|
| Auth | Store the bearer token from `/auth/login`; attach `Authorization: Bearer <token>` in the API client. |
|
||||||
|
|
||||||
|
Principles:
|
||||||
|
- **The API contract is the source of truth.** The UI adapts to `11-BACKEND-PHASE1.md`, never the reverse.
|
||||||
|
- **Keep components thin;** put data-fetching and derived state in hooks, request/response shaping in the API client.
|
||||||
|
- **One typed client method per endpoint**, returning the DTO type from `types/`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. User flows
|
||||||
|
|
||||||
|
The flow below is the end-to-end Phase-1 journey: replenishment need → procurement → receiving → QC hold → stock available → stock operations, with the reorder loop closing back. Colour = functional role (roles are conceptual; RBAC is not enforced in Phase 1).
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
START([Replenishment need]):::trigger
|
||||||
|
ALERT[/Reorder alert raised<br/>available at or below reorder point/]:::system
|
||||||
|
|
||||||
|
START --> REQ[Create Requisition<br/>Procurement officer]:::proc
|
||||||
|
ALERT --> REQ
|
||||||
|
REQ --> SUBREQ[Submit requisition]:::proc
|
||||||
|
SUBREQ --> RFQDEC{RFQ needed?}:::decision
|
||||||
|
RFQDEC -->|Yes| RFQ[Create RFQ and send to vendors]:::proc
|
||||||
|
RFQ --> QUOTE[Record vendor quotations]:::proc
|
||||||
|
QUOTE --> COMPARE[Compare and select vendor]:::proc
|
||||||
|
COMPARE --> PO[Create Purchase Order]:::proc
|
||||||
|
RFQDEC -->|No| PO
|
||||||
|
|
||||||
|
PO --> APPRDEC{approvalRequired?}:::decision
|
||||||
|
APPRDEC -->|No - Phase 1 default| AUTOAPP[[Auto-approved on creation]]:::system
|
||||||
|
APPRDEC -->|Yes| PENDING[Pending approval]:::proc
|
||||||
|
PENDING --> APPROVE{Approver decision}:::decision
|
||||||
|
APPROVE -->|Reject| REJECTED([PO rejected]):::endp
|
||||||
|
APPROVE -->|Approve| APPROVED[PO Approved and issued to vendor]:::proc
|
||||||
|
AUTOAPP --> APPROVED
|
||||||
|
|
||||||
|
APPROVED --> ARRIVE([Goods arrive from vendor]):::trigger
|
||||||
|
ARRIVE --> GRN[Create GRN against PO<br/>Storekeeper]:::wh
|
||||||
|
GRN --> ENTER[Enter qty, bin, batch/serial]:::wh
|
||||||
|
ENTER --> TOLDEC{Over-receipt beyond tolerance?}:::decision
|
||||||
|
TOLDEC -->|Yes| BLOCKREC([Blocked / warn]):::endp
|
||||||
|
TOLDEC -->|No| HOLDDEC{Inspection required?}:::decision
|
||||||
|
HOLDDEC -->|Yes| RECVHOLD[Receive as OnHold]:::wh
|
||||||
|
HOLDDEC -->|No| RECVOK[Receive as Available]:::wh
|
||||||
|
RECVHOLD --> CONFIRM[Confirm GRN]:::wh
|
||||||
|
RECVOK --> CONFIRM
|
||||||
|
CONFIRM --> FIFO[[Create FIFO layer + post stock ledger + update PO qtyReceived]]:::system
|
||||||
|
|
||||||
|
FIFO --> HELDQ{Line on hold?}:::decision
|
||||||
|
HELDQ -->|Yes| INSPECT[QC inspection<br/>Inventory controller]:::controller
|
||||||
|
INSPECT --> INSPDEC{Pass inspection?}:::decision
|
||||||
|
INSPDEC -->|Release| AVAIL
|
||||||
|
INSPDEC -->|Reject| PRET[Create Purchase Return]:::proc
|
||||||
|
PRET --> RETPOST[[Post outbound ledger]]:::system
|
||||||
|
RETPOST --> RETEND([Returned to vendor]):::endp
|
||||||
|
HELDQ -->|No| AVAIL([Stock Available]):::stock
|
||||||
|
|
||||||
|
AVAIL --> OPS{Stock operation}:::decision
|
||||||
|
OPS -->|Transfer| TR1[Create transfer<br/>source and destination]:::wh
|
||||||
|
OPS -->|Adjust| AD1[Create adjustment + reason code<br/>Inventory controller]:::controller
|
||||||
|
OPS -->|Count| CN1[Create count - cycle or full<br/>Storekeeper]:::wh
|
||||||
|
|
||||||
|
TR1 --> TRDISP[Dispatch]:::wh
|
||||||
|
TRDISP --> TRAVAIL{Available covers qty?}:::decision
|
||||||
|
TRAVAIL -->|No| TRBLOCK([Negative-stock block]):::endp
|
||||||
|
TRAVAIL -->|Yes| TRIT[[Consume source layers, status In-Transit]]:::system
|
||||||
|
TRIT --> TRREC[Receive at destination]:::wh
|
||||||
|
TRREC --> TRDONE[[Create destination layer at inherited cost - cost-preserving]]:::system
|
||||||
|
TRDONE --> AVAIL
|
||||||
|
|
||||||
|
AD1 --> ADDEC{Decrease exceeds available?}:::decision
|
||||||
|
ADDEC -->|Yes| ADBLOCK([Negative-stock block]):::endp
|
||||||
|
ADDEC -->|No| ADPOST[[Auto-post: FIFO consume/create + ledger]]:::system
|
||||||
|
ADPOST --> AVAIL
|
||||||
|
|
||||||
|
CN1 --> CNSNAP[[Snapshot system qty]]:::system
|
||||||
|
CNSNAP --> CNCOUNT[Enter counted qty]:::wh
|
||||||
|
CNCOUNT --> CNVAR[[Compute variance]]:::system
|
||||||
|
CNVAR --> CNPOST[Post count<br/>Inventory controller]:::controller
|
||||||
|
CNPOST --> CNADJ[[Variance adjustment + ledger]]:::system
|
||||||
|
CNADJ --> AVAIL
|
||||||
|
|
||||||
|
AVAIL -.->|monitor available vs reorder point| ALERT
|
||||||
|
|
||||||
|
classDef trigger fill:#fff2cc,stroke:#d6b656,color:#333;
|
||||||
|
classDef proc fill:#d5e8d4,stroke:#82b366,color:#333;
|
||||||
|
classDef wh fill:#ffe6cc,stroke:#d79b00,color:#333;
|
||||||
|
classDef controller fill:#e1d5e7,stroke:#9673a6,color:#333;
|
||||||
|
classDef system fill:#dae8fc,stroke:#6c8ebf,color:#333;
|
||||||
|
classDef stock fill:#d5e8d4,stroke:#2d7d2d,color:#173d17;
|
||||||
|
classDef decision fill:#f8f4d0,stroke:#b0a133,color:#333;
|
||||||
|
classDef endp fill:#f8cecc,stroke:#b85450,color:#333;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.1 Flows → API mapping
|
||||||
|
Each screen calls the endpoints in `11-BACKEND-PHASE1.md`. System steps (blue) are performed server-side — the UI only triggers them and renders the result.
|
||||||
|
|
||||||
|
| Flow | Screens | Key endpoints |
|
||||||
|
|---|---|---|
|
||||||
|
| Login | Login | `POST /auth/login` |
|
||||||
|
| Replenishment | Reorder alerts | `GET /stock/reorder-alerts`, `POST /stock/reorder-alerts/{itemId}/requisition` |
|
||||||
|
| Procurement | Requisition, RFQ, PO | `POST /requisitions`, `/rfqs`, `/rfqs/{id}/quotations`, `GET /rfqs/{id}/comparison`, `POST /purchase-orders`, `PUT /purchase-orders/{id}` |
|
||||||
|
| Receiving | GRN | `POST /grns`, `POST /grns/{id}/confirm`, `POST /grns/{id}/lines/{lineId}/release` |
|
||||||
|
| Returns | Purchase return | `POST /purchase-returns` |
|
||||||
|
| Stock enquiry | Stock views | `GET /stock/on-hand`, `/stock/ledger`, `/stock/valuation` |
|
||||||
|
| Transfer | Transfer | `POST /stock-transfers`, `/dispatch`, `/receive` |
|
||||||
|
| Adjustment | Adjustment | `POST /stock-adjustments` |
|
||||||
|
| Count | Count | `POST /stock-counts`, `PUT /stock-counts/{id}/counts`, `POST /stock-counts/{id}/post` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Validation posture (read carefully)
|
||||||
|
|
||||||
|
**Validate on the client for UX; never trust the client. The server is the authority.** The browser can be bypassed (devtools, direct API calls, replays), so client checks exist only to give fast feedback and reduce round-trips — they are **never** the enforcement point.
|
||||||
|
|
||||||
|
### 3.1 Two categories
|
||||||
|
**Client-side (UX only — safe to check locally):** purely input-level facts the browser already has.
|
||||||
|
- Required fields present.
|
||||||
|
- Format: SKU pattern, numeric fields numeric, date format, positive integers.
|
||||||
|
- Range/bounds: `qty > 0`, `unitPrice >= 0`, `factor > 0`.
|
||||||
|
- Simple cross-field input rules: transfer `destWarehouseId != srcWarehouseId`.
|
||||||
|
- Enum membership via constrained dropdowns (`itemType`, `trackingMode`, `countType`, `holdStatus`).
|
||||||
|
|
||||||
|
**Server-authoritative (client MUST NOT assume — only the server can judge):** anything depending on current server state.
|
||||||
|
- **Stock availability / negative-stock block** (depends on live ledger) — `STOCK_NEGATIVE_BLOCKED`.
|
||||||
|
- **FIFO layer sufficiency** on any issue.
|
||||||
|
- **Over/under-receipt tolerance** vs PO open quantity — `OVER_RECEIPT_TOLERANCE`.
|
||||||
|
- **SKU uniqueness** — `SKU_DUPLICATE`.
|
||||||
|
- **Batch expiry / on-hold issuability** — `EXPIRED_BATCH_BLOCKED`, `ONHOLD_NOT_ISSUABLE`.
|
||||||
|
- **PO editability** (status-dependent) — `PO_NOT_EDITABLE`.
|
||||||
|
- **Referential existence/active status** of item/vendor/warehouse/bin.
|
||||||
|
- **Concurrency** (stale edit) — `CONCURRENCY_CONFLICT` via `ETag`/`If-Match`.
|
||||||
|
- **Reason code required/valid** — `REASON_CODE_REQUIRED`.
|
||||||
|
|
||||||
|
> Rule of thumb: if answering "is this allowed?" requires knowing the **current stock, a document's status, or another user's change**, it is server-authoritative. Do not gate submission on a client-side guess about it, and do not show it as "valid" until the server confirms.
|
||||||
|
|
||||||
|
### 3.2 Handling server responses
|
||||||
|
- Always send the request and handle the outcome; the server response is the truth.
|
||||||
|
- On `400` `ValidationProblemDetails`, map `errors` to fields.
|
||||||
|
- On `409` / `422` domain errors, read `code` and show a specific message (e.g. `STOCK_NEGATIVE_BLOCKED` → "Not enough available stock"). Keep a `code → message` map in `lib/` so messages are consistent.
|
||||||
|
- On `412` (`CONCURRENCY_CONFLICT`), tell the user the record changed and refetch before retrying.
|
||||||
|
- Never silently swallow a `ProblemDetails`; surface it.
|
||||||
|
|
||||||
|
### 3.3 What not to do
|
||||||
|
- Don't disable the submit button based on a client assumption about stock, availability, or status.
|
||||||
|
- Don't reimplement FIFO/tolerance/negative-stock logic in the browser — you cannot see the live ledger.
|
||||||
|
- Don't trust quantities computed client-side for posting; send inputs and let the server compute costed movements.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Error & empty states
|
||||||
|
- Every list screen handles loading, empty, and error states explicitly.
|
||||||
|
- Surface the API client's normalized error (from `ProblemDetails`) with the domain `code` message where present.
|
||||||
|
- For transactional actions (GRN confirm, transfer dispatch/receive, adjustment, count post), show the server's returned side effects (created/consumed layers, ledger refs) as confirmation rather than assuming success.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*End of 20-FRONTEND.md. API contract: `11-BACKEND-PHASE1.md`. Record work: `Frontend/PROGRESS.md`.*
|
||||||
Reference in New Issue
Block a user