feat: CRM Clinicas SaaS - MVP completo
- Auth: Login/Register con creacion de clinica - Dashboard: KPIs reales, graficas recharts - Pacientes: CRUD completo con busqueda - Agenda: FullCalendar, drag-and-drop, vista recepcion - Expediente: Notas SOAP, signos vitales, CIE-10 - Facturacion: Facturas con IVA, campos CFDI SAT - Inventario: Productos, stock, movimientos, alertas - Configuracion: Clinica, equipo, catalogo servicios - Supabase self-hosted: 18 tablas con RLS multi-tenant - Docker + Nginx para produccion Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
64
supabase/migrations/006_inventory.sql
Normal file
64
supabase/migrations/006_inventory.sql
Normal file
@@ -0,0 +1,64 @@
|
||||
CREATE TABLE products (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
|
||||
sku TEXT,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
category TEXT NOT NULL DEFAULT 'insumo' CHECK (category IN ('medicamento','insumo','material','equipo')),
|
||||
unit TEXT NOT NULL DEFAULT 'pieza',
|
||||
purchase_price DECIMAL(10,2) NOT NULL DEFAULT 0,
|
||||
sale_price DECIMAL(10,2) NOT NULL DEFAULT 0,
|
||||
min_stock INT NOT NULL DEFAULT 0,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
ALTER TABLE invoice_items ADD CONSTRAINT fk_invoice_items_product FOREIGN KEY (product_id) REFERENCES products(id);
|
||||
|
||||
CREATE TABLE inventory_stock (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
|
||||
product_id UUID NOT NULL REFERENCES products(id) ON DELETE CASCADE,
|
||||
current_stock DECIMAL(10,2) NOT NULL DEFAULT 0,
|
||||
last_updated TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(clinic_id, product_id)
|
||||
);
|
||||
|
||||
CREATE TABLE inventory_movements (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
|
||||
product_id UUID NOT NULL REFERENCES products(id) ON DELETE CASCADE,
|
||||
type TEXT NOT NULL CHECK (type IN ('entrada','salida','ajuste','merma')),
|
||||
quantity DECIMAL(10,2) NOT NULL,
|
||||
reason TEXT,
|
||||
reference_id TEXT,
|
||||
created_by UUID REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_movements_product ON inventory_movements(product_id, created_at DESC);
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_inventory_stock()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
INSERT INTO inventory_stock (clinic_id, product_id, current_stock, last_updated)
|
||||
VALUES (NEW.clinic_id, NEW.product_id, NEW.quantity, NOW())
|
||||
ON CONFLICT (clinic_id, product_id)
|
||||
DO UPDATE SET current_stock = inventory_stock.current_stock + NEW.quantity, last_updated = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
CREATE TRIGGER trg_update_stock AFTER INSERT ON inventory_movements FOR EACH ROW EXECUTE FUNCTION update_inventory_stock();
|
||||
|
||||
ALTER TABLE products ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY "View clinic products" ON products FOR SELECT USING (clinic_id = auth.clinic_id());
|
||||
CREATE POLICY "Manage clinic products" ON products FOR ALL USING (clinic_id = auth.clinic_id());
|
||||
|
||||
ALTER TABLE inventory_stock ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY "View clinic stock" ON inventory_stock FOR SELECT USING (clinic_id = auth.clinic_id());
|
||||
CREATE POLICY "Manage clinic stock" ON inventory_stock FOR ALL USING (clinic_id = auth.clinic_id());
|
||||
|
||||
ALTER TABLE inventory_movements ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY "View clinic movements" ON inventory_movements FOR SELECT USING (clinic_id = auth.clinic_id());
|
||||
CREATE POLICY "Insert clinic movements" ON inventory_movements FOR INSERT WITH CHECK (clinic_id = auth.clinic_id());
|
||||
Reference in New Issue
Block a user