Files
ERP-core/Frontend/PROGRESS.md
T
Sasanka 0415794473 Refactor API modules to remove mock implementations and integrate real endpoints
- Updated stock-transfers.ts to replace mock data with API requests for stock transfers.
- Refactored stock.ts to utilize API calls for stock inquiries instead of mock data.
- Modified uoms.ts to implement real API requests for unit of measure operations.
- Transitioned vendors.ts to use actual API endpoints for vendor management, removing mock data handling.
- Updated warehouses.ts to replace mock implementations with real API calls for warehouse and bin management.
- Refactored wastage.ts to utilize stock adjustments and reason codes APIs, removing mock data dependencies.
- Adjusted procurement.ts to align with backend DTO shapes, ensuring consistency with planned backend structures.
2026-07-14 16:01:47 +05:30

36 KiB
Raw Blame History

Frontend — PROGRESS (Phase 1: Inventory & Supply Chain)

Legend: [ ] not started · [~] in progress · [x] done Spec: docs/20-FRONTEND.md (flows + rules) · docs/11-BACKEND-PHASE1.md (API contract) Convention: docs/01-DOC-GUIDE.md §6. Update this file in the same commit as the code. When ticking [x], append a short note + any deviation.

0. Foundation

  • NEXT_PUBLIC_API_BASE_URL wired (.env.local / .env.local.example, alongside the existing NEXT_PUBLIC_AUTH_API_BASE_URL) — points at Backend/ERPCore's https profile (7112)
  • Typed API client / fetch wrapper (lib/api-client.ts: apiRequest/apiRequestWithETag/buildQuery) + bearer token handling (lib/auth-token.ts, degrades gracefully — no login endpoint wired yet, see §1)
  • Shared TS types mirroring API DTOs (types/common.ts, types/master-data.ts, types/procurement.ts, types/grn.ts) — built only for the subset GRN needs (Item/Warehouse/Bin/Vendor/Uom/PurchaseOrder/Grn)
  • [~] Client validation helpers (lib/validations/grn.ts) — deviation: uses zod (already a project dependency, used by lib/validations.ts/login), not hand-rolled, to stay consistent with the codebase's existing pattern rather than introduce a second validation approach. UX-only; server remains authoritative (docs/20-FRONTEND.md §3)
  • ProblemDetails normalizer (ApiError in lib/api-client.ts) + code → message map (lib/error-map.ts)

Scope note: this foundation was built alongside the GRN feature and only covers the endpoints GRN consumes (items, warehouses/bins, vendors, uoms, purchase-orders, grns). Other master-data/procurement API methods still need their own lib/api/*.ts files when those screens are built.

2026-07-14 — mock data removed, lib/api/mock-data.ts deleted. Every lib/api/*.ts file's commented-out real fetch block was restored and the in-memory mock block deleted, per user request. Master Data + Procurement modules (categories, uoms, warehouses, items, vendors, requisitions, rfqs, purchase-orders) now call the live backend (Backend/ERPCore §1/§2, both implemented + smoke-tested). GRN/Stock/Purchase-Return/Reason-Code modules (grns, stock, stock-transfers, stock-adjustments, stock-counts, purchase-returns, reason-codes, wastage) also now call real (documented-or-assumed) endpoint paths, but no backend controller exists for any of them yet (Backend/PROGRESS.md §3/§4/§5 are unstarted, and Purchase Return is explicitly deferred) — those calls will 404 against a running backend until that work happens. This was a deliberate tradeoff the user confirmed explicitly (see the two AskUserQuestion exchanges this session) rather than silently faking data.

1. Auth

  • [~] Login screen — UI built (app/login); not yet wired to POST /auth/login / token storage
  • [~] Forgot password — add email screen — UI built (app/login/forgot); not yet wired to API
  • [~] Forgot password — verify OTP screen — UI built (app/login/forgot/otp); not yet wired to API
  • [~] Forgot password — change password screen — UI built (app/login/forgot/reset); not yet wired to API

2026-07-14: left untouched during the mock-data removal pass — there was never a mock lib/api/auth.ts to begin with (these screens simply don't call anything yet), and Backend/PROGRESS.md §6 confirms no POST /auth/login controller exists (ICurrentUser/JWT validation are wired, but there's no token issuer). Nothing to wire until that lands.

2. Master Data screens

  • Items (app/dashboard/products list + filters, /new create, /[id] full edit + ETag/If-Match with 412-conflict handling + activate/deactivate) — FR-MD-01/08. Reused the pre-existing "Products" sidebar entry/stub route rather than adding a new nav item. 2026-07-14: wired to the live backend (lib/api/items.ts), no more mock data.
  • UOM + conversions (app/dashboard/products/uoms flat list + create dialog; conversions edited inline on the Item detail page via PUT /items/{itemId}/uom-conversions) — FR-MD-02/03. 2026-07-14: wired to the live backend. Note: the real ItemDetailDto does not include a conversions field (only Reorder) — the Item detail page's conversion editor now round-trips purely through PUT /items/{itemId}/uom-conversions's own request/response, not the GET response.
  • Categories (app/dashboard/products/categories indented tree view + create dialog with parent picker) — FR-MD-04. 2026-07-14: wired to the live backend.
  • Vendors (app/dashboard/vendors list + search/status-filter + create dialog, [id] full edit + ETag/If-Match with 412-conflict handling + activate/deactivate) — FR-MD-06. First screen this session to exercise the ETag/If-Match/412 pattern end-to-end (lib/api-client.ts's ApiResult<T> was built earlier but unused until now). 2026-07-14: wired to the live backend.
  • Warehouses + Bins (app/dashboard/warehouse list + create-warehouse dialog, [id] bin list + create-bin dialog) — FR-WH-01/FR-MD-07. 2026-07-14: wired to the live backend; added warehousesApi.get() (the page needed it but the original stub never had it) and wrapped the real GET /warehouses/{id}/bins (IReadOnlyList<BinDto>, not paged) into a synthetic single-page PagedResponse<Bin> so existing .items-based call sites didn't need touching.
  • Item reorder settings — edited inline on the Item detail page (per-warehouse point/qty rows) via PUT /items/{itemId}/reorder — FR-MD-05. 2026-07-14: wired to the live backend.

3. Procurement screens

  • Requisition (app/dashboard/procurement/requisitions list, /new create, /[id] detail + Submit) — FR-PROC-01. 2026-07-14: wired to the live backend; dropped RequisitionSummary.lineCount (the real RequisitionSummaryDto doesn't return it) from the list screen.
  • RFQ + quotations + comparison view (.../rfqs list, /new create with vendor multi-invite, /[id] detail: lines, comparison matrix, record-quotation form, "Create PO from vendor") — FR-PROC-02. 2026-07-14: wired to the live backend, with real contract mismatches found and fixed (see the dedicated deviation note below — this one needed real rework, not just an API-client swap).
  • Purchase Order (.../purchase-orders list, /new create — auto-approved, prefillable from a Requisition or an RFQ+vendor quotation via query params — /[id] detail: edit-while-open with ETag/If-Match, Cancel with reason) — FR-PROC-03..07. 2026-07-14: wired to the live backend.
  • [~] Purchase Return (.../purchase-returns list, /new create against a Confirmed/Closed GRN's lines) — FR-PROC-08; also reachable from a Rejected GRN line via a "Create Return" button on the GRN detail page. Still [~]: the API client now calls real endpoint paths, but Backend/PROGRESS.md §2 explicitly defers Purchase Return until GRN + Stock Core exist — these calls 404 against a live backend today.

2026-07-14 — RFQ real-backend contract mismatches found while removing mock data (Backend/ERPCore/Dtos/Procurement/RfqDtos.cs / RfqService.cs vs. the frontend's speculative types):

  • The backend does not persist which vendors were invited to an RFQ (RfqService.MapRfq never sets a vendor list) — Rfq/RfqSummary no longer carry vendorIds. types/procurement.ts and the [id]/list pages were updated to stop relying on it; the RFQ detail page now shows "Quoted: ..." (derived from RfqComparison.vendorIds, i.e. vendors who have actually submitted a quotation) instead of "Invited: ...".
  • CreateRfqRequest.RequisitionId is [Required] server-side, not optional as the frontend assumed — app/dashboard/procurement/rfqs/new/page.tsx now requires picking a Submitted requisition (a picker was added for the case where one wasn't passed in via ?requisitionId=) before an RFQ can be created.
  • RfqComparisonDto's real shape is { rfqId, vendorIds, rows: [{ itemId, qty, quotes: [{ vendorId, quotationId, unitPrice, leadDays }] }] } — the frontend's assumed { lines: [{ cells }] } naming was wrong; types/procurement.ts (RfqComparisonRow/RfqComparisonCell) and both consuming pages (RFQ detail, PO-from-RFQ prefill) were corrected to match.
  • The "Record a quotation" vendor picker on the RFQ detail page now offers any active vendor who hasn't already quoted (matching what AddQuotationAsync actually validates — vendor exists + hasn't already quoted, not "was invited") rather than a now-nonexistent "pending invited vendors" list.
  • GET /rfqs (list) still does not exist on RfqsController (only GET /rfqs/{id}) — rfqsApi.list() calls it anyway per the user's "remove all mock data" instruction, so the RFQ list screen 404s until that endpoint is added. Flagged here for whoever picks up Backend/PROGRESS.md §2.
  • Also fixed while cross-checking DTOs: ReqLine.requiredBy is nullable (DateOnly? server-side, not a mandatory string), and RequisitionSummary never had a lineCount field (removed from the requisitions list column and the RFQ picker label).

4. Receiving screens

  • [~] GRN list (app/dashboard/receiving/grn/page.tsx) — loading/empty/error states, links to detail
  • [~] GRN create (app/dashboard/receiving/grn/new/page.tsx) — "Against PO" (picks an Approved/PartiallyReceived PO, prefills open lines) and "Direct receipt" (vendor + manual lines) modes; per-line bin, qty, unit cost, hold status, and batch (batchNo+expiryDate) or serial-number-list capture driven by the item's trackingMode
  • [~] GRN confirm (app/dashboard/receiving/grn/[id]/page.tsx) — renders returned createdLayers/ledgerRefs/poStatus as a confirmation panel (20-FRONTEND §4); sends a stable Idempotency-Key per detail-page session
  • [~] Inspection hold release / reject — Release/Reject buttons per on-hold line, shown once the GRN is Confirmed
  • Sidebar: added "Receiving" nav entry (components/Layouts/AppSidebar.tsx) → /dashboard/receiving/grn

[~] not [x], by design: these screens are built against the documented+planned contract in docs/11-BACKEND-PHASE1.md §4, but no GRN backend exists yet (this was frontend-only work; see the deviation below).

2026-07-14 — mock data removed: lib/api/grns.ts now calls real endpoint paths (apiRequest/apiRequestWithETag against /grns) instead of an in-memory store. There is still no GrnsController on the backend (Backend/PROGRESS.md §3 unstarted), so every call here 404s against a running backend — this was a deliberate, user-confirmed tradeoff (see AskUserQuestion exchange this session), not an oversight. Also added grnsApi.getWithETag() (the assumed GET /grns/{id} didn't have an ETag-returning variant, but app/dashboard/receiving/grn/[id]/edit/page.tsx's update() call needs an If-Match token to send — matching the same pattern purchase-orders.ts already uses for get/getWithETag).

Deviation — GET /grns and GET /grns/{id}: the API doc only specifies POST /grns, POST /grns/{id}/confirm, POST /grns/{id}/lines/{id}/release (no list/detail read). A list screen and a confirm/release screen both need to reload a GRN, so lib/api/grns.ts (grnsApi.list/grnsApi.get) and types/grn.ts assume these two GET endpoints will exist once the backend is built — flag this to whoever implements Backend/PROGRESS.md §3 so docs/11-BACKEND-PHASE1.md gets the corresponding doc update.

5. Stock screens

  • [~] Stock hub (app/dashboard/stock/page.tsx) — card grid linking to all 7 areas below
  • [~] Stock enquiry (.../stock/enquiry) — onHand/available/onHold/inTransit/reserved, search by SKU/name + warehouse filter, links to Valuation per row
  • [~] Ledger view (.../stock/ledger) — filterable by item/warehouse/date range, paginated
  • [~] Valuation view (.../stock/valuation) — item+warehouse picker (also reachable via ?itemId=&warehouseId= from Enquiry), FIFO layer breakdown + totals
  • [~] Transfer (.../stock/transfers list, /new create, /[id] dispatch → receive) — cost-preserving per line (FR-STK-06)
  • [~] Adjustment (.../stock/adjustments list, /new create) — reason code mandatory, auto-posts on submit (no separate confirm step, matching FR-STK-07)
  • [~] Count (.../stock/counts list, /new create, /[id] enter counts → post) — posting creates a linked variance adjustment
  • [~] Reorder alerts (.../stock/reorder-alerts) — items ≤ reorder point, one-click "Create requisition"
  • [~] Wastage (.../stock/wastage report, /new record) — not a documented endpoint or SRS document type: damage/theft-loss/expiry write-offs are modeled as Stock Adjustments with a loss-type reason code (FR-STK-07); lib/api/wastage.ts is a frontend-only lens over stockAdjustmentsApi + the shared mock ledger (filters to reason codes DMG/LOSS/EXPWO, flattens adjustment lines to per-item wastage records, sums matching outbound ledger entries for cost). No new backend concept — confirmed with the user before building (asked whether "Wastage" meant this vs. a distinct document type).
  • Sidebar: added "Stock" nav entry (components/Layouts/AppSidebar.tsx) → /dashboard/stock; top-bar titles mapped per route (components/Layouts/Header.tsx)

[~] not [x], by design — same posture as §4 Receiving: built frontend-only (user request), against the documented+assumed Stock Core contract (docs/11-BACKEND-PHASE1.md §5), with no real backend.

2026-07-14 — mock data removed: the in-memory Stock Core (mockStockLayers/mockStockLedger/receiveLayer/consumeFifo/etc., previously in lib/api/mock-data.ts) is gone. stock.ts/stock-transfers.ts/stock-adjustments.ts/stock-counts.ts/reason-codes.ts now call real endpoint paths; stockApi.onHandList() was rewritten to compose real (itemsApi.list × warehousesApi.list × stockApi.onHand per pair) calls instead of iterating a mock-derived key set. wastage.ts was rewritten the same way — it now calls reasonCodesApi.list()/stockAdjustmentsApi.list()+get()/stockApi.ledger() instead of reading mock arrays directly, so wastageReasonCodeIds() and wastageApi.list() are now async (both call sites in app/dashboard/stock/wastage/{page,new/page}.tsx were updated accordingly). None of §5's backend exists yet (Backend/PROGRESS.md §4/§5 unstarted), so every one of these calls 404s against a running backend — a deliberate, user-confirmed tradeoff (see AskUserQuestion exchange this session), not an oversight. tsc --noEmit and eslint are clean (only the pre-existing login/page.tsx error and the established set-state-in-effect pattern remain, unchanged from before this pass).

Deviations (same pattern as GRN, see §4): GET/detail list endpoints for transfers/adjustments/counts (lib/api/stock-transfers.ts, stock-adjustments.ts, stock-counts.ts) are assumed extensions beyond docs/11-BACKEND-PHASE1.md §5.4-5.6, which document only the transactional POSTs/PUT. stockApi.onHandList() (used by the Enquiry screen) is also not a documented endpoint — it's a frontend-only convenience; a real backend would want a proper list endpoint instead. Flag all of these to whoever implements Backend/PROGRESS.md §4/§5 (Stock Core + stock transactions).

Simplifications (mock-data limitations, not spec decisions): StockLayer has no per-bin field (matches the real ER model, docs/10 Part C.5 — only StockLedger carries bin_id), so Count lines don't attempt bin-level snapshotting. Transfers don't expose batch selection in the create UI (FIFO picks layers regardless of batch). Adjustment increases always cost at "last known cost" for that item/warehouse (FR-STK-07); there's no landed-cost/manual-cost override. In-transit quantity is shown for visibility at the destination warehouse only and is not subtracted a second time from the source's available (dispatch already reduced the source layer's qtyRemaining) — the docs' available = onHand onHold reserved inTransit(out) formula is ambiguous on this point given dispatch semantics; this was a judgment call, noted here for whoever builds the real backend to confirm or correct.

6. Validation posture (20-FRONTEND §3)

  • [~] Client format/required/range checks on all forms — done for GRN create (lib/validations/grn.ts); not yet done for other forms
  • Surface server ProblemDetails incl. domain codes; map to fields/messages — lib/error-map.ts (errorMessage/fieldErrors), used by GRN create/detail
  • 412 conflict → prompt refetch before retry — apiRequestWithETag surfaces the ETag but no screen edits a GRN yet (GRN has no PUT), so untested in practice
  • No client-side gating on stock/availability/status (server-authoritative) — GRN create always submits to the server and surfaces OVER_RECEIPT_TOLERANCE/etc. via error-map.ts rather than pre-blocking

7. UX states

  • [~] Loading / empty / error states on every list — done for the GRN list/create/detail screens; other screens still unbuilt
  • Transactional actions show server-returned side effects as confirmation — GRN confirm renders createdLayers/ledgerRefs/poStatus from the response

Done

2026-07-13 — GRN screens + frontend foundation (frontend-only; no backend changes)

  • Foundation: lib/api-client.ts, lib/error-map.ts, lib/auth-token.ts, types/{common,master-data,procurement,grn}.ts, lib/api/{grns,purchase-orders,warehouses,items,vendors,uoms}.ts — scoped to what the GRN flow needs, not the full API surface.
  • Added the shadcn select primitive (npx shadcn add select) — wasn't in components/ui/ yet; needed for PO/vendor/warehouse/bin/item/hold-status pickers.
  • Screens: GRN list, GRN create (PO-based + direct receipt, batch/serial capture by trackingMode), GRN detail (confirm + release/reject). Sidebar nav entry added.
  • This was explicitly frontend-only (user interrupted an initial backend+frontend plan and asked for frontend only). No GRN backend exists — Backend/PROGRESS.md §3/§4 are unchanged. The screens are built against the contract in docs/11-BACKEND-PHASE1.md §4 plus two assumed-but-undocumented endpoints (GET /grns, GET /grns/{id}, see §4 note above); none of it is runnable end-to-end yet.
  • Verified: tsc --noEmit clean for all new/edited files (one pre-existing, unrelated error remains in app/login/page.tsx); eslint clean aside from two react-hooks/set-state-in-effect warnings matching an already-existing pattern in hooks/use-mobile.ts; all three routes confirmed rendering (200, correct content, no error boundary) via SSR against the dev server.

2026-07-13 — Stock Management screens (frontend-only; no backend changes)

  • types/stock.ts: full DTO set for on-hand, ledger, valuation, transfers, adjustments, counts, reorder alerts (docs/11 §5).
  • lib/api/mock-data.ts gained a real in-memory Stock Core: mockStockLayers/mockStockLedger + receiveLayer/consumeFifo/postLedgerEntry/computeOnHand/lastKnownCost helpers, plus mockItemReorders and mockReasonCodes seed data. lib/api/grns.ts's confirm() was refactored to post through these helpers instead of fabricating a response, and now also accrues PO qtyReceived/recomputes PO status — so GRN and Stock screens are genuinely connected this session.
  • New API modules: lib/api/stock.ts (on-hand/ledger/valuation/reorder-alerts), stock-transfers.ts, stock-adjustments.ts, stock-counts.ts, reason-codes.ts — same commented-real-block + active-mock-block pattern as the GRN modules.
  • Screens: hub, Enquiry, Ledger, Valuation, Transfers (list/new/detail with dispatch+receive), Adjustments (list/new, auto-post), Counts (list/new/detail with enter-counts+post), Reorder Alerts. New shared badge set components/stock/status-badges.tsx (same fixed-size red/green/yellow convention as components/receiving/status-badges.tsx). Sidebar + header-title mappings added.
  • Same posture as the GRN pass: [~] not [x], frontend built ahead of a nonexistent Stock Core backend, deviations/simplifications recorded in the §5 note above. tsc --noEmit and eslint clean (only the same pre-existing/established issues as the GRN pass).

2026-07-13 — Wastage screens (frontend-only; no backend changes)

  • lib/api/wastage.ts: no new backend concept — confirmed with the user that "Wastage" should be a focused UI lens over the just-built Stock Adjustments (damage/theft-loss/expiry write-off reason codes), not a distinct document type. Filters mockStockAdjustments to loss-type reason codes, flattens to per-item WastageRecords, and computes cost per record from matching outbound mockStockLedger entries.
  • Screens: .../stock/wastage (report — totals cards, warehouse/reason filters, per-item table) and .../stock/wastage/new (single-line record form, reason dropdown restricted to wastage-type codes, posts via the existing stockAdjustmentsApi.create). Added a "Wastage" card to the Stock hub and header-title mappings.
  • Verified: tsc --noEmit clean (same pre-existing login/page.tsx error only); eslint clean aside from one more instance of the already-established set-state-in-effect pattern.

2026-07-13 — Warehouse Management screens (frontend-only; no backend changes)

  • Scope, per user selection out of the four FR-WH sub-areas offered (Warehouses & Bins / Stock Locator / Batch & Serial / Putaway): Warehouses & Bins master data only (FR-WH-01, FR-MD-07). The other three (bin-level Stock Locator, Batch/Serial tracking, Putaway) were not built — flagged here so a future pass knows they're still open, not forgotten.
  • lib/api/warehouses.ts gained create/get/createBin (previously list/listBins only, read-only) — duplicate-code validation mirrors the real SKU_DUPLICATE-style 400 pattern used elsewhere. mock-data.ts gained allocateWarehouseId/allocateBinId.
  • Screens: app/dashboard/warehouse (list + "New Warehouse" Dialog form) and app/dashboard/warehouse/[id] (bin list + "New Bin" Dialog form) — used components/ui/dialog.tsx instead of a full page for these two-field creates, since a whole page felt heavy for that. Sidebar "Warehouses" entry + header-title mapping added.
  • Housekeeping: removed two stray duplicate route folders (app/dashboard/receiving/grn/create new GRN/, .../view GRN/) that were byte-for-byte copies of the real new/ and [id]/ GRN pages under garbled folder names — almost certainly an IDE artifact from an earlier malformed file-open path, not intentional work (confirmed untracked in git before removing). Also noted, but deliberately left alone: app/warehouse/*, components/warehouse/, lib/warehouse/ are pre-existing empty scaffold folders (no files at all) from initial project setup — Warehouse Management was built under app/dashboard/warehouse/* instead so it gets the dashboard chrome (sidebar/header) for free, consistent with every other screen this session.
  • Verified: tsc --noEmit clean (same pre-existing login/page.tsx error only); eslint clean aside from one exhaustive-deps warning (not an error) on [id]/page.tsx's loadBins helper.

2026-07-13 — Vendor (Supplier) management screens (frontend-only; no backend changes)

  • Confirmed with the user first: "supplier/shop" has no distinct "Shop" entity in the SRS/docs — scoped this to the documented Vendor master (FR-MD-06, docs/11 §2.4), "supplier" being the standard ERP synonym.
  • lib/api/vendors.ts extended from list-only to get/create/update/updateStatus. This is the first screen to exercise the ETag/If-Match/412 pattern: mock-data.ts gained a per-vendor concurrency-token map (getVendorVersion/bumpVendorVersion/initVendorVersion, standing in for the real backend's xmin — the public Vendor type has no version field of its own since it travels as an HTTP ETag header, not a body field) so update() genuinely rejects a stale If-Match with CONCURRENCY_CONFLICT, matching docs/11 §1.6 and 20-FRONTEND.md §3.2.
  • Screens: app/dashboard/vendors (list, search + status filter, "New Vendor" dialog) and app/dashboard/vendors/[id] (full edit form using the real apiRequestWithETag-shaped ApiResult<T>, a dedicated conflict banner with "Reload before retrying" per the 412 UX rule rather than a generic toast, and an Activate/Deactivate toggle via PATCH status, FR-MD-08 — deactivate, not hard-delete). Sidebar "Vendors" entry + header-title mapping added.
  • Verified: tsc --noEmit clean (same pre-existing login/page.tsx error only); eslint clean aside from the same established set-state-in-effect pattern used throughout this session.
  • Follow-up (same day): vendorsApi.list() didn't actually paginate (always returned page 1 / all matches, same latent gap the GRN list had before its own pagination pass) — fixed to slice by page/pageSize properly, added the same Previous/Next pagination controls used on the GRN and Stock list screens, and seeded 8 more sample vendors so there's something real to page through.

2026-07-13 — Procurement screens: Requisition → RFQ → PO → Purchase Return (frontend-only; no backend changes)

  • types/procurement.ts grew from a GRN-support subset (PO read types only) to the full §3 DTO set: Requisition/ReqLine, Rfq/RfqLine/Quotation/RfqComparison, PO create/update/cancel request types, PurchaseReturn/PurchaseReturnLine — mirrors docs/11-BACKEND-PHASE1.md §3 request/response JSON exactly (no deliveryDate field on PO lines, since the documented POST /purchase-orders example doesn't carry one despite FR-PROC-03's prose — contract-over-prose per docs/20-FRONTEND.md §1).
  • lib/api/mock-data.ts: added mockRequisitions/mockRfqs/mockQuotations/mockPurchaseReturns + allocators, a PO concurrency-token map (getPoVersion/bumpPoVersion/initPoVersion, same out-of-band ETag pattern as vendors), and consumeLayerByGrnLine — a new consumption path deliberately separate from consumeFifo: a Purchase Return disposes of the exact layer its GRN line created (often OnHold/Rejected, which consumeFifo's hold filter would otherwise skip), not "the oldest open layer for this item/warehouse". Seeded Requisition #210 to match the existing mockPurchaseOrders[0].requisitionId so the two screens cross-reference.
  • New API modules: lib/api/requisitions.ts, lib/api/rfqs.ts (create/addQuotation/comparison — comparison is computed client-side from recorded quotations), lib/api/purchase-returns.ts. lib/api/purchase-orders.ts extended from list/get-only (its original GRN-support scope) to full create/update/cancel; added getWithETag/isPoEditable without touching the existing plain get() GRN's create-flow already depends on, so no existing call site broke.
  • Wiring, not just new screens: stockApi.createReorderRequisition (Stock → Reorder Alerts, built in an earlier session) previously fabricated a response with no backing record; it now pushes a real row into mockRequisitions, so a reorder-triggered requisition genuinely shows up in the new Requisitions list — same "connect the mock modules together" posture as GRN confirm → Stock Core.
  • Screens: Requisition (list/new/detail+Submit), RFQ (list/new with checkbox vendor multi-invite reusing the Stock Count's Checkbox pattern/new with quotation-recording form + vendor-by-line comparison table/"Create PO from vendor"), Purchase Order (list/new — accepts ?requisitionId= or ?rfqId=&vendorId= to prefill lines and pricing/detail with inline edit-while-open using the vendor [id] page's ETag+412-conflict-banner pattern, plus a Cancel-with-reason flow client-disabled when any line has receipts), Purchase Return (list/new — pick a Confirmed/Closed GRN, checkbox+qty its lines; also reachable via a new "Create Return" button next to Rejected lines on the GRN detail page, matching the SRS flow diagram's Reject→Purchase Return step). New components/procurement/status-badges.tsx (same fixed-width badge convention as Receiving/Stock) and lib/validations/procurement.ts (zod-free hand-rolled, matching the GRN validation file's style, not its zod deviation).
  • Sidebar: added "Procurement" nav entry between Vendors and Receiving; header title mappings added for all new routes.
  • Same [~] posture as every other module this session: built against the documented+assumed Procurement contract (docs/11-BACKEND-PHASE1.md §3), no Procurement backend exists (Backend/PROGRESS.md §2 unchanged).
  • Verified: tsc --noEmit clean (same pre-existing login/page.tsx error only); eslint clean aside from the same established set-state-in-effect pattern used throughout this session (confirmed it also fires on the pre-existing grn/page.tsx/vendors/page.tsx/hooks/use-mobile.ts — not a regression); npm run build compiles successfully via Turbopack (same pre-existing login type-check failure blocks the full build, unrelated). All 12 new routes plus the GRN-detail "Create Return" link (including three query-param-prefilled variants) confirmed rendering 200 with no error boundary via SSR against the dev server.

2026-07-13 — Master Data screens: Items, UOM, Categories, Reorder settings (frontend-only; no backend changes)

  • types/master-data.ts: ItemListItem (the GRN/PO/Requisition/RFQ item-picker subset built in earlier sessions) is now a derived view of a new full Item type — SKU/name/description/category/baseUom/defaultVendor/type/trackingMode/taxClass/status plus reorder: ItemReorderSetting[] (matches the documented GET /items/{itemId} example inline) and conversions: UomConversion[] (deviation: the doc's example response only shows reorder, and conversions are otherwise reachable only via PUT /items/{itemId}/uom-conversions with no matching GET — embedding them on the full resource, like the assumed GET /grns/GET /grns/{id} reads elsewhere in this app, lets the Item detail screen show current conversions before editing). Also added Category/CategoryTreeNode, CreateUomRequest, CreateCategoryRequest, and Item create/update/reorder/conversion request types (docs/11 §2.1-2.3).
  • lib/api/mock-data.ts: mockItems changed storage shape from ItemListItem[] to full Item[] (only mock-data.ts and lib/api/items.ts touched it directly, confirmed by grep, so no other call site broke) — lib/api/items.ts's list() now maps down to ItemListItem, same "full record → mapped summary" pattern as mockPurchaseOrdersPurchaseOrderSummary. Added a per-item concurrency-token map (getItemVersion/bumpItemVersion/initItemVersion, same out-of-band ETag pattern as vendors/POs), mockCategories seeded with a 2-root/1-child tree matching the category IDs the existing sample items already reference (12 "Fasteners" under 3 "Hardware"; 20 "Power Tools"), and a UOM id allocator.
  • New API modules: lib/api/categories.ts (list/tree/createtree() builds the nested structure client-side from the flat list, since the mock has no separate tree-storage concept). lib/api/items.ts grew from list-only (its original GRN-picker scope) to full get/create/update/updateStatus/updateReorder/updateUomConversions; lib/api/uoms.ts gained create.
  • Screens: Items (app/dashboard/productsreused the pre-existing "Products" sidebar entry and stub route rather than adding a new nav item, since it was already wired to an empty placeholder page; list has search + category/tracking-mode/status filters + pagination, /new create, /[id] detail combining three independently-saved sections in one page — basic info with ETag/If-Match + 412-conflict banner mirroring the Vendor [id] page, a Reorder Settings row-editor posting PUT /items/{itemId}/reorder, and a UOM Conversions row-editor posting PUT /items/{itemId}/uom-conversions — matching how the API groups these as sub-resources of Item rather than separate top-level screens). UOM (app/dashboard/products/uoms — flat list + create dialog, same shape as the Warehouses list). Categories (app/dashboard/products/categories — indented recursive tree view + create dialog with a parent picker). lib/validations/master-data.ts added (hand-rolled, matching the GRN validation file's style, not its zod deviation). Header title mappings added for all /dashboard/products/* routes.
  • Housekeeping: removed app/dashboard/vendors/view vendors/ — confirmed byte-for-byte identical to vendors/[id]/page.tsx and untracked in git, same IDE-artifact pattern (malformed file-open path) as the garbled GRN duplicate folders removed in the Warehouse Management pass; noted here rather than silently dropped. Left app/dashboard/receiving/grn/[id]/edit/ alone — it's untracked too but is a distinct, non-duplicate GRN-edit screen, not an artifact.
  • Same [~] posture as every other module this session: built against the documented+assumed Master Data contract (docs/11-BACKEND-PHASE1.md §2), no Master Data backend exists (Backend/PROGRESS.md §1 unchanged).
  • Verified: tsc --noEmit clean after clearing a stale .next type cache that still referenced the just-deleted view vendors route (same pre-existing login/page.tsx error only remains); eslint clean aside from the same established set-state-in-effect pattern; npm run build compiles successfully via Turbopack (same pre-existing login type-check failure, unrelated). All 5 new/changed routes confirmed rendering 200 with no error boundary against the dev server (one false-alarm 500 during testing traced to an unrelated stale process already bound to port 3000, not this code — retested clean on the actual dev server port).

2026-07-14 — Mock data removed everywhere; Master Data + Procurement wired to the live backend

  • User asked to integrate the (now-built) backend into the frontend, frontend-only, no mock data. Confirmed scope first: the backend only has controllers for Master Data (Backend/PROGRESS.md §1) and Procurement minus returns (§2) — GRN (§3), Stock Core (§4), Stock Transactions (§5), and Auth (§6) have no controllers at all yet. User's explicit instruction after that was still "remove mock data in all" — so every lib/api/*.ts module was switched to real fetch calls, accepting that GRN/Stock/Purchase-Return/Reason-Code calls will 404 against a live backend until that work exists (not silently left mocked).
  • Deleted lib/api/mock-data.ts entirely and restored the real-fetch implementation in every one of: categories, uoms, warehouses (added missing get(); wrapped the real non-paged GET /warehouses/{id}/bins array response into a synthetic PagedResponse<Bin> so existing .items call sites kept working), items, vendors, requisitions, purchase-orders, purchase-returns, reason-codes, grns (added getWithETag() for the GRN edit page's If-Match), stock, stock-transfers, stock-adjustments, stock-counts. wastage.ts had no prior real-mode block (it's a frontend-only lens with no documented endpoint of its own) — rewrote it to compose the now-real reasonCodesApi/stockAdjustmentsApi/stockApi calls instead of reading mock arrays directly; its two exports became async as a result, and both call sites (app/dashboard/stock/wastage/{page,new/page}.tsx) were updated.
  • Did not blindly trust the frontend's pre-written "real implementation" comments — cross-checked every Master Data/Procurement DTO against the actual Backend/ERPCore/Dtos/**/*.cs and controllers before wiring, since those blocks were written speculatively before/alongside the real backend and had drifted in the RFQ case (see the dedicated §3 deviation note above): the backend doesn't persist RFQ-invited vendors, requisitionId is required (not optional) to create an RFQ, RfqComparisonDto uses rows/quotes/quotationId (not lines/cells), and RequisitionSummaryDto has no lineCount. Fixed types/procurement.ts and the RFQ list/detail/new pages plus the PO-from-RFQ prefill accordingly, rather than shipping types that would silently be undefined at runtime.
  • Left app/login/* untouched — no mock auth existed to remove, and there's still no POST /auth/login controller to wire to.
  • Verified: tsc --noEmit clean (only the pre-existing, unrelated login/page.tsx resolver-typing error remains — confirmed pre-existing via git history, not introduced here). eslint shows the same established react-hooks/set-state-in-effect/static-components pattern as before, confirmed unchanged by spot-checking it also fires on files untouched this session (app/login/forgot/reset/page.tsx). Did not start the backend/Postgres or click through the UI live in this pass — verification was type-check + lint only; the Master Data/Procurement screens should be smoke-tested against a running dotnet run + Postgres before considering this "done" in practice.