from sqlalchemy import text from sqlmodel import SQLModel, Session, create_engine from app.config import settings engine = create_engine(settings.DATABASE_URL) def init_db(): SQLModel.metadata.create_all(engine) def run_migrations(): """Run idempotent schema migrations for columns added after initial create.""" with engine.connect() as conn: try: conn.execute( text( "ALTER TABLE transaction ADD COLUMN IF NOT EXISTS deferred_to_next_cycle BOOLEAN NOT NULL DEFAULT false" ) ) conn.commit() except Exception: conn.rollback() try: conn.execute(text("ALTER TYPE currency ADD VALUE IF NOT EXISTS 'EUR'")) conn.commit() except Exception: conn.rollback() try: conn.execute( text("ALTER TYPE transactiontype ADD VALUE IF NOT EXISTS 'SALARY'") ) conn.commit() except Exception: conn.rollback() try: conn.execute( text( """ CREATE TABLE IF NOT EXISTS savingsaccrual ( id SERIAL PRIMARY KEY, year INTEGER NOT NULL, month INTEGER NOT NULL, memp_amount DOUBLE PRECISION NOT NULL DEFAULT 200000, mpat_amount DOUBLE PRECISION NOT NULL DEFAULT 200000, trigger_transaction_id INTEGER, applied_at TIMESTAMP NOT NULL DEFAULT NOW(), notes TEXT, CONSTRAINT savingsaccrual_year_month_key UNIQUE (year, month) ) """ ) ) conn.commit() except Exception: conn.rollback() try: conn.execute( text( """ INSERT INTO savingsaccrual (year, month, memp_amount, mpat_amount, notes) VALUES (2026, 2, 200000, 200000, 'Seeded: historical baseline'), (2026, 3, 200000, 200000, 'Seeded: historical baseline') ON CONFLICT (year, month) DO NOTHING """ ) ) conn.commit() except Exception: conn.rollback() def get_session(): with Session(engine) as session: yield session