# Contract → Invoice Auto-generation UI

**Date:** 2026-05-31  
**Status:** Draft  
**Spec:** 76  
**Tier:** All tiers  
**Depends on:** `contracts-esignature`, `invoices-core`, `customers-module`, `foundation-auth-rbac`  
**Referenced by:** `contracts-esignature`, `invoices-core`

---

## Overview

UI for generating an invoice from a fully-signed contract. Spec 48 (`contracts-esignature`) defines: the contract data model, the `invoices.contract_id` foreign key, the completion flow, and a URL stub `/invoices/new?contract_id={id}`. This spec owns the invoice pre-fill UI and the UX flow that bridges contract completion to invoice creation.

---

## Trigger Points

Invoice generation from a contract is triggered from two places:

### 1. Contract completion flow (spec 48 completion flow)

When all signatories have signed, the completion flow shows:

```
┌──────────────────────────────────────────────────────────────┐
│  ✓ Contract signed by all parties                           │
│                                                              │
│  [Download PDF]  [Generate Invoice →]  [Done]               │
└──────────────────────────────────────────────────────────────┘
```

"Generate Invoice" → navigates to `/invoices/new?contract_id={id}`.

### 2. Contract detail view (`/contracts/:id`)

When `contracts.status = 'SIGNED'` (all signed) and no invoice is linked yet (`invoices.contract_id = {contractId}` returns empty):

```
┌──────────────────────────────────────────────────────────────┐
│  Contract: Website Development Agreement                     │
│  Status: ✓ Signed — All parties signed                   │
│                                                              │
│  [Download PDF]     [Generate Invoice]                       │
└──────────────────────────────────────────────────────────────┘
```

If an invoice is already linked: "View Invoice →" replaces the button.

---

## Invoice New Page (`/invoices/new?contract_id={id}`)

The existing "New Invoice" form (spec 15) is extended to accept `contract_id` as a query param. When present:

1. Fetch contract data: `GET /api/contracts/:id` (includes `customer_id`, `title`, `pricing` from content JSONB)
2. Pre-fill the invoice form:

```
┌──────────────────────────────────────────────────────────────┐
│  New Invoice  ·  From contract: Website Development Agmt     │
│                                                              │
│  Customer:   [Acme Corp ▾]              ← pre-filled        │
│  Project:    [Acme Corp — Website ▾]    ← pre-filled if linked│
│                                                              │
│  Line items (pre-filled from contract pricing):             │
│  ┌──────────────────────────────────────────────────────┐   │
│  │  Description             Qty   Price      Subtotal   │   │
│  │  Website Design          1     ₪12,000    ₪12,000    │   │
│  │  Development             1     ₪18,000    ₪18,000    │   │
│  └──────────────────────────────────────────────────────┘   │
│  [Add line item]                                             │
│                                                              │
│  Linked contract: Website Development Agreement [×]         │
│  (Invoice will be linked; view via /contracts/:id)           │
│                                                              │
│  [Save Draft]                      [Send Invoice]            │
└──────────────────────────────────────────────────────────────┘
```

Pre-fill logic:
- `customer_id` from `contracts.customer_id`
- Line items: extracted from contract `content` JSONB if a `pricing` section exists; otherwise empty
- `invoices.contract_id = {contractId}` set on save (links the two records)

The `contract_id` link field is shown read-only with an `[×]` to detach if the user doesn't want to link them.

---

## Contract Detail: Invoice Tab

`/contracts/:id` gains an "Invoice" tab showing:

```
┌──────────────────────────────────────────────────────────────┐
│  Contract: Website Development Agreement                     │
│  [Details]  [Signatories]  [Invoice]                        │
│                                                              │
│  ── Invoice ────────────────────────────────────────────── │
│                                                              │
│  Invoice #INV-0042  ·  ₪35,700  ·  TAX_ISSUED              │
│  [View Invoice →]                                            │
│                                                              │
└──────────────────────────────────────────────────────────────┘
```

If no invoice linked and contract is SIGNED: shows the "Generate Invoice" CTA above.

---

## API

```
GET  /api/contracts/:id/invoice
  → get linked invoice (if exists)
     returns: { invoice: { id, invoiceNumber, total, status } } | null

POST /api/invoices (existing endpoint)
  → extended: accepts contract_id in body
     Validation: contract must be SIGNED, belong to same tenant
     Sets invoices.contract_id = contract_id
```

---

## Architecture Decisions

| Decision | Choice | Reason |
|----------|--------|--------|
| Pre-fill from contract | Not blank invoice | Contract already captures the agreed scope + price; re-entry is waste and a source of errors |
| Line items extracted from `pricing` JSONB section | Not a dedicated pricing table | Contract pricing is already in the content JSONB (spec 48 template sections); extraction is one JSONB lookup |
| Detachable link | User can remove `[×]` | Some contracts are informational; not all result in a billable invoice; forced link would confuse |
| Invoice tab on contract detail | Not just a link | Provides bilateral navigation (invoice → contract, contract → invoice); reduces context switching |
