feat: custom fields per project
Text, number, date or option-list fields defined in the project settings (archived, never removed; their type is fixed once saved). Tickets hold them, edited in the side panel, listed under the title and filtered by option in the list view; also over GraphQL, REST (?field=ID:value) and MCP. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -82,7 +82,8 @@ store como auditoría, pero desde la API no se puede deshacer.
|
||||
|
||||
### Configuración por proyecto
|
||||
|
||||
Cada proyecto tiene sus **estados**, **transiciones**, **tipos de ticket** y **áreas** (`Project.settings`). Todos
|
||||
Cada proyecto tiene sus **estados**, **transiciones**, **tipos de ticket**, **áreas** y **campos personalizados**
|
||||
(`Project.settings`). Todos
|
||||
empiezan con los valores por defecto de abajo, y el propietario o un admin los cambia con `updateProjectSettings` (o
|
||||
`PUT /api/projects/{project}/settings`); el cambio es un evento más (`ProjectSettingsChangedEvent`).
|
||||
|
||||
@@ -112,6 +113,29 @@ Reglas:
|
||||
Los proyectos creados antes de que existiera la configuración usan los valores por defecto (sus eventos no la
|
||||
llevan). Al arrancar, la API rellena la categoría de los tickets del read model que no la tienen.
|
||||
|
||||
### Campos personalizados
|
||||
|
||||
Datos propios del proyecto en cada ticket («Navegador», «Cliente», «Coste»…), en `settings.customFields`: `id`, `name`,
|
||||
`type` y `archived`, más `options` si es una lista. Un proyecto tiene como mucho 30, y empieza sin ninguno.
|
||||
|
||||
| Tipo | Valor |
|
||||
| -------- | ---------------------------------------------------------------- |
|
||||
| `TEXT` | Hasta 500 caracteres |
|
||||
| `NUMBER` | Un número (se guarda en forma canónica: `1,5` → `1.5`) |
|
||||
| `DATE` | `YYYY-MM-DD` |
|
||||
| `SELECT` | Una de sus `options` (de 1 a 50, de hasta 60 caracteres, únicas) |
|
||||
|
||||
- Como el resto de la configuración, un campo no se borra: se archiva. Su **tipo no cambia** una vez guardado, para que
|
||||
los valores que ya hay sigan teniendo sentido. Un campo archivado se puede vaciar en un ticket, pero no rellenar.
|
||||
- Al guardar la configuración, si `customFields` no viene se quedan como estaban (los clientes que no los conocen no
|
||||
los pierden).
|
||||
- En el ticket: GraphQL `Issue.customFields` es una lista `{ fieldId, value }` (solo los que tienen valor) y se cambia
|
||||
con `customFields: [{ fieldId, value }]` en `createIssue` / `updateIssue`; por REST es un objeto
|
||||
`{ "BROWSER": "Safari" }`. En ambos se mandan solo los campos que cambian, y `null` o `""` vacía uno.
|
||||
- Filtrar: `issues(filter: { customFields: [{ fieldId, value }] })` o `GET /api/projects/{project}/issues?field=BROWSER:Safari`
|
||||
(se puede repetir).
|
||||
- `IssueUpdatedEvent` lleva el mapa entero de valores ya validado, así la proyección solo lo copia.
|
||||
|
||||
### Workflow por defecto
|
||||
|
||||
```mermaid
|
||||
|
||||
Reference in New Issue
Block a user