FloCafe
FloCafe는 구독 없이 무료로 주문·테이블·주방 디스플레이·로컬 데이터를 관리하는 로컬 퍼스트 레스토랑 POS입니다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Suite web para gestión de inventario, etiquetas de bodega, almacén físico, manifiestos, alistamiento, devoluciones, conteos de mercancía y trazabilidad. Todo centralizado en una sola base de datos de tiendas y productos.
Frontend: HTML/CSS/JS vanilla (sin frameworks, sin build ni bundler)
Backend: Vercel Serverless Functions (Node.js ESM)
Base de datos: Supabase PostgreSQL (30 tablas) + Supabase Storage (fotos de conteos y recepciones)
Deploy: Vercel (producción automática desde main)
Cada módulo es una página independiente (HTML + JS + CSS propios). Solo comparten el código de public/shared/. Todos se abren desde el menú lateral del generador de etiquetas.
/)auto (Halloween del 1 de oct al 2 de nov, Navidad del 3 de nov al 6 de ene), halloween, navidad o ninguna/stockforge/almacen4.html)productos.ubicacion → el QR de la etiqueta se actualiza solo/manifiesto/manifiesto.html)localStorage (hasta 24 h): si la pestaña se recarga o se cierra, se ofrece continuar donde se quedó/alistamiento/alistamiento.html)pdf-lib, sin regenerarlas)/devoluciones/devoluciones.html)devoluciones_registro con la fecha del día. El conteo por día/semana/mes se consulta en el Panel de Coordinador, pestaña "Devoluciones"/conteos/conteos.html)conteos; la foto se comprime en el navegador y se sube al bucket privado conteos de Supabase Storagegoogle/, instalación en google/README.md). Los que fallen quedan pendientes y se reenvían con "Enviar pendientes"--kiosk-printing imprime sin cuadro de diálogo (pasos en la propia pestaña)trasladar_parcial_conteo. Queda historial en conteos_traslados, la fila de Google Sheets se mueve a la pestaña nueva y se genera el mensaje para WhatsApp. Si se pide imprimir al trasladar, la estación imprime la etiqueta nueva/recepcion/recepcion.html)recepciones (vía POST /api/conteos?accion=recepcion); la foto va al bucket conteos, carpeta recepciones//trazabilidad.html)/api/tiendas para el filtro de tiendas)/reporte/reporte_distribuidor_proveedor.html)/mercado/mercado.html)La BD tiene 30 tablas en Supabase PostgreSQL. Todas tienen RLS activado y ninguna política: con la anon key o el JWT de un usuario no se puede leer ni escribir ninguna tabla ni ejecutar ninguna función directamente. Toda lectura y escritura pasa por la API (api/*.js), que usa la service role y aplica perfiles y permisos. Las tablas principales tampoco conceden INSERT/UPDATE/DELETE/TRUNCATE a anon ni authenticated, y las funciones solo las ejecuta service_role (con search_path fijo). El estado aplicado en producción y la regla para cambios están en supabase/ESTADO_PRODUCCION.md.
Core (etiquetas e inventario) — supabase-schema.sql:
tiendas — catálogo de tiendasproductos — inventario por tienda (codigo, nombre, ubicacion)configuracion — logos y ajustes clave-valor (escritos con la función upsert_config)STOCKFORGE (almacén físico) — supabase-schema.sql:
zonas, racks, celdas — estructura física del almacénskus — productos en celdas (vinculados a productos vía producto_id)responsables, rack_responsables, rack_tiendas — asignacionescelda_responsables, celda_tiendas — asignaciones por celdaaudits, changelog — trazabilidadmovimientos — historial de trasladosbodega_config — configuración global del almacénEl guardado del almacén reemplaza el estado completo en una sola transacción con la función replace_warehouse_state (supabase/replace_warehouse_state.sql).
Devoluciones — supabase/devoluciones_registro.sql:
devoluciones_registro — una fila por guía devuelta (guia, fecha). El mismo script define la función devoluciones_por_diaConteos — supabase/conteos.sql:
conteos — un registro por conteo de mercancía. El script también crea el bucket privado conteos para las fotos y la función trasladar_parcial_conteoconteos_traslados — historial de traslados de conteos entre tiendas/IDs (quién, cuándo, de dónde a dónde)Recepciones — supabase/recepciones.sql:
recepciones — una fila por recepción de mercancía (tienda, cajas, recibido por, foto, fecha y hora del servidor) y su seguimiento: llegada del ajuste de inventario y cierre (quién y cuándo)Dispositivos vinculados — supabase/dispositivos.sql:
dispositivos — equipos autorizados a enviar Conteos y Devoluciones (nombre y hash de su credencial). Sin acceso público: solo la API la leeMercado y Alistamiento — supabase/mercado.sql (sin acceso público: solo la API, porque guarda nombres de clientes):
mercado_cortes — encabezado de cada corte (fecha, horario, archivo, responsables, personas, totales, BASE, validación)mercado_lineas — el mercado: una fila por artículo y ubicación con su cantidadmercado_guias — el alistamiento: una fila por guía y artículo, con destinatariomercado_asignaciones — qué persona hizo cada tienda en cada cortemercado_alistado — artículos marcados como alistados (quién y cuándo)mercado_inventarios, mercado_ubicaciones, mercado_ubicaciones_cargas — tiendas (nombre corto, persona habitual) y tablas de ubicacionesmercado_guardar_corte, mercado_asignar, mercado_historial y mercado_reemplazar_ubicaciones. Al final del archivo hay consultas de auditoría de ejemplo (unidades y guías por persona, por tienda y día, buscar una guía)El campo skus.producto_id vincula STOCKFORGE con el catálogo de etiquetas. Cuando un SKU tiene producto_id, al guardar el almacén se actualiza automáticamente productos.ubicacion con la posición física.
No hay sistema de migraciones: todos los .sql se ejecutan a mano en el SQL Editor de Supabase. Si se modifica uno, hay que volver a ejecutarlo; todos se pueden ejecutar varias veces sin reabrir nada. Antes de ejecutar cualquier .sql en producción: respaldo + supabase/remediacion/etapa1/06_VERIFICACION.sql antes y después.
effi-etiquetas/
├── api/
│ ├── _supabase.js # cliente Supabase admin compartido
│ ├── _cors.js # CORS centralizado (env var ALLOWED_ORIGIN)
│ ├── _auth.js # requireUser(): valida el token del coordinador
│ ├── _rateLimit.js # rate limit en memoria para endpoints públicos
│ ├── _sheets.js # envío opcional de conteos a Google Sheets
│ ├── auth.js # login con Supabase Auth
│ ├── refresh.js # renovar token JWT (auto-refresh en frontend)
│ ├── change-password.js # cambio de contraseña del coordinador
│ ├── users.js # listar/crear/eliminar coordinadores
│ ├── config.js # GET/POST logos y configuración
│ ├── tiendas.js # CRUD tiendas + inventario completo
│ ├── productos.js # CRUD productos
│ ├── stockforge.js # estado completo del almacén (auth en POST)
│ ├── devoluciones.js # registro y conteo diario de guías devueltas
│ ├── conteos.js # conteos: alta, consulta, traslado, impresión, Sheets
│ └── mercado.js # mercado y alistamiento: cortes, asignaciones, avance, ubicaciones
├── public/
│ ├── index.html/js/css # generador de etiquetas + panel de coordinador ("/")
│ ├── decoracion.js # decoración de temporada
│ ├── navidad.css # tema Navidad (solo pantalla)
│ ├── halloween.css # tema Halloween (solo pantalla)
│ ├── trazabilidad.html # trazabilidad de inventario (archivo único)
│ ├── shared/
│ │ ├── api.js # capa HTTP compartida (fetch + JSON + errores)
│ │ ├── dom-utils.js # esc(), notify(), STORAGE_KEYS, guard de sesión
│ │ └── tokens.css # colores compartidos y foco accesible
│ ├── stockforge/
│ │ └── almacen4.html/js/css # STOCKFORGE (mapa de almacén)
│ ├── manifiesto/
│ │ ├── manifiesto.html/js/css
│ │ ├── manifiesto-scan-ui.js # capa visual del escaneo
│ │ ├── manifiesto-sesion.js # avance guardado
│ │ └── manifiesto-guardas.js # protección de modales contra la pistola
│ ├── alistamiento/
│ │ └── alistamiento.html/js/css
│ ├── devoluciones/
│ │ └── devoluciones.html/js/css
│ ├── conteos/
│ │ └── conteos.html/js/css # pantalla móvil pública
│ └── reporte/
│ ├── reporte_distribuidor_proveedor.html
│ └── reporte.css/js
├── supabase/
│ ├── replace_warehouse_state.sql # función de guardado completo de STOCKFORGE
│ ├── devoluciones_registro.sql # tabla + función devoluciones_por_dia
│ ├── conteos.sql # tablas, bucket y trasladar_parcial_conteo
│ ├── dispositivos.sql # dispositivos vinculados por QR
│ ├── ESTADO_PRODUCCION.md # estado de seguridad aplicado y regla para cambios
│ └── remediacion/etapa1/ # scripts de referencia, reversión y 06_VERIFICACION.sql
├── google/
│ ├── conteos-sheets.gs # Apps Script receptor de conteos
│ └── README.md # instalación de la copia a Google Sheets
├── .env.example # variables requeridas documentadas
├── package.json
├── supabase-schema.sql # schema base (16 tablas + RLS + upsert_config)
└── vercel.json # rewrites para servir /public como raíz + headers de seguridad (CSP)
| Rol | Acceso | Puede |
|---|---|---|
| Usuario | Sin login | Ver inventario, imprimir etiquetas, usar Manifiestos, Alistamiento, Devoluciones, Trazabilidad y registrar Conteos |
| Coordinador | Login email + contraseña | Todo lo anterior, más: crear/editar/eliminar tiendas y productos, subir logos, configuración, gestionar coordinadores, cambiar contraseña, ver devoluciones y conteos, trasladar conteos, estación de impresión, Reporte Distribuidor y editar STOCKFORGE, según los permisos de su perfil |
Cada cuenta del modo coordinador tiene un perfil: administrador (todo), coordinador o bodeguero. El administrador decide en "Usuarios y permisos" qué puede hacer cada perfil; la API lo comprueba en cada petición.
El coordinador usa el mismo usuario en el generador de etiquetas y en STOCKFORGE, pero cada uno mantiene su propia sesión: iniciar sesión en uno no inicia sesión en el otro. El generador guarda la sesión en localStorage (persiste entre pestañas); STOCKFORGE en sessionStorage (se borra al cerrar la pestaña). En ambos el token JWT se auto-renueva al expirar sin cerrar sesión.
supabase-schema.sqlsupabase/replace_warehouse_state.sqlsupabase/devoluciones_registro.sqlsupabase/conteos.sqlsupabase/dispositivos.sqlsupabase/mercado.sqlSUPABASE_URLanon public key → SUPABASE_ANON_KEYservice_role key → SUPABASE_SERVICE_ROLE_KEYCopiar .env.example a .env:
SUPABASE_URL=https://xxxx.supabase.co
SUPABASE_ANON_KEY=eyJxxx...
SUPABASE_SERVICE_ROLE_KEY=eyJxxx...
ALLOWED_ORIGIN=https://tu-proyecto.vercel.app,https://tu-dominio-custom.com
# Opcionales (copia de conteos a Google Sheets, ver google/README.md)
SHEETS_WEBHOOK_URL=
SHEETS_WEBHOOK_SECRET=
ALLOWED_ORIGINacepta una lista separada por comas (útil para permitir a la vez el dominio de producción y los dominios de preview de Vercel). No hay wildcard: si el origen de la petición no está en la lista, el navegador bloquea la respuesta cross-origin. En desarrollo local (vercel dev, mismo origen para frontend y API) no hace falta configurarla.
Desde JSON exportado: Coordinador → Configuración → Importar JSON (procesa en lotes de 10, mucho más rápido que uno por uno)
npm install -g vercel
vercel dev
Abre http://localhost:3000. No hay build ni linter: vercel dev sirve /public y /api en el mismo origen contra un proyecto real de Supabase.
Pruebas del cálculo de Mercado y Alistamiento (no necesitan Supabase):
npm install
npm test
Usan exports reales sin datos personales en tests/fixtures/ y comparan contra las tablas dinámicas de la hoja original. Para agregar otro export como caso de prueba, quítale los datos personales con node tests/anonimizar.mjs <entrada.xlsx> tests/fixtures/<nombre>.xlsx. El caso obligatorio CORTE031110.xlsx (503 unidades en 320 filas; PRIVADO ALI CR, ID 29 = 176 unidades en 86 filas) se salta hasta que el archivo esté en tests/fixtures/.
localStorage. Si no hay conexión, muestra el último inventario conocido.index.js, sin CDN).localStorage como caché. Carga desde caché primero y luego sincroniza con el servidor en segundo plano.productos.ubicacion). Valida el body antes de reemplazar el estado, lo hace en una sola transacción (replace_warehouse_state) y deja el guardado en la bitácora.auth, refresh, devoluciones, conteos) tienen rate limit en memoria por instancia: es una primera barrera, no un límite garantizado.api/ sin _ al inicio) por despliegue; con una más el despliegue falla y producción se queda en la versión anterior. Hoy hay 12. Para agregar un endpoint, súmalo como ?accion= a uno existente. (reporte-tiendas.js, que nadie usaba, se quitó por eso; está en el historial de git.)public/shared/api.js centraliza el fetch + parseo JSON + manejo de errores. Cada módulo conserva su propia lógica de autenticación/renovación de token.esc() antes de ir a innerHTML para prevenir XSS almacenado.vercel.json. Para agregar un script, hoja de estilos o fuente de otro CDN hay que añadir su origen a la directiva correspondiente o el navegador lo bloqueará en producción.