init commit

This commit is contained in:
2025-12-10 22:09:31 +09:00
commit b79adf1c69
361 changed files with 47414 additions and 0 deletions

28
app/db/models/__init__.py Normal file
View File

@@ -0,0 +1,28 @@
"""Database models"""
from app.db.models.user import User
from app.db.models.family import Family, FamilyMember, FamilyInvite, FamilyRole
from app.db.models.account import Account, AccountType
from app.db.models.category import Category, CategoryType
from app.db.models.transaction import Transaction, TransactionType
from app.db.models.budget import Budget, BudgetPeriod
from app.db.models.goal import Goal
__all__ = [
# Models
"User",
"Family",
"FamilyMember",
"FamilyInvite",
"Account",
"Category",
"Transaction",
"Budget",
"Goal",
# Enums
"FamilyRole",
"AccountType",
"CategoryType",
"TransactionType",
"BudgetPeriod",
]

50
app/db/models/account.py Normal file
View File

@@ -0,0 +1,50 @@
"""Account (wallet) model"""
from sqlalchemy import Column, Integer, String, Float, DateTime, Boolean, ForeignKey, Enum
from sqlalchemy.orm import relationship
from datetime import datetime
from enum import Enum as PyEnum
from app.db.database import Base
class AccountType(str, PyEnum):
"""Types of accounts"""
CARD = "card"
CASH = "cash"
DEPOSIT = "deposit"
GOAL = "goal"
OTHER = "other"
class Account(Base):
"""Account model - represents a user's wallet or account"""
__tablename__ = "accounts"
id = Column(Integer, primary_key=True)
family_id = Column(Integer, ForeignKey("families.id"), nullable=False, index=True)
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
name = Column(String(255), nullable=False)
account_type = Column(Enum(AccountType), default=AccountType.CARD)
description = Column(String(500), nullable=True)
# Balance
balance = Column(Float, default=0.0)
initial_balance = Column(Float, default=0.0)
# Status
is_active = Column(Boolean, default=True)
is_archived = Column(Boolean, default=False)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
family = relationship("Family", back_populates="accounts")
owner = relationship("User", back_populates="accounts")
transactions = relationship("Transaction", back_populates="account")
def __repr__(self) -> str:
return f"<Account(id={self.id}, name={self.name}, balance={self.balance})>"

50
app/db/models/budget.py Normal file
View File

@@ -0,0 +1,50 @@
"""Budget model for budget tracking"""
from sqlalchemy import Column, Integer, Float, String, DateTime, Boolean, ForeignKey, Enum
from sqlalchemy.orm import relationship
from datetime import datetime
from enum import Enum as PyEnum
from app.db.database import Base
class BudgetPeriod(str, PyEnum):
"""Budget periods"""
DAILY = "daily"
WEEKLY = "weekly"
MONTHLY = "monthly"
YEARLY = "yearly"
class Budget(Base):
"""Budget model - spending limits"""
__tablename__ = "budgets"
id = Column(Integer, primary_key=True)
family_id = Column(Integer, ForeignKey("families.id"), nullable=False, index=True)
category_id = Column(Integer, ForeignKey("categories.id"), nullable=True)
# Budget details
name = Column(String(255), nullable=False)
limit_amount = Column(Float, nullable=False)
spent_amount = Column(Float, default=0.0)
period = Column(Enum(BudgetPeriod), default=BudgetPeriod.MONTHLY)
# Alert threshold (percentage)
alert_threshold = Column(Float, default=80.0)
# Status
is_active = Column(Boolean, default=True)
# Timestamps
start_date = Column(DateTime, nullable=False)
end_date = Column(DateTime, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
family = relationship("Family", back_populates="budgets")
category = relationship("Category", back_populates="budgets")
def __repr__(self) -> str:
return f"<Budget(id={self.id}, name={self.name}, limit={self.limit_amount})>"

47
app/db/models/category.py Normal file
View File

@@ -0,0 +1,47 @@
"""Category model for income/expense categories"""
from sqlalchemy import Column, Integer, String, DateTime, Boolean, ForeignKey, Enum
from sqlalchemy.orm import relationship
from datetime import datetime
from enum import Enum as PyEnum
from app.db.database import Base
class CategoryType(str, PyEnum):
"""Types of categories"""
EXPENSE = "expense"
INCOME = "income"
class Category(Base):
"""Category model - income/expense categories"""
__tablename__ = "categories"
id = Column(Integer, primary_key=True)
family_id = Column(Integer, ForeignKey("families.id"), nullable=False, index=True)
name = Column(String(255), nullable=False)
category_type = Column(Enum(CategoryType), nullable=False)
emoji = Column(String(10), nullable=True)
color = Column(String(7), nullable=True) # Hex color
description = Column(String(500), nullable=True)
# Status
is_active = Column(Boolean, default=True)
is_default = Column(Boolean, default=False)
# Order for UI
order = Column(Integer, default=0)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
family = relationship("Family", back_populates="categories")
transactions = relationship("Transaction", back_populates="category")
budgets = relationship("Budget", back_populates="category")
def __repr__(self) -> str:
return f"<Category(id={self.id}, name={self.name}, type={self.category_type})>"

98
app/db/models/family.py Normal file
View File

@@ -0,0 +1,98 @@
"""Family and Family-related models"""
from sqlalchemy import Column, Integer, String, DateTime, Boolean, ForeignKey, Enum
from sqlalchemy.orm import relationship
from datetime import datetime
from enum import Enum as PyEnum
from app.db.database import Base
class FamilyRole(str, PyEnum):
"""Roles in family"""
OWNER = "owner"
MEMBER = "member"
RESTRICTED = "restricted"
class Family(Base):
"""Family model - represents a family group"""
__tablename__ = "families"
id = Column(Integer, primary_key=True)
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
name = Column(String(255), nullable=False)
description = Column(String(500), nullable=True)
currency = Column(String(3), default="RUB") # ISO 4217 code
invite_code = Column(String(20), unique=True, nullable=False, index=True)
# Settings
notification_level = Column(String(50), default="all") # all, important, none
accounting_period = Column(String(20), default="month") # week, month, year
# Status
is_active = Column(Boolean, default=True)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
members = relationship("FamilyMember", back_populates="family", cascade="all, delete-orphan")
invites = relationship("FamilyInvite", back_populates="family", cascade="all, delete-orphan")
accounts = relationship("Account", back_populates="family", cascade="all, delete-orphan")
categories = relationship("Category", back_populates="family", cascade="all, delete-orphan")
budgets = relationship("Budget", back_populates="family", cascade="all, delete-orphan")
goals = relationship("Goal", back_populates="family", cascade="all, delete-orphan")
def __repr__(self) -> str:
return f"<Family(id={self.id}, name={self.name}, currency={self.currency})>"
class FamilyMember(Base):
"""Family member model - user membership in family"""
__tablename__ = "family_members"
id = Column(Integer, primary_key=True)
family_id = Column(Integer, ForeignKey("families.id"), nullable=False, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
role = Column(Enum(FamilyRole), default=FamilyRole.MEMBER)
# Permissions
can_edit_budget = Column(Boolean, default=True)
can_manage_members = Column(Boolean, default=False)
# Timestamps
joined_at = Column(DateTime, default=datetime.utcnow, nullable=False)
# Relationships
family = relationship("Family", back_populates="members")
user = relationship("User", back_populates="family_members")
def __repr__(self) -> str:
return f"<FamilyMember(family_id={self.family_id}, user_id={self.user_id}, role={self.role})>"
class FamilyInvite(Base):
"""Family invite model - pending invitations"""
__tablename__ = "family_invites"
id = Column(Integer, primary_key=True)
family_id = Column(Integer, ForeignKey("families.id"), nullable=False, index=True)
invite_code = Column(String(20), unique=True, nullable=False, index=True)
created_by_id = Column(Integer, ForeignKey("users.id"), nullable=False)
# Invite validity
is_active = Column(Boolean, default=True)
expires_at = Column(DateTime, nullable=True)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
# Relationships
family = relationship("Family", back_populates="invites")
def __repr__(self) -> str:
return f"<FamilyInvite(id={self.id}, family_id={self.family_id})>"

44
app/db/models/goal.py Normal file
View File

@@ -0,0 +1,44 @@
"""Savings goal model"""
from sqlalchemy import Column, Integer, Float, String, DateTime, Boolean, ForeignKey
from sqlalchemy.orm import relationship
from datetime import datetime
from app.db.database import Base
class Goal(Base):
"""Goal model - savings goals with progress tracking"""
__tablename__ = "goals"
id = Column(Integer, primary_key=True)
family_id = Column(Integer, ForeignKey("families.id"), nullable=False, index=True)
account_id = Column(Integer, ForeignKey("accounts.id"), nullable=True)
# Goal details
name = Column(String(255), nullable=False)
description = Column(String(500), nullable=True)
target_amount = Column(Float, nullable=False)
current_amount = Column(Float, default=0.0)
# Priority
priority = Column(Integer, default=0)
# Status
is_active = Column(Boolean, default=True)
is_completed = Column(Boolean, default=False)
# Deadlines
target_date = Column(DateTime, nullable=True)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
completed_at = Column(DateTime, nullable=True)
# Relationships
family = relationship("Family", back_populates="goals")
account = relationship("Account")
def __repr__(self) -> str:
return f"<Goal(id={self.id}, name={self.name}, target={self.target_amount})>"

View File

@@ -0,0 +1,57 @@
"""Transaction model for income/expense records"""
from sqlalchemy import Column, Integer, Float, String, DateTime, Boolean, ForeignKey, Text, Enum
from sqlalchemy.orm import relationship
from datetime import datetime
from enum import Enum as PyEnum
from app.db.database import Base
class TransactionType(str, PyEnum):
"""Types of transactions"""
EXPENSE = "expense"
INCOME = "income"
TRANSFER = "transfer"
class Transaction(Base):
"""Transaction model - represents income/expense transaction"""
__tablename__ = "transactions"
id = Column(Integer, primary_key=True)
family_id = Column(Integer, ForeignKey("families.id"), nullable=False, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
account_id = Column(Integer, ForeignKey("accounts.id"), nullable=False, index=True)
category_id = Column(Integer, ForeignKey("categories.id"), nullable=True)
# Transaction details
amount = Column(Float, nullable=False)
transaction_type = Column(Enum(TransactionType), nullable=False)
description = Column(String(500), nullable=True)
notes = Column(Text, nullable=True)
tags = Column(String(500), nullable=True) # Comma-separated tags
# Receipt
receipt_photo_url = Column(String(500), nullable=True)
# Recurring transaction
is_recurring = Column(Boolean, default=False)
recurrence_pattern = Column(String(50), nullable=True) # daily, weekly, monthly, etc.
# Status
is_confirmed = Column(Boolean, default=True)
# Timestamps
transaction_date = Column(DateTime, nullable=False, index=True)
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
family = relationship("Family")
user = relationship("User", back_populates="transactions")
account = relationship("Account", back_populates="transactions")
category = relationship("Category", back_populates="transactions")
def __repr__(self) -> str:
return f"<Transaction(id={self.id}, amount={self.amount}, type={self.transaction_type})>"

35
app/db/models/user.py Normal file
View File

@@ -0,0 +1,35 @@
"""User model"""
from sqlalchemy import Column, Integer, String, DateTime, Boolean, Text
from sqlalchemy.orm import relationship
from datetime import datetime
from app.db.database import Base
class User(Base):
"""User model - represents a Telegram user"""
__tablename__ = "users"
id = Column(Integer, primary_key=True)
telegram_id = Column(Integer, unique=True, nullable=False, index=True)
username = Column(String(255), nullable=True)
first_name = Column(String(255), nullable=True)
last_name = Column(String(255), nullable=True)
phone = Column(String(20), nullable=True)
# Account status
is_active = Column(Boolean, default=True)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
last_activity = Column(DateTime, nullable=True)
# Relationships
family_members = relationship("FamilyMember", back_populates="user")
accounts = relationship("Account", back_populates="owner")
transactions = relationship("Transaction", back_populates="user")
def __repr__(self) -> str:
return f"<User(id={self.id}, telegram_id={self.telegram_id}, username={self.username})>"