---
phase: screens
kind: level
level: form
---

# Level — Form screens (SmartForm)

Use this level when the user needs to **create**, **edit** or **inspect** a
single entity instance via a structured input form.

## When to use

Trigger words: "form", "edit", "create", "new", "fill", "update", "details",
"fiche", "saisir", "modifier".

Mandatory when:
- The section code ends in `-detail`, `-edit`, `-create`, `-form`, `-new`.
- The bound use cases include verbs like "create", "register", "update",
  "modify", "edit", "fill in".

## What to produce

One SmartForm per detail section. Pick the layout based on entity complexity.

### Flat fields (small entities, < 8 fields)

```markdown
### SCR-HR-DEPARTMENTS-CREATE-001 — Nouveau département (SmartForm)
- **Entité** : Department (ENT-005)
- **Permission** : `hr.departments.create`
- **Cas d'usage liés** : UC-HR-DEPARTMENTS-CREATE-001
- **Mode** : create
- **Champs** : code (text, requis, col 4), name (text, requis, col 8), manager (lookup → Employee), description (textarea)
```

### Tabbed layout (complex entities, multiple aspects)

```markdown
### SCR-HR-EMPLOYEES-DETAIL-001 — Fiche employé (SmartForm)
- **Entité** : Employee (ENT-003)
- **Permission** : `hr.employees.update`
- **Cas d'usage liés** : UC-HR-EMPLOYEES-DETAIL-001, UC-HR-EMPLOYEES-DETAIL-002
- **Mode** : edit
- **Onglet « Information »** : code (text, requis, readonly), firstName, lastName, email (requis), phone
- **Onglet « Emploi »** : department (lookup → Department, requis), manager (lookup → Employee), position (requis), hireDate (date, requis), status (enum: active/inactive/onLeave)
- **Onglet « Rémunération »** : salary (currency), bonusYTD (currency), totalCompensation (currency, readonly, calculé `salary + bonusYTD`)
```

The richer per-field config (col span, `type`, `options`, `computed`) is in
`references/smartcomponents.md`.

## Field choice algorithm

For each entity attribute, decide whether to include it:

| Attribute kind            | `create` | `edit` | `detail` |
|---------------------------|:--------:|:------:|:--------:|
| Required scalar           | yes      | yes    | yes      |
| Optional scalar           | yes      | yes    | yes      |
| FK (lookup)               | yes      | yes    | yes      |
| Status enum               | usually no | yes  | yes      |
| Computed                  | **NEVER**| yes (readonly) | yes (readonly) |
| `createdAt`/`updatedAt`   | no       | no (or readonly) | yes (readonly) |
| `id` / pk                 | no (auto)| readonly | readonly |
| Sensitive (password hash) | special  | special| no       |

## Tabs guidelines

- 2 to 5 tabs maximum.
- Each tab groups attributes by **business meaning** (Identity, Employment,
  Compensation, History) — not by technical type.
- Each tab needs ≥2 fields, otherwise inline them elsewhere.
- Computed-heavy read-only summary tabs come last.

## Mode rules

- `create` → fields for a new entity. Computed fields ABSENT. Related tabs
  FORBIDDEN (the entity does not exist yet).
- `edit` → fields for an existing entity. Computed fields PRESENT and `readonly`.
  Audit fields optional and `readonly` if shown. Related tabs allowed.
- `detail` → all fields read-only. Use for view-only pages (HR profile, audit
  views). Related tabs allowed and recommended when the entity has incoming
  1:N relationships.

## Related entity tabs

A real fiche is the **hub** for everything connected to it (a Client → its
Factures, Contacts, Litiges, Contrats). In `edit`/`detail` mode a SmartForm can
expose those linked collections as extra tabs **inside the same page**, each
embedding a list of the related entity scoped by a foreign key back to the
parent.

`screen.md` entry with related tabs:

```markdown
### SCR-CRM-CLIENTS-DETAIL-001 — Fiche client (SmartForm)
- **Entité** : Client (ENT-001)
- **Permission** : `crm.clients.read`
- **Cas d'usage liés** : UC-CRM-CLIENTS-DETAIL-001
- **Mode** : detail
- **Onglet « Informations »** : code (text, readonly), name (text), siret (text), category (lookup → ClientCategory)
- **Onglet « Adresse de facturation »** : billingStreet, billingZip, billingCity
- **Onglet lié « Factures »** : entité Invoice, FK clientId → SCR-CRM-INVOICES-LIST-001 (`crm.invoices.read`)
- **Onglet lié « Contacts »** : entité Contact, FK clientId → SCR-CRM-CONTACTS-LIST-001
- **Onglet lié « Litiges »** : entité Claim, FK clientId → SCR-CRM-CLAIMS-LIST-001
```

### Rules for related tabs

1. Allowed **only** in `edit`/`detail` mode (forbidden in `create` — no parent ID).
2. The related entity must exist in the module's `entité.md`.
3. The relationship FK must exist on the related entity, pointing back to the
   parent (Client → Invoices ⇒ `Invoice` has `clientId`).
4. The navigation target must be an existing `SmartListView` for that related
   entity (the generator hands it the parent ID as a pre-filter).
5. A per-tab permission is optional but recommended — it gates whether the tab
   appears for the current actor.

### Common related-tab patterns

| Parent entity | Typical related tabs                              |
|---------------|---------------------------------------------------|
| Client        | Factures, Contacts, Avoirs, Litiges, Contrats     |
| Project       | Tâches, Documents, Membres, Jalons, Tickets       |
| Order         | Lignes, Paiements, Livraisons, Avoirs             |
| Employee      | Contrats, Absences, Évaluations, Formations       |
| Account (CRM) | Opportunités, Activités, Contacts, Documents      |

An entity with ≥1 incoming 1:N FK should have at least one related tab. The
audit warns (SCR-009) when this is missed.

## Common mistakes

- **Computed fields in create mode** → fails type-mapping rule.
- **`readonly` as a string** → it is a strict boolean.
- **Lookup field without an entity** → fails post-check rule 5.
- **More than 5 inner tabs** → split into multiple sections instead (inner and
  related tabs share the tab bar — count both).
- **Related tabs in create mode** → forbidden, no parent ID yet.
- **A navigation target pointing to a non-existent screen** → fails SCR-008.
- **Required field with no business reason** → ask the user first.
