# packages/shared/ — Código compartido (@gustito/shared)

Reglas al trabajar en `packages/shared/**`. Se carga solo al tocar este directorio.

## Qué es

Fuente única de verdad de dominio para las tres apps (tienda, panel, cerebrito): tipos,
máquina de estados, dinero, pagos, canales realtime y el changelog versionado. Se
consume como TS directo, sin build propio. El barrel es `src/index.ts`; `src/fiscal.ts`
NO se exporta ahí a propósito (ver comentario en `index.ts:10-12`) — es del módulo
instalable homologado, no de Gustito, y así no viaja en el bundle de la tienda ni del
panel. Quien de verdad lo necesita importa `@gustito/shared/fiscal` directo.

## Piezas canónicas (una regla vive en UN solo lugar)

- **`domain.ts`** — `ESTADOS_PEDIDO`, `siguientesEstados()` y `esTransicionValida()`
  son la máquina de estados del pedido, espejo de la RPC `cambiar_estado_pedido` en la
  BD. Ojo con `siguientesEstados`: el `pickup` salta `en_camino` (línea 41). También
  vive aquí `METODOS_PAGO` (la lista; las reglas de cada método están en `pagos.ts`).
- **`money.ts`** — todo el dinero pasa por aquí: `bsDesdeUsd`, `formatUsd`, `formatBs`,
  `precioDual`, `sumarCarrito`. Nada de redondear o formatear plata a mano en otro lado.
- **`pagos.ts`** — qué método necesita referencia (`necesitaReferencia`) y cuál necesita
  que alguien vaya a verificar que la plata llegó (`necesitaVerificacion`). NO son la
  misma pregunta: punto de venta pide referencia (para cuadrar caja) pero no verificación
  (el banco ya aprobó en el momento). La regla de verdad vive en la BD
  (`fn_pago_necesita_referencia`, `fn_exigir_referencia`); esto es el espejo de pantalla.
- **`realtime.ts`** — nombres de canal que DEBEN calzar con la BD. `canalPanelRestaurante()`
  arma `rest-bcast-${restauranteId}`, que tiene que ser igual al topic que emite el
  trigger `fn_bcast_panel` y a la policy de `realtime.messages`
  (`'rest-bcast-' || restaurante_id`). El GPS de repartidor va por `canalUbicacionPedido()`,
  BROADCAST puro (no toca Postgres), para que la frecuencia del GPS no genere escrituras.
- **`opciones.ts`** — `parsearOpciones()` es la fuente canónica del parseo de notas de
  ítem. El cerebrito lleva una copia idéntica en `apps/cerebrito/src/comanda.ts`, y hay
  un test cable-trampa (`apps/cerebrito/test/test_copia_opciones.mjs`) que revienta si
  las dos copias divergen. Si tocás el formato acá, hay que tocarlo allá también.
- **`changelog.ts`** — `CHANGELOG_ENTRIES`, `CURRENT_VERSION` (derivado) y `bumpVersion()`.
  Alimenta la sección "Mejoras" del panel y el número de versión del sidebar.

## Cómo actualizar el changelog (IMPORTANTE)

Cada vez que algo VISIBLE al negocio llega a producción, agregar UNA entrada al INICIO
de `CHANGELOG_ENTRIES` en `src/changelog.ts`:

1. Calcular la versión: `bumpVersion(CURRENT_VERSION, 'feature'|'improvement'|'fix'|'security')`
   — `'feature'` sube el 2º número (`X.Y+1.0`); el resto sube el 3º (`X.Y.Z+1`). El 1º
   número (major) solo se toca a mano, en un rediseño grande.
2. Poner la entrada nueva arriba del array, con esa versión, fecha `YYYY-MM-DD`, y
   `title`/`summary`/`details` en lenguaje NO técnico — como se lo explicarías al dueño
   del negocio, no a un programador (mirá las entradas existentes como ejemplo de tono).
3. `CURRENT_VERSION` se deriva sola de `CHANGELOG_ENTRIES[0]` (`changelog.ts:164`); no
   se toca a mano nunca.

Cuándo SÍ agregar entrada: función nueva visible, cambio de UX que se nota, mejora de
proceso, parche de seguridad relevante, bug que afectaba la operación real (como el caso
del punto de venta sin referencia que se cuenta en `pagos.ts:1-9`). Cuándo NO: refactor
interno, cambios de docs, optimizaciones invisibles para quien usa el panel o la tienda.

## Anti-patrones (NO hacer)

| No | Sí | Por qué |
|---|---|---|
| Agregar `export * from './fiscal'` al barrel `index.ts` | Importar `@gustito/shared/fiscal` directo desde el módulo instalable | Rompe la frontera de homologación; el cálculo fiscal no puede viajar en el bundle de Gustito |
| Cambiar el nombre que arma `canalPanelRestaurante()` u otra función de `realtime.ts` sin más | Cambiarlo a la vez en la migración del trigger `fn_bcast_panel` y en la policy de `realtime.messages` | Si el nombre de canal se desincroniza, el panel deja de recibir broadcasts y no hay error visible |
| Copiar `parsearOpciones()` o la lista de `METODOS_PAGO`/reglas de pago en otra app | Importar la función/tipo desde `@gustito/shared` | Ya reventó una vez (el enredo del punto de venta contado en `pagos.ts`); dos copias divergen tarde o temprano |
| Editar `CURRENT_VERSION` a mano en `changelog.ts` | Agregar la entrada nueva arriba en `CHANGELOG_ENTRIES`; la constante se deriva sola | `CURRENT_VERSION` existe justamente para que sea imposible desincronizarla |
