Some checks are pending
CI/CD Pipeline / Backend CI (push) Waiting to run
CI/CD Pipeline / Frontend CI (push) Waiting to run
CI/CD Pipeline / WhatsApp Service CI (push) Waiting to run
CI/CD Pipeline / Mobile CI (push) Waiting to run
CI/CD Pipeline / Docker Build (./apps/backend, ./apps/backend/Dockerfile, backend) (push) Blocked by required conditions
CI/CD Pipeline / Docker Build (./apps/frontend, ./apps/frontend/Dockerfile, frontend) (push) Blocked by required conditions
CI/CD Pipeline / Docker Build (./apps/whatsapp-service, ./apps/whatsapp-service/Dockerfile, whatsapp-service) (push) Blocked by required conditions
CI/CD Pipeline / Deploy to Production (push) Blocked by required conditions
- Move 7 non-standard folders to _archive/ - Archive 3 extra root files - Update _MAP.md with standardized structure Standard: SIMCO-ESTANDAR-ORCHESTRATION v1.0.0 Level: CONSUMER (L2) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1278 lines
33 KiB
Markdown
1278 lines
33 KiB
Markdown
# MiChangarrito - Arquitectura de Base de Datos
|
|
|
|
## Resumen
|
|
|
|
- **Motor:** PostgreSQL 15
|
|
- **Puerto desarrollo:** 5432 (instancia compartida del workspace)
|
|
- **Base de datos:** michangarrito_dev
|
|
- **Usuario:** michangarrito_dev
|
|
- **Arquitectura:** Multi-tenant con Row Level Security (RLS)
|
|
|
|
---
|
|
|
|
## Schemas
|
|
|
|
| Schema | Proposito | Tablas Principales |
|
|
|--------|-----------|-------------------|
|
|
| `public` | Tenants y configuracion global | tenants, tenant_configs |
|
|
| `auth` | Autenticacion y usuarios | users, sessions, otp_codes |
|
|
| `catalog` | Productos y categorias | products, categories, product_templates |
|
|
| `sales` | Ventas y pagos | sales, sale_items, payments, daily_closures |
|
|
| `inventory` | Stock y movimientos | inventory, inventory_movements, stock_alerts |
|
|
| `customers` | Clientes y fiados | customers, fiados, fiado_payments |
|
|
| `orders` | Pedidos de clientes | orders, order_items, deliveries |
|
|
| `subscriptions` | Planes y tokens IA | plans, subscriptions, token_packages, token_usage |
|
|
| `messaging` | WhatsApp y notificaciones | conversations, messages, notifications |
|
|
|
|
---
|
|
|
|
## Schema: public
|
|
|
|
### tenants
|
|
Tabla raiz multi-tenant.
|
|
|
|
```sql
|
|
CREATE TABLE public.tenants (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Identificacion
|
|
name VARCHAR(100) NOT NULL,
|
|
slug VARCHAR(50) UNIQUE NOT NULL,
|
|
business_type VARCHAR(50) NOT NULL, -- abarrotes, comida, fonda, etc.
|
|
|
|
-- Contacto
|
|
phone VARCHAR(20) NOT NULL,
|
|
email VARCHAR(100),
|
|
address TEXT,
|
|
city VARCHAR(50),
|
|
state VARCHAR(50),
|
|
zip_code VARCHAR(10),
|
|
|
|
-- Configuracion
|
|
timezone VARCHAR(50) DEFAULT 'America/Mexico_City',
|
|
currency VARCHAR(3) DEFAULT 'MXN',
|
|
tax_rate DECIMAL(5,2) DEFAULT 16.00,
|
|
tax_included BOOLEAN DEFAULT true,
|
|
|
|
-- WhatsApp
|
|
whatsapp_number VARCHAR(20),
|
|
whatsapp_verified BOOLEAN DEFAULT false,
|
|
uses_platform_number BOOLEAN DEFAULT true,
|
|
|
|
-- Suscripcion (referencia)
|
|
current_plan_id UUID,
|
|
subscription_status VARCHAR(20) DEFAULT 'trial', -- trial, active, past_due, cancelled
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'active',
|
|
onboarding_completed BOOLEAN DEFAULT false,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_tenants_slug ON public.tenants(slug);
|
|
CREATE INDEX idx_tenants_phone ON public.tenants(phone);
|
|
CREATE INDEX idx_tenants_status ON public.tenants(status);
|
|
```
|
|
|
|
### tenant_configs
|
|
Configuraciones adicionales por tenant.
|
|
|
|
```sql
|
|
CREATE TABLE public.tenant_configs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Horarios
|
|
opening_hour TIME DEFAULT '08:00',
|
|
closing_hour TIME DEFAULT '22:00',
|
|
working_days INTEGER[] DEFAULT ARRAY[1,2,3,4,5,6], -- 0=domingo
|
|
|
|
-- Tickets
|
|
ticket_header TEXT,
|
|
ticket_footer TEXT DEFAULT 'Gracias por su compra',
|
|
print_logo BOOLEAN DEFAULT false,
|
|
|
|
-- Notificaciones
|
|
daily_summary_enabled BOOLEAN DEFAULT true,
|
|
daily_summary_time TIME DEFAULT '21:00',
|
|
low_stock_alerts BOOLEAN DEFAULT true,
|
|
|
|
-- Fiados
|
|
fiados_enabled BOOLEAN DEFAULT true,
|
|
default_fiado_limit DECIMAL(10,2) DEFAULT 500.00,
|
|
fiado_reminder_days INTEGER DEFAULT 7,
|
|
|
|
-- Pedidos
|
|
delivery_enabled BOOLEAN DEFAULT false,
|
|
delivery_fee DECIMAL(10,2) DEFAULT 0.00,
|
|
delivery_radius_km DECIMAL(5,2),
|
|
|
|
-- Metodos de pago habilitados
|
|
payment_cash BOOLEAN DEFAULT true,
|
|
payment_card_mercadopago BOOLEAN DEFAULT false,
|
|
payment_card_clip BOOLEAN DEFAULT false,
|
|
payment_codi BOOLEAN DEFAULT false,
|
|
payment_transfer BOOLEAN DEFAULT false,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
UNIQUE(tenant_id)
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Schema: auth
|
|
|
|
### users
|
|
Usuarios del sistema (duenos y empleados).
|
|
|
|
```sql
|
|
CREATE TABLE auth.users (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Identificacion
|
|
phone VARCHAR(20) NOT NULL,
|
|
email VARCHAR(100),
|
|
name VARCHAR(100) NOT NULL,
|
|
|
|
-- Autenticacion
|
|
pin_hash VARCHAR(255), -- PIN de 4 digitos hasheado
|
|
biometric_enabled BOOLEAN DEFAULT false,
|
|
biometric_key TEXT,
|
|
|
|
-- Rol
|
|
role VARCHAR(20) NOT NULL DEFAULT 'owner', -- owner, employee, viewer
|
|
permissions JSONB DEFAULT '{}',
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'active',
|
|
last_login_at TIMESTAMPTZ,
|
|
failed_attempts INTEGER DEFAULT 0,
|
|
locked_until TIMESTAMPTZ,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
UNIQUE(tenant_id, phone)
|
|
);
|
|
|
|
CREATE INDEX idx_users_tenant ON auth.users(tenant_id);
|
|
CREATE INDEX idx_users_phone ON auth.users(phone);
|
|
```
|
|
|
|
### sessions
|
|
Sesiones activas.
|
|
|
|
```sql
|
|
CREATE TABLE auth.sessions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
|
|
-- Token
|
|
token_hash VARCHAR(255) NOT NULL,
|
|
refresh_token_hash VARCHAR(255),
|
|
|
|
-- Metadata
|
|
device_type VARCHAR(20), -- mobile, web
|
|
device_info JSONB,
|
|
ip_address VARCHAR(45),
|
|
|
|
-- Expiracion
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
refresh_expires_at TIMESTAMPTZ,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
last_activity_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_sessions_user ON auth.sessions(user_id);
|
|
CREATE INDEX idx_sessions_token ON auth.sessions(token_hash);
|
|
```
|
|
|
|
### otp_codes
|
|
Codigos OTP para verificacion.
|
|
|
|
```sql
|
|
CREATE TABLE auth.otp_codes (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
phone VARCHAR(20) NOT NULL,
|
|
|
|
code VARCHAR(6) NOT NULL,
|
|
purpose VARCHAR(20) NOT NULL, -- login, verify_phone, reset_pin
|
|
|
|
attempts INTEGER DEFAULT 0,
|
|
max_attempts INTEGER DEFAULT 3,
|
|
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
used_at TIMESTAMPTZ,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_otp_phone ON auth.otp_codes(phone, purpose);
|
|
```
|
|
|
|
---
|
|
|
|
## Schema: catalog
|
|
|
|
### categories
|
|
Categorias de productos.
|
|
|
|
```sql
|
|
CREATE TABLE catalog.categories (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
|
|
name VARCHAR(50) NOT NULL,
|
|
description TEXT,
|
|
icon VARCHAR(50),
|
|
color VARCHAR(7), -- hex color
|
|
sort_order INTEGER DEFAULT 0,
|
|
|
|
status VARCHAR(20) DEFAULT 'active',
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
UNIQUE(tenant_id, name)
|
|
);
|
|
|
|
-- RLS
|
|
ALTER TABLE catalog.categories ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY tenant_isolation ON catalog.categories
|
|
USING (tenant_id = current_setting('app.current_tenant')::UUID);
|
|
```
|
|
|
|
### products
|
|
Catalogo de productos.
|
|
|
|
```sql
|
|
CREATE TABLE catalog.products (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
category_id UUID REFERENCES catalog.categories(id) ON DELETE SET NULL,
|
|
|
|
-- Identificacion
|
|
name VARCHAR(100) NOT NULL,
|
|
description TEXT,
|
|
sku VARCHAR(50),
|
|
barcode VARCHAR(50),
|
|
|
|
-- Precios
|
|
price DECIMAL(10,2) NOT NULL,
|
|
cost_price DECIMAL(10,2), -- precio de compra
|
|
compare_price DECIMAL(10,2), -- precio anterior/tachado
|
|
|
|
-- Inventario
|
|
track_inventory BOOLEAN DEFAULT true,
|
|
stock_quantity INTEGER DEFAULT 0,
|
|
low_stock_threshold INTEGER DEFAULT 5,
|
|
|
|
-- Presentacion
|
|
unit VARCHAR(20) DEFAULT 'pieza', -- pieza, kg, litro, etc.
|
|
|
|
-- Multimedia
|
|
image_url TEXT,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'active',
|
|
is_featured BOOLEAN DEFAULT false,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_products_tenant ON catalog.products(tenant_id);
|
|
CREATE INDEX idx_products_category ON catalog.products(category_id);
|
|
CREATE INDEX idx_products_barcode ON catalog.products(tenant_id, barcode);
|
|
CREATE INDEX idx_products_name ON catalog.products USING gin(to_tsvector('spanish', name));
|
|
|
|
-- RLS
|
|
ALTER TABLE catalog.products ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY tenant_isolation ON catalog.products
|
|
USING (tenant_id = current_setting('app.current_tenant')::UUID);
|
|
```
|
|
|
|
### product_templates
|
|
Templates de productos por proveedor (Bimbo, Coca-Cola, etc.).
|
|
|
|
```sql
|
|
CREATE TABLE catalog.product_templates (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Proveedor
|
|
provider_name VARCHAR(50) NOT NULL, -- bimbo, cocacola, sabritas, etc.
|
|
provider_logo_url TEXT,
|
|
|
|
-- Producto
|
|
name VARCHAR(100) NOT NULL,
|
|
description TEXT,
|
|
barcode VARCHAR(50),
|
|
suggested_price DECIMAL(10,2),
|
|
category_suggestion VARCHAR(50),
|
|
|
|
-- Presentaciones
|
|
unit VARCHAR(20) DEFAULT 'pieza',
|
|
|
|
-- Multimedia
|
|
image_url TEXT,
|
|
|
|
-- Metadata
|
|
business_types TEXT[], -- ['abarrotes', 'tienda']
|
|
popularity INTEGER DEFAULT 0,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_templates_provider ON catalog.product_templates(provider_name);
|
|
CREATE INDEX idx_templates_barcode ON catalog.product_templates(barcode);
|
|
```
|
|
|
|
---
|
|
|
|
## Schema: sales
|
|
|
|
### sales
|
|
Registro de ventas.
|
|
|
|
```sql
|
|
CREATE TABLE sales.sales (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Numeracion
|
|
ticket_number VARCHAR(20) NOT NULL,
|
|
|
|
-- Montos
|
|
subtotal DECIMAL(10,2) NOT NULL,
|
|
discount_amount DECIMAL(10,2) DEFAULT 0,
|
|
discount_percent DECIMAL(5,2) DEFAULT 0,
|
|
tax_amount DECIMAL(10,2) DEFAULT 0,
|
|
total DECIMAL(10,2) NOT NULL,
|
|
|
|
-- Pago
|
|
payment_method VARCHAR(20) NOT NULL, -- cash, card_mercadopago, card_clip, codi, transfer, fiado
|
|
payment_status VARCHAR(20) DEFAULT 'completed', -- pending, completed, refunded
|
|
payment_reference TEXT, -- referencia externa del pago
|
|
|
|
-- Efectivo
|
|
cash_received DECIMAL(10,2),
|
|
change_amount DECIMAL(10,2),
|
|
|
|
-- Cliente (opcional)
|
|
customer_id UUID REFERENCES customers.customers(id),
|
|
|
|
-- Fiado (si aplica)
|
|
is_fiado BOOLEAN DEFAULT false,
|
|
fiado_id UUID,
|
|
|
|
-- Usuario que registro
|
|
created_by UUID REFERENCES auth.users(id),
|
|
|
|
-- Notas
|
|
notes TEXT,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'completed', -- completed, cancelled, refunded
|
|
cancelled_at TIMESTAMPTZ,
|
|
cancelled_reason TEXT,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_sales_tenant ON sales.sales(tenant_id);
|
|
CREATE INDEX idx_sales_ticket ON sales.sales(tenant_id, ticket_number);
|
|
CREATE INDEX idx_sales_date ON sales.sales(tenant_id, created_at);
|
|
CREATE INDEX idx_sales_customer ON sales.sales(customer_id);
|
|
|
|
-- RLS
|
|
ALTER TABLE sales.sales ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY tenant_isolation ON sales.sales
|
|
USING (tenant_id = current_setting('app.current_tenant')::UUID);
|
|
```
|
|
|
|
### sale_items
|
|
Detalle de productos vendidos.
|
|
|
|
```sql
|
|
CREATE TABLE sales.sale_items (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
sale_id UUID NOT NULL REFERENCES sales.sales(id) ON DELETE CASCADE,
|
|
product_id UUID REFERENCES catalog.products(id),
|
|
|
|
-- Producto (snapshot)
|
|
product_name VARCHAR(100) NOT NULL,
|
|
product_sku VARCHAR(50),
|
|
|
|
-- Cantidades
|
|
quantity DECIMAL(10,3) NOT NULL,
|
|
unit_price DECIMAL(10,2) NOT NULL,
|
|
|
|
-- Descuento por item
|
|
discount_amount DECIMAL(10,2) DEFAULT 0,
|
|
|
|
-- Total
|
|
subtotal DECIMAL(10,2) NOT NULL,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_sale_items_sale ON sales.sale_items(sale_id);
|
|
CREATE INDEX idx_sale_items_product ON sales.sale_items(product_id);
|
|
```
|
|
|
|
### payments
|
|
Registro de pagos (para pagos parciales o multiples metodos).
|
|
|
|
```sql
|
|
CREATE TABLE sales.payments (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
sale_id UUID REFERENCES sales.sales(id),
|
|
fiado_id UUID, -- Si es pago de fiado
|
|
subscription_id UUID, -- Si es pago de suscripcion
|
|
|
|
-- Metodo
|
|
method VARCHAR(20) NOT NULL,
|
|
provider VARCHAR(20), -- mercadopago, clip, stripe, oxxo
|
|
|
|
-- Montos
|
|
amount DECIMAL(10,2) NOT NULL,
|
|
fee_amount DECIMAL(10,2) DEFAULT 0, -- comision del proveedor
|
|
net_amount DECIMAL(10,2), -- monto neto
|
|
|
|
-- Referencias
|
|
external_id TEXT, -- ID del proveedor
|
|
external_status VARCHAR(20),
|
|
receipt_url TEXT,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'pending', -- pending, completed, failed, refunded
|
|
|
|
-- Metadata
|
|
metadata JSONB,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_payments_tenant ON sales.payments(tenant_id);
|
|
CREATE INDEX idx_payments_sale ON sales.payments(sale_id);
|
|
CREATE INDEX idx_payments_external ON sales.payments(external_id);
|
|
```
|
|
|
|
### daily_closures
|
|
Cortes de caja.
|
|
|
|
```sql
|
|
CREATE TABLE sales.daily_closures (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Periodo
|
|
closure_date DATE NOT NULL,
|
|
opened_at TIMESTAMPTZ,
|
|
closed_at TIMESTAMPTZ,
|
|
|
|
-- Montos esperados (calculados)
|
|
expected_cash DECIMAL(10,2) DEFAULT 0,
|
|
expected_card DECIMAL(10,2) DEFAULT 0,
|
|
expected_other DECIMAL(10,2) DEFAULT 0,
|
|
expected_total DECIMAL(10,2) DEFAULT 0,
|
|
|
|
-- Montos reales (ingresados)
|
|
actual_cash DECIMAL(10,2),
|
|
actual_card DECIMAL(10,2),
|
|
actual_other DECIMAL(10,2),
|
|
actual_total DECIMAL(10,2),
|
|
|
|
-- Diferencia
|
|
cash_difference DECIMAL(10,2),
|
|
|
|
-- Resumen
|
|
total_sales INTEGER DEFAULT 0,
|
|
total_cancelled INTEGER DEFAULT 0,
|
|
total_fiados DECIMAL(10,2) DEFAULT 0,
|
|
|
|
-- Usuario
|
|
closed_by UUID REFERENCES auth.users(id),
|
|
notes TEXT,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'open', -- open, closed
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
UNIQUE(tenant_id, closure_date)
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Schema: inventory
|
|
|
|
### inventory_movements
|
|
Movimientos de inventario.
|
|
|
|
```sql
|
|
CREATE TABLE inventory.inventory_movements (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
product_id UUID NOT NULL REFERENCES catalog.products(id) ON DELETE CASCADE,
|
|
|
|
-- Tipo
|
|
movement_type VARCHAR(20) NOT NULL, -- purchase, sale, adjustment, loss, return
|
|
|
|
-- Cantidades
|
|
quantity DECIMAL(10,3) NOT NULL, -- positivo o negativo
|
|
previous_stock DECIMAL(10,3) NOT NULL,
|
|
new_stock DECIMAL(10,3) NOT NULL,
|
|
|
|
-- Costo (para compras)
|
|
unit_cost DECIMAL(10,2),
|
|
total_cost DECIMAL(10,2),
|
|
|
|
-- Referencia
|
|
reference_type VARCHAR(20), -- sale, purchase_order, manual
|
|
reference_id UUID,
|
|
|
|
-- Notas
|
|
notes TEXT,
|
|
|
|
-- Usuario
|
|
created_by UUID REFERENCES auth.users(id),
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_inventory_tenant ON inventory.inventory_movements(tenant_id);
|
|
CREATE INDEX idx_inventory_product ON inventory.inventory_movements(product_id);
|
|
CREATE INDEX idx_inventory_date ON inventory.inventory_movements(created_at);
|
|
```
|
|
|
|
### stock_alerts
|
|
Alertas de stock bajo.
|
|
|
|
```sql
|
|
CREATE TABLE inventory.stock_alerts (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
product_id UUID NOT NULL REFERENCES catalog.products(id) ON DELETE CASCADE,
|
|
|
|
-- Niveles
|
|
current_stock INTEGER NOT NULL,
|
|
threshold INTEGER NOT NULL,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'active', -- active, resolved, ignored
|
|
|
|
-- Notificacion
|
|
notified_at TIMESTAMPTZ,
|
|
resolved_at TIMESTAMPTZ,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Schema: customers
|
|
|
|
### customers
|
|
Clientes del negocio.
|
|
|
|
```sql
|
|
CREATE TABLE customers.customers (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Identificacion
|
|
name VARCHAR(100) NOT NULL,
|
|
phone VARCHAR(20),
|
|
email VARCHAR(100),
|
|
|
|
-- Direccion (para entregas)
|
|
address TEXT,
|
|
address_reference TEXT,
|
|
latitude DECIMAL(10,8),
|
|
longitude DECIMAL(11,8),
|
|
|
|
-- Fiados
|
|
fiado_enabled BOOLEAN DEFAULT true,
|
|
fiado_limit DECIMAL(10,2),
|
|
current_fiado_balance DECIMAL(10,2) DEFAULT 0,
|
|
|
|
-- Estadisticas
|
|
total_purchases DECIMAL(12,2) DEFAULT 0,
|
|
purchase_count INTEGER DEFAULT 0,
|
|
last_purchase_at TIMESTAMPTZ,
|
|
|
|
-- WhatsApp
|
|
whatsapp_opt_in BOOLEAN DEFAULT false,
|
|
|
|
-- Notas
|
|
notes TEXT,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'active',
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_customers_tenant ON customers.customers(tenant_id);
|
|
CREATE INDEX idx_customers_phone ON customers.customers(tenant_id, phone);
|
|
CREATE INDEX idx_customers_name ON customers.customers USING gin(to_tsvector('spanish', name));
|
|
|
|
-- RLS
|
|
ALTER TABLE customers.customers ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY tenant_isolation ON customers.customers
|
|
USING (tenant_id = current_setting('app.current_tenant')::UUID);
|
|
```
|
|
|
|
### fiados
|
|
Registro de fiados (creditos a clientes).
|
|
|
|
```sql
|
|
CREATE TABLE customers.fiados (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
customer_id UUID NOT NULL REFERENCES customers.customers(id) ON DELETE CASCADE,
|
|
sale_id UUID REFERENCES sales.sales(id),
|
|
|
|
-- Monto
|
|
original_amount DECIMAL(10,2) NOT NULL,
|
|
paid_amount DECIMAL(10,2) DEFAULT 0,
|
|
remaining_amount DECIMAL(10,2) NOT NULL,
|
|
|
|
-- Fechas
|
|
due_date DATE,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'pending', -- pending, partial, paid, overdue, cancelled
|
|
|
|
-- Notas
|
|
description TEXT,
|
|
|
|
-- Recordatorios
|
|
last_reminder_at TIMESTAMPTZ,
|
|
reminder_count INTEGER DEFAULT 0,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_fiados_tenant ON customers.fiados(tenant_id);
|
|
CREATE INDEX idx_fiados_customer ON customers.fiados(customer_id);
|
|
CREATE INDEX idx_fiados_status ON customers.fiados(status);
|
|
```
|
|
|
|
### fiado_payments
|
|
Pagos de fiados.
|
|
|
|
```sql
|
|
CREATE TABLE customers.fiado_payments (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
fiado_id UUID NOT NULL REFERENCES customers.fiados(id) ON DELETE CASCADE,
|
|
|
|
amount DECIMAL(10,2) NOT NULL,
|
|
payment_method VARCHAR(20) NOT NULL,
|
|
|
|
notes TEXT,
|
|
|
|
created_by UUID REFERENCES auth.users(id),
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Schema: orders
|
|
|
|
### orders
|
|
Pedidos de clientes (via WhatsApp u otros).
|
|
|
|
```sql
|
|
CREATE TABLE orders.orders (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
customer_id UUID REFERENCES customers.customers(id),
|
|
|
|
-- Numeracion
|
|
order_number VARCHAR(20) NOT NULL,
|
|
|
|
-- Canal
|
|
channel VARCHAR(20) NOT NULL, -- whatsapp, app, web
|
|
|
|
-- Montos
|
|
subtotal DECIMAL(10,2) NOT NULL,
|
|
delivery_fee DECIMAL(10,2) DEFAULT 0,
|
|
discount_amount DECIMAL(10,2) DEFAULT 0,
|
|
total DECIMAL(10,2) NOT NULL,
|
|
|
|
-- Tipo
|
|
order_type VARCHAR(20) NOT NULL, -- pickup, delivery
|
|
|
|
-- Entrega
|
|
delivery_address TEXT,
|
|
delivery_notes TEXT,
|
|
estimated_delivery_at TIMESTAMPTZ,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'pending',
|
|
-- pending, confirmed, preparing, ready, delivering, completed, cancelled
|
|
|
|
-- Pago
|
|
payment_status VARCHAR(20) DEFAULT 'pending', -- pending, paid, refunded
|
|
payment_method VARCHAR(20),
|
|
|
|
-- Timestamps
|
|
confirmed_at TIMESTAMPTZ,
|
|
preparing_at TIMESTAMPTZ,
|
|
ready_at TIMESTAMPTZ,
|
|
completed_at TIMESTAMPTZ,
|
|
cancelled_at TIMESTAMPTZ,
|
|
cancelled_reason TEXT,
|
|
|
|
-- Notas
|
|
customer_notes TEXT,
|
|
internal_notes TEXT,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_orders_tenant ON orders.orders(tenant_id);
|
|
CREATE INDEX idx_orders_customer ON orders.orders(customer_id);
|
|
CREATE INDEX idx_orders_status ON orders.orders(status);
|
|
CREATE INDEX idx_orders_date ON orders.orders(created_at);
|
|
```
|
|
|
|
### order_items
|
|
Items del pedido.
|
|
|
|
```sql
|
|
CREATE TABLE orders.order_items (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
order_id UUID NOT NULL REFERENCES orders.orders(id) ON DELETE CASCADE,
|
|
product_id UUID REFERENCES catalog.products(id),
|
|
|
|
-- Producto (snapshot)
|
|
product_name VARCHAR(100) NOT NULL,
|
|
|
|
-- Cantidades
|
|
quantity DECIMAL(10,3) NOT NULL,
|
|
unit_price DECIMAL(10,2) NOT NULL,
|
|
subtotal DECIMAL(10,2) NOT NULL,
|
|
|
|
-- Notas especiales
|
|
notes TEXT,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Schema: subscriptions
|
|
|
|
### plans
|
|
Planes de suscripcion.
|
|
|
|
```sql
|
|
CREATE TABLE subscriptions.plans (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Identificacion
|
|
name VARCHAR(50) NOT NULL,
|
|
code VARCHAR(20) UNIQUE NOT NULL, -- changarrito, tiendita
|
|
description TEXT,
|
|
|
|
-- Precio
|
|
price_monthly DECIMAL(10,2) NOT NULL,
|
|
price_yearly DECIMAL(10,2),
|
|
currency VARCHAR(3) DEFAULT 'MXN',
|
|
|
|
-- Incluido
|
|
included_tokens INTEGER NOT NULL, -- tokens IA incluidos
|
|
features JSONB, -- lista de features
|
|
|
|
-- Limites
|
|
max_products INTEGER,
|
|
max_users INTEGER DEFAULT 1,
|
|
whatsapp_own_number BOOLEAN DEFAULT false,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'active',
|
|
|
|
-- Stripe
|
|
stripe_price_id_monthly VARCHAR(100),
|
|
stripe_price_id_yearly VARCHAR(100),
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Insertar planes iniciales
|
|
INSERT INTO subscriptions.plans (name, code, price_monthly, included_tokens, max_products, features) VALUES
|
|
('Changarrito', 'changarrito', 99.00, 500, 100, '{"pos": true, "inventory": true, "reports_basic": true}'),
|
|
('Tiendita', 'tiendita', 199.00, 2000, null, '{"pos": true, "inventory": true, "reports_advanced": true, "whatsapp_own": true, "customers": true, "fiados": true}');
|
|
```
|
|
|
|
### subscriptions
|
|
Suscripciones activas.
|
|
|
|
```sql
|
|
CREATE TABLE subscriptions.subscriptions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
plan_id UUID NOT NULL REFERENCES subscriptions.plans(id),
|
|
|
|
-- Periodo
|
|
billing_cycle VARCHAR(10) DEFAULT 'monthly', -- monthly, yearly
|
|
current_period_start TIMESTAMPTZ NOT NULL,
|
|
current_period_end TIMESTAMPTZ NOT NULL,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'active', -- trialing, active, past_due, cancelled, paused
|
|
cancel_at_period_end BOOLEAN DEFAULT false,
|
|
cancelled_at TIMESTAMPTZ,
|
|
|
|
-- Pagos
|
|
payment_method VARCHAR(20), -- card, oxxo, iap_ios, iap_android
|
|
|
|
-- Stripe
|
|
stripe_subscription_id VARCHAR(100),
|
|
stripe_customer_id VARCHAR(100),
|
|
|
|
-- Trial
|
|
trial_ends_at TIMESTAMPTZ,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_subscriptions_tenant ON subscriptions.subscriptions(tenant_id);
|
|
CREATE INDEX idx_subscriptions_stripe ON subscriptions.subscriptions(stripe_subscription_id);
|
|
```
|
|
|
|
### token_packages
|
|
Paquetes de tokens para compra.
|
|
|
|
```sql
|
|
CREATE TABLE subscriptions.token_packages (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
name VARCHAR(50) NOT NULL,
|
|
tokens INTEGER NOT NULL,
|
|
price DECIMAL(10,2) NOT NULL,
|
|
currency VARCHAR(3) DEFAULT 'MXN',
|
|
|
|
-- Bonus
|
|
bonus_tokens INTEGER DEFAULT 0,
|
|
|
|
-- Stripe
|
|
stripe_price_id VARCHAR(100),
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'active',
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Insertar paquetes
|
|
INSERT INTO subscriptions.token_packages (name, tokens, price) VALUES
|
|
('Recarga Basica', 1000, 29.00),
|
|
('Recarga Plus', 3000, 69.00),
|
|
('Recarga Pro', 8000, 149.00),
|
|
('Recarga Mega', 20000, 299.00);
|
|
```
|
|
|
|
### token_usage
|
|
Consumo de tokens.
|
|
|
|
```sql
|
|
CREATE TABLE subscriptions.token_usage (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Tokens
|
|
tokens_used INTEGER NOT NULL,
|
|
|
|
-- Contexto
|
|
action VARCHAR(50) NOT NULL, -- chat, report, ocr, transcription
|
|
description TEXT,
|
|
|
|
-- LLM info
|
|
model VARCHAR(50),
|
|
input_tokens INTEGER,
|
|
output_tokens INTEGER,
|
|
|
|
-- Referencia
|
|
reference_type VARCHAR(20),
|
|
reference_id UUID,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_token_usage_tenant ON subscriptions.token_usage(tenant_id);
|
|
CREATE INDEX idx_token_usage_date ON subscriptions.token_usage(created_at);
|
|
```
|
|
|
|
### tenant_token_balance
|
|
Balance de tokens por tenant.
|
|
|
|
```sql
|
|
CREATE TABLE subscriptions.tenant_token_balance (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Balance
|
|
available_tokens INTEGER DEFAULT 0,
|
|
used_tokens INTEGER DEFAULT 0,
|
|
|
|
-- Ultimo reset (mensual)
|
|
last_reset_at TIMESTAMPTZ,
|
|
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
UNIQUE(tenant_id)
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Schema: messaging
|
|
|
|
### conversations
|
|
Conversaciones de WhatsApp.
|
|
|
|
```sql
|
|
CREATE TABLE messaging.conversations (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID REFERENCES public.tenants(id), -- null si es plataforma
|
|
|
|
-- Participante
|
|
phone_number VARCHAR(20) NOT NULL,
|
|
contact_name VARCHAR(100),
|
|
|
|
-- Tipo
|
|
conversation_type VARCHAR(20) NOT NULL, -- owner, customer, support, onboarding
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'active', -- active, archived, blocked
|
|
|
|
-- Ultimo mensaje
|
|
last_message_at TIMESTAMPTZ,
|
|
last_message_preview TEXT,
|
|
unread_count INTEGER DEFAULT 0,
|
|
|
|
-- WhatsApp
|
|
wa_conversation_id VARCHAR(100),
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_conversations_tenant ON messaging.conversations(tenant_id);
|
|
CREATE INDEX idx_conversations_phone ON messaging.conversations(phone_number);
|
|
```
|
|
|
|
### messages
|
|
Mensajes individuales.
|
|
|
|
```sql
|
|
CREATE TABLE messaging.messages (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
conversation_id UUID NOT NULL REFERENCES messaging.conversations(id) ON DELETE CASCADE,
|
|
|
|
-- Direccion
|
|
direction VARCHAR(10) NOT NULL, -- inbound, outbound
|
|
|
|
-- Contenido
|
|
message_type VARCHAR(20) NOT NULL, -- text, image, audio, video, document, location
|
|
content TEXT,
|
|
media_url TEXT,
|
|
media_mime_type VARCHAR(50),
|
|
|
|
-- LLM (si fue procesado)
|
|
processed_by_llm BOOLEAN DEFAULT false,
|
|
llm_response_id UUID,
|
|
tokens_used INTEGER,
|
|
|
|
-- WhatsApp
|
|
wa_message_id VARCHAR(100),
|
|
wa_status VARCHAR(20), -- sent, delivered, read, failed
|
|
wa_timestamp TIMESTAMPTZ,
|
|
|
|
-- Error
|
|
error_code VARCHAR(20),
|
|
error_message TEXT,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_messages_conversation ON messaging.messages(conversation_id);
|
|
CREATE INDEX idx_messages_wa ON messaging.messages(wa_message_id);
|
|
```
|
|
|
|
### notifications
|
|
Notificaciones push y WhatsApp.
|
|
|
|
```sql
|
|
CREATE TABLE messaging.notifications (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
user_id UUID REFERENCES auth.users(id),
|
|
|
|
-- Tipo
|
|
notification_type VARCHAR(50) NOT NULL, -- low_stock, new_order, fiado_reminder, daily_summary
|
|
|
|
-- Canales
|
|
channels TEXT[] NOT NULL, -- ['push', 'whatsapp']
|
|
|
|
-- Contenido
|
|
title VARCHAR(100) NOT NULL,
|
|
body TEXT NOT NULL,
|
|
data JSONB,
|
|
|
|
-- Estado por canal
|
|
push_sent BOOLEAN DEFAULT false,
|
|
push_sent_at TIMESTAMPTZ,
|
|
whatsapp_sent BOOLEAN DEFAULT false,
|
|
whatsapp_sent_at TIMESTAMPTZ,
|
|
|
|
-- Lectura
|
|
read_at TIMESTAMPTZ,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_notifications_tenant ON messaging.notifications(tenant_id);
|
|
CREATE INDEX idx_notifications_user ON messaging.notifications(user_id);
|
|
```
|
|
|
|
---
|
|
|
|
## Funciones Utiles
|
|
|
|
### Generador de numeros de ticket
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION sales.generate_ticket_number(p_tenant_id UUID)
|
|
RETURNS VARCHAR(20) AS $$
|
|
DECLARE
|
|
v_date TEXT;
|
|
v_sequence INTEGER;
|
|
v_ticket VARCHAR(20);
|
|
BEGIN
|
|
v_date := TO_CHAR(CURRENT_DATE, 'YYMMDD');
|
|
|
|
SELECT COALESCE(MAX(
|
|
CAST(SUBSTRING(ticket_number FROM 8) AS INTEGER)
|
|
), 0) + 1
|
|
INTO v_sequence
|
|
FROM sales.sales
|
|
WHERE tenant_id = p_tenant_id
|
|
AND ticket_number LIKE v_date || '-%';
|
|
|
|
v_ticket := v_date || '-' || LPAD(v_sequence::TEXT, 4, '0');
|
|
|
|
RETURN v_ticket;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
### Trigger de actualizacion
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION update_updated_at()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Aplicar a todas las tablas relevantes
|
|
CREATE TRIGGER update_tenants_updated_at
|
|
BEFORE UPDATE ON public.tenants
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
|
|
-- (repetir para otras tablas)
|
|
```
|
|
|
|
### Funcion de balance de fiados
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION customers.update_customer_fiado_balance()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
UPDATE customers.customers
|
|
SET current_fiado_balance = (
|
|
SELECT COALESCE(SUM(remaining_amount), 0)
|
|
FROM customers.fiados
|
|
WHERE customer_id = COALESCE(NEW.customer_id, OLD.customer_id)
|
|
AND status IN ('pending', 'partial', 'overdue')
|
|
)
|
|
WHERE id = COALESCE(NEW.customer_id, OLD.customer_id);
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER update_fiado_balance
|
|
AFTER INSERT OR UPDATE OR DELETE ON customers.fiados
|
|
FOR EACH ROW EXECUTE FUNCTION customers.update_customer_fiado_balance();
|
|
```
|
|
|
|
---
|
|
|
|
## Indices Adicionales para Performance
|
|
|
|
```sql
|
|
-- Ventas por fecha (reportes)
|
|
CREATE INDEX idx_sales_tenant_date ON sales.sales(tenant_id, DATE(created_at));
|
|
|
|
-- Productos mas vendidos
|
|
CREATE INDEX idx_sale_items_product_count ON sales.sale_items(product_id);
|
|
|
|
-- Fiados vencidos
|
|
CREATE INDEX idx_fiados_overdue ON customers.fiados(tenant_id, due_date)
|
|
WHERE status IN ('pending', 'partial');
|
|
|
|
-- Busqueda de productos por nombre
|
|
CREATE INDEX idx_products_search ON catalog.products
|
|
USING gin(to_tsvector('spanish', name || ' ' || COALESCE(description, '')));
|
|
```
|
|
|
|
---
|
|
|
|
## Row Level Security (RLS)
|
|
|
|
Todas las tablas que manejan datos de tenant tienen RLS habilitado.
|
|
|
|
```sql
|
|
-- Configurar tenant en cada request
|
|
SET app.current_tenant = 'uuid-del-tenant';
|
|
|
|
-- Ejemplo de policy
|
|
CREATE POLICY tenant_isolation ON catalog.products
|
|
FOR ALL
|
|
USING (tenant_id = current_setting('app.current_tenant')::UUID)
|
|
WITH CHECK (tenant_id = current_setting('app.current_tenant')::UUID);
|
|
```
|
|
|
|
---
|
|
|
|
## Extensiones PostgreSQL
|
|
|
|
| Extension | Proposito |
|
|
|-----------|-----------|
|
|
| `uuid-ossp` | Generacion de UUIDs |
|
|
| `pgcrypto` | Encriptacion de datos sensibles |
|
|
| `pg_trgm` | Busqueda por similitud (fuzzy search) |
|
|
| `btree_gin` | Indices GIN para busquedas combinadas |
|
|
|
|
---
|
|
|
|
## Convenciones
|
|
|
|
### Columnas Comunes
|
|
Todas las tablas siguen estas convenciones:
|
|
|
|
| Columna | Tipo | Descripcion |
|
|
|---------|------|-------------|
|
|
| `id` | UUID PRIMARY KEY | Identificador unico |
|
|
| `tenant_id` | UUID NOT NULL | Referencia al tenant (multi-tenant) |
|
|
| `created_at` | TIMESTAMPTZ DEFAULT NOW() | Fecha de creacion |
|
|
| `updated_at` | TIMESTAMPTZ DEFAULT NOW() | Fecha de ultima modificacion |
|
|
| `is_active` | BOOLEAN DEFAULT TRUE | Estado activo/inactivo |
|
|
|
|
### Indices Estandar
|
|
- PK en `id`
|
|
- Indice en `tenant_id` para todas las tablas multi-tenant
|
|
- Indices en Foreign Keys
|
|
- Indices en columnas de busqueda frecuente
|
|
- Indices GIN para busqueda full-text
|
|
|
|
### Triggers Estandar
|
|
- `set_updated_at`: Actualiza `updated_at` automaticamente en cada UPDATE
|
|
- `update_customer_fiado_balance`: Recalcula balance de fiados del cliente
|
|
|
|
---
|
|
|
|
## Conexion
|
|
|
|
### Desarrollo
|
|
```
|
|
Host: localhost
|
|
Puerto: 5432
|
|
Database: michangarrito_dev
|
|
Usuario: michangarrito_dev
|
|
Password: (ver .env)
|
|
```
|
|
|
|
### Produccion
|
|
```
|
|
Host: (configurar en .env)
|
|
Puerto: 5432
|
|
Database: michangarrito
|
|
Usuario: michangarrito
|
|
SSL: Requerido
|
|
```
|
|
|
|
---
|
|
|
|
## Scripts de Base de Datos
|
|
|
|
| Script | Ubicacion | Descripcion |
|
|
|--------|-----------|-------------|
|
|
| `create-database.sh` | database/scripts/ | Crear BD desde cero |
|
|
| `recreate-database.sh` | database/scripts/ | Recrear BD (destructivo) |
|
|
| `drop-and-recreate-database.sh` | database/scripts/ | Eliminar y recrear BD |
|
|
|
|
### Orden de Ejecucion de Schemas
|
|
```
|
|
00-extensions.sql -> Extensiones PostgreSQL
|
|
01-schemas.sql -> Creacion de schemas
|
|
02-functions.sql -> Funciones utilitarias
|
|
03-public.sql -> Schema public (tenants)
|
|
04-auth.sql -> Schema auth
|
|
05-catalog.sql -> Schema catalog
|
|
06-sales.sql -> Schema sales
|
|
07-inventory.sql -> Schema inventory
|
|
08-customers.sql -> Schema customers
|
|
09-orders.sql -> Schema orders
|
|
10-subscriptions.sql -> Schema subscriptions
|
|
11-messaging.sql -> Schema messaging
|
|
12-integrations.sql -> Schema integrations
|
|
```
|
|
|
|
---
|
|
|
|
## Referencias
|
|
|
|
- [Environment Inventory](../../orchestration/environment/ENVIRONMENT-INVENTORY.yml)
|
|
- [Contexto del Proyecto](../../orchestration/00-guidelines/CONTEXTO-PROYECTO.md)
|
|
- [Database Inventory](../../orchestration/inventarios/DATABASE_INVENTORY.yml)
|
|
- [Especificacion de Componentes](./ESPECIFICACION-COMPONENTES.md)
|
|
|
|
---
|
|
|
|
**Version:** 2.0.0
|
|
**Fecha:** 2026-01-10
|
|
**Actualizado:** Integracion de contenido de 90-transversal/arquitectura/
|