> ## Documentation Index
> Fetch the complete documentation index at: https://firespark.cloud/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Planos

> Pisos, estructura y mesas de cada local, definidos como JSON validado.

Un **plano** es una vista aproximada desde arriba de un local: sus pisos, la estructura fija de cada piso (paredes, mostrador, kioscos, puertas, columnas, zonas) y las mesas con sus asientos. Los planos son opcionales y se activan por local.

En el dashboard **dibujas** el plano. La ocupación de las mesas no se marca aquí: llega desde el POS cuando un grupo se sienta y se abren sus órdenes.

## Activa un plano

1. En el dashboard, entra a **Stores → Floor plans**.
2. Abre un local y activa **Mostrar plano**.
3. Agrega un piso. Arrastra mesas y estructura desde la paleta al lienzo, dibuja paredes esquina por esquina y zonas como rectángulos, o pega el JSON del piso con **Editar JSON**. Selecciona elementos para moverlos, cambiar su tamaño o rotarlos. Todas las acciones funcionan también con el teclado, y **Deshacer** (Ctrl/Cmd+Z) revierte cualquier cambio.
4. Selecciona **Guardar**. Cada piso con cambios se valida y se guarda.

Un local puede tener varios pisos. Cada piso tiene un `level` (0 para la planta baja y negativo para subsuelos) que se usa para ordenarlos.

## Unidades y coordenadas

Las medidas usan **unidades abstractas**: 1 unidad equivale más o menos a 1 metro. El origen es la esquina superior izquierda del piso. `x` crece hacia la derecha y `y` hacia abajo. Cada elemento es una caja ubicada en `x`, `y` con un `width` y un `depth`, más una `rotation` opcional en grados alrededor de su centro.

## JSON del piso

El editor, el API y las vistas del plano usan el mismo esquema. Vive en `@repo/validations` (`vStoreFloors.floor()`), así que todas las superficies lo validan igual.

| Campo | Tipo | Reglas |
| - | - | - |
| `id` | string | Único por local. No cambia después del primer guardado |
| `name` | string | Obligatorio |
| `level` | entero | De -10 a 100 |
| `plan.version` | `1` | Versión del esquema |
| `plan.width` | número | De 1 a 500 unidades |
| `plan.depth` | número | De 1 a 500 unidades |
| `plan.elements` | arreglo | Estructura, hasta 500 elementos |
| `plan.tables` | arreglo | Mesas, hasta 300 por piso |

Cada elemento y cada mesa tiene `id`, `x`, `y`, `width`, `depth` y `rotation` (desde 0 y menor que 360). Todo debe caber dentro del piso y los ids no se pueden repetir en el piso.

* Los **elementos** suman `kind` (`wall`, `counter`, `kiosk`, `door`, `column` o `zone`) y un `label` opcional.
* Las **paredes con trazo** son paredes que además tienen `points` (de 2 a 200 esquinas sobre el eje de la pared, en unidades del piso) y un `thickness` opcional (de 0.1 a 2, por defecto 0.2). Su caja es el rectángulo que envuelve la pared dibujada y su `rotation` es 0. Solo las paredes pueden tener `points` o `thickness`, y cada esquina debe quedar dentro del piso. Las paredes sin `points` siguen siendo cajas simples, así que los pisos anteriores siguen siendo válidos.
* Las **mesas** suman `label` (único por piso, sin distinguir mayúsculas), `seats` (de 1 a 50) y `shape` (`square`, `round` o `rect`). Los ids de mesa son únicos en todos los pisos del local, porque el POS y las reservas se refieren a ellos.

### Ejemplo

```json theme={null}
{
  "id": "planta-baja",
  "name": "Planta baja",
  "level": 0,
  "plan": {
    "version": 1,
    "width": 20,
    "depth": 14,
    "elements": [
      { "id": "wall-n", "kind": "wall", "x": 0, "y": 0, "width": 20, "depth": 0.2, "rotation": 0 },
      { "id": "wall-e", "kind": "wall", "x": 11.9, "y": 0.1, "width": 8.1, "depth": 13.9, "rotation": 0, "thickness": 0.2, "points": [{ "x": 19.9, "y": 0.2 }, { "x": 19.9, "y": 13.9 }, { "x": 12, "y": 13.9 }] },
      { "id": "door-1", "kind": "door", "label": "Entrada", "x": 8, "y": 13.8, "width": 2, "depth": 0.2, "rotation": 0 },
      { "id": "counter-1", "kind": "counter", "label": "Caja", "x": 1, "y": 1, "width": 6, "depth": 1.2, "rotation": 0 },
      { "id": "kiosk-1", "kind": "kiosk", "x": 10.5, "y": 12, "width": 1, "depth": 1, "rotation": 0 },
      { "id": "zone-1", "kind": "zone", "label": "Terraza", "x": 14, "y": 1, "width": 5.5, "depth": 6.5, "rotation": 0 }
    ],
    "tables": [
      { "id": "t1", "label": "1", "seats": 2, "shape": "square", "x": 1.5, "y": 4, "width": 1, "depth": 1, "rotation": 0 },
      { "id": "t4", "label": "4", "seats": 4, "shape": "round", "x": 1.5, "y": 7.3, "width": 1.4, "depth": 1.4, "rotation": 0 },
      { "id": "t6", "label": "6", "seats": 6, "shape": "rect", "x": 1.5, "y": 10.6, "width": 2.4, "depth": 1.2, "rotation": 0 }
    ]
  }
}
```

## Cómo se guarda

Un piso se guarda como dos tipos de datos, a propósito:

* **La estructura como JSON.** Paredes, mostradores, kioscos, puertas, columnas y zonas se guardan en un solo documento `jsonb` por piso (`store_floors.layout`). Nada más se refiere a ellos, siempre se leen y editan juntos, y guardarlos como un documento hace que cada edición sea atómica.
* **Las mesas como filas.** Cada mesa es una fila en `store_tables`. Las mesas tienen vida propia: el POS las marca como ocupadas, las agrupa y las vincula a órdenes, y las reservas apuntarán a ellas. Las filas le dan a cada mesa una identidad estable, llaves foráneas, seguridad por fila y actualizaciones en tiempo real por fila cuando cambia su estado, sin reescribir todo el piso.

Al guardar, el API toma el JSON del piso, guarda la estructura en el documento del piso y crea, actualiza o elimina las filas de mesas en una sola transacción.

## Conceptos relacionados

> [card] [Locales](https://firespark.cloud/docs/es/concepts/stores.md)
>
> Ubicaciones que tienen planos

> [card] [Órdenes](https://firespark.cloud/docs/es/concepts/orders.md)
>
> Órdenes abiertas para grupos sentados


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.