@

docs(wiki): flatten to root pages (Forgejo wikis are not nested)

Forgejo/Gitea wikis are flat — pages in real subdirectories 404 (only the page
title may contain a "/", stored as an escaped flat file). The folder layout broke
every moved page on the live wiki. Move all pages back to the repo root and make
links bare page names again. Hierarchy is now expressed via _Sidebar.md headings
(slashes removed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
Hardik 2026-06-19 14:06:07 +05:30
parent 61b224abb5
commit d6b4073d28
29 changed files with 130 additions and 130 deletions

@ -116,8 +116,8 @@ lib/
└── validations/{po.ts,user.ts} # Zod schemas
```
See the [Data Model](System/Data-Model), [PO Lifecycle](System/PO-Lifecycle), and
[Roles and Permissions](System/Roles-and-Permissions) for the core domain logic.
See the [Data Model](Data-Model), [PO Lifecycle](PO-Lifecycle), and
[Roles and Permissions](Roles-and-Permissions) for the core domain logic.
## API surface
@ -147,12 +147,12 @@ All data **mutations** are Server Actions co-located with their page
- Authorisation is centralised in `lib/permissions.ts`
(`hasPermission` / `requirePermission`). **Server Actions call
`requirePermission()` at the top before any DB write**; Server Components gate
whole page segments. Full matrix on [Roles and Permissions](System/Roles-and-Permissions).
whole page segments. Full matrix on [Roles and Permissions](Roles-and-Permissions).
## Development conventions
- **Trunk**: `master`. Work lands via PRs (`feat/`/`fix/`/`chore/`, or
`claude/issue-N` from the [automated pipeline](Ops/Issue-to-Deploy-Pipeline)).
`claude/issue-N` from the [automated pipeline](Issue-to-Deploy-Pipeline)).
Production is whatever `vX.Y.Z` tag is deployed; staging is a deployed instance
of latest `master`, not a branch.
- **Commits**: Conventional Commits (`feat:`, `fix:`, `refactor:`).

@ -2,7 +2,7 @@
Mirrors `CHANGELOG.md` at the repo root (the authoritative copy). Releases are
tagged `vX.Y.Z`; the deployed production version is whichever tag is currently
checked out in `~/pms`. See [Deployment and Operations](Ops/Deployment-and-Operations).
checked out in `~/pms`. See [Deployment and Operations](Deployment-and-Operations).
## [Unreleased]
@ -12,10 +12,10 @@ checked out in `~/pms`. See [Deployment and Operations](Ops/Deployment-and-Opera
`/admin/companies` CRUD. A PO is billed under a selected company (name, short
`code`, GST number, address, phone/mobile, contact + invoice email, invoice
address); details populate the exported PO header / invoice block. See
[Purchase Orders](Product/Purchase-Orders#companies-multi-company-invoicing).
[Purchase Orders](Purchase-Orders#companies-multi-company-invoicing).
- **Structured PO numbers** (`lib/po-number.ts`) — `COMPANY/VESSEL/ID/FY`; Indian
financial year; system IDs start at 9000; imported POs keep their original
number. See [Purchase Orders](Product/Purchase-Orders#po-numbering).
number. See [Purchase Orders](Purchase-Orders#po-numbering).
- **3-level accounting-code hierarchy**`Account.parentId` self-relation
(Top → Sub → Leaf), 6-digit codes seeded from
`prisma/accounting-codes-data.ts`. Only leaf codes are PO-selectable, via a
@ -26,24 +26,24 @@ checked out in `~/pms`. See [Deployment and Operations](Ops/Deployment-and-Opera
`poDate ?? approvedAt ?? createdAt`.
- **Submitter vendor creation**`create_vendor` lets Technical/Manning add
vendors; created **unverified**, verified when a PO closes/pays with them, on
import, or via Manager/Accounts/Admin. See [Vendors](Product/Vendors-and-GST-Lookup).
import, or via Manager/Accounts/Admin. See [Vendors](Vendors-and-GST-Lookup).
- **Import PO → Closed**`/po/import` saves a parsed Excel PO directly as
`CLOSED`, auto-detecting company, matching vessel, auto-creating vendor,
products, and per-vendor prices.
- **Inventory feature flag** (`NEXT_PUBLIC_INVENTORY_ENABLED`) — site
stock/consumption gated; PO catalogue stays available. Inventory increments at
**PO approval**. See [Inventory and Catalogue](Product/Inventory-and-Catalogue).
**PO approval**. See [Inventory and Catalogue](Inventory-and-Catalogue).
- **Dashboards** — Accounts gains a "Payments Completed This Month" card.
- **Automated issue-to-deploy pipeline** — Report Issue button → Forgejo issue →
Claude watcher triage/fix → PR → tag-triggered deploy. See
[Issue-to-Deploy Pipeline](Ops/Issue-to-Deploy-Pipeline).
[Issue-to-Deploy Pipeline](Issue-to-Deploy-Pipeline).
### Changed
- **Cost centre is now a Vessel only.** The Vessel-or-Site cost-centre model was
removed: `PurchaseOrder.vesselId` is required, `costCentreRef` is gone, and
`Vessel` no longer links to a `Site`. Vessels are surfaced as **"Cost Centre"**
(`/admin/vessels` → "Cost Centre Management"). See [Glossary](Overview/Glossary).
(`/admin/vessels` → "Cost Centre Management"). See [Glossary](Glossary).
- **Closed PO list** — submitters see only their own `CLOSED` POs;
Managers/SuperUsers see all.
- **Sidebar** reorganised into **Purchasing** and **Administration** (role-aware);
@ -60,7 +60,7 @@ checked out in `~/pms`. See [Deployment and Operations](Ops/Deployment-and-Opera
- Production `P2022 … column does not exist` after deploy — caused by shipping
code whose Prisma client expected a column before `migrate deploy` ran.
Migrations must be applied before the new build serves traffic (now in the
README and the [deploy workflow](Ops/Deployment-and-Operations#release--deploy-flow)).
README and the [deploy workflow](Deployment-and-Operations#release--deploy-flow)).
---

@ -25,7 +25,7 @@ enum ActionType {
enum RequestStatus { PENDING APPROVED DENIED }
```
`POStatus` drives the [PO Lifecycle](System/PO-Lifecycle); `ActionType` rows form the
`POStatus` drives the [PO Lifecycle](PO-Lifecycle); `ActionType` rows form the
per-PO audit trail.
## Entity relationships
@ -64,7 +64,7 @@ The central entity. Key fields:
| Field | Notes |
|---|---|
| `poNumber` (unique) | `COMPANY/VESSEL/ID/FY` — see [Purchase Orders](Product/Purchase-Orders) |
| `poNumber` (unique) | `COMPANY/VESSEL/ID/FY` — see [Purchase Orders](Purchase-Orders) |
| `status` | `POStatus`, default `DRAFT` |
| `totalAmount` | `Decimal(12,2)`, **GST-inclusive** sum of line items |
| `paidAmount?` | accumulates across partial payments |
@ -86,7 +86,7 @@ totalPrice Decimal(12,2), gstRate Decimal(5,4) default 0.18, sortOrder, size?,
deliveredQuantity? , productId?, accountId?` + `poId` (cascade).
- `totalPrice = quantity × unitPrice × (1 + gstRate)`; the PO `totalAmount` is
the sum of line `totalPrice`. See [GST](Product/Purchase-Orders#gst-calculation).
the sum of line `totalPrice`. See [GST](Purchase-Orders#gst-calculation).
- `deliveredQuantity` supports **partial receipt**.
- `accountId` allows a **per-line accounting code** override.
@ -104,7 +104,7 @@ Every state transition and notable event writes one row. `metadata` is flexible
### Notification
`subject, body, link?, isRead (default false), sentAt, status (default "sent")`
+ optional `poId`, `userId`. Backs the in-app notification bell; every email
event is also persisted here. See [Notifications](Product/Notifications).
event is also persisted here. See [Notifications](Notifications).
## Reference / catalogue models
@ -141,13 +141,13 @@ latitude?, longitude?, isVerified (default false), isActive`. `VendorContact[]`:
- Submitters can **create vendors** (unverified). A vendor becomes verified on a
closing/paying PO, on import, or via Manager/Accounts/Admin.
- `latitude`/`longitude` geocoded from `pincode` for vendor-distance sorting.
- See [Vendors and GST Lookup](Product/Vendors-and-GST-Lookup).
- See [Vendors and GST Lookup](Vendors-and-GST-Lookup).
### Product + ProductVendorPrice
`Product`: `code (unique), name, description?, lastPrice?, lastVendorId?,
isActive`. `ProductVendorPrice`: one row per `(productId, vendorId)` with
`price`. On payment confirmation, `lastPrice`/`lastVendorId` and per-vendor
prices are updated. See [Inventory and Catalogue](Product/Inventory-and-Catalogue).
prices are updated. See [Inventory and Catalogue](Inventory-and-Catalogue).
### Site / ItemInventory / ItemConsumption (inventory, feature-flagged)
- `Site`: `name, code (unique), address?, latitude?, longitude?, isActive`.
@ -186,4 +186,4 @@ chronology is a useful changelog of schema evolution, e.g.:
> **Always apply migrations before new code serves traffic.** `pnpm build` runs
> only `prisma generate`; deploying code whose client expects a not-yet-migrated
> column yields `P2022 … column does not exist`. The deploy workflow runs
> `migrate deploy`. See [Deployment and Operations](Ops/Deployment-and-Operations).
> `migrate deploy`. See [Deployment and Operations](Deployment-and-Operations).

@ -37,7 +37,7 @@ same host.
All production env vars must be set (auth, DB, R2, Resend, optionally
Forgejo/GST). Server-side env lives in `~/pms/App/.env`. The full list and the
dev/prod split is on [Environment Variables](Build-and-Run/Environment-Variables).
dev/prod split is on [Environment Variables](Environment-Variables).
## Release & deploy flow
@ -66,7 +66,7 @@ Watch progress under **Actions** in Forgejo, or `pm2 logs forgejo-runner`.
> not-yet-migrated column yields `P2022 … column does not exist` at runtime. The
> deploy workflow runs `migrate deploy` for you; for manual deploys, run it (and
> restart) before/with the swap. This was a real production incident — see
> [Changelog](Overview/Changelog).
> [Changelog](Changelog).
## Staging (smoke test before deploy)
@ -106,5 +106,5 @@ data.**
to missing fork information" even when `mergeable: true`. Fix: close and reopen
the PR (UI or API). Resolves on upgrade past v10.
See [Issue-to-Deploy Pipeline](Ops/Issue-to-Deploy-Pipeline) for the automation, and
See [Issue-to-Deploy Pipeline](Issue-to-Deploy-Pipeline) for the automation, and
`automation/README.md` for the full runbook.

@ -32,7 +32,7 @@ shadcn/ui in `App/components/`.
- Cards: `rounded-lg`, `shadow-sm`, 16 px padding.
- Status badges: pill shape, colour-coded to match the
[PO lifecycle](System/PO-Lifecycle#status-badges) state colours.
[PO lifecycle](PO-Lifecycle#status-badges) state colours.
- Tables: alternating row shading, sticky header on scroll.
- Forms: floating labels; validation errors below the field in `danger` colour.
- Buttons: primary = blue fill, secondary = white with border, danger = red fill.
@ -47,17 +47,17 @@ shadcn/ui in `App/components/`.
- **Inline table editing** — manager line-item editing happens inline on the
approval page (not a modal) so the rest of the PO stays visible.
- **Live GST summary** — below the line-items table: Taxable, GST, Grand Total
update as you type. See [Purchase Orders](Product/Purchase-Orders#gst-calculation).
update as you type. See [Purchase Orders](Purchase-Orders#gst-calculation).
- **Product autocomplete** — typing in a line-item name field fuzzy-searches the
catalogue; the dropdown shows product name/code and per-vendor price hints
(e.g. "Vendor A: ₹1,200 · Vendor B: ₹1,050").
- **Cheapest / ★ Closest tags** — computed independently so both can show at
once. See [Inventory and Catalogue](Product/Inventory-and-Catalogue#cheapest--closest-tags).
once. See [Inventory and Catalogue](Inventory-and-Catalogue#cheapest--closest-tags).
- **Cart persistence**`localStorage` under a fixed key; survives navigation,
local to the device/user; a `cart-updated` event lets components react live.
- **Mobile** — Manager/Accounts get mobile cards + a bottom nav; other roles get
a "Desktop Required" overlay. See
[Pages and Navigation](Product/Pages-and-Navigation#mobile).
[Pages and Navigation](Pages-and-Navigation#mobile).
- **Environment banner** — a fixed non-prod banner via `NEXT_PUBLIC_ENV_LABEL`
(`EnvBanner`); renders nothing in production.
@ -71,6 +71,6 @@ shadcn/ui in `App/components/`.
wired but not populated.
> A non-functional **Reports** UX mockup also exists under
> [Planned Features](Planned/Planned-Features) →
> [Reports — UX Mockup](Planned/Reports-Mockup) — proposed layouts and chart
> [Planned Features](Planned-Features) →
> [Reports — UX Mockup](Reports-Mockup) — proposed layouts and chart
> treatments for spend reporting.

@ -1,6 +1,6 @@
# PPMS — E2E Test Framework Reference
> _Migrated from the retired `Docs/e2e-test-framework.md`. Index: [Testing](Quality/Testing)._
> _Migrated from the retired `Docs/e2e-test-framework.md`. Index: [Testing](Testing)._
This document describes the Playwright-based end-to-end test framework for the
PPMS portal: its stack, directory layout, configuration, shared utilities, and

@ -1,6 +1,6 @@
# PPMS — E2E Test Plan
> _Migrated from the retired `Docs/e2e-test-plan.md`. Index: [Testing](Quality/Testing)._
> _Migrated from the retired `Docs/e2e-test-plan.md`. Index: [Testing](Testing)._
**Version:** 1.0
**Date:** 2026-05-17

@ -38,14 +38,14 @@ Server-side env on pms1 lives in `~/pms/App/.env`; locally in `App/.env.local`
- **SSO at module load**`auth.ts` evaluates the `AZURE_AD_*` vars when the
module loads, so they must be *present* (even as placeholders) for the app to
start in non-SSO environments. See [Architecture](System/Architecture#auth--authorisation).
start in non-SSO environments. See [Architecture](Architecture#auth--authorisation).
- **Storage / email auto-toggle** — with R2/Resend unset in dev, uploads go to
`.dev-uploads/` and emails print to the terminal. See
[File Storage](Product/File-Storage) and [Notifications](Product/Notifications).
[File Storage](File-Storage) and [Notifications](Notifications).
- **Inventory flag** — `INVENTORY_ENABLED = NEXT_PUBLIC_INVENTORY_ENABLED !==
"false"`, i.e. enabled unless explicitly `"false"`.
- **Env banner**`EnvBanner` renders nothing when `NEXT_PUBLIC_ENV_LABEL` is
unset, so production is unaffected; staging sets it to the
"INTERNAL DEV / STAGING - NOT PRODUCTION" string.
- **GstService** has its own `PORT` (default 3003); the portal reaches it via
`GST_SERVICE_URL`. See [Vendors and GST Lookup](Product/Vendors-and-GST-Lookup).
`GST_SERVICE_URL`. See [Vendors and GST Lookup](Vendors-and-GST-Lookup).

@ -7,12 +7,12 @@ with the detail, or names the code that implements it.
- **Full PO lifecycle** — DRAFT → … → CLOSED with manager approval, vendor
validation, payment, and receipt confirmation. Enforced by
[the state machine](System/PO-Lifecycle); every change is an audit row.
[the state machine](PO-Lifecycle); every change is an audit row.
- **Partial payments & partial receipts**`PARTIALLY_PAID` /
`PARTIALLY_CLOSED` loop until fully settled (`deliveredQuantity` per line).
- **Structured PO numbers**`COMPANY/VESSEL/ID/FY` (e.g. `PMS/HNR1/9000/2024-25`);
system IDs start at 9000; imported POs keep their original number.
See [Purchase Orders](Product/Purchase-Orders#po-numbering).
See [Purchase Orders](Purchase-Orders#po-numbering).
- **GST-inclusive totals** — per-line `gstRate` (default 18%); live taxable/GST/
grand-total summary in the form.
- **Manager line-item editing** — managers adjust quantities, prices, GST,
@ -28,18 +28,18 @@ with the detail, or names the code that implements it.
## Catalogue, vendors, inventory
- **Multi-company invoicing** — bill a PO under a sister company (PMS/HNR/DEI);
details flow to the exported PO. See [Data Model](System/Data-Model#company--multi-company-invoicing).
details flow to the exported PO. See [Data Model](Data-Model#company--multi-company-invoicing).
- **3-level accounting codes** — Top → Sub → Leaf (6-digit); only leaf codes
selectable, via a searchable combobox.
- **Vendor management** — submitter-created (unverified) vendors;
auto-verify-on-payment; **GSTIN lookup** via the GST microservice; geocoded
vendor-distance sourcing. See [Vendors and GST Lookup](Product/Vendors-and-GST-Lookup).
vendor-distance sourcing. See [Vendors and GST Lookup](Vendors-and-GST-Lookup).
- **Product catalogue** — editable at `/admin/products`, read-only at
`/inventory/items`; per-vendor price history; "Cheapest" / "★ Closest" tags.
- **Cart** — collect items (localStorage) → create a PO pre-filled.
- **Site inventory (feature-flagged)** — stock per site, daily consumption log;
inventory incremented at PO **approval**. Gated by
`NEXT_PUBLIC_INVENTORY_ENABLED`. See [Inventory and Catalogue](Product/Inventory-and-Catalogue).
`NEXT_PUBLIC_INVENTORY_ENABLED`. See [Inventory and Catalogue](Inventory-and-Catalogue).
## Platform
@ -64,8 +64,8 @@ with the detail, or names the code that implements it.
- **Report Issue button** — any signed-in user files a Forgejo issue from the
header (`lib/forgejo.ts`).
- **Automated issue→fix→PR pipeline** + **tag-triggered deploy**. See
[Issue-to-Deploy Pipeline](Ops/Issue-to-Deploy-Pipeline).
[Issue-to-Deploy Pipeline](Issue-to-Deploy-Pipeline).
- **Staging instance** against a daily **prod-mirror test DB** for smoke testing.
For the screen-by-screen breakdown, see [Pages and Navigation](Product/Pages-and-Navigation).
For what shipped recently, see [Changelog](Overview/Changelog).
For the screen-by-screen breakdown, see [Pages and Navigation](Pages-and-Navigation).
For what shipped recently, see [Changelog](Changelog).

@ -58,4 +58,4 @@ R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_BUCKET_NAME, R2_PUBLIC
```
In development these can be left as placeholders. See
[Environment Variables](Build-and-Run/Environment-Variables).
[Environment Variables](Environment-Variables).

@ -21,7 +21,7 @@ In development the app needs **only a database and an auth secret**. Cloudflare
R2 and Resend are **not** required — file uploads land in `.dev-uploads/` and
emails are printed to the terminal (lines prefixed `📧 [DEV EMAIL]`). The switch
is automatic, driven by `NODE_ENV` (`next dev` → development, `next build/start`
→ production). See [File Storage](Product/File-Storage) and [Notifications](Product/Notifications).
→ production). See [File Storage](File-Storage) and [Notifications](Notifications).
## Setup
@ -46,7 +46,7 @@ pnpm dev # Next.js + Turbopack at http://localhost:3000
> `auth.ts` reads the Azure/Entra SSO variables at **module load**. In a non-SSO
> dev environment, set placeholder values for `AZURE_AD_*` so the app boots. See
> [Environment Variables](Build-and-Run/Environment-Variables).
> [Environment Variables](Environment-Variables).
## Seed credentials
@ -95,7 +95,7 @@ pnpm email:preview # live-preview email templates at http://localhost:3001
- **GstService** (`GstService/`) — a small Express + Playwright microservice that
proxies the GST portal CAPTCHA/lookup. Optional in dev; defaults to
`http://localhost:3003`. See [Vendors and GST Lookup](Product/Vendors-and-GST-Lookup).
`http://localhost:3003`. See [Vendors and GST Lookup](Vendors-and-GST-Lookup).
## Project layout
@ -112,4 +112,4 @@ App/
└── tests/ # unit (Vitest), integration (Vitest+DB), e2e (Playwright)
```
See [Architecture](System/Architecture) for the full layer breakdown and [Data Model](System/Data-Model) for the schema.
See [Architecture](Architecture) for the full layer breakdown and [Data Model](Data-Model) for the schema.

@ -6,7 +6,7 @@ original design — the definitions below are the **shipped** meanings.
| Term | Meaning |
|---|---|
| **PPMS** | "Pelagia Payment Management System" — the in-UI brand for Pelagia Portal (login, sidebar, title). |
| **Purchase Order (PO)** | The central record: a request to buy goods/services, tracked through its [lifecycle](System/PO-Lifecycle) from DRAFT to CLOSED. |
| **Purchase Order (PO)** | The central record: a request to buy goods/services, tracked through its [lifecycle](PO-Lifecycle) from DRAFT to CLOSED. |
| **Cost Centre** | **A Vessel.** Every PO is raised against a Vessel (`PurchaseOrder.vesselId`, required). Surfaced as "Cost Centre" everywhere in the UI (`/admin/vessels` → "Cost Centre Management"). The earlier Vessel-or-Site cost-centre model was removed. |
| **Vessel** | A ship; the cost centre a PO is charged to. Has a unique `code` used in PO numbers. |
| **Accounting Code** | A budget head: a leaf in the 3-level `Account` hierarchy (Top Category → Sub-Category → Leaf), 6-digit numeric. Only leaf codes are PO-selectable. Previously labelled "Account". |
@ -14,7 +14,7 @@ original design — the definitions below are the **shipped** meanings.
| **PO Number** | Auto-formatted `COMPANY/VESSEL/ID/FY` (e.g. `PMS/HNR1/9000/2024-25`). System IDs start at 9000; imported POs keep their original number. |
| **FY** | Indian financial year (AprMar), rendered `YYYY-YY` in PO numbers. |
| **Vendor** | A supplier. Submitter-created vendors are **unverified** until a PO closes/pays with them, on import, or a Manager/Accounts/Admin verifies. The formal `vendorId` is the verified code. |
| **GSTIN** | 15-char Indian GST identification number; looked up via the [GST microservice](Product/Vendors-and-GST-Lookup) to auto-fill vendor details. |
| **GSTIN** | 15-char Indian GST identification number; looked up via the [GST microservice](Vendors-and-GST-Lookup) to auto-fill vendor details. |
| **Site** | A port/depot/office that holds inventory; used for delivery and vendor-distance sourcing. **Not** a cost centre. |
| **Product / Item** | A catalogue entry (`code`, `name`). Tracks `lastPrice`/`lastVendor` and per-vendor prices, updated on payment. |
| **Line Item** | A row on a PO: name, qty, unit, unit price, GST rate; optional product link and per-line accounting code. |
@ -27,5 +27,5 @@ original design — the definitions below are the **shipped** meanings.
| **pms1** | The single Ubuntu server hosting the app, DB, Forgejo, and CI runner. |
| **`ppms`** | The pm2 process running the production app on port 3000. |
| **`pelagia` / `pelagia_test`** | Production DB / its daily mirror used for staging + autofix verification. |
| **Report Issue** | Header button that files a Forgejo issue, kicking off the [issue-to-deploy pipeline](Ops/Issue-to-Deploy-Pipeline). |
| **Report Issue** | Header button that files a Forgejo issue, kicking off the [issue-to-deploy pipeline](Issue-to-Deploy-Pipeline). |
| **Staging** | A deployed instance of latest `master` (pm2 `ppms-staging`, port 3200, SSH-tunnel only) for pre-release smoke testing. |

18
Home.md

@ -29,13 +29,13 @@ This wiki is the project's living reference. It is synthesised from the in-repo
## Start here
- **New to the codebase?** → [Getting Started](Build-and-Run/Getting-Started)
- **Understanding the system?** → [Architecture](System/Architecture) · [Data Model](System/Data-Model) · [PO Lifecycle](System/PO-Lifecycle)
- **Who can do what?** → [Roles and Permissions](System/Roles-and-Permissions)
- **What's been built?** → [Feature Catalogue](Product/Feature-Catalogue) · [Pages and Navigation](Product/Pages-and-Navigation) · [Workflows](Product/Workflows)
- **Designing UI?** → [Design System](Product/Design-System)
- **What's coming?** → [Planned Features](Planned/Planned-Features)
- **Operating it?** → [Deployment and Operations](Ops/Deployment-and-Operations) · [Issue-to-Deploy Pipeline](Ops/Issue-to-Deploy-Pipeline)
- **Unsure of a term?** → [Glossary](Overview/Glossary)
- **New to the codebase?** → [Getting Started](Getting-Started)
- **Understanding the system?** → [Architecture](Architecture) · [Data Model](Data-Model) · [PO Lifecycle](PO-Lifecycle)
- **Who can do what?** → [Roles and Permissions](Roles-and-Permissions)
- **What's been built?** → [Feature Catalogue](Feature-Catalogue) · [Pages and Navigation](Pages-and-Navigation) · [Workflows](Workflows)
- **Designing UI?** → [Design System](Design-System)
- **What's coming?** → [Planned Features](Planned-Features)
- **Operating it?** → [Deployment and Operations](Deployment-and-Operations) · [Issue-to-Deploy Pipeline](Issue-to-Deploy-Pipeline)
- **Unsure of a term?** → [Glossary](Glossary)
> The full page list is in the sidebar. See [Changelog](Overview/Changelog) for what shipped recently and [Open Questions](Overview/Open-Questions) for decisions still pending sign-off.
> The full page list is in the sidebar. See [Changelog](Changelog) for what shipped recently and [Open Questions](Open-Questions) for decisions still pending sign-off.

@ -50,7 +50,7 @@ regardless of whether the list is sorted by Price or Distance. Selecting a site
also auto-switches the active sort to **Distance** (a `useEffect` keyed on the
site id resets it on every site change — important because Next.js soft
navigation preserves React state). With no site selected, neither distance tag
shows. See [Testing](Quality/Testing) for the specs pinning this down.
shows. See [Testing](Testing) for the specs pinning this down.
## Cart

@ -91,5 +91,5 @@ So the fix stage verifies against realistic data without touching production:
- Schema-migration issues are routed to `interactive`, so the unattended fixer
should not be altering the schema.
See [Deployment and Operations](Ops/Deployment-and-Operations) for the deploy
See [Deployment and Operations](Deployment-and-Operations) for the deploy
workflow and staging, and `automation/README.md` for the authoritative runbook.

@ -32,7 +32,7 @@ The switch is automatic on `NODE_ENV`. Preview templates live with
## Event → recipient matrix
Driven by the side-effects declared per transition in the
[state machine](System/PO-Lifecycle#transition-table):
[state machine](PO-Lifecycle#transition-table):
| Event | Side-effect | Notified |
|---|---|---|
@ -64,5 +64,5 @@ Separate from PO notifications: any signed-in user can file a bug from the heade
via the **Report Issue** button (`components/layout/report-issue-button.tsx`
`report-issue-actions.ts``lib/forgejo.ts`), which creates a Forgejo issue
labelled `portal`. That kicks off the
[Issue-to-Deploy Pipeline](Ops/Issue-to-Deploy-Pipeline). Requires `FORGEJO_URL`,
[Issue-to-Deploy Pipeline](Issue-to-Deploy-Pipeline). Requires `FORGEJO_URL`,
`FORGEJO_REPO`, `FORGEJO_TOKEN` (token scope `write:issue`).

@ -12,10 +12,10 @@ product — annotated below. Update as they resolve.
| 4 | Is SSO required, or is internal credential management enough? | **Resolved** — both: Microsoft Entra SSO **and** a credentials provider; SSO users have nullable passwords. |
| 5 | What currency/currencies? Multi-currency with FX in scope? | **Partly**`currency` defaults to `INR`; multi-currency/FX not implemented. |
| 6 | Hard-delete vs permanent archive for rejected POs; retention window? | **Open**. |
| 7 | Public document URLs vs always-signed/authenticated downloads? | **Resolved** — downloads are auth-gated/presigned (dev route 404s in prod). See [File Storage](Product/File-Storage). |
| 7 | Public document URLs vs always-signed/authenticated downloads? | **Resolved** — downloads are auth-gated/presigned (dev route 404s in prod). See [File Storage](File-Storage). |
| 8 | Row-level vessel/account restrictions per submitter? | **Open** — any submitter can raise a PO against any cost centre. |
| 9 | Expected volume (POs/day, concurrent users) — for pool sizing / `pms1` resourcing? | **Open**. |
| 10 | Should manager analytics count only CLOSED POs, or all from MGR_APPROVED onwards? | **Resolved** — "Approved this month" counts by `approvedAt` (all POs approved in the period), not just those currently in `MGR_APPROVED`. See [Changelog](Overview/Changelog). |
| 10 | Should manager analytics count only CLOSED POs, or all from MGR_APPROVED onwards? | **Resolved** — "Approved this month" counts by `approvedAt` (all POs approved in the period), not just those currently in `MGR_APPROVED`. See [Changelog](Changelog). |
For the design-era spec context, see `Docs/01-design-document.md` and
`Docs/DESIGN.md`.

@ -20,7 +20,7 @@ DRAFT → SUBMITTED → MGR_REVIEW → MGR_APPROVED → SENT_FOR_PAYMENT → PAI
- **Partial payments** (`PARTIALLY_PAID`) and **partial receipts**
(`PARTIALLY_CLOSED`) loop until the full amount/quantity is settled.
- **Imported POs** are created directly in `CLOSED` (historical record,
bypassing approval). See [Purchase Orders](Product/Purchase-Orders#import-po--closed).
bypassing approval). See [Purchase Orders](Purchase-Orders#import-po--closed).
- Terminal states: **REJECTED**, **CLOSED**.
## Transition table
@ -71,14 +71,14 @@ for the current PO status and the signed-in user's role.
Side-effects are declared per transition (`EMAIL_MANAGER`, `EMAIL_SUBMITTER`,
`EMAIL_ACCOUNTS`, `EMAIL_SUBMITTER_AND_MANAGER`) and dispatched via
`lib/notifier.ts` — never directly from UI handlers. See
[Notifications](Product/Notifications) for the event→recipient matrix and templates.
[Notifications](Notifications) for the event→recipient matrix and templates.
Two non-email side-effects worth calling out, applied in the server actions:
- **Product price auto-update** — on payment confirmation, each line item with a
`productId` updates `Product.lastPrice`/`lastVendorId` and upserts the
per-vendor price; a `PRODUCT_PRICE_UPDATED` action is logged. See
[Inventory and Catalogue](Product/Inventory-and-Catalogue).
[Inventory and Catalogue](Inventory-and-Catalogue).
- **Inventory increment** — at **approval**, ordered quantities are added to
`ItemInventory` when the PO has a `siteId`.

@ -3,7 +3,7 @@
Work that is **designed but not yet shipped** — proposals, mockups, and post-MVP
ideas. Nothing on these pages is wired to the database or live in the product;
they exist to align on scope and UX before implementation. For what *is* shipped,
see the [Feature Catalogue](Product/Feature-Catalogue).
see the [Feature Catalogue](Feature-Catalogue).
## Reports (spend reporting)
@ -12,7 +12,7 @@ A post-MVP **Reports** section for spend analysis across **cost centres**
comparison/trend charts, drill-downs, and a pinned filter toolbar (granularity,
financial year, Top-N scope).
**[Reports — UX Mockup](Planned/Reports-Mockup)** — annotated screens of the
**[Reports — UX Mockup](Reports-Mockup)** — annotated screens of the
proposed layout and navigation, with a link to the interactive
`reports-mockup.html`.
@ -20,4 +20,4 @@ proposed layout and navigation, with a link to the interactive
*Add new planned features here as they are designed. When a planned feature
ships, move its page out of `Planned/` into the relevant section and record it in
the [Changelog](Overview/Changelog) and [Feature Catalogue](Product/Feature-Catalogue).*
the [Changelog](Changelog) and [Feature Catalogue](Feature-Catalogue).*

@ -1,6 +1,6 @@
# Playwright Test Design — Pelagia Portal
> _Migrated from the retired `Docs/PLAYRIGHT_TEST_DESIGN.md`. Index: [Testing](Quality/Testing)._
> _Migrated from the retired `Docs/PLAYRIGHT_TEST_DESIGN.md`. Index: [Testing](Testing)._
This document describes how to save, structure, and extend the Playwright verification
scripts written during development sessions. Every script here was used to confirm a

@ -2,8 +2,8 @@
This page covers the mechanics specific to purchase orders: numbering, GST,
the create/edit forms, company invoicing, accounting codes, and Excel import.
For the status graph see [PO Lifecycle](System/PO-Lifecycle); for the schema see
[Data Model](System/Data-Model).
For the status graph see [PO Lifecycle](PO-Lifecycle); for the schema see
[Data Model](Data-Model).
## PO numbering
@ -57,7 +57,7 @@ PO `totalAmount`.
(`/api/products/search`).
3. **Terms & Conditions** — Delivery, Dispatch, Inspection, Transit Insurance,
Payment Terms, Others (all optional text → `tc*` fields).
4. **Documents** — drag-and-drop / browse uploader (see [File Storage](Product/File-Storage)).
4. **Documents** — drag-and-drop / browse uploader (see [File Storage](File-Storage)).
Footer: **Save as Draft** / **Submit for Approval** (and **Update & Resubmit**
when editing an `EDITS_REQUESTED` PO, which returns it to `MGR_REVIEW`).
@ -68,7 +68,7 @@ Validation lives in `lib/validations/po.ts` (Zod), which also exports
> **Form selector gotcha** (for tests): the PO form labels are visual-only — no
> `htmlFor`/`id` binding. Use `name`-attribute selectors
> (`input[name="title"]`, `select[name="vesselId"]`). See [Testing](Quality/Testing).
> (`input[name="title"]`, `select[name="vesselId"]`). See [Testing](Testing).
## Companies (multi-company invoicing)
@ -87,7 +87,7 @@ groups leaf codes by sub-category in a searchable, portal-rendered combobox
`accountId`. Seed data: `prisma/accounting-codes-data.ts`.
> "Accounting Code" replaces the older "Account" label. The **Cost Centre** is a
> separate concept — it is the Vessel. See [Glossary](Overview/Glossary).
> separate concept — it is the Vessel. See [Glossary](Glossary).
## Payments

@ -4,7 +4,7 @@ Authorisation is centralised in `lib/permissions.ts`. Server Actions call
`requirePermission(role, permission)` at the top before any DB write;
`hasPermission(role, permission)` gates UI and page segments. The PO state
machine adds a second gate (status + role) on top of permissions — see
[PO Lifecycle](System/PO-Lifecycle).
[PO Lifecycle](PO-Lifecycle).
## The seven roles
@ -73,5 +73,5 @@ Beyond the permission map, server actions and the state machine enforce:
- **Receipt confirmation** is the submitter's own PO (or SUPERUSER/MANAGER).
- **Payment date** is compulsory and cannot be in the future.
See the [Testing](Quality/Testing) page for the permission test matrix that pins these
See the [Testing](Testing) page for the permission test matrix that pins these
rules down with integration tests.

@ -1,6 +1,6 @@
# Pelagia Portal — Test Plan
> _Migrated from the retired `Docs/TEST_PLAN.md`. Index: [Testing](Quality/Testing)._
> _Migrated from the retired `Docs/TEST_PLAN.md`. Index: [Testing](Testing)._
**Version:** 1.0
**Date:** 2026-05-09

@ -3,10 +3,10 @@
The index for the test suite. Deep references (migrated from the retired
`Docs/`):
- [Test Plan](Quality/Test-Plan) — unit/integration/E2E coverage + permission matrix
- [E2E Test Framework](Quality/E2E-Test-Framework) — Playwright stack, helpers, selector conventions
- [E2E Test Plan](Quality/E2E-Test-Plan) — feature-coverage matrix and test cases
- [Playwright Test Design](Quality/Playwright-Test-Design) — verification-script design principles
- [Test Plan](Test-Plan) — unit/integration/E2E coverage + permission matrix
- [E2E Test Framework](E2E-Test-Framework) — Playwright stack, helpers, selector conventions
- [E2E Test Plan](E2E-Test-Plan) — feature-coverage matrix and test cases
- [Playwright Test Design](Playwright-Test-Design) — verification-script design principles
Three layers, all run on PRs. Commands run from `App/`.
@ -74,8 +74,8 @@ suite isolates with a `PREFIX` constant and cleans up via
## E2E tests (Playwright)
Browser-level checks against a live dev server + Postgres. The full E2E
framework reference is the [E2E Test Framework](Quality/E2E-Test-Framework) page;
the feature-coverage matrix is the [E2E Test Plan](Quality/E2E-Test-Plan).
framework reference is the [E2E Test Framework](E2E-Test-Framework) page;
the feature-coverage matrix is the [E2E Test Plan](E2E-Test-Plan).
### Config (`playwright.config.ts`)
@ -96,7 +96,7 @@ login redirect.
### Shared helpers (`tests/e2e/helpers/login.ts`)
- `USERS` — the seed credentials (see [Getting Started](Build-and-Run/Getting-Started#seed-credentials)).
- `USERS` — the seed credentials (see [Getting Started](Getting-Started#seed-credentials)).
- `login(page, creds)` — fills `/login`, waits up to **20 s** for the redirect
(bcrypt + DB can exceed Playwright's 5 s default).
- `createDraftPo(page, title)` / `submitPo(page, title)` — minimal PO setup.
@ -121,7 +121,7 @@ Rebrand, dashboard status badges, submit button, notification bell, export gate
vendor auto-verify, admin bordered buttons, profile + signature, inventory
Cheapest/★Closest tags, cart icon, item/vendor detail pages, mobile
(Desktop-Required overlay, manager cards, accounts payment, bottom nav),
edit-highlight diff. See the [E2E Test Plan](Quality/E2E-Test-Plan) for the full
edit-highlight diff. See the [E2E Test Plan](E2E-Test-Plan) for the full
matrix and known flaky/needs-fix items.
## Out of scope / known gaps

@ -1,7 +1,7 @@
# Vendors and GST Lookup
Vendors are suppliers a PO can be raised against. The model and its evolution
are on [Data Model](System/Data-Model#vendor--vendorcontact); this page covers the
are on [Data Model](Data-Model#vendor--vendorcontact); this page covers the
verification lifecycle, distance-based sourcing, and the GST microservice that
backs GSTIN lookup.
@ -33,7 +33,7 @@ and a `VendorContact[]` list (name, role, mobile, email, isPrimary). Managed at
detail / items pages, selecting a **Site** re-sorts vendors by proximity to that
site; the nearest vendor gets a **★ Closest** tag and the lowest price gets a
**Cheapest** tag — computed independently so both can show at once, regardless
of the active sort. See [Inventory and Catalogue](Product/Inventory-and-Catalogue).
of the active sort. See [Inventory and Catalogue](Inventory-and-Catalogue).
## GSTIN lookup (GstService)

@ -2,8 +2,8 @@
Step-by-step user workflows, migrated from the original UI/UX spec
(`Docs/DESIGN.md`, now retired). For the screen list see
[Pages and Navigation](Product/Pages-and-Navigation); for the status graph see
[PO Lifecycle](System/PO-Lifecycle).
[Pages and Navigation](Pages-and-Navigation); for the status graph see
[PO Lifecycle](PO-Lifecycle).
## Submit a purchase order (Technical / Manning)
@ -35,7 +35,7 @@ Step-by-step user workflows, migrated from the original UI/UX spec
- **Reject** → write a reason; PO is closed permanently.
> A vendor must be assigned before approval, and only **verified** vendors may be
> assigned. See [Roles and Permissions](System/Roles-and-Permissions).
> assigned. See [Roles and Permissions](Roles-and-Permissions).
## Process a payment (Accounts)
@ -63,7 +63,7 @@ Step-by-step user workflows, migrated from the original UI/UX spec
name, address, and pincode.
4. Save; location is geocoded silently from the pincode for distance sorting.
Detail: [Vendors and GST Lookup](Product/Vendors-and-GST-Lookup).
Detail: [Vendors and GST Lookup](Vendors-and-GST-Lookup).
## Source items by proximity (Manager)
@ -86,7 +86,7 @@ Detail: [Vendors and GST Lookup](Product/Vendors-and-GST-Lookup).
1. Open **Sites** → click a site.
2. View the current-stock bar chart and the 30-day consumption line chart.
3. Use **Log Consumption** to record a daily drawdown (product, date, quantity,
note). See [Inventory and Catalogue](Product/Inventory-and-Catalogue).
note). See [Inventory and Catalogue](Inventory-and-Catalogue).
## Import a PO from Excel (Manager / SuperUser / Admin)
@ -95,7 +95,7 @@ Detail: [Vendors and GST Lookup](Product/Vendors-and-GST-Lookup).
auto-detected and the vessel auto-matched by code.
3. The PO is saved **directly as `CLOSED`** (a historical record), auto-creating
the vendor and any unknown products. See
[Purchase Orders](Product/Purchase-Orders#import-po--closed).
[Purchase Orders](Purchase-Orders#import-po--closed).
## Export PO history (Auditor / Manager)
@ -108,7 +108,7 @@ Detail: [Vendors and GST Lookup](Product/Vendors-and-GST-Lookup).
## User stories (priority reference)
From the original spec (`Docs/01-design-document.md` §8); IDs are referenced by
the [Test Plan](Quality/Test-Plan). P0 = must-have, P1 = should-have.
the [Test Plan](Test-Plan). P0 = must-have, P1 = should-have.
**Submitter (Technical / Manning)**

@ -2,43 +2,43 @@
- [Home](Home)
**Overview/**
- [Glossary](Overview/Glossary)
- [Changelog](Overview/Changelog)
- [Open Questions](Overview/Open-Questions)
**Overview**
- [Glossary](Glossary)
- [Changelog](Changelog)
- [Open Questions](Open-Questions)
**Build-and-Run/**
- [Getting Started](Build-and-Run/Getting-Started)
- [Environment Variables](Build-and-Run/Environment-Variables)
**Build & Run**
- [Getting Started](Getting-Started)
- [Environment Variables](Environment-Variables)
**System/**
- [Architecture](System/Architecture)
- [Data Model](System/Data-Model)
- [PO Lifecycle](System/PO-Lifecycle)
- [Roles and Permissions](System/Roles-and-Permissions)
**System**
- [Architecture](Architecture)
- [Data Model](Data-Model)
- [PO Lifecycle](PO-Lifecycle)
- [Roles and Permissions](Roles-and-Permissions)
**Product/**
- [Feature Catalogue](Product/Feature-Catalogue)
- [Pages and Navigation](Product/Pages-and-Navigation)
- [Workflows](Product/Workflows)
- [Purchase Orders](Product/Purchase-Orders)
- [Vendors and GST Lookup](Product/Vendors-and-GST-Lookup)
- [Inventory and Catalogue](Product/Inventory-and-Catalogue)
- [Notifications](Product/Notifications)
- [File Storage](Product/File-Storage)
- [Design System](Product/Design-System)
**Product**
- [Feature Catalogue](Feature-Catalogue)
- [Pages and Navigation](Pages-and-Navigation)
- [Workflows](Workflows)
- [Purchase Orders](Purchase-Orders)
- [Vendors and GST Lookup](Vendors-and-GST-Lookup)
- [Inventory and Catalogue](Inventory-and-Catalogue)
- [Notifications](Notifications)
- [File Storage](File-Storage)
- [Design System](Design-System)
**Planned/**
- [Planned Features](Planned/Planned-Features)
- [Reports Mockup](Planned/Reports-Mockup)
**Planned**
- [Planned Features](Planned-Features)
- [Reports Mockup](Reports-Mockup)
**Quality/**
- [Testing](Quality/Testing)
- [Test Plan](Quality/Test-Plan)
- [E2E Test Framework](Quality/E2E-Test-Framework)
- [E2E Test Plan](Quality/E2E-Test-Plan)
- [Playwright Test Design](Quality/Playwright-Test-Design)
**Quality**
- [Testing](Testing)
- [Test Plan](Test-Plan)
- [E2E Test Framework](E2E-Test-Framework)
- [E2E Test Plan](E2E-Test-Plan)
- [Playwright Test Design](Playwright-Test-Design)
**Ops/**
- [Deployment and Operations](Ops/Deployment-and-Operations)
- [Issue-to-Deploy Pipeline](Ops/Issue-to-Deploy-Pipeline)
**Ops**
- [Deployment and Operations](Deployment-and-Operations)
- [Issue-to-Deploy Pipeline](Issue-to-Deploy-Pipeline)