Commit graph

226 commits

Author SHA1 Message Date
74d20cd452 Merge pull request 'feat(po): per-company logo, stamp & brand bar on exported POs' (#48) from feat/company-header-logo-branding into master
All checks were successful
Refresh staging / refresh (push) Successful in 8s
Reviewed-on: #48
2026-06-20 20:01:46 +00:00
467f0ddea4 Merge branch 'master' into feat/company-header-logo-branding
All checks were successful
PR checks / checks (pull_request) Successful in 32s
2026-06-20 20:01:32 +00:00
64fefd15a8 Merge pull request 'feat(staging): auto-refresh staging on every push to master' (#46) from feat/staging-auto-refresh into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Reviewed-on: #46
2026-06-20 19:55:12 +00:00
1071cb226f feat(po): per-company logo, stamp & brand bar on exported POs
All checks were successful
PR checks / checks (pull_request) Successful in 31s
Companies can upload a logo and a stamp/seal (Admin → Companies → Edit →
Branding); both render on exported PDF and XLSX purchase orders. A fixed
brand-colour bar (#92D050, matching the sample PO) runs along the bottom of
every export.

- Company.logoKey / stampKey + migration
- buildCompanyAssetKey() deterministic storage keys (overwrite-in-place)
- uploadCompanyAsset / removeCompanyAsset server actions (≤4MB PNG/JPG/WebP,
  manage_vessels_accounts gated)
- CompanyBrandingUploader in the company edit dialog with live previews
- Export route embeds logo (top-left), stamp (signatory block) and brand bar
  in both ExcelJS and print-HTML paths
- Unit test (storage keys) + integration test (branding actions)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 01:17:23 +05:30
a0c6ccba3c Merge branch 'master' into feat/staging-auto-refresh
All checks were successful
PR checks / checks (pull_request) Successful in 30s
2026-06-20 19:39:45 +00:00
9f8297aa7e feat(staging): auto-refresh staging on every push to master
All checks were successful
PR checks / checks (pull_request) Successful in 30s
New .forgejo/workflows/staging.yml rebuilds ppms-staging to latest master on every
merge (push to master) on the host runner, so staging always mirrors the trunk;
concurrency-coalesced + workflow_dispatch. Also drops --update-env from staging-up.sh
(and unsets FORGEJO_*) so the runner's ephemeral token can't leak into ppms-staging.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 01:07:49 +05:30
783051933a Merge pull request 'fix: Add a PO line items units entry for months and year' (#45) from claude/issue-44 into master
Reviewed-on: #45
2026-06-20 19:05:08 +00:00
Claude (auto-fix)
e9e618fda8 feat(po): add week, month, year to line-item unit options
All checks were successful
PR checks / checks (pull_request) Successful in 31s
The PO line-items Unit of Measure dropdown only offered hr/day among
time-based units. Add week, month and year so durations beyond days can
be selected, as requested. UOM_OPTIONS is the single source of truth and
`unit` is validated as a free-form string, so no schema/validation change
is needed.

Fixes #44

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 00:32:39 +05:30
5bb3549142 Merge pull request 'fix(deploy): don't inject CI runner token into ppms' (#43) from fix/deploy-no-update-env into master
Reviewed-on: #43
2026-06-20 18:29:07 +00:00
2d6681014d fix(deploy): don't inject the CI runner token into ppms (drop --update-env)
All checks were successful
PR checks / checks (pull_request) Successful in 31s
The deploy job runs inside the Forgejo Actions runner, whose env includes an
ephemeral FORGEJO_TOKEN (per-job token, revoked when the job ends). 'pm2 restart
--update-env' injected it into ppms, where it shadowed the real PAT in .env
(Next.js won't override an already-set process.env var) — so the Report Issue
button 401'd once the job token expired. Plain restart keeps the daemon's clean env.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 23:57:01 +05:30
1feb43186d Merge pull request 'fix(automation): triage owns routing for every portal issue' (#39) from fix/triage-owns-portal-routing into master
All checks were successful
PR checks / checks (pull_request) Successful in 30s
Reviewed-on: #39
2026-06-19 08:45:21 +00:00
794bbf8e7e Merge branch 'master' into fix/triage-owns-portal-routing
All checks were successful
PR checks / checks (pull_request) Successful in 31s
2026-06-19 08:41:45 +00:00
520b1527e0 feat(automation): triage classifies bug vs feature
Triage now writes CLAUDE_TRIAGE_TYPE.txt (bug|feature) and the watcher applies the
matching label to every triaged issue (additive). Previously bug/feature labels were
never applied by the pipeline. Also shows the type in the triage comment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 14:10:48 +05:30
aec6d2971f fix(automation): triage owns routing for every portal issue
The Report Issue button (older deployed build) stamps claude-queue at creation, so
triage skipped those issues and they went straight to auto-fix (e.g. #37, a large
localization feature that should be interactive).

Triage now claims a portal issue until it carries a new `triaged` marker (or is
in progress/done) — claude-queue is no longer a skip reason. On routing to
interactive it strips the stray claude-queue; on claude-queue it adds triaged.
Manual queue still works for NON-portal issues (triage never claims those).

Resilient regardless of which button build is deployed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 14:00:44 +05:30
064e4ebf66 Merge pull request 'docs: retire Docs/ to the project wiki' (#38) from docs/retire-docs-to-wiki into master
Reviewed-on: #38
2026-06-19 08:29:01 +00:00
56a947747c @
docs: retire Docs/ to the project wiki

The design, architecture, and test docs under Docs/ have been migrated to the
Forgejo wiki (the living reference). Remove them here and leave a tombstone
Docs/README.md mapping each old file to its wiki page.

Also gitignore the nested wiki working clone (pelagia-portal.wiki/), which is a
separate repo checked out beside this one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
2026-06-19 13:56:50 +05:30
0fe043e833 Merge pull request 'ci: PR policy enforcement (tests + docs) and checks' (#36) from ci/pr-checks into master
Reviewed-on: #36
2026-06-19 07:37:39 +00:00
8406397602 Merge branch 'master' into ci/pr-checks
All checks were successful
PR checks / checks (pull_request) Successful in 30s
2026-06-19 07:36:28 +00:00
938ff6df89 test+ci: green the test baseline and make type-check + unit tests hard gates
All checks were successful
PR checks / checks (pull_request) Successful in 30s
Green-lights the test suite so the PR checks can enforce it:
- Fix the NextAuth v5 auth() mock typing across all integration tests (cast to a
  simple async fn so mockResolvedValue accepts the session) — clears ~86 errors.
- Fix stale test values: intent 'resubmit'->'submit' / 'save'->'draft'; ParsedImportLine
  .description -> .name; approvepo -> approvePo; add missing beforeEach/beforeAll imports.
- permissions: MANAGER *can* process_payment (intentional since e1340b9) — update the
  stale assertion.
- po-import-parser: skip the Sample_PO.xlsx fixture tests when the file is absent (it
  lives outside the repo); synthetic-workbook tests still cover the parser.

type-check is now 0 errors and unit tests pass (167 passed, 13 skipped). pr-checks.yml
flips type-check (whole project) and unit tests to HARD gates.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 13:03:54 +05:30
debac55a8a ci: enforce PR policy (tests-present + app type-check) and PR template
All checks were successful
PR checks / checks (pull_request) Successful in 39s
All changes now land via PR. New .forgejo/workflows/pr-checks.yml runs on every PR
to master and (1) fails code PRs that lack a test change, (2) blocks new app-code type
errors. Unit tests are advisory until the baseline is green; lint is omitted (it needs
an interactive ESLint migration). PR template carries the docs/tests checklist.

Also makes the autofix watcher require a test (issue-12 style) + doc updates in every
fix, so its PRs satisfy the new gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 12:49:32 +05:30
8a614878d2 Merge pull request 'docs: bring product docs up to date' (#35) from docs/sync-product-docs into master
Reviewed-on: #35
2026-06-19 07:17:36 +00:00
b0140cf7e1 Merge branch 'master' into docs/sync-product-docs 2026-06-19 07:17:23 +00:00
58a5a00594 docs: bring CLAUDE.md, README, Docs and CHANGELOG up to date with current product
Reflects this iteration's domain/feature changes across the docs set:
- Cost centre = Vessel only (labelled 'Cost Centre'); costCentreRef/Site removed
- Companies (multi-company invoicing) on POs and exports
- 3-level 6-digit accounting-code hierarchy; leaf-only PO selection
- Structured PO numbers COMPANY/VESSEL/ID/FY (ids from 9000)
- Compulsory payment date; editable poDate; export date = approval date
- Submitter vendor creation (unverified until proven); verifyVendor
- Import PO -> CLOSED with auto vendor/product creation
- Inventory flag; inventory added at approval; partial pay/receipt states
- Microsoft Entra SSO (nullable passwordHash); profile reachable by all roles
- README: roles, domain concepts, db:seed:prod, migrate-before-serve callout
- CHANGELOG: Added/Changed/Fixed for the above

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 12:43:24 +05:30
157b58daf5 Merge pull request 'fix: Approved this month for manager only shows POs that are currently in Approved state' (#34) from claude/issue-32 into master
Reviewed-on: #34
2026-06-19 07:10:18 +00:00
791e99f3fd Merge commit '859be8c8d0' into fix-pr34
# Conflicts:
#	App/app/(portal)/history/history-filters.tsx
2026-06-19 12:35:35 +05:30
859be8c8d0 Merge pull request 'fix: Suggestion - allow multiple statuses in the search for PO history' (#33) from claude/issue-31 into master
Reviewed-on: #33
2026-06-19 07:00:31 +00:00
b3e6f6181a Merge branch 'master' into claude/issue-31 2026-06-19 06:59:39 +00:00
7713601be7 Merge pull request 'fix: PO details: show all attachments, grouped by type' (#27) from claude/issue-10 into master
Reviewed-on: #27
2026-06-19 06:57:08 +00:00
f17df1ec6b docs: update design docs to the actual self-hosted architecture
The original design docs assumed Vercel + Supabase + GitHub Actions. Reality is a
single self-hosted pms1 server (Next.js under pm2, native PostgreSQL 16, Forgejo
Actions runner, Pangolin/Traefik tunnel).

- 02-architecture.md: CI/CD + Hosting rows, deployment diagram (section 10),
  CI/testing note, branch strategy, and secrets location.
- e2e-test-plan.md / e2e-test-framework.md: GitHub Actions -> Forgejo Actions.
- 03-open-questions.md: drop the Vercel-serverless aside.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 12:12:04 +05:30
e31014d45c docs: document the issue-to-deploy pipeline, staging, and test DB
- App/README.md: add FORGEJO_*/NEXT_PUBLIC_ENV_LABEL env vars and an
  'Operations & Automation' section pointing to automation/README.md.
- App/CLAUDE.md: complete the env var list (AZURE_AD_*, FORGEJO_*, GST_SERVICE_URL,
  NEXT_PUBLIC_ENV_LABEL) and note the prod-mirror test DB used by autofix/staging.
- .env.example: document NEXT_PUBLIC_ENV_LABEL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 12:07:55 +05:30
Claude (auto-fix)
fdc3ebdac9 fix(dashboard): count all POs approved this month, not just current MGR_APPROVED
The manager dashboard "Approved This Month" card only counted POs whose
current status is MGR_APPROVED, so approvals that had already moved on to
payment, delivery, or closure dropped out of the count. Managers could not
see what happened to the POs they approved this month.

- Count every PO whose `approvedAt` falls in the current month across all
  post-approval statuses (MGR_APPROVED → ... → CLOSED). `approvedAt` is set
  once at approval and persists, so it is the correct anchor.
- Introduce a shared `POST_APPROVAL_STATUSES` constant (includes the
  previously-omitted PARTIALLY_CLOSED). This also fixes Total Approved Spend
  and the vessel/monthly breakdowns, which were silently dropping
  partially-received POs.
- Make the card a link into /history with an approval-date filter applied
  (?approvedFrom=<startOfMonth>) so a click shows the full set with each PO's
  current status, as requested.
- Add `approvedFrom`/`approvedTo` filtering to the history page, its filter
  UI, and the reports export route so the deep-link and exports stay in sync.

Scope note: the count remains org-wide, consistent with every other card on
the manager dashboard.

Adds an integration test covering the moved-on case and the date window.

Fixes #32
2026-06-19 12:07:53 +05:30
b472c149b4 feat(automation): lock staging to SSH tunnel + dev banner + desktop shortcut
- staging-up.sh binds the dev server to 127.0.0.1 (tunnel-only, no public access)
  and sets NEXT_PUBLIC_ENV_LABEL so the 'INTERNAL DEV / STAGING - NOT PRODUCTION'
  banner shows.
- staging-tunnel.cmd: Windows launcher that opens the SSH tunnel + browser
  (wired to a desktop shortcut).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 11:59:25 +05:30
b592358db0 feat(app): env-gated banner (EnvBanner) for non-prod environments
Renders a thin fixed banner only when NEXT_PUBLIC_ENV_LABEL is set; production
leaves it unset so nothing shows. Used to mark the staging instance.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 11:56:34 +05:30
Claude (auto-fix)
e94c7f99a3 feat(history): allow filtering PO history by multiple statuses
The PO history page previously allowed only a single status filter. This
enhances it to accept multiple statuses that are OR-ed together (e.g.
Closed + Approved shows all POs in either state), as requested.

- Status filter is now a multi-select checkbox dropdown that serialises
  selections as repeated `status` query params.
- History page and the reports export endpoint read all `status` values
  and query with `status: { in: [...] }` (OR semantics).
- Single-status and no-status cases remain unchanged.

Verified OR-query semantics against the test DB and confirmed both routes
compile and respond. type-check passes for the changed files.

Fixes #31

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 11:53:42 +05:30
4da39fe5d1 fix(automation): apply master migrations to the test DB
The test DB mirrors prod, which can be behind master, so the latest code 500s on
columns prod doesn't have yet (e.g. poDate from the optional-PO-date feature).

- staging-up.sh runs prisma migrate deploy after install.
- refresh-test-db.sh re-applies master migrations after each nightly data copy,
  so the running staging/autofix DB stays at the schema of the code under test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 11:51:59 +05:30
7daf3091bc feat(automation): staging-up.sh for pre-deploy smoke testing on pms1
Brings up pm2 'ppms-staging' on port 3200 from the latest master, against the
prod-mirror test DB in safe dev mode. Re-run to refresh to newer master.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 11:40:06 +05:30
12e6d16061 feat(automation): test DB mirror + dev-server env for autofix verification
- automation/refresh-test-db.sh: daily pg_dump of prod (pelagia) into a throwaway
  mirror (pelagia_test) on pms1; cron at 03:30. ~10MB, refresh ~1s.
- Autofix clone ~/pelagia-autofix/App/.env points DATABASE_URL at pelagia_test in
  safe dev mode (no Resend/SSO secrets -> console email, local storage), port 3100.
- Fix prompt: Claude may run integration tests against the test DB and start a dev
  server on port 3100 ONLY; stop it by port (fuser -k 3100/tcp), never broad pkill
  (production also runs a next-server on 3000).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 04:49:15 +05:30
Claude (auto-fix)
4e6175153d fix(po): show all attachments grouped by type on PO details
All PO attachments are stored as PODocument rows whose lifecycle stage
(submission vs delivery) is encoded in the storageKey prefix. The PO
details screen previously listed them in a single flat "Attachments"
block, giving no indication of which were submission documents (invoice,
quotation) versus delivery receipts.

Add lib/attachments.ts to derive a user-facing group from the storageKey
prefix (submission / payment / delivery / other) and render each
non-empty group as a labelled subsection on the PO details screen, in
lifecycle order. Unknown prefixes fall back to an "Other" group so
nothing is ever hidden.

Fixes #10
2026-06-19 04:43:44 +05:30
3e711a171c feat(automation): port issue watcher to bash for pms1 (cron, 24/7)
- automation/claude-issue-watcher.sh: Linux port of the watcher (curl + jq +
  flock). Same triage + fix phases. On Linux the PS 5.1 encoding/array quirks
  don't apply, so it's simpler.
- Auth preflight: no-ops until Claude Code is signed in on the host (or an
  ANTHROPIC_API_KEY is set), so cron can be enabled before sign-in.
- Runs on pms1 under cron every 10 min; Windows scheduled task is disabled so the
  two machines don't race the Forgejo queue.
- .gitattributes pins *.sh to LF.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 04:32:06 +05:30
080dafb473 feat(automation): add triage phase to issue watcher
Portal issues now file with only the 'portal' label. The watcher runs two phases:
  1. Triage — Claude reads each untriaged 'portal' issue (analysis only), posts a
     requirements-breakdown comment, and routes it to 'claude-queue' (auto-fixable)
     or 'interactive' (needs human steering).
  2. Fix — unchanged; processes 'claude-queue' issues into PRs.

The triage breakdown is posted without the bot marker so the fix stage reads it
back as refined requirements.

PS 5.1 fixes found while validating:
  - Send API bodies as UTF-8 bytes (Invoke-RestMethod mangled non-ASCII, e.g. the
    em-dash in Claude's breakdown, so Forgejo rejected the JSON)
  - Build the labels array body by hand (ConvertTo-Json unwraps a single-element
    array to a scalar, which Forgejo rejects)
  - Triage output via two plain files (label + markdown) instead of one JSON blob
    (embedded-newline markdown broke ConvertFrom-Json)
  - Read triage files as UTF-8; additive label POST + a guard so Set-IssueLabels
    can never wipe an issue's labels

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 04:20:21 +05:30
23e5243442 Merge pull request 'fix: Allow attachments (incl. delivery receipt) at delivery confirmation' (#25) from claude/issue-9 into master
Reviewed-on: #25
2026-06-18 22:34:37 +00:00
64634ccb5e Merge branch 'master' into claude/issue-9 2026-06-18 22:34:18 +00:00
69901ba079 Merge pull request 'fix: Exported PO must include line item optional description' (#23) from claude/issue-8 into master
Reviewed-on: #23
2026-06-18 22:32:27 +00:00
Claude (auto-fix)
9adc93e54a fix(receipt): upsert Receipt record on repeat confirmations with notes
Partial-receipt flows call confirmReceipt multiple times. The nested
`create` on the Receipt relation threw a unique-constraint error on the
second call when both confirmations supplied notes, preventing any
delivery from completing and blocking attachment uploads.

Changed to `upsert` so subsequent confirmations update the existing
Receipt row's notes instead of failing.

Adds integration tests covering full receipt, partial receipt, the
upsert scenario (two confirmations each with notes), and permission guards.

Fixes #9
2026-06-19 04:01:26 +05:30
Claude (auto-fix)
d7be141589 fix(export): include optional line item description in PDF and XLSX exports
The POLineItem model has both a required `name` and an optional `description`
field. The export was only rendering `name` (with description as a fallback),
dropping the optional description entirely when a name was present.

- PDF: renders description in smaller italic text below the item name
- XLSX: appends description on a new line within the cell (wrapText enabled)
- Row height in XLSX now accounts for the extra line when description exists

Fixes #8

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 03:43:49 +05:30
600f637de2 Merge pull request 'fix: Closed PO list filters are wrong for manager and submitter' (#21) from claude/issue-6 into master
Reviewed-on: #21
2026-06-18 22:10:25 +00:00
464475f62c Merge pull request 'fix: Approved POs should show approval date as the PO date (screen + export)' (#22) from claude/issue-5 into master
Reviewed-on: #22
2026-06-18 22:10:06 +00:00
Claude (auto-fix)
5d3b45a3a4 fix(po-detail): show approval date as PO date on detail screen
The PO date field was only displayed when a submitter explicitly set it.
For approved POs without an explicit date, the approval date is now shown.
Precedence: submitter-set poDate → approvedAt → createdAt (matches the
export route which already used this logic).

Fixes #5
2026-06-19 03:39:19 +05:30
Claude (auto-fix)
a37ca068c2 fix(my-orders): correct closed PO filters for manager and submitter
Managers and superusers were silently filtered to only their own
submitted POs because submitterId: userId was applied unconditionally.
Submitters were also shown MGR_APPROVED, SENT_FOR_PAYMENT,
PAID_DELIVERED and REJECTED orders alongside CLOSED ones.

Fix: managers/superusers see all CLOSED POs (no submitterId filter);
submitters see only their own CLOSED POs.

Fixes #6
2026-06-19 03:34:21 +05:30
b19ab695eb feat(automation): add manual run-watcher launcher for desktop shortcut
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 03:28:12 +05:30