From 8b56c79f5f361061e47dad7facbd3a2b6a89539d Mon Sep 17 00:00:00 2001 From: Hardik Date: Wed, 24 Jun 2026 15:53:05 +0530 Subject: [PATCH] docs: changelog + PdfService README for reports, email-to-vendor, microservices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The repo CHANGELOG had fallen behind. Adds [Unreleased] entries for the recently shipped work and a README for the previously undocumented PdfService. Added (changelog): - Reports — Purchasing spend analytics (cost centres + accounting codes). - Email PO to vendor + the PdfService microservice (cached per PO). - EpfoService + PdfService microservices and their release auto-deploy. - Unsaved-changes prompt on PO create/edit (#18). - Crew login-on-hire for management ranks. - Delivery Locations (#19), T&C catalogue (#11), advance payment (#92). Fixed (changelog): - "Email to vendor" never rendered (auth middleware bounced the svc fetch) — #127. - Reports charts all one colour (RSC client/server boundary) — #120. New: PdfService/README.md (endpoints, token/origin security, env, app integration). Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 10 ++++++++ PdfService/README.md | 54 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 PdfService/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 855b5f8..ad32504 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ ### Added +- **Reports — Purchasing spend analytics** (`view_analytics`: Manager / SuperUser / Auditor / Admin) — `/reports/cost-centres` and `/reports/accounting-codes`, each an index → drill-down → detail. KPI tiles, comparison + trend charts (one colour per item), Top-N tables, per-row sparklines, and CSV export; URL-driven filters (granularity Weekly / Monthly / Yearly, financial year, Top/Bottom-N, an "Add to graph" custom comparison). Spend = post-approval POs by `approvedAt`/`totalAmount`, allocated across each PO's line-item accounting codes. Pure, unit-tested core in `lib/reports.ts`. +- **Email PO to vendor** (issue #14) — one-click Outlook draft to the vendor's primary contact with a **7-day download link** to the PO PDF. Rendered by the new **PdfService** microservice (Express + Playwright → headless Chromium) and stored in R2; the PDF is **cached per PO**, so repeat sends reuse the copy and only refresh the link. +- **Microservices** — `EpfoService` (UAN / EPFO assisted-lookup proxy; live portal nav stubbed behind `EPFO_LIVE`) and `PdfService` (PO → PDF) join `GstService`. All three are **auto-deployed on each release tag** via the root `ecosystem.config.js` + `deploy.yml` (`pm2 startOrReload … --update-env`). +- **Unsaved-changes prompt** (issue #18) — leaving the PO create/edit screen with unsaved edits offers **Save as draft / Discard / Stay** (in-app navigation) or the browser's native warning (refresh / close). +- **Crew login on hire** (crewing, feature-flagged) — onboarding, direct placement, and admin crew-create accept an explicit **login email + initial password** for management ranks (`Rank.grantsLogin`), creating the `SITE_STAFF` login in one step. +- **Delivery Locations** (issue #19) — admin-managed `Company`+address list backing the PO "Place of Delivery" dropdown, gated by `manage_delivery_locations` (Manager / SuperUser / Admin). +- **Terms & Conditions catalogue** (issue #11) — admin-managed, user-defined T&C categories + clauses feeding a dynamic PO terms editor; the chosen rows are a JSON snapshot on `PurchaseOrder.terms`. +- **Advance payment on approval** (issue #92) — the approving Manager sets how much is paid first; the resolved absolute amount is stored on `PurchaseOrder.suggestedAdvancePayment` and prefills the first Accounts payment. - **Companies (multi-company invoicing)** — new `Company` model and `/admin/companies` CRUD. A PO is billed under a selected company (name, short `code`, GST number, address, phone/mobile, contact + invoice email, invoice address). The company's details populate the exported PO header / invoice block. - **Structured PO numbers** (`lib/po-number.ts`) — `COMPANY/VESSEL/ID/FY` (e.g. `PMS/HNR1/9000/2024-25`); Indian financial year; system-generated IDs start at 9000. Imported POs keep their original number. - **3-level accounting-code hierarchy** — `Account.parentId` self-relation (Top Category → Sub-Category → Leaf), 6-digit numeric codes seeded from `prisma/accounting-codes-data.ts`. Only leaf codes are PO-selectable, via a searchable, portal-rendered combobox. @@ -29,4 +37,6 @@ ### Fixed +- **"Email to vendor" never rendered a real PDF** (issue #14) — the auth middleware redirected PdfService's unauthenticated `svc`-token export fetch to `/login` before the route's token check ran, so the bypass never executed. `/api/po//export` is now allowed through when its `svc` token matches `PDF_SERVICE_TOKEN` (`lib/pdf-export-auth.ts`); everything else stays auth-gated. +- **Reports comparison charts all rendered one colour** — `SERIES_COLORS` lived in a `"use client"` module and was imported by the server-component report pages, where a plain value becomes a client-reference proxy (so `SERIES_COLORS[i]` was `undefined` and recharts fell back to its default stroke). Moved the palette to a dependency-free shared module (`lib/report-colors.ts`). - Production `P2022 … column does not exist` after deploy — caused by shipping code whose Prisma client expected a column before `migrate deploy` had run. Migrations must be applied before the new build serves traffic (now documented in the README). diff --git a/PdfService/README.md b/PdfService/README.md new file mode 100644 index 0000000..0c91fb4 --- /dev/null +++ b/PdfService/README.md @@ -0,0 +1,54 @@ +# PdfService + +Renders a PPMS purchase order to a real **PDF** for the **"Email PO to vendor"** +feature — a standalone **Express + Playwright** microservice, mirroring +`GstService` / `EpfoService`. + +The app's `/api/po/:id/export?format=pdf&pdf=1` produces a print-styled HTML page; +PdfService loads that URL in **headless Chromium** and prints it to an A4 PDF. The +export URL carries a short-lived **`svc` token** so the export route serves the +page without a user session (the app's auth middleware allows that one route +through when the token matches — see `App/lib/pdf-export-auth.ts`). + +## Endpoints + +| Method | Path | Body / Headers | Returns | +|---|---|---|---| +| GET | `/health` | — | `{ status, browser }` | +| POST | `/pdf` | `{ url }` + header `x-pdf-token` | `application/pdf` (else `401` / `400` / `403` / `502`) | + +## Security + +- **Token** — when `PDF_SERVICE_TOKEN` is set, `/pdf` requires a matching + `x-pdf-token` header (the app and PdfService share the secret). +- **Origin allow-list (anti-SSRF)** — when `ALLOWED_ORIGIN` is set, PdfService + only navigates to URLs whose origin matches it. +- Both unset (dev) → checks are skipped. + +## Env + +``` +PORT=3005 +PDF_SERVICE_TOKEN= # shared secret with the app (app side: PDF_SERVICE_TOKEN) +ALLOWED_ORIGIN= # e.g. http://localhost:3000 (optional) +``` + +## Run + +``` +npm install +npm run dev # tsx watch src/index.ts +npm run build && npm start # node dist/index.js +``` + +## App integration + +`App/lib/pdf-service.ts` (`renderPoPdf`) POSTs `{ url }` to `/pdf`. The app gates +the feature on `PDF_SERVICE_URL` + `PDF_SERVICE_TOKEN` (`isPdfServiceConfigured()`), +uploads the returned PDF to R2 at a **per-PO key** (reused across sends), and +returns a `mailto:` with a 7-day presigned link. `APP_INTERNAL_URL` is the base URL +PdfService reaches the app at (falls back to `NEXTAUTH_URL`). + +On **pms1** the service is auto-deployed on each release tag via the root +`ecosystem.config.js` (pm2 `pdf-service`, port 3005) — see +[Deployment and Operations](https://git.pelagiamarine.com/shad0w/pelagia-portal/wiki/Deployment-and-Operations#microservices).