FloCafe
FloCafe is the free, local-first restaurant POS in the FloPOS ecosystem. Run orders, tables, kitchen display, and local data without a software subscription.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 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.