init commit
This commit is contained in:
252
.history/migrations/versions/001_initial_20251210203855.py
Normal file
252
.history/migrations/versions/001_initial_20251210203855.py
Normal file
@@ -0,0 +1,252 @@
|
||||
"""Initial schema migration
|
||||
|
||||
Revision ID: 001_initial
|
||||
Revises:
|
||||
Create Date: 2025-12-10
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '001_initial'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Create enum types
|
||||
# Note: PostgreSQL doesn't support IF NOT EXISTS for CREATE TYPE
|
||||
# Check and create types individually
|
||||
try:
|
||||
op.execute('CREATE TYPE family_role AS ENUM (\'owner\', \'member\', \'restricted\')')
|
||||
except Exception:
|
||||
pass # Type already exists
|
||||
|
||||
try:
|
||||
op.execute('CREATE TYPE account_type AS ENUM (\'card\', \'cash\', \'deposit\', \'goal\', \'other\')')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
op.execute('CREATE TYPE category_type AS ENUM (\'expense\', \'income\')')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
op.execute('CREATE TYPE transaction_type AS ENUM (\'expense\', \'income\', \'transfer\')')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
op.execute('CREATE TYPE budget_period AS ENUM (\'daily\', \'weekly\', \'monthly\', \'yearly\')')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Create users table
|
||||
op.create_table(
|
||||
'users',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('telegram_id', sa.Integer(), nullable=False),
|
||||
sa.Column('username', sa.String(length=255), nullable=True),
|
||||
sa.Column('first_name', sa.String(length=255), nullable=True),
|
||||
sa.Column('last_name', sa.String(length=255), nullable=True),
|
||||
sa.Column('phone', sa.String(length=20), nullable=True),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true'),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('last_activity', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('telegram_id')
|
||||
)
|
||||
op.create_index(op.f('ix_users_telegram_id'), 'users', ['telegram_id'], unique=True)
|
||||
|
||||
# Create families table
|
||||
op.create_table(
|
||||
'families',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('owner_id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('description', sa.String(length=500), nullable=True),
|
||||
sa.Column('currency', sa.String(length=3), nullable=False, server_default='RUB'),
|
||||
sa.Column('invite_code', sa.String(length=20), nullable=False),
|
||||
sa.Column('notification_level', sa.String(length=50), nullable=False, server_default='all'),
|
||||
sa.Column('accounting_period', sa.String(length=20), nullable=False, server_default='month'),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true'),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['owner_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('invite_code')
|
||||
)
|
||||
op.create_index(op.f('ix_families_invite_code'), 'families', ['invite_code'], unique=True)
|
||||
|
||||
# Create family_members table
|
||||
op.create_table(
|
||||
'family_members',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('family_id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('role', postgresql.ENUM('owner', 'member', 'restricted', name='family_role'), nullable=False, server_default='member'),
|
||||
sa.Column('can_edit_budget', sa.Boolean(), nullable=False, server_default='true'),
|
||||
sa.Column('can_manage_members', sa.Boolean(), nullable=False, server_default='false'),
|
||||
sa.Column('joined_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['family_id'], ['families.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_family_members_family_id'), 'family_members', ['family_id'], unique=False)
|
||||
op.create_index(op.f('ix_family_members_user_id'), 'family_members', ['user_id'], unique=False)
|
||||
|
||||
# Create family_invites table
|
||||
op.create_table(
|
||||
'family_invites',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('family_id', sa.Integer(), nullable=False),
|
||||
sa.Column('invite_code', sa.String(length=20), nullable=False),
|
||||
sa.Column('created_by_id', sa.Integer(), nullable=False),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true'),
|
||||
sa.Column('expires_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['family_id'], ['families.id'], ),
|
||||
sa.ForeignKeyConstraint(['created_by_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('invite_code')
|
||||
)
|
||||
op.create_index(op.f('ix_family_invites_family_id'), 'family_invites', ['family_id'], unique=False)
|
||||
op.create_index(op.f('ix_family_invites_invite_code'), 'family_invites', ['invite_code'], unique=True)
|
||||
|
||||
# Create accounts table
|
||||
op.create_table(
|
||||
'accounts',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('family_id', sa.Integer(), nullable=False),
|
||||
sa.Column('owner_id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('account_type', postgresql.ENUM('card', 'cash', 'deposit', 'goal', 'other', name='account_type'), nullable=False, server_default='card'),
|
||||
sa.Column('description', sa.String(length=500), nullable=True),
|
||||
sa.Column('balance', sa.Float(), nullable=False, server_default='0'),
|
||||
sa.Column('initial_balance', sa.Float(), nullable=False, server_default='0'),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true'),
|
||||
sa.Column('is_archived', sa.Boolean(), nullable=False, server_default='false'),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['family_id'], ['families.id'], ),
|
||||
sa.ForeignKeyConstraint(['owner_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_accounts_family_id'), 'accounts', ['family_id'], unique=False)
|
||||
|
||||
# Create categories table
|
||||
op.create_table(
|
||||
'categories',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('family_id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('category_type', postgresql.ENUM('expense', 'income', name='category_type'), nullable=False),
|
||||
sa.Column('emoji', sa.String(length=10), nullable=True),
|
||||
sa.Column('color', sa.String(length=7), nullable=True),
|
||||
sa.Column('description', sa.String(length=500), nullable=True),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true'),
|
||||
sa.Column('is_default', sa.Boolean(), nullable=False, server_default='false'),
|
||||
sa.Column('order', sa.Integer(), nullable=False, server_default='0'),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['family_id'], ['families.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_categories_family_id'), 'categories', ['family_id'], unique=False)
|
||||
|
||||
# Create transactions table
|
||||
op.create_table(
|
||||
'transactions',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('family_id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('account_id', sa.Integer(), nullable=False),
|
||||
sa.Column('category_id', sa.Integer(), nullable=True),
|
||||
sa.Column('amount', sa.Float(), nullable=False),
|
||||
sa.Column('transaction_type', postgresql.ENUM('expense', 'income', 'transfer', name='transaction_type'), nullable=False),
|
||||
sa.Column('description', sa.String(length=500), nullable=True),
|
||||
sa.Column('notes', sa.Text(), nullable=True),
|
||||
sa.Column('tags', sa.String(length=500), nullable=True),
|
||||
sa.Column('receipt_photo_url', sa.String(length=500), nullable=True),
|
||||
sa.Column('is_recurring', sa.Boolean(), nullable=False, server_default='false'),
|
||||
sa.Column('recurrence_pattern', sa.String(length=50), nullable=True),
|
||||
sa.Column('is_confirmed', sa.Boolean(), nullable=False, server_default='true'),
|
||||
sa.Column('transaction_date', sa.DateTime(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['account_id'], ['accounts.id'], ),
|
||||
sa.ForeignKeyConstraint(['category_id'], ['categories.id'], ),
|
||||
sa.ForeignKeyConstraint(['family_id'], ['families.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_transactions_family_id'), 'transactions', ['family_id'], unique=False)
|
||||
op.create_index(op.f('ix_transactions_transaction_date'), 'transactions', ['transaction_date'], unique=False)
|
||||
|
||||
# Create budgets table
|
||||
op.create_table(
|
||||
'budgets',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('family_id', sa.Integer(), nullable=False),
|
||||
sa.Column('category_id', sa.Integer(), nullable=True),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('limit_amount', sa.Float(), nullable=False),
|
||||
sa.Column('spent_amount', sa.Float(), nullable=False, server_default='0'),
|
||||
sa.Column('period', postgresql.ENUM('daily', 'weekly', 'monthly', 'yearly', name='budget_period'), nullable=False, server_default='monthly'),
|
||||
sa.Column('alert_threshold', sa.Float(), nullable=False, server_default='80'),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true'),
|
||||
sa.Column('start_date', sa.DateTime(), nullable=False),
|
||||
sa.Column('end_date', sa.DateTime(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['category_id'], ['categories.id'], ),
|
||||
sa.ForeignKeyConstraint(['family_id'], ['families.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_budgets_family_id'), 'budgets', ['family_id'], unique=False)
|
||||
|
||||
# Create goals table
|
||||
op.create_table(
|
||||
'goals',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('family_id', sa.Integer(), nullable=False),
|
||||
sa.Column('account_id', sa.Integer(), nullable=True),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('description', sa.String(length=500), nullable=True),
|
||||
sa.Column('target_amount', sa.Float(), nullable=False),
|
||||
sa.Column('current_amount', sa.Float(), nullable=False, server_default='0'),
|
||||
sa.Column('priority', sa.Integer(), nullable=False, server_default='0'),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true'),
|
||||
sa.Column('is_completed', sa.Boolean(), nullable=False, server_default='false'),
|
||||
sa.Column('target_date', sa.DateTime(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('completed_at', sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['account_id'], ['accounts.id'], ),
|
||||
sa.ForeignKeyConstraint(['family_id'], ['families.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_goals_family_id'), 'goals', ['family_id'], unique=False)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table('goals')
|
||||
op.drop_table('budgets')
|
||||
op.drop_table('transactions')
|
||||
op.drop_table('categories')
|
||||
op.drop_table('accounts')
|
||||
op.drop_table('family_invites')
|
||||
op.drop_table('family_members')
|
||||
op.drop_table('families')
|
||||
op.drop_table('users')
|
||||
|
||||
op.execute('DROP TYPE budget_period')
|
||||
op.execute('DROP TYPE transaction_type')
|
||||
op.execute('DROP TYPE category_type')
|
||||
op.execute('DROP TYPE account_type')
|
||||
op.execute('DROP TYPE family_role')
|
||||
Reference in New Issue
Block a user