customer-finance-summaryrun.md00_intake/stub.mdThe Value section's finance block (paid, valueDelivered, totalContract,
invoiceDue {amount, ref, dueDate}) is dummy. The underlying data mostly exists — quote/proposal
carry the contract total/price, and invoice carries amount/total/paid/paidAt per
milestone — but nothing aggregates them into the customer-facing figures, and valueDelivered
needs the milestone delivery status from feature 2.
totalContract — from the accepted quote / proposal pricepaid — sum of paid invoicesvalueDelivered — sum of the amounts of delivered milestones (milestone amount virtual ×
completion from milestone-delivery-tracking)invoiceDue — the next unpaid invoice: amount, ref (invoice id/number), dueDateEUR/USD/GBP), not hard-coded.customer-activity-feed.ref shown on the due card (invoice number vs. _id) and the currency formatting.valueDelivered depends on milestone completion — ensure the delivered-amount definition matches
the milestone feature's status.touches: packages/services/src/db/models/{invoice,quote,proposal,milestone}.ts (read),
packages/services/src/server (aggregation),
apps/web/components/dashboard/customer/value-section.tsx.01_define/output/spec.mdThe customer dashboard's Value section finance block — "Paid so far", "Work delivered" vs. total
contract, and the "Next payment due" card — renders entirely from apps/web/lib/mock/customer.ts
(ValueSection takes a hard-coded finance prop: paid, valueDelivered, totalContract,
invoiceDue {ref, dueDate, amount}). The underlying data already exists — the accepted quote
carries the contract total and currency, and each invoice carries amount/total/paid per
milestone — but nothing aggregates them into the customer-facing figures, and valueDelivered
needs the milestone completion signal now exposed by milestone-delivery-tracking. This is sequence
5 of 9 in the customer-dashboard-data epic and advances Build the Bridge / Q2 2026 Objective 3 —
Validate Technical Infrastructure & Payout Flow: the finance summary proves the
delivery → invoicing money flow renders as real, tenant-scoped figures to the customer.
Extend the shared customer project envelope (customerProjectService, established by
customer-project-foundation and already extended by milestone-delivery-tracking) with a typed
finance block, resolved once from the signed-in customer's active project lead → accepted
proposal → its quote + per-milestone invoices. No new top-level fetch entry; the other sections
keep reading the same envelope.
totalContract — the accepted quote's total (falling back to the totalAmount virtual,
amount + serviceFee) for the active project's accepted proposal.paid — the sum of total (falling back to amount) across the project's paid invoices
(invoice.paid === true), scoped to the accepted proposal's milestones.valueDelivered — percentage-weighted: totalContract × (progress ÷ 100), reusing the
done-milestone progress already computed on the envelope. The value-delivered bar therefore
tracks the progress bar exactly; no per-milestone money split is introduced.invoiceDue — the next unpaid invoice (paid === false) for the project, ordered by due
date ascending: { ref, dueDate, amount }, or null when nothing is outstanding.ref — the name of the milestone the invoice belongs to (e.g. "Discovery & setup").dueDate — read from a new invoice.dueDate field (see schema change), falling back to the
linked milestone's existing dueDate so the card shows a real date today before any write path
populates the invoice field. Returned as an ISO string or null.amount — the invoice total (falling back to amount).currency — the accepted quote's currency (EUR | USD | GBP, default EUR), carried on
the finance block so the UI formats money in the project's real currency rather than a
hard-coded symbol.Add an optional dueDate?: Date field to the invoice model. It is read by the aggregation
(authoritative when present, milestone-dueDate fallback otherwise). No write path, editor, or
backfill is built this run — populating it is left to seeding / a future invoicing write flow.
Add the invoice read helpers needed for the aggregation in a tenant-scoped invoice read
service (following the existing index.ts class + instance.ts singleton pattern, consumed by
customerProjectService — invoices are not re-queried elsewhere).
Teach fmt to honour a currency. apps/web/lib/dashboard-utils.ts fmt currently hard-codes
£; extend it to accept the envelope's currency (EUR/USD/GBP symbol) so the finance figures render
in the right currency. Other call sites keep working (default unchanged or threaded as needed).
Wire the ValueSection finance column to the envelope. The customer dashboard page passes the
envelope's finance block (and currency) to ValueSection; the "Paid so far" / "Work delivered"
stat boxes, the value-delivered-vs-total progress bar, and the "Next payment due" card all read
real aggregates. The invoiceDue card renders a clean "nothing due" state (no card / empty
state) when invoiceDue is null, and guards the progress bar against a zero totalContract.
Retire the finance half of the mock. Remove the finance block from
apps/web/lib/mock/customer.ts; the activity half of the Value section stays on the mock until
customer-activity-feed (sequence 6) lands.
Sequence 5 of the customer-dashboard-data epic. Extends the envelope from
customer-project-foundation (sequence 1) and consumes the milestone completion signal /
progress from milestone-delivery-tracking (sequence 2) — so Build is gated on both being merged
first. This is a build-ordering note, not an unresolved requirement.
paid, valueDelivered, and totalContract on the customer dashboard reflect real data — the
accepted quote total, the sum of paid invoices, and totalContract × done-milestone progress
respectively — not values from lib/mock/customer.ts.ref,
its due date, and amount — or a clean "nothing due" state when no invoice is outstanding.EUR/USD/GBP), not a
hard-coded £.invoice model carries an optional dueDate field; existing invoice documents (no
dueDate) remain valid and the card falls back to the linked milestone's due date.finance block is returned on the customerProjectService envelope (not a separate
fetch), and the finance data is removed from apps/web/lib/mock/customer.ts while the Value
section's activity half still renders unchanged.invoice.dueDate field.customer-activity-feed (sequence 6).valueDelivered (percentage-weighting is the chosen basis)./customer/invoices page — the "View invoices" link target is unchanged.02_build/output/notes.mdpackages/services/src/db/models/invoice.ts: added an optional dueDate?: Date field
(read-only this run; no write path) so the next-payment-due date can be authoritative when set.packages/services/src/db/services/invoice/index.ts: added findProjectInvoicesByQuote +
ProjectFinanceInvoiceRow — tenant-scoped read of an accepted quote's invoices, resolving each
to { paid, amount (total ?? amount), dueDate (invoice.dueDate ?? milestone.dueDate), milestoneName }.packages/services/src/db/services/customer-project/index.ts: extended the shared project
envelope with a typed finance block (CustomerProjectFinance). financeFrom aggregates the
accepted proposal's quote + invoices: totalContract (quote total / amount+serviceFee), paid
(sum of paid invoices), valueDelivered (percentage-weighted: totalContract × progress ÷ 100,
reusing the milestone progress), currency (from the quote), and invoiceDue (the next unpaid
invoice by due date, or null). Resolved from the same active-project chain — no extra fetch entry.packages/services/src/db/services/index.ts: exported CustomerProjectFinance and
ProjectFinanceInvoiceRow.apps/web/lib/dashboard-utils.ts: fmt now takes a currency (EUR/USD/GBP symbol), defaulting to
GBP so existing call sites are unchanged.apps/web/components/dashboard/customer/value-section.tsx: finance column reads currency-aware
figures, renders a clean "nothing due" state when no invoice is outstanding, formats the ISO due
date, and guards the progress bar against a zero totalContract.apps/web/app/(app)/customer/dashboard/page.tsx: passes project.finance (envelope) to
ValueSection instead of the mock.apps/web/lib/mock/customer.ts: removed the finance block (and the now-unused formatMonthDay
helper); the activity half of the Value section still renders from the mock.paid / valueDelivered / totalContract reflect real data — accepted quote total, sum of
paid invoices, and totalContract × done-milestone progress — via the envelope, not the mock.ref, due
date, amount), or a clean "nothing due" state when none is outstanding.tenantId and chained from the customer's active project.£.invoice carries an optional dueDate; existing docs (no dueDate) stay valid and the card
falls back to the linked milestone's due date.finance is returned on the customerProjectService envelope (no separate fetch); the mock's
finance block is removed while the Value section's activity half renders unchanged.valueDelivered is intentionally percentage-weighted (a Define decision), so the value-delivered
bar tracks the progress bar exactly rather than summing per-invoice money.findProjectInvoicesByQuote filters { tenantId, quote } (no dedicated index — consistent with
the existing archiveByQuoteIds query); invoice volume per quote is small.invoice.dueDate has no writer this run; populating it (seeding / future invoicing flow)
is out of scope, hence the milestone-dueDate fallback to keep the card meaningful today.03_release/output/changelog.mdPublished as apps/help/app/changelog/2026-06-22-customer-finance-summary/page.mdx.
See your real finances on your project (customer)
You can now see where your project stands financially, right on your dashboard. It shows how much you have paid so far, the value delivered against your total contract, and your next payment due — which milestone it covers, when it is due, and how much — all in your project's own currency. When nothing is outstanding, you will see a clear "nothing due" note instead of placeholder figures.
03_release/output/investor-update.mdWho it's for: Customers What shipped: The customer dashboard now shows real paid, value-delivered, total-contract and next-payment-due figures, in the project's currency. Why it matters: Proves the delivery-to-invoicing money flow renders as real, tenant-scoped figures — advancing Build the Bridge, Objective 3: Validate Technical Infrastructure & Payout Flow.
All figures are scoped to the signed-in customer's active project — no cross-tenant leakage.
Dig deeper: <merged-PR URL> · <changelog entry URL>