diff --git a/AGENTS.md b/AGENTS.md index ac62b18..d307ece 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,7 +16,7 @@ MRP Codex is a modular Manufacturing Resource Planning platform intended to be a - filesystem-backed attachments - CRM customers/vendors, hierarchy, contacts, lifecycle metadata, and attachments - inventory items, BOMs, warehouses, locations, transactions, item attachments, and item pricing -- sales quotes, sales orders, and purchase orders +- sales quotes, sales orders, approvals, revision history, and purchase orders - purchase-order supporting documents and vendor-side purchasing visibility - shipping shipments, packing-slip PDFs, shipping labels, bills of lading, and logistics attachments - projects with customer/commercial/shipment linkage, owners, due dates, notes, and attachments @@ -117,11 +117,10 @@ If implementation changes invalidate those docs, update them in the same change Near-term priorities are: -1. Sales approvals and document revision history -2. Planning and gantt scheduling with live project/manufacturing data -3. Inventory transfers, reservations, and deeper stock controls -4. Broader audit-trail coverage and operational diagnostics -5. Code-splitting and bundle-size reduction +1. Planning and gantt scheduling with live project/manufacturing data +2. Inventory transfers, reservations, and deeper stock controls +3. Broader audit-trail coverage and operational diagnostics +4. Code-splitting and bundle-size reduction When adding new modules, preserve the ability to extend the system without refactoring the existing app shell. diff --git a/CHANGELOG.md b/CHANGELOG.md index 63a314a..c11bf2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ This file is the running release and change log for MRP Codex. Keep it updated w ### Added +- Sales approval actions with approved-by/approved-at stamps on quotes and sales orders +- Automatic sales-document revision history with authored reasons and per-revision snapshots - Projects domain foundation with customer, owner, due date, priority, notes, and attachment support - Project linkage to sales quotes, sales orders, and shipments for cross-module delivery tracking - Project list/detail/create/edit workflows and app-shell navigation entry @@ -20,11 +22,12 @@ This file is the running release and change log for MRP Codex. Keep it updated w - The dashboard now treats Projects as a live first-class module alongside CRM, inventory, sales, and shipping - The dashboard now treats Manufacturing as a live first-class module alongside CRM, inventory, sales, shipping, and projects +- Sales quote and sales-order detail pages now surface approval state and revision history directly in the operational workflow - Project editing now uses searchable pickers for customer, owner, quote, sales-order, and shipment linkage instead of static operational dropdowns - Project detail now surfaces linked work orders and can launch pre-seeded manufacturing records - Purchase-order detail now links back to the vendor CRM record and supports direct supporting-document management on the PO itself - Vendor CRM detail now exposes purchasing activity and can launch pre-seeded purchase orders -- Roadmap and project docs now treat sales approvals and document revision history as the next active priority after the vendor-document and stabilization pass +- Roadmap and project docs now treat planning and gantt scheduling as the next active priority after the sales approval and revision-history slice ## 2026-03-15 diff --git a/INSTRUCTIONS.md b/INSTRUCTIONS.md index c8195ce..d6b6d06 100644 --- a/INSTRUCTIONS.md +++ b/INSTRUCTIONS.md @@ -16,6 +16,7 @@ This repository implements the platform foundation milestone: - CRM foundation through reseller hierarchy, contacts, attachments, and lifecycle metadata - inventory master data, BOM, warehouse, stock-location, transactions, and item attachments - sales quotes and sales orders with quick actions and quote conversion +- sales approvals, approval stamps, and automatic revision history on quotes and sales orders - purchase orders with quick actions and searchable vendor/SKU entry - purchase orders restricted to inventory items flagged as purchasable - purchase receiving foundation with inventory posting and receipt history @@ -59,7 +60,6 @@ This repository implements the platform foundation milestone: ## Next roadmap candidates -- sales approvals and document revision history - planning and gantt scheduling with live project/manufacturing data - inventory transfers, reservations, and deeper stock controls - broader audit and operations maturity diff --git a/README.md b/README.md index 1a99d0d..3298222 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Current foundation scope includes: - CRM contact history, account contacts, and shared attachments - inventory item master, BOM, warehouse, stock-location, and stock-transaction flows - sales quotes and sales orders with searchable customer and SKU entry +- sales approvals, approval stamps, and automatic revision history on quotes and sales orders - purchase orders with searchable vendor and SKU entry, restricted to purchasable inventory items - purchase receiving with warehouse/location posting and receipt history against purchase orders - branded quote, sales-order, and purchase-order PDFs through the shared backend document pipeline @@ -44,17 +45,15 @@ Planned cross-module execution areas: Near-term priorities: -1. Sales approvals and revision history -2. Planning and gantt scheduling with live project/manufacturing data -3. Inventory transfers, reservations, and deeper stock controls -4. Broader audit-trail coverage and operational diagnostics -5. Code-splitting and bundle-size reduction +1. Planning and gantt scheduling with live project/manufacturing data +2. Inventory transfers, reservations, and deeper stock controls +3. Broader audit-trail coverage and operational diagnostics +4. Code-splitting and bundle-size reduction Revisit / deferred items: - local Windows Prisma migration reliability - frontend code-splitting and bundle-size reduction -- sales approvals and revision history - inventory transfers, reservations, and deeper stock controls - deeper audit-trail coverage @@ -222,11 +221,13 @@ The current sales foundation supports: - quote conversion into a sales order - line-level unit prices populated from the selected inventory item default price - branded quote and sales-order PDFs through the shared document pipeline +- approval stamps and revision history directly on quote and sales-order detail pages +- revision-reason capture when editing customer-facing sales documents QOL direction: - line duplication and faster keyboard-heavy line editing -- stronger revision/approval flow +- revision comparison view and restore-style workflows - richer PDF output for quotes and sales orders This module introduces `sales.read` and `sales.write` permissions. After updating the code, restart the server against the migrated database so bootstrap can upsert the new permissions onto the default administrator role. @@ -311,6 +312,7 @@ As of March 14, 2026, the latest committed domain migrations include: - purchase-order foundation - purchase receiving foundation - branded sales and purchasing PDF templates +- sales approvals and document revision history - inventory default price support - sales totals and commercial fields - shipping foundation diff --git a/ROADMAP.md b/ROADMAP.md index 4b464f7..849683e 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -58,7 +58,7 @@ MRP Codex is being built as a streamlined, modular manufacturing resource planni - Prisma migration execution is committed and documented, but local Windows Node 24 schema-engine behavior remains inconsistent; use Node 22 or Docker for migration execution - The frontend bundle is functional but should be code-split later, especially around the gantt module - CRM reporting is now functional, but broader account-role depth and downstream document rollups can still evolve later -- The current sales/purchasing/shipping foundation still does not include approvals, revisions, vendor exception handling, or deeper carrier integration +- The current sales/purchasing/shipping foundation now includes sales approvals and revision history, but still needs vendor exception handling, deeper carrier integration, and richer document comparison tooling - The dashboard is now live-data driven, but still needs richer KPI widgets, alerts, recent-activity queues, and exception reporting as more transactional depth is added - The new projects domain is foundational but still needs milestones, project rollups, and deeper inventory/purchasing/manufacturing tie-ins - The new manufacturing domain is foundational but still needs routings, labor capture, work-center views, and capacity-aware planning tie-ins @@ -119,6 +119,7 @@ QOL subfeatures: Foundation slice shipped: +- Sales approval stamps and automatic revision history on quotes and sales orders - Purchase-order supporting documents through the shared attachment pipeline - Vendor-detail purchasing visibility for recent purchase-order activity @@ -257,7 +258,6 @@ QOL subfeatures: - Local Windows Prisma migration reliability still needs a cleaner documented workflow or tooling wrapper - Frontend bundle splitting is still deferred; the Vite chunk-size warning remains -- Sales approvals and document revision history were planned but not yet built - Inventory transactions exist, but transfers, reservations, and more advanced stock controls still need follow-up - CRM document rollups and broader account-role depth were deferred until more downstream modules exist - Audit-trail depth is still thin outside the current record/update flows @@ -275,8 +275,7 @@ QOL subfeatures: ## Near-term priority order -1. Sales approvals and document revision history -2. Planning and scheduling with live project/manufacturing data -3. Inventory transfers, reservations, and deeper stock controls -4. Broader audit-trail coverage and operational diagnostics -5. Code-splitting and bundle-size reduction +1. Planning and scheduling with live project/manufacturing data +2. Inventory transfers, reservations, and deeper stock controls +3. Broader audit-trail coverage and operational diagnostics +4. Code-splitting and bundle-size reduction diff --git a/client/src/lib/api.ts b/client/src/lib/api.ts index 0a7fb54..2b279ea 100644 --- a/client/src/lib/api.ts +++ b/client/src/lib/api.ts @@ -58,6 +58,7 @@ import type { SalesCustomerOptionDto, SalesDocumentDetailDto, SalesDocumentInput, + SalesDocumentRevisionDto, SalesDocumentStatus, SalesDocumentSummaryDto, } from "@mrp/shared/dist/sales/types.js"; @@ -537,6 +538,12 @@ export const api = { token ); }, + approveQuote(token: string, quoteId: string) { + return request(`/api/v1/sales/quotes/${quoteId}/approve`, { method: "POST" }, token); + }, + getQuoteRevisions(token: string, quoteId: string) { + return request(`/api/v1/sales/quotes/${quoteId}/revisions`, undefined, token); + }, convertQuoteToSalesOrder(token: string, quoteId: string) { return request(`/api/v1/sales/quotes/${quoteId}/convert`, { method: "POST" }, token); }, @@ -566,6 +573,12 @@ export const api = { token ); }, + approveSalesOrder(token: string, orderId: string) { + return request(`/api/v1/sales/orders/${orderId}/approve`, { method: "POST" }, token); + }, + getSalesOrderRevisions(token: string, orderId: string) { + return request(`/api/v1/sales/orders/${orderId}/revisions`, undefined, token); + }, getPurchaseOrders(token: string, filters?: { q?: string; status?: PurchaseOrderStatus; vendorId?: string }) { return request( `/api/v1/purchasing/orders${buildQueryString({ diff --git a/client/src/modules/sales/SalesDetailPage.tsx b/client/src/modules/sales/SalesDetailPage.tsx index 584512e..ac81815 100644 --- a/client/src/modules/sales/SalesDetailPage.tsx +++ b/client/src/modules/sales/SalesDetailPage.tsx @@ -21,6 +21,7 @@ export function SalesDetailPage({ entity }: { entity: SalesDocumentEntity }) { const [isUpdatingStatus, setIsUpdatingStatus] = useState(false); const [isConverting, setIsConverting] = useState(false); const [isOpeningPdf, setIsOpeningPdf] = useState(false); + const [isApproving, setIsApproving] = useState(false); const [shipments, setShipments] = useState([]); const canManage = user?.permissions.includes(permissions.salesWrite) ?? false; @@ -119,6 +120,27 @@ export function SalesDetailPage({ entity }: { entity: SalesDocumentEntity }) { } } + async function handleApprove() { + if (!token) { + return; + } + + setIsApproving(true); + setStatus(`Approving ${config.singularLabel.toLowerCase()}...`); + + try { + const nextDocument = + entity === "quote" ? await api.approveQuote(token, activeDocument.id) : await api.approveSalesOrder(token, activeDocument.id); + setDocument(nextDocument); + setStatus(`${config.singularLabel} approved.`); + } catch (error: unknown) { + const message = error instanceof ApiError ? error.message : `Unable to approve ${config.singularLabel.toLowerCase()}.`; + setStatus(message); + } finally { + setIsApproving(false); + } + } + return (
@@ -129,6 +151,9 @@ export function SalesDetailPage({ entity }: { entity: SalesDocumentEntity }) {

{activeDocument.customerName}

+ + Rev {activeDocument.currentRevisionNumber} +
@@ -148,6 +173,16 @@ export function SalesDetailPage({ entity }: { entity: SalesDocumentEntity }) { Edit {config.singularLabel.toLowerCase()} + {activeDocument.status !== "APPROVED" ? ( + + ) : null} {entity === "quote" ? (
-

Subtotal

-
${activeDocument.subtotal.toFixed(2)}
+

Approval

+
{activeDocument.approvedAt ? new Date(activeDocument.approvedAt).toLocaleDateString() : "Pending"}
+
{activeDocument.approvedByName ?? "No approver recorded"}
@@ -229,6 +265,36 @@ export function SalesDetailPage({ entity }: { entity: SalesDocumentEntity }) {
${activeDocument.total.toFixed(2)}
+
+
+
+

Revision History

+

Automatic snapshots are recorded when the document changes status, content, or approval state.

+
+
+ {activeDocument.revisions.length === 0 ? ( +
+ No revisions have been recorded yet. +
+ ) : ( +
+ {activeDocument.revisions.map((revision) => ( +
+
+
+
Rev {revision.revisionNumber}
+
{revision.reason}
+
+
+
{new Date(revision.createdAt).toLocaleString()}
+
{revision.createdByName ?? "System"}
+
+
+
+ ))} +
+ )} +

Customer

diff --git a/client/src/modules/sales/SalesFormPage.tsx b/client/src/modules/sales/SalesFormPage.tsx index f5b9336..86958d6 100644 --- a/client/src/modules/sales/SalesFormPage.tsx +++ b/client/src/modules/sales/SalesFormPage.tsx @@ -56,6 +56,7 @@ export function SalesFormPage({ entity, mode }: { entity: SalesDocumentEntity; m taxPercent: document.taxPercent, freightAmount: document.freightAmount, notes: document.notes, + revisionReason: "", lines: document.lines.map((line) => ({ itemId: line.itemId, description: line.description, @@ -290,6 +291,17 @@ export function SalesFormPage({ entity, mode }: { entity: SalesDocumentEntity; m className="w-full rounded-3xl border border-line/70 bg-page px-2 py-2 text-text outline-none transition focus:border-brand" /> + {mode === "edit" ? ( + + ) : null}