Registrar y asignar un tag NFC¶
Esta guía te lleva paso a paso por la tarea concreta de dejar operativo un tag NFC en blanco: darlo de alta en stock y asignarlo a un sanitario, para que cuando el personal apoye el celular sobre la placa, el tap registre trabajos contra el sanitario correcto.
Alcance: qué es 'registrar un dispositivo NFC' acá
En WorkDone Sanitarios, registrar un dispositivo NFC significa dar de alta una placa/tag NFC — la entidad TagNfc, el chip físico que se pega al sanitario — y asignarla a un sanitario. NO se trata de registrar un teléfono ni un dispositivo de operario (eso es otra cosa: el Dispositivo que se vincula en el login). Acá hablamos del chip que el personal acerca con el celular para registrar una limpieza.
El ciclo de vida completo del tag (estados, transiciones válidas y auditoría) vive en la referencia Ciclo de vida de los tags NFC. Esta página es la receta operativa; aquella, el porqué.
Prerequisitos¶
| Necesitás | Detalle | Cómo verificarlo |
|---|---|---|
| Rol ADMIN | Las mutaciones NFC desde la app móvil (alta-stock, asignar, desasignar, reasignar) son solo ADMIN. Un operador de limpieza o supervisor no puede ejecutarlas. |
Drawer → Configuración → "Rol del operario actual" debe decir ADMIN. La pantalla Gestión NFC solo aparece para ADMIN. |
| Un tag NFC físico en blanco | NTAG215 o NTAG213. En producción se usan los antimetal (Atlantico3D) sobre superficie metálica; para pruebas cualquier NTAG común alcanza. | Un chip que al resolverlo dé NO_REGISTRADO. |
| El sanitario destino ya creado | El tag se asigna a un sanitario existente (ej. AEP-T-B12). |
Aparece en el árbol Sucursal›Sector›Sanitario de GET /api/v1/app/sanitarios/disponibles (con tiene_tag por cada uno). |
| La app móvil logueada como ADMIN | El alta de stock solo se hace desde la app, porque hay que leer el chip onsite. No existe alta de stock en el BackOffice web. | Que la app esté sincronizando (chip 🟢 Conectado abajo). |
El alta de stock NO se puede hacer desde la web
ALTA_STOCK requiere leer el chip físico en el lugar, así que solo existe en la app móvil (rol ADMIN). El BackOffice web puede desasignar, reasignar, dar de baja y re-dar de alta, pero no dar de alta en stock.
El recorrido en una imagen¶
flowchart TD
A[Tag NFC en blanco] -->|Paso 1 ALTA_STOCK| B[EN_STOCK]
B -->|Paso 2 ASIGNACION| C[ASIGNADO]
C -->|Paso 3 verificar resolver| D[Tap registra trabajos]
Y los tres estados posibles del tag, con las transiciones que vas a usar:
stateDiagram-v2
[*] --> EN_STOCK: ALTA_STOCK
EN_STOCK --> ASIGNADO: ASIGNACION
ASIGNADO --> EN_STOCK: DESASIGNACION
ASIGNADO --> ASIGNADO: REASIGNACION otro sanitario
EN_STOCK --> BAJA: BAJA
BAJA --> EN_STOCK: RE_ALTA solo web
Cómo leer el diagrama
ALTA_STOCKes idempotente: si el tag ya existe, devuelve el existente sin generar evento.REASIGNACIONes un bucle sobreASIGNADO: el tag cambia de sanitario en una sola operación atómica, sin pasar porEN_STOCK.BAJAsale solo desdeEN_STOCK. Un tagASIGNADOhay que desasignarlo primero.RE_ALTA(BAJA → EN_STOCK) está disponible solo desde el BackOffice web, nunca por la app/sync.
Paso 1 — Alta en stock¶
Con la app en la pantalla Gestión NFC, apoyá el celular sobre el tag en blanco. La app resuelve el estado (NO_REGISTRADO) y te ofrece "Dar de alta en stock". Confirmá con un alias descriptivo (ej. tag #14 lote mayo).

La app móvil esperando el tap: "Acercá el celular a un tag". Apoyás el celular sobre el chip para que la app lo lea y resuelva su estado. El campo "DEV — simular tag (UUID)" solo aparece en builds de desarrollo, para simular el tap sin un chip físico.
Esto graba/registra el tag como EN_STOCK en el backend. El endpoint que la app encola y envía por sync es:
POST /api/v1/app/nfc/alta-stock
Authorization: Bearer <jwt-operario-ADMIN>
Content-Type: application/json
Es idempotente: podés re-leer sin miedo
alta-stock es idempotente por uuid normalizado: si el tag ya existe, el backend devuelve el tag actual sin crear evento ni modificar nada. Volver a apoyar el celular sobre un tag ya dado de alta no rompe nada ni duplica registros.
Grabar el tag físico (NDEF)
Si además vas a grabar el chip (con una app tipo "NFC Tools" / "NFC TagWriter"), escribí en el NDEF la URL https://workdone.lubeca.tech/t/{uuid}, donde {uuid} es el mismo uuid_tag que diste de alta. Ese UUID es la fuente de verdad: el backend normaliza el UUID (trim, mayúsculas, sin espacios) en cada lectura, así que el formato no importa, pero el valor sí tiene que coincidir.
Resultado verificable: al resolver el tag, su estado pasa a EN_STOCK.
Paso 2 — Asignar a un sanitario¶
Con el tag ya EN_STOCK, la app te ofrece "Asignar a sanitario". Elegí el sanitario destino del árbol Sucursal›Sector›Sanitario y confirmá. La app encola y sincroniza:
POST /api/v1/app/nfc/asignar
Authorization: Bearer <jwt-operario-ADMIN>
Content-Type: application/json
Esto ejecuta la transición EN_STOCK → ASIGNADO: el tag queda vinculado al sanitario, con asignado_en seteado y activo = true.
Conflicto de asignación: el primero en sincronizar gana
Solo puede haber un tag ASIGNADO por sanitario (constraint en la base). Si el sanitario destino ya tiene un tag asignado, o si el tag no está EN_STOCK, la operación devuelve CONFLICTO_ASIGNACION (HTTP 409). Cuando dos dispositivos asignan offline al mismo sanitario, el primero en el orden de sincronización gana y el segundo recibe el conflicto (el orden lo define ts_local_device ASC). Si te pasa, la app revierte el optimismo local y te avisa.
Resultado verificable: al resolver el tag, su estado pasa a ASIGNADO y trae los datos del sanitario.
Paso 3 — Verificar¶
Confirmá que el tag quedó operativo resolviéndolo:
GET /api/v1/app/nfc/resolver/aaaa1111-1111-1111-1111-111111111111
Authorization: Bearer <jwt-operario>
Respuesta esperada:
{
"estado": "ASIGNADO",
"alias": "tag #14 lote mayo",
"sanitario": {
"id": 12,
"codigo": "AEP-T-B12",
"nombre": "Baño Hombres Terminal A",
"sector": "Terminal A",
"sucursal": "AEP"
}
}
Prueba final (la que vale): que un operario de limpieza apoye el celular sobre el tag. Debería pasar a estado VERDE y arrancar el timer del trabajo. La app resuelve taps solo contra tags en estado ASIGNADO, así que si llegaste hasta acá, el tag ya está operativo.
Operaciones adicionales¶
Una vez asignado, el tag puede moverse o retirarse. Todas estas mutaciones pasan por el mismo servicio (TagNfcLifecycleService) y dejan auditoría.
Reasignar a otro sanitario¶
Mueve el tag de un sanitario a otro en un solo paso atómico, sin pasar por EN_STOCK:
{
"uuid_tag": "aaaa1111-1111-1111-1111-111111111111",
"sanitario_nuevo_id": 20,
"notas": "se movió la placa al baño contiguo"
}
Reasignar NO es 'desasignar + asignar'
REASIGNACION genera un único evento de auditoría con sanitario_anterior y sanitario_nuevo poblados, para preservar la trazabilidad del movimiento como una sola operación. Caso borde: si el sanitario nuevo es el mismo que el anterior, no se valida el conflicto.
Desasignar (devolver a stock)¶
Limpia el sanitario y devuelve el tag a EN_STOCK.
Dar de baja¶
La baja (desactivar definitivamente) y la re-alta se hacen desde el BackOffice web, no por la app móvil. Recordá: BAJA solo es posible desde EN_STOCK — un tag asignado hay que desasignarlo primero.
Desde el BackOffice, la pantalla Tags NFC lista todos los tags ya registrados y te deja gestionarlos: filtrarlos por estado (Todos / En stock / Asignados / Baja), y desasignar o reasignar cada uno con los botones de la columna Acciones.

La pantalla Tags NFC del BackOffice: tabla con Alias, UUID, Estado, Sanitario y fecha de asignación, más las acciones Desasignar / Reasignar por fila. El aviso "Alta de tags" recuerda que el alta se hace desde la app móvil (requiere leer el chip onsite); desde acá solo se gestionan los tags ya registrados.
Tabla de transiciones válidas¶
| Acción | De → A | Dónde |
|---|---|---|
ALTA_STOCK |
[*] → EN_STOCK |
Solo app móvil (leer el chip onsite) |
ASIGNACION |
EN_STOCK → ASIGNADO |
App o web |
DESASIGNACION |
ASIGNADO → EN_STOCK |
App o web |
REASIGNACION |
ASIGNADO → ASIGNADO (otro sanitario, atómico) |
App o web |
BAJA |
EN_STOCK → BAJA |
App o web — solo desde EN_STOCK |
RE_ALTA |
BAJA → EN_STOCK |
Solo web (BackOffice) |
Problemas comunes¶
403 / ROL_INSUFICIENTE — 'no me deja dar de alta ni asignar'
Las mutaciones NFC son solo ADMIN. Si tu operario no es ADMIN, el backend rechaza la operación con HTTP 403 y código ROL_INSUFICIENTE. Verificá tu rol en Configuración. En la cola de sync, los ítems NFC de un operario no-ADMIN se ignoran sin abortar el sync.
TRANSICION_INVALIDA — 'la operación no aplica al estado del tag'
La máquina de estados rechazó la operación porque el tag no está en el estado de origen esperado (HTTP 409). Ejemplos: dar de baja un tag ASIGNADO (hay que desasignar primero), o desasignar/reasignar un tag que no está ASIGNADO. Resolvé el tag primero (GET /nfc/resolver/{uuid}) para ver en qué estado está y qué transición corresponde.
CONFLICTO_ASIGNACION — 'el sanitario ya tiene tag'
El sanitario destino ya tiene un tag ASIGNADO, o el tag no está EN_STOCK (HTTP 409). Solo puede haber un tag asignado por sanitario: desasigná el actual o reasignalo, según corresponda.
TAG_DESCONOCIDO al tapear — 'el tag no registra nada'
Si un operario apoya el celular sobre un tag que no fue dado de alta (whitelist estricta: tag no registrado = inutilizable), el tap genera un trabajo en modo OFFLINE_PENDIENTE_RESOLUCION y dispara una alerta TAG_DESCONOCIDO. La solución es volver al Paso 1 y dar de alta ese UUID.
Auditoría: todo cambio queda registrado
Cada transición exitosa persiste un TagNfcEvento (tabla inmutable). El evento captura el tag, el UUID normalizado, la acción, los sanitarios anterior/nuevo, el operario, el dispositivo, la fecha y las notas. Nada cambia el estado de un tag sin dejar rastro. El detalle completo del modelo de auditoría está en Ciclo de vida de los tags NFC.
Para el contrato completo de los endpoints NFC, ver API HTTP — App móvil · gestión NFC. Para entender la máquina de estados en profundidad, Ciclo de vida de los tags NFC. Si todavía no tenés el backend corriendo para probar esto, empezá por Montar el entorno del backend.