Commit graph

12 commits

Author SHA1 Message Date
bae4f9e795 Lock quote letterheads to customer companies
Add a per-company quote letterhead lock backed by `companies.confirmed_letterhead`. The quote process flow prompts for a default when unset, permits only the confirmed letterhead with a triple-click per-quote override, and enforces the rule during PDF generation and email sending. Also add company settings controls and API, email-ingest defaults, migration and backfill tooling, and unit coverage.
2026-10-31 10:20:23 +02:00
9b3f30a01f feat(process): show the blue price check on both Process pages
The blue price-check badge now appears under the purple AI marker on
every line the AI filled (reviewed or not):

  - quote Process page, mobile card: its own row under the header, so
    the one-line summary is readable on a phone
  - quote Process page, desktop table: inside the Description cell
    rather than the cramped 32-px "#" column, which has no room for a
    summary
  - order Process page: its own full-width row under the markers, so
    the summary is readable on a phone

The summary truncates rather than wrapping — the popover always shows
it in full.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 21:45:46 +02:00
40094be3f3 feat(quote-assistant): purple AI lines on process and quote pages
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 19:56:02 +02:00
58831683d4 feat(delivery-notes): edit the note before quoting, and fix the QuickBooks error
Four things, three of them the operator asked for and one a bug I
introduced earlier in this session.

## Delivery note editor, first in the flow

New "Delivery note" button ahead of "Create quote": quantity, cost,
markup and selling price per line, one Save for the whole note.

Sits FIRST deliberately. The quote is priced from the DN's lines, so
a wrong quantity or a stale cost here becomes a wrong number on a
customer-facing document. Fixing it at the source is the point.

It saves through the existing
`POST /api/v1/delivery-notes/items/[id]/pricing`, which takes cost +
selling + quantity in one call and re-renders the DN PDF. No new
endpoint. One Save rather than per-line saves, so a half-typed cost
can't be left behind on one line while the others commit.

Cost and markup are the inputs; selling is derived, same rule as the
quote review modal. A line that already carries a selling price KEEPS
it and shows the markup that price actually implies — verified live:
a line at R259.74 selling on R200 cost opened showing 29.87%, not
the 25% default, so a deliberate price is never overwritten.

## The QuickBooks invoice error

The modal refused to send when the order number was empty, and a
counter note has no parent order, so it opened blank and blocked. The
operator could not tell that an empty field was the only thing wrong.

The field now seeds with the DELIVERY NOTE number (which is what
`order_number` did before, so nothing changes for notes that have one).
QuickBooks needs a number and the DN number is real, unique and
customer-recognisable. Still editable. Verified live: a note with no
order now opens with DN26100814.

## The signed copy — stated, not toggled

The operator asked for an option to include the signed copy. There
isn't one to offer: when a signed copy exists it REPLACES the rendered
PDF at quote-creation time, so the attachment was already the signed
document. A checkbox there would be a control that cannot change
anything — the exact complaint that started this work.

Instead the send modal now says so plainly when the outgoing PDF is
the customer's signed copy.

## My bug

The editor's first fetch was a bare `fetch()` with no Authorization
header. `assertBrowserSession` 401s without it — the same mistake as
earlier this session, caught in the browser because the dialog hung on
"Loading items…". Fixed and re-verified.

Full suite 3817 passing, tsc clean, no lint warnings.
2026-10-09 09:27:09 +02:00
7555196166 feat(audit): implement audit trail for delivery notes and orders
- Added an audit trail feature that logs every create, update, and delete action on delivery notes and orders, capturing detailed changes per field.
- Introduced the `AuditTimeline` component to display the history of changes for delivery notes and orders, enhancing traceability.
- Updated the `page.tsx` files for delivery notes and orders to fetch and display audit logs, ensuring users can view the history of modifications.
- Enhanced the `package.json` with a new migration script for setting up the audit log functionality.

This feature improves accountability and transparency in the management of delivery notes and orders.
2026-09-30 01:48:44 +02:00
cd359b75a9 feat(quotes): split attachments into internal vs outbound with operator upload UI
Splits the quote attachment model along the seam that actually matters:
'operator-only references' (drawings the operator consulted, supplier
emails they filed away) vs 'customer-facing files' (supplier quotes,
drawings, NDAs that need to ride along on the email).

Schema (PB): new `internal_attachments` + `outbound_attachments` fields
on the `quotes` collection, maxSelect 10 each. Existing `attachments`
field kept for back-compat but is no longer the source of truth for
outbound emails.

API:
- GET/POST/DELETE /api/v1/quotes/[id]/internal-attachments[/{filename}]
- GET/POST/DELETE /api/v1/quotes/[id]/outbound-attachments[/{filename}]
- POST /api/v1/quotes/[id]/attachments/upload kept for legacy `attachments`

MCP: `quotes.send_email` description now correctly states
`selectedAttachmentIds` are PB file tokens from `outbound_attachments`
(supplier quotes, drawings, NDAs), not IDs of the legacy `attachments`
collection.

Frontend: `ProcessOutboundAttachmentsCard` component lets the operator
upload/remove customer-facing files during the process page; approval +
process pages now read the new fields.

Outbound Send workflow (deployed as `PIEAkPYYYZLGl6Pj`):
- Old workflow (id `WF_Email_Send_SMTP`) and stale repo file
  (`WFDASH — Quote Outbound Send.json`) replaced.
- New revision intersects `quotes.outbound_attachments` with
  `email_outbound_attachment_ids`, downloads each from
  `/api/files/quotes/<id>/<token>`, and emits
  `options.attachments: [{name, content, type}]` consumed by
  `Send Email (SMTP)` (PO Send pattern).
- Failure path flipped from `send_failed` to `mail_failed` so
  `retryQuoteEmail` can find failed sends (it only accepts the
  latter). Previously failed sends were stranded.

Email Monitor (v5) workflow fixes:
- Reply-branch auth: any authenticated request now passes through;
  pre-fix bug rejected branches that the IMAP trigger hadn't seen yet.
- Reply detection: bulletproof subject regex
  (/Q-?\d{4}-?\d{2}-?\d{3}/) accepts both legacy `Q-2026-09-246`
  and current `Q202609246` formats; canonicalised digits-only for
  PB `~` substring match.

Delivery notes: new pricing endpoint at
/api/v1/delivery-notes/items/[id]/pricing + service-layer methods.

Snapshots kept for forensics: PRE-FIX-REPLY-{BRANCH-AUTH,DETECTION}.json
and push-payload.baseline.json are the previously-deployed workflow
shapes, retained so we can diff/rollback if regression hits.

Verified:
- PUT workflow PIEAkPYYYZLGl6Pj -> 200, active, 25 nodes
- quotes-service.test.ts: 117 pass (4 pre-existing failures unchanged)
- mcp/route.test.ts: 118 pass (2 pre-existing failures unchanged)
- node --check on new Code node JS passes

Docs: docs/quotes-outbound-attachments-deploy.md covers the deploy
procedure; docs/api-mcp-layer.md description sync.
2026-09-09 13:43:56 +02:00
Dave
7986784eaf chore(tests): fix ~60 pre-existing TS errors across 11 test files
Typecheck cleanup after the deploy blocker push — `tsc --noEmit` was
silent on `d11a184` baseline but ~60 errors hid across test files
that only the compiler surfaces (the build was clean because test
files are excluded from the prod bundle). Touched:

- src/app/api/mcp/route.test.ts (16) — generic on `vi.fn()` for
  tuple inference; `as unknown as` two-step cast on record literals
- src/app/api/mcp/po-agent-workflow.test.ts (1) — spread instead of
  duplicate key
- src/components/buyer-tasks/buyer-task-group-card.test.tsx (5) —
  `PurchaseOrderEmailPayload` now requires `subject`/`body`
- src/components/collection-confirm/confirm-stock-dialog.test.tsx (1)
  — removed shadowed `id` from `makeRow` overrides
- src/components/item-intel-popover.test.tsx (2) — missing row
  fields + boolean→object for `verified`
- src/components/quotes/process-item-card.test.tsx (2) — full
  QuoteItem stub; generic on `vi.fn()`
- src/components/sprint-item-pricing-modal.test.tsx (3) — full
  SprintItem/QuoteItemAttachment/QuoteAttachment stubs
- src/lib/server/orders-service-review.test.ts (~60) — annotated
  `getOne` return type to break union widening on the expand option
- src/lib/server/orders-service.test.ts (2) — removed bogus
  `includeArchivedCounts` param; `rfq_number` now whitelisted on Order
- src/lib/server/quotes-service.test.ts (3) — two-step cast on
  Quote header; `unit?` now on QuoteItem
- src/lib/server/stock-purchase-items-service.test.ts (~10) — typed
  `pbMock` via `StockPurchaseItemsPb` so `*WithPb(pb,...)` checks

Also added two optional fields to `src/lib/types.ts`:
- `QuoteItem.unit?` — pre-2026 rows have no unit, so it's optional
- `Order.rfq_number?` — populated by the n8n email agent, inherited
  verbatim by `splitOrder`

Build: clean. Typecheck: 0 errors. Tests: 14 pre-existing runtime
failures + 1 pre-existing unhandled error in `supplier-form.test.tsx`
remain — confirmed they exist on `d11a184` baseline, so they are
outside the cleanup scope and unchanged by this commit.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-27 04:43:40 +02:00
Dave
50944a3526 feat(quotes): add operator-facing notes functionality to quotes
- Introduced a new `quote_notes` field to store operator-facing free-text notes, distinct from the existing `notes` field used for rejection audit logs.
- Updated the quote processing page to include a `QuoteNoteCard` for editing and displaying the operator notes, ensuring seamless integration into the PDF generation flow.
- Enhanced the PDF generation logic to render the operator notes in a blue callout below the totals, improving visibility for end-users.
- Implemented a new service function `setQuoteNotes` to handle the saving of notes, including trimming whitespace and handling non-string inputs defensively.
- Added unit tests for the `setQuoteNotes` function to ensure correct behavior and data integrity when updating notes.
- Updated relevant types and documentation to reflect the new functionality and its intended use cases.
2026-08-17 15:22:16 +02:00
Dave
43de97501c feat(tv): add TV dashboard + refine workflows
Add full PIN-protected TV dashboard stack including auth, redirects, map view, APIs, and server services.

Also include these critical refinements:
- Overhaul PO number generation to use per-company monthly sequences
- Rework quote rejection to bounce quotes back to operator queues instead of marking them terminal
- Add post_processing order tab group for clearer workflow visibility
- Fix collection pool tooltip positioning, overflow clipping, and default collapsed state
- Refactor customer list API, clean up unused form props, and fix item intel popover target ID handling
- Add comprehensive tests for new components and update existing test expectations
2026-08-13 20:55:22 +02:00
Dave
e9ffa6df4e feat(quotes/process): add mobile card layout for quote items
Replace the cramped horizontal table layout on mobile viewports with responsive vertical cards for each quote item. The new `ProcessItemCard` component provides full-width form fields, properly sized 44px tap targets for all actions, and maintains full parity with the desktop table implementation by reusing the same state handlers and callbacks. Add comprehensive test coverage for the new component, and fix existing mobile overflow issues in the quote process page header and table layout.
2026-08-10 10:47:36 +02:00
SJCoetz
63048e51c9 feat: add AI quote processing pipeline and admin settings UI
Add complete end-to-end AI-powered quote extraction workflow:
- Disable PB SDK auto-cancellation to support parallel batch requests
- Add required runtime dependencies for AI processing and document parsing
- Implement AI settings backend, operator UI, and REST API routes
- Add quote ingestion, insight population, and manual re-run AI endpoints
- Build service layers for quote items, AI settings, and structured item insights
- Fix attachment upload handling and quote item history blob parsing
- Add supporting migrations, smoke tests, and shared utility functions
- Export previously private helper functions for cross-service reuse
2026-08-09 16:17:29 +02:00
SJCoetz
4444842ef5 feat: collection-map updates, todays-stops, quote/approval rework, buyer-tasks, e2e
- Add todays-stops test and component to collection-map
- Expand collection-map sidebar and pin-model
- Rework orders/[id]/process page
- Add quotes/[id]/page and process pages
- Add quotes/approvals/[id] page
- Add buyer-tasks generate-po route
- Add quotes partial-approve and pdf routes
- Add purchase-orders [id]/pdf route
- Add e2e create-flows spec
- Add superpowers plan doc
- Update pb_schema-2.json
- Update form components (company, customer, order, quote, supplier)
- Update UI components (button, command, dialog)
- Add collection-pool test components

Co-authored-by: Orca <help@stably.ai>
2026-08-03 09:33:45 +02:00