[Issue]: Add Duplicate PO button #142
Labels
No labels
bug
claude-failed
claude-pr
claude-queue
claude-working
epic
feature
interactive
portal
triaged
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: shad0w/pelagia-portal#142
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Raised by
Eeshan Singh (eeshan.singh@pelagiamarine.com, TECHNICAL) — via portal Report Issue button
Description
For anyone who is allowed to create POs, browsing a PO should show a duplicate PO button that creates a new PO form pre-filled with the values from the duplicated PO.
Priority
P2 — Medium
Context
/po/cmqwbs64y01e7uxaw4kfyd5uyClaude triage
Triage — Issue #142: Add Duplicate PO button
Type: Feature (new capability — a "Duplicate" affordance on PO detail)
Routing: claude-queue
Priority: P2 — Medium
Interpretation
Anyone with
create_pobrowsing a PO should see a Duplicate button. Clicking it opensthe New Purchase Order form pre-filled with the source PO's values, so the user can tweak
and submit a fresh PO. Crucially this is a prefilled form, not a created record — nothing is
written until the user saves/submits, exactly like the existing cart → new PO prefill flow.
The codebase already establishes this pattern:
new/page.tsxreads a?cart=query param andmaps it onto
initialLineItems/initialVendorIdprops thatNewPoFormconsumes. Duplicateis the same shape with a richer field set sourced from an existing PO by id.
Action items
components/po/po-detail.tsxheader action area(
po-detail.tsx:198, alongside Edit / Export / Email). Render it gated byhasPermission(currentRole, "create_po")(the component already receivescurrentRole;import
hasPermissionfromlib/permissions). A plain<Link href={/po/new?duplicate=${po.id}}>.Decide visibility: simplest is "any status the user can view"; acceptable since it only
prefills a new form.
duplicateparam —app/(portal)/po/new/page.tsx. Whenduplicate=<id>ispresent, fetch that PO (
db.purchaseOrder.findUniquewithlineItems,account,vendor,vessel,company) and map it onto the form's initial props. Reuse the existingcreate_pogate already at the top of the page.NewPoForminitial props —app/(portal)/po/new/new-po-form.tsx. It alreadyaccepts
initialLineItems,initialVendorId,initialVesselId,initialCompanyId. Mostother fields are uncontrolled (
title,projectCode,dateRequired,placeOfDelivery,PI/quotation + requisition fields) and the accounting code / terms are controlled state.
Add
defaultValue/initial props for the fields to copy:initialTitle,initialAccountId(→
defaultAccountId),initialProjectCode,initialPlaceOfDelivery,initialTerms(→
termsstate), and optionally the quotation/requisition fields. Mechanical, followingthe existing prop pattern.
lineItemsForEditorshape already inpo-detail.tsx:111(Decimal →
Number()), copyingname/description/quantity/unit/size/unitPrice/gstRate(and
accountIdfor multi-account POs). DropproductId? — keep it; it links back cleanly.tests/unittest asserting thesource PO maps to the expected initial props / rendered defaults).
Files / areas involved
components/po/po-detail.tsx— new Duplicate Link +create_pogateapp/(portal)/po/new/page.tsx— read?duplicate=, fetch + map source POapp/(portal)/po/new/new-po-form.tsx— additional initial-value props/defaultslib/permissions.ts— read-only (hasPermission,create_po); no permission changestests/unit/— new test for the mappingWhat is explicitly NOT touched
No DB migration (no new model/field — prefill only, source PO unchanged), no auth/permissions
changes (only reads the existing
create_pogrant), no payments/money logic, no externalsystems. Attachments, status/dates, payment refs, and audit history are not copied — a
duplicate starts as a clean draft.
Open questions (sensible defaults assumed, not blockers)
accounting code, project code, delivery, line items, vendor, T&C). Exclude attachments,
PO/payment dates, status, payment data. (Quotation/requisition refs: lean toward copying.)
blank/unverified vendor.
to non-DRAFT if reviewers prefer, but no functional reason to restrict.
These are low-risk presentation choices with clear defaults; none requires business content,
design sign-off, or schema/permission decisions.
Routing rationale: Localized 3-file UI prefill change that directly extends the existing cart→new-PO pattern, gated by an existing permission with no DB migration, auth/payments, or external dependency, and fully verifiable by type-check / lint / a unit test — fits an unattended claude-queue run.
Routing:
claude-queue| Type:feature[Claude] Started working on this issue on branch
claude/issue-142.[Claude] Opened PR #145 with a proposed fix. Review and merge it, then create a release tag to deploy.