-- ===================================================== -- Script SQL para Sistema de Contabilidad General -- Base de datos: PostgreSQL -- Fecha: 2026-01-28 -- ===================================================== -- ===================================================== -- 1. CREAR TABLAS -- ===================================================== -- Tabla: categorias_ingreso CREATE TABLE IF NOT EXISTS public.categorias_ingreso ( id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, nombre character varying(100) COLLATE pg_catalog."default" NOT NULL, descripcion character varying(255) COLLATE pg_catalog."default", activa boolean NOT NULL DEFAULT true, fecha_creacion timestamp without time zone NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'), CONSTRAINT pk_categorias_ingreso PRIMARY KEY (id) ); -- Tabla: categorias_egreso CREATE TABLE IF NOT EXISTS public.categorias_egreso ( id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, nombre character varying(100) COLLATE pg_catalog."default" NOT NULL, descripcion character varying(255) COLLATE pg_catalog."default", activa boolean NOT NULL DEFAULT true, fecha_creacion timestamp without time zone NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'), CONSTRAINT pk_categorias_egreso PRIMARY KEY (id) ); -- Tabla: reportes_mensuales_generales CREATE TABLE IF NOT EXISTS public.reportes_mensuales_generales ( id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, mes integer NOT NULL, anio integer NOT NULL, saldo_inicial numeric(18,2) NOT NULL DEFAULT 0, fecha_creacion timestamp without time zone NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'), cerrado boolean NOT NULL DEFAULT false, CONSTRAINT pk_reportes_mensuales_generales PRIMARY KEY (id), CONSTRAINT uk_mes_anio_general UNIQUE (mes, anio), CONSTRAINT ck_mes_rango CHECK (mes >= 1 AND mes <= 12), CONSTRAINT ck_anio_valido CHECK (anio >= 2000 AND anio <= 2100) ); -- Crear tipo enum para tipo_movimiento_general si no existe DO $$ BEGIN CREATE TYPE tipo_movimiento_general AS ENUM ('Ingreso', 'Egreso'); EXCEPTION WHEN duplicate_object THEN null; END $$; -- Tabla: movimientos_generales CREATE TABLE IF NOT EXISTS public.movimientos_generales ( id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, reporte_mensual_general_id bigint, tipo tipo_movimiento_general NOT NULL, categoria_ingreso_id bigint, categoria_egreso_id bigint, monto numeric(18,2) NOT NULL, fecha timestamp without time zone NOT NULL, descripcion character varying(200) COLLATE pg_catalog."default" NOT NULL DEFAULT '', numero_comprobante character varying(50) COLLATE pg_catalog."default", CONSTRAINT pk_movimientos_generales PRIMARY KEY (id), CONSTRAINT fk_movimientos_reporte FOREIGN KEY (reporte_mensual_general_id) REFERENCES public.reportes_mensuales_generales (id) ON DELETE CASCADE, CONSTRAINT fk_movimientos_categoria_ingreso FOREIGN KEY (categoria_ingreso_id) REFERENCES public.categorias_ingreso (id) ON DELETE RESTRICT, CONSTRAINT fk_movimientos_categoria_egreso FOREIGN KEY (categoria_egreso_id) REFERENCES public.categorias_egreso (id) ON DELETE RESTRICT, CONSTRAINT ck_monto_positivo CHECK (monto > 0), CONSTRAINT ck_categoria_tipo CHECK ( (tipo = 'Ingreso' AND categoria_ingreso_id IS NOT NULL AND categoria_egreso_id IS NULL) OR (tipo = 'Egreso' AND categoria_egreso_id IS NOT NULL AND categoria_ingreso_id IS NULL) ) ); -- ===================================================== -- 2. CREAR ÍNDICES -- ===================================================== CREATE INDEX IF NOT EXISTS idx_movimientos_reporte ON public.movimientos_generales (reporte_mensual_general_id); CREATE INDEX IF NOT EXISTS idx_movimientos_fecha ON public.movimientos_generales (fecha); CREATE INDEX IF NOT EXISTS idx_movimientos_categoria_ingreso ON public.movimientos_generales (categoria_ingreso_id); CREATE INDEX IF NOT EXISTS idx_movimientos_categoria_egreso ON public.movimientos_generales (categoria_egreso_id); CREATE INDEX IF NOT EXISTS idx_reportes_anio ON public.reportes_mensuales_generales (anio); -- ===================================================== -- 3. INSERTAR DATOS INICIALES - CATEGORÍAS DE INGRESO -- ===================================================== INSERT INTO public.categorias_ingreso (nombre, descripcion, activa) VALUES ('Ofrendas', 'Ofrendas regulares de los cultos', true), ('Donaciones', 'Donaciones especiales de hermanos y visitantes', true), ('Diezmos', 'Diezmos de los miembros', true), ('Eventos Especiales', 'Ingresos de eventos, conferencias, retiros', true), ('Alquileres', 'Ingresos por alquiler de instalaciones', true), ('Ventas', 'Ventas de materiales, libros u otros productos', true), ('Otros Ingresos', 'Ingresos diversos no categorizados', true) ON CONFLICT DO NOTHING; -- ===================================================== -- 4. INSERTAR DATOS INICIALES - CATEGORÍAS DE EGRESO -- ===================================================== INSERT INTO public.categorias_egreso (nombre, descripcion, activa) VALUES ('Agua', 'Pago del servicio de agua', true), ('Luz', 'Pago del servicio de electricidad', true), ('Teléfono/Internet', 'Servicios de telefonía e internet', true), ('Impuestos', 'Pago de impuestos municipales, prediales, etc.', true), ('Funeraria', 'Gastos relacionados con servicios funerarios', true), ('Mantenimiento Edificio', 'Reparaciones y mantenimiento de las instalaciones', true), ('Suministros Ministerio', 'Materiales para ministerios (niños, jóvenes, etc.)', true), ('Salarios Personal', 'Salarios de pastores y personal administrativo', true), ('Eventos', 'Gastos de organización de eventos', true), ('Transporte', 'Gastos de transporte y combustible', true), ('Limpieza', 'Servicios de limpieza y productos', true), ('Seguridad', 'Servicio de seguridad o vigilancia', true), ('Otros Gastos', 'Gastos diversos no categorizados', true) ON CONFLICT DO NOTHING; -- ===================================================== -- 5. COMENTARIOS SOBRE LAS TABLAS -- ===================================================== COMMENT ON TABLE public.categorias_ingreso IS 'Categorías para clasificar los ingresos de la iglesia'; COMMENT ON TABLE public.categorias_egreso IS 'Categorías para clasificar los egresos de la iglesia'; COMMENT ON TABLE public.reportes_mensuales_generales IS 'Reportes mensuales de contabilidad general de la iglesia'; COMMENT ON TABLE public.movimientos_generales IS 'Movimientos individuales de ingresos y egresos'; -- ===================================================== -- 6. VERIFICACIÓN -- ===================================================== -- Verificar que se crearon las tablas SELECT table_name, (SELECT COUNT(*) FROM information_schema.columns WHERE table_name = t.table_name) as columnas FROM information_schema.tables t WHERE table_schema = 'public' AND table_name IN ( 'categorias_ingreso', 'categorias_egreso', 'reportes_mensuales_generales', 'movimientos_generales' ) ORDER BY table_name; -- Verificar datos iniciales de categorías SELECT 'Categorías de Ingreso' as tipo, COUNT(*) as total FROM public.categorias_ingreso UNION ALL SELECT 'Categorías de Egreso' as tipo, COUNT(*) as total FROM public.categorias_egreso; -- ===================================================== -- FIN DEL SCRIPT -- =====================================================