diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b1b5ddb..98bc2b2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -27,7 +27,9 @@ bun run build
For UI changes, check the affected flow in light and dark mode at desktop and narrow viewport widths. Include before/after screenshots when appearance changes.
-For database changes, add a forward-only migration and verify both SQLite and PostgreSQL behaviour. The normal test run exercises SQLite. Set `TEST_POSTGRES_DATABASE_URL` to a dedicated PostgreSQL test database to run the same migration contract in an isolated temporary schema. Never rewrite a migration that may already have been deployed.
+For database changes, add a forward-only migration and verify both SQLite and PostgreSQL behaviour. The normal test run exercises SQLite. Set `TEST_POSTGRES_DATABASE_URL` to a dedicated PostgreSQL test database to run the migration contract and invoice editing transactions in isolated temporary schemas. Never rewrite a migration that may already have been deployed.
+
+Use `bun run test --no-file-parallelism` when `TEST_POSTGRES_DATABASE_URL` is set. Kysely discovers migration tables across accessible schemas, so these temporary schema migrations must run one suite at a time.
## Pull requests
diff --git a/README.md b/README.md
index 337df01..9bc0f20 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@
## What Folio does
-- Creates drafts and searchable A4 PDF invoices in multiple currencies. Issued invoices retain the customer and seller details used at the time.
+- Creates drafts and searchable A4 PDF invoices in multiple currencies. Published invoices, including paid and void invoices, can be edited after acknowledging a warning. Previous versions and PDFs remain available in the edit history; edits update the existing invoice and reports. Recorded payments keep their original amounts and currencies.
- Records full and partial payments, blocks overpayments, and tracks outstanding and overdue balances.
- Stores customers, billing addresses and reusable invoice lines.
- Shows customer address maps after consent, with options to allow once or remember the choice in this browser.
diff --git a/src/app/(app)/invoices/[id]/edit/page.tsx b/src/app/(app)/invoices/[id]/edit/page.tsx
index d11ab0f..c212d55 100644
--- a/src/app/(app)/invoices/[id]/edit/page.tsx
+++ b/src/app/(app)/invoices/[id]/edit/page.tsx
@@ -24,7 +24,7 @@ function inputDecimal(value: number, scale: number, digits: number) {
export default async function EditInvoicePage({ params }: EditInvoicePageProps) {
const [business, { id }] = await Promise.all([requireBusiness(), params]);
const detail = await getInvoiceDetail(business.id, id);
- if (!detail || detail.invoice.lifecycle !== "draft") notFound();
+ if (!detail) notFound();
const options = await getInvoiceEditorOptions(
business.id,
{ includeCustomerId: detail.invoice.customer_id },
@@ -36,11 +36,13 @@ export default async function EditInvoicePage({ params }: EditInvoicePageProps)
This is a historical copy. Open the current invoice to make changes.
+ ++ Open the version saved before each edit. +
++ {revision.invoice_number} · {formatMoney(revision.total_cents, revision.currency)} +
++ Before edit by {revision.actor_name} · {historyDateFormatter.format(new Date(revision.created_at))} +
+ + ))} +