Base de datos

Este inventario se genera desde la base de datos real (information_schema / pg_catalog de Supabase staging, PostgreSQL 17.6), no de migraciones ni de memoria. Fuente: docs/DATABASE_ERD.md.

iFondita — ERD / Inventario del schema Supabase (generado del schema VIVO)

Fecha: 2026-07-13 · Regenerado desde el schema desplegado (staging Supabase pdfkobkatghthmjgdkov, PostgreSQL 17.6). Reemplaza el ERD anterior (obsoleto, pre-Bloque B). Este documento se genera leyendo information_schema / pg_catalog de la DB real, no de migraciones ni de memoria.

  • 60 tablas · 70 claves foráneas (FK) · 41 CHECK constraints · 19 UNIQUE constraints.
  • Todas las PK son id uuid DEFAULT gen_random_uuid() salvo que se indique. Timestamps *_at son timestamptz DEFAULT now().
  • El pos-backend se conecta como service_role (bypassa RLS) y aísla por restaurant_id en middleware (requireRestaurant). La DB tiene RLS habilitado (Modelo-1: authenticated filtrado por auth.uid()), relevante para el acceso directo del dashboard-owner vía Supabase Auth.
  • Contrato clave del dinero: orders.payment_details (jsonb) con lines[] de métodos-hoja (cash|card|transfer|online) es la fuente de verdad del cobro. Ver api/openapi.yaml.

Anotación de estado (post-Bloque B, 8/8): el núcleo orders/order_items/tables/cash_sessions fue reconciliado en A0-A2; el auth de owner (restaurants.auth_user_id) migró a Supabase Auth en pieza D. Tablas retiradas del linaje viejo (restaurant_staff, restaurant_sessions, staff, cash_register, daily_closings, tickets, tablecloth_*) ya no existen en este schema.


Diagrama de relaciones (FKs)

Notación: A ||--o{ B : "col" = B.col referencia a A.id. La anotación (cascade|set null|restrict) indica la regla ON DELETE cuando no es NO ACTION.


Inventario de tablas por dominio

Plataforma, Auth y Onboarding

restaurants

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
auth_user_iduuidNO
owner_nametextNO
restaurant_nametextNO
slugtextNO
phonetextNO
emailtext
addresstextNO
coloniatext
geogeographyNO
zone_iduuidzones.id
photo_urltext
logo_urltext
food_typestext[]'{}'::text[]
service_modeltext'a_la_carta'::text
business_hoursjsonbNO
tiertext'bronze'::text
avg_ticketnumeric(10,2)
is_activebooleantrue
contract_statustext'pending'::text
onboarding_stepinteger0
onboarding_completed_attimestamptz
referral_codetext
force_logoutbooleanfalse
deletion_requested_attimestamptz
is_deletedbooleanfalse
created_attimestamptznow()
updated_attimestamptznow()
owner_emailtext
last_logintimestamptz
plantext'free'::text
totp_secrettext
totp_activobooleanfalse
totp_recovery_codesjsonb'[]'::jsonb
cerrado_hoybooleanfalse
cerrado_hoy_razontext
cerrado_hoy_hastatimestamptz
logo_thumb_urltext
carta_plantilla_idinteger1
carta_coloresjsonb
mantel_styletext
social_linksjsonb'{}'::jsonb
fidelidad_activobooleantrue
fidelidad_configjsonb'{"recompensa": "Un guisado gratis", "como_…
propinas_activobooleanfalse
propinas_opcionesjsonb
propinas_modotext
metodos_pago_configjsonb
spei_clabetext
spei_bancotext
spei_beneficiariotext
fiscal_rfctext
fiscal_razon_socialtext
fiscal_regimentext
fiscal_cptext
pac_providertext
pac_api_keytext
pac_api_secrettext
cfdi_activobooleanfalse
jwt_secrettext
rappi_hmac_secrettext
rappi_store_idtext
uber_eats_hmac_secrettext
uber_eats_store_idtext
whatsapp_creditos_usadosinteger0
mesa_margen_capacidadinteger
calletext
numero_exttext
ciudadtext
cptext
descripciontext
foto_portada_urltext

operators

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
nametextNO
pin_hashtextNO
roletextNO'mesero'::text
nivelintegerNO4
activebooleanNOtrue
emailtext
password_hashtext
twilio_phonetext
otp_codetext
otp_expires_attimestamptz
created_attimestamptzNOnow()

admin_users

PK: id · filas (staging): 1

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
auth_user_iduuidNO
roletextNO
display_nametextNO
emailtextNO
phonetext
assigned_zonesuuid[]'{}'::uuid[]
is_activebooleantrue
two_factor_enabledbooleanfalse
created_attimestamptznow()

fogon_users

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
emailtextNO
password_hashtextNO
nombretext
roltextNO'admin'::text
modulosjsonbNO'["all"]'::jsonb
totp_secrettext
totp_activobooleanNOfalse
totp_recovery_codesjsonbNO'[]'::jsonb
activobooleanNOtrue
created_attimestamptzNOnow()

active_sessions

PK: id · filas (staging): 0

Nota: DORMANT — tabla de auditoría de sesiones; 0 filas, sin escritura activa desde el pos-backend tras retirar restaurant_sessions.

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
user_iduuidNO
user_typetextNO
device_idtext
device_infojsonb
is_revokedbooleanfalse
last_active_attimestamptznow()
created_attimestamptznow()

rate_limits

PK: id · filas (staging): 1

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
action_typetextNO
identifiertextNO
attemptsinteger1
first_attempt_attimestamptznow()
blocked_untiltimestamptz

app_versions

PK: id · filas (staging): 0

Nota: Config de versiones de app móvil (force-update). Sin superficie POS activa.

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
platformtextNO
min_versiontextNO
recommended_versiontextNO
force_updatebooleanfalse
store_urltext
updated_attimestamptznow()

onboarding_progress

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
step_accountbooleanNOtrue
step_profilebooleanNOfalse
step_menubooleanNOfalse
step_tablesbooleanNOfalse
step_tableclothbooleanNOfalse
step_devicebooleanNOfalse
step_first_orderbooleanNOfalse
dismissedbooleanNOfalse
completed_attimestamptz
created_attimestamptzNOnow()
updated_attimestamptzNOnow()

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
tipotextNO
versiontextNO
titletext
contenidotextNO
resumen_cambiostext
activobooleanNOfalse
es_cambio_materialbooleanNOfalse
created_byuuidfogon_users.id (set null)
created_attimestamptzNOnow()

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
document_iduuidNOlegal_documents.id (cascade)
usuario_tipotextNO'anonimo'::text
usuario_iduuid
iptext
device_fingerprinttext
accepted_attimestamptzNOnow()

deploy_log

PK: id · filas (staging): 0

Nota: Bitácora de deploys (panel Fogón system-health). Operacional, no de negocio.

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
ambientetext
resultadotext
deploado_portext
created_attimestamptzNOnow()

POS — Núcleo (órdenes, mesas, caja)

orders

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id
table_iduuidtables.id
order_numberintegerNOnextval('orders_order_number_seq'::regclass)
order_typetextNO'dine_in'::text
statustextNO'open'::text
guest_countinteger1
subtotalnumeric(10,2)0
tax_amountnumeric(10,2)0
discount_amountnumeric(10,2)0
discount_reasontext
tip_amountnumeric(10,2)0
tip_percentagenumeric(5,2)
totalnumeric(10,2)0
payment_methodtext
payment_detailsjsonb
notestext
is_splitbooleanfalse
parent_order_iduuidorders.id
cc_pickup_timetimestamptz
cc_statustext
paid_attimestamptz
cancelled_attimestamptz
cancellation_reasontext
created_attimestamptznow()
updated_attimestamptznow()

order_items

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
order_iduuidNOorders.id (cascade)
menu_item_iduuidNOmenu_items.id
quantityintegerNO1
unit_pricenumeric(10,2)NO
modifiersjsonb'[]'::jsonb
notestext
combo_iduuidcombos.id
combo_slottext
kds_statustext'pending'::text
kds_started_attimestamptz
kds_ready_attimestamptz
sent_to_kds_attimestamptz
created_attimestamptznow()

tables

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id
nametextNO
sectiontext
capacityjsonbNO'{"chairs": 4}'::jsonb
positionjsonb
statustext'free'::text
current_order_iduuid
status_changed_attimestamptznow()
is_activebooleantrue
created_attimestamptznow()

cash_sessions

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id
opened_byuuidNO
closed_byuuid
opening_amountnumeric(10,2)NO
expected_cashnumeric(10,2)
actual_cashnumeric(10,2)
differencenumeric(10,2)
total_salesnumeric(10,2)
total_ordersinteger
total_cancellationsinteger0
total_tipsnumeric(10,2)0
by_payment_methodjsonb
top_itemsjsonb
qr_scans_todayinteger0
reservations_summaryjsonb
opened_attimestamptznow()
closed_attimestamptz

restaurant_floors

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
numerointegerNO1
nombretextNO'Planta baja'::text
anchointegerNO800
altointegerNO600
activobooleanNOtrue
created_attimestamptzNOnow()

restaurant_services

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id
click_collect_enabledbooleanfalse
cc_prep_time_minutesinteger20
cc_max_concurrent_ordersinteger5
cc_custom_hoursjsonb
cc_allows_buffetbooleanfalse
reservations_enabledbooleanfalse
res_max_advance_daysinteger7
res_tolerance_minutesinteger15
res_min_party_sizeinteger1
res_max_party_sizeinteger8
res_confirmation_modetext'auto'::text
res_require_phonebooleantrue
loyalty_enabledbooleantrue
loyalty_rewardtext'dessert_free'::text
loyalty_reward_customtext
updated_attimestamptznow()

restaurant_blocked_dates

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id
blocked_datedateNO
reasontext

Menú, Modificadores y Precios

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id
category_iduuidmenu_categories.id
nametextNO
descriptiontext
pricenumeric(10,2)NO
photo_urltext
is_availablebooleantrue
is_daily_specialbooleanfalse
tagstext[]'{}'::text[]
allergenstext[]'{}'::text[]
prep_time_minutesinteger15
display_orderinteger0
is_activebooleantrue
created_attimestamptznow()

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id
nametextNO
display_orderinteger0
is_activebooleantrue

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
nametextNO
created_attimestamptzNOnow()

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
nombretextNO
tipotextNO'opcional'::text
selecciontextNO'unico'::text
min_seleccionintegerNO0
max_seleccionintegerNO1
activobooleanNOtrue
created_attimestamptzNOnow()

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
group_iduuidNOmenu_item_modifier_groups.id (cascade)
nombretextNO
precio_extranumericNO0
activobooleanNOtrue
created_attimestamptzNOnow()

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
menu_item_iduuidNOmenu_items.id (cascade)
group_iduuidNOmenu_item_modifier_groups.id (cascade)
created_attimestamptzNOnow()

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
menu_item_iduuidmenu_items.id (set null)
operator_iduuidoperators.id (set null)
actiontextNO
porciones_restantesinteger
motivotext
created_attimestamptzNOnow()

combos

PK: id · filas (staging): 0

Nota: Combos / comida corrida. Schema listo.

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id
nametextNO
pricenumeric(10,2)NO
combo_typetext'comida_corrida'::text
is_activebooleantrue
created_attimestamptznow()

combo_slots

PK: id · filas (staging): 0

Nota: Slots de combo. Schema listo.

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
combo_iduuidNOcombos.id
slot_nametextNO
display_orderinteger0
min_selectionsinteger1
max_selectionsinteger1

combo_slot_options

PK: id · filas (staging): 0

Nota: Opciones por slot de combo. Schema listo.

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
combo_slot_iduuidNOcombo_slots.id
menu_item_iduuidNOmenu_items.id
extra_pricenumeric(10,2)0
is_availablebooleantrue

daily_menus

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
datedateNO
pricenumeric
is_publishedbooleanNOfalse
created_attimestamptzNOnow()

daily_menu_items

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
daily_menu_iduuidNOdaily_menus.id (cascade)
menu_item_iduuidNOmenu_items.id (cascade)
group_iduuidmenu_groups.id (set null)
sort_orderintegerNO0
is_activebooleanNOtrue
created_attimestamptzNOnow()

price_schemes

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
nombretext
tipotextNO'porcentaje'::text
descuento_porcentajenumericNO0
horario_iniciotime
horario_fintime
activobooleanNOtrue
created_attimestamptzNOnow()

price_scheme_items

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
price_scheme_iduuidNOprice_schemes.id (cascade)
menu_item_iduuidNOmenu_items.id (cascade)
precio_especialnumericNO

buffet_configs

PK: id · filas (staging): 0

Nota: Buffet: modelo de servicio soportado en schema; superficie de cobro por buffet aún no cableada al POS.

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id
nametext'Buffet'::text
price_adultnumeric(10,2)NO
price_childnumeric(10,2)
price_seniornumeric(10,2)
includestext
start_timetime
end_timetime
is_activebooleantrue

import_logs

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
operator_iduuidoperators.id (set null)
filenametext
total_rowsinteger
importedintegerNO0
errorsintegerNO0
duplicatesintegerNO0
statustextNO'processing'::text
error_detailsjsonb
created_attimestamptzNOnow()
completed_attimestamptz

Inventario y Recetas

inventory_items

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id
nametextNO
unittextNO
current_stocknumeric(10,3)0
min_stocknumeric(10,3)0
cost_per_unitnumeric(10,2)
last_restocked_attimestamptz
created_attimestamptznow()

inventory_movements

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
inventory_item_iduuidNOinventory_items.id
movement_typetextNO
quantitynumeric(10,3)NO
notestext
created_byuuid
created_attimestamptznow()

recipes

PK: id · filas (staging): 0

Nota: Recetas → escandallo de inventario. Best-effort (no bloquea cobros).

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
menu_item_iduuidNOmenu_items.id
restaurant_iduuidNOrestaurants.id

recipe_ingredients

PK: id · filas (staging): 0

Nota: Ingredientes por receta. Best-effort.

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
recipe_iduuidNOrecipes.id
ingredient_iduuidNOinventory_items.id
quantitynumeric(10,3)NO
unittextNO

Fiscal, Facturación y Gastos

fiscal_invoices

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
folio_seqbigintNO
restaurant_iduuidNOrestaurants.id (restrict)
tipotextNO
conceptotext
subtotalnumeric(10,2)NO
ivanumeric(10,2)NO
totalnumeric(10,2)NO
estadotextNO'pendiente'::text
fecha_emisiontimestamptzNOnow()
fecha_vencimientotimestamptz
foliotext
created_attimestamptzNOnow()

invoice_items

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
invoice_iduuidNOfiscal_invoices.id (cascade)
descripciontextNO
cantidadintegerNO1
precio_unitarionumeric(10,2)NO
subtotalnumeric(10,2)NO
created_attimestamptzNOnow()

fiscal_closes

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id
folio_numberintegerNO
close_datedateNO
total_salesnumeric(12,2)NO
taxable_salesnumeric(12,2)NO
tax_amountnumeric(12,2)NO
cash_totalnumeric(12,2)0
card_totalnumeric(12,2)0
transfer_totalnumeric(12,2)0
total_ordersintegerNO
total_cancellationsinteger0
total_tipsnumeric(12,2)0
closed_byuuidNO
pdf_urltext
created_attimestamptznow()

expenses

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
amountnumeric(10,2)NO
categorytextNO'otro'::text
descriptiontext
suppliertext
notetext
datedateNOCURRENT_DATE
created_byuuidoperators.id (set null)
created_attimestamptzNOnow()

Fidelidad, CRM y Recepción

loyalty_customers

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
phonetextNO
nametext
stampsintegerNO0
total_redeemedintegerNO0
aprovecho_user_iduuid
created_attimestamptzNOnow()
updated_attimestamptzNOnow()

loyalty_history

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
customer_iduuidNOloyalty_customers.id (cascade)
order_iduuid
actiontextNO
stamps_changeintegerNO
created_byuuidoperators.id (set null)
created_attimestamptzNOnow()

crm_interactions

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidrestaurants.id (cascade)
prospecto_iduuid
tipotextNO
direcciontextNO'saliente'::text
resumentextNO
resultadotext
siguiente_acciontext
siguiente_fechadate
created_byuuidfogon_users.id (set null)
created_attimestamptzNOnow()

waitlist

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
nombretextNO
personasintegerNO
telefonotext
fuentetextNO'manual'::text
estadotextNO'esperando'::text
hora_llegadatimestamptzNOnow()
hora_asignaciontimestamptz
mesa_iduuidtables.id (set null)

cupones

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
codigotextNO
tipotextNO
valornumericNO
usos_maximosinteger
usos_actualesintegerNO0
fecha_findate
descripciontext
activobooleanNOtrue
created_attimestamptzNOnow()

Publicidad, Manteles y Crecimiento (superficie E)

advertisers

PK: id · filas (staging): 1

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
emailtextNO
nametextNO
companytextNO
phonetext
statustextNO'active'::text
created_attimestamptznow()
auth_user_iduuid
logo_urltext

campaigns

PK: id · filas (staging): 4

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
advertiser_iduuidNOadvertisers.id
nametextNO
packagetextNO
total_costnumericNO
statustextNO'draft'::text
qr_code_urltext
qr_scansintegerNO0
created_attimestamptznow()
expires_attimestamptz

invoices

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
advertiser_iduuidNOadvertisers.id
campaign_iduuidNOcampaigns.id
amountnumericNO
statustextNO'pending'::text
stripe_payment_intent_idtext
created_attimestamptznow()

qr_scans

PK: id · filas (staging): 1

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
campaign_iduuidNOcampaigns.id
locationtextNO
restaurant_iduuid
scanned_attimestamptznow()
user_iduuid

napkins

PK: id · filas (staging): 0

Nota: Ads Fase 2 (servilletas impresas). Schema listo; flujo diferido.

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
created_by_admin_iduuidNO
nametextNO
anuncio_idsuuid[]'{}'::uuid[]
quantityintegerNO0
statustextNO'draft'::text
pdf_urltext
created_attimestamptznow()

napkin_distributions

PK: id · filas (staging): 0

Nota: Ads Fase 2 (distribución de servilletas). Diferido.

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
napkin_iduuidNOnapkins.id
restaurant_iduuidNOrestaurants.id
statustextNO'pending'::text
distributed_attimestamptz
received_attimestamptz
created_attimestamptznow()

promoters

PK: id · filas (staging): 0

Nota: Crecimiento: promotores de zona. Schema listo; sin flujo activo.

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
nametextNO
phonetextNO
referral_codetextNO
commission_per_activationnumeric(10,2)300.00
assigned_zonestext[]'{}'::text[]
is_activebooleantrue
created_attimestamptznow()

referrals

PK: id · filas (staging): 0

Nota: Crecimiento: referidos multi-tipo. Schema listo; sin flujo activo.

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
referrer_typetextNO
referrer_iduuidNO
referred_typetextNO
referred_iduuid
referral_codetextNO
statustext'pending'::text
created_attimestamptznow()

mantel_designs

PK: id · filas (staging): 0

Nota: Editor de manteles (Fogón). Activo en panel Fogón.

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
nombretextNO
configjsonbNO'{}'::jsonb
preview_urltext
estadotextNO'borrador'::text
created_attimestamptzNOnow()

mantel_stock

PK: id · filas (staging): 0

Nota: Inventario de manteles físicos. Fase 1 manteles.

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
stock_actualintegerNO0
stock_minimointegerNO200
ultimo_reabastecimientotimestamptz
updated_attimestamptzNOnow()

solicitudes_manteles

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
plantextNO'basico'::text
cantidadintegerNO50
estadotextNO'solicitado'::text
archivo_arte_urltext
brieftext
notas_clientetext
tracking_numbertext
fecha_estimada_entregadate
revision_comentariotext
revision_attimestamptz
created_attimestamptzNOnow()

Notificaciones y Zonas

push_subscriptions

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
restaurant_iduuidNOrestaurants.id (cascade)
endpointtextNO
p256dhtextNO
authtextNO
user_agenttext
activobooleanNOtrue
created_attimestamptzNOnow()

zones

PK: id · filas (staging): 0

ColumnaTipoNullDefaultReferencia (FK)
id 🔑uuidNOgen_random_uuid()
nametextNO
slugtextNO
polygongeography
statustext'planned'::text
min_restaurantsinteger5
created_attimestamptznow()

CHECK constraints (enumeraciones y validaciones)

El schema no usa tipos ENUM nativos; los dominios cerrados se modelan con CHECK (col = ANY(ARRAY[...])). Valores permitidos reales:

TablaColumna(s)Valores / regla
active_sessionsuser_typerestaurant, advertiser, comensal, admin
admin_usersrolesuper_admin, admin_ops, admin_commercial, admin_content, support, promoter, viewer
advertisersstatusactive, inactive
app_versionsplatformios, android, web
campaignspackagebasic, zone, premium
campaignsstatusdraft, active, paused, completed
comboscombo_typecomida_corrida, desayuno, cena, promo
cuponesCHECK (((tipo <> 'porcentaje') OR (valor <= (100))))
cuponestipoporcentaje, monto_fijo
cuponesCHECK ((valor > (0)))
expensesCHECK ((amount > (0)))
expensescategorymercado, gas, limpieza, servicios, nomina, otro
fiscal_invoicesestadopendiente, pagada, vencida, cancelada
fogon_usersroladmin, superadmin, soporte, finanzas, TI
import_logsstatusprocessing, completed, failed
inventory_movementsmovement_typepurchase, consumption, adjustment, waste
invoicesstatuspending, paid, failed
loyalty_historyactionstamp, redeem
menu_86_logaction86, restore
menu_item_modifier_groupsseleccionunico, multiple
menu_item_modifier_groupstipoopcional, obligatorio
napkin_distributionsstatuspending, received, in_use, exhausted
napkinsstatusdraft, pending_print, printed, distributed
operatorsCHECK (((nivel >= 1) AND (nivel <= 5)))
operatorsroleduena, mesero, cocina, caja, repartidor
order_itemskds_statuspending, preparing, ready, served, cancelled
orderscc_statuspending, accepted, preparing, ready, picked_up, no_show, cancelled
ordersorder_typedine_in, takeout, click_collect, delivery
orderspayment_methodcash, card, transfer, mixed, online
ordersstatusopen, preparing, ready, served, paid, cancelled
referralsreferred_typerestaurant, advertiser, comensal
referralsreferrer_typepromoter, restaurant, advertiser, comensal
referralsstatuspending, registered, activated, paid
restaurant_servicesres_confirmation_modeauto, manual
restaurantscontract_statuspending, active, suspended, terminated
restaurantsplanfree, basic, pro
restaurantsservice_modela_la_carta, comida_corrida, buffet, mixed
restaurantstierbronze, silver, gold
tablesstatusfree, occupied, served, check_requested, dirty, reserved
waitlistestadoesperando, asignado, expirado, no_show, cancelada
zonesstatusplanned, prospecting, active, paused

UNIQUE constraints e índices parciales relevantes

TablaConstraint / índiceDefinición
admin_usersadmin_users_auth_user_id_keyUNIQUE (auth_user_id)
advertisersadvertisers_email_keyUNIQUE (email)
advertisersadvertisers_auth_user_id_keyUNIQUE (auth_user_id)
cuponescupones_restaurant_id_codigo_keyUNIQUE (restaurant_id, codigo)
daily_menusdaily_menus_restaurant_id_date_keyUNIQUE (restaurant_id, date)
fiscal_closesfiscal_closes_restaurant_id_folio_number_keyUNIQUE (restaurant_id, folio_number)
fiscal_invoicesfiscal_invoices_folio_keyUNIQUE (folio)
loyalty_customersloyalty_customers_restaurant_id_phone_keyUNIQUE (restaurant_id, phone)
mantel_stockmantel_stock_restaurant_id_keyUNIQUE (restaurant_id)
menu_item_modifier_group_linksmenu_item_modifier_group_links_menu_item_id_group_id_keyUNIQUE (menu_item_id, group_id)
onboarding_progressonboarding_progress_restaurant_id_keyUNIQUE (restaurant_id)
promoterspromoters_referral_code_keyUNIQUE (referral_code)
push_subscriptionspush_subscriptions_restaurant_id_endpoint_keyUNIQUE (restaurant_id, endpoint)
restaurant_blocked_datesrestaurant_blocked_dates_restaurant_id_blocked_date_keyUNIQUE (restaurant_id, blocked_date)
restaurant_servicesrestaurant_services_restaurant_id_keyUNIQUE (restaurant_id)
restaurantsrestaurants_slug_keyUNIQUE (slug)
restaurantsrestaurants_referral_code_keyUNIQUE (referral_code)
restaurantsrestaurants_auth_user_id_keyUNIQUE (auth_user_id)
zoneszones_slug_keyUNIQUE (slug)
cash_sessionscash_sessions_one_open_per_restaurant (UNIQUE parcial)... USING btree (restaurant_id) WHERE (closed_at IS NULL)
operatorsidx_operators_email (UNIQUE parcial)... USING btree (restaurant_id, lower(email)) WHERE (email IS NOT NULL)
ordersidx_orders_table (parcial)... USING btree (table_id) WHERE (status = 'open'::text)
restaurantsidx_restaurants_zone (parcial)... USING btree (zone_id) WHERE (is_active = true)

cash_sessions_one_open_per_restaurant (UNIQUE parcial WHERE closed_at IS NULL) es el candado de A0-A2 T5: garantiza una sola caja abierta por fonda. Dos POST /api/cash/open concurrentes → el segundo recibe 23505 traducido a 400 Ya hay una caja abierta.


Notas de estado: DORMANT, diferidas y deuda técnica

  • active_sessions — DORMANT. 0 filas; la auditoría vía restaurant_sessions fue retirada (arregla issue #101). Se conserva como superficie futura de gestión de sesiones/dispositivos.
  • Sin tabla reservations — el router reception.ts expone /reservas, pero las reservas no tienen tabla propia en este schema (superficie de reservas parcialmente diferida). waitlist sí es real.
  • Sin tablas de comensal/Aprovecho en public — los usuarios Aprovecho viven en Supabase Auth / otra capa; loyalty_customers.aprovecho_user_id es el puente. qr_scans.user_id es nullable por lo mismo.
  • CFDI (fiscal_invoices/invoice_items) e inventario (recipes/recipe_ingredients) — schema presente; los flujos son best-effort o diferidos (F1 CFDI sin demanda real; inventario no bloquea cobros).
  • Ads Fase 2 (napkins, napkin_distributions, promoters, referrals) — schema listo, flujo diferido. La superficie E activa hoy es QR/paquete (campaigns + qr_scans).
  • restaurants es amplia (72 columnas) — concentra config de fonda, fiscal, delivery (rappi/uber HMAC), 2FA (totp_*), fidelidad y auth (auth_user_id → Supabase). Deuda conocida: la entidad restaurants estuvo triplicada históricamente (PG UUID + Supabase UUID + migraciones SERIAL); este schema es el UUID canónico.
  • operators.role (duena|mesero|cocina|caja|repartidor) alimenta requireRole. operatorId sufre un type-lie conocido (middleware lo tipa number, en runtime es el uuid de operators.id); cash.ts lo coacciona a string localmente.

Generado automáticamente desde information_schema/pg_catalog de la DB desplegada. Para regenerar: extraer los TSV de schema y correr el generador. 60 tablas verificadas.