init commit
This commit is contained in:
28
app/db/models/__init__.py
Normal file
28
app/db/models/__init__.py
Normal 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
50
app/db/models/account.py
Normal 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
50
app/db/models/budget.py
Normal 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
47
app/db/models/category.py
Normal 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
98
app/db/models/family.py
Normal 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
44
app/db/models/goal.py
Normal 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})>"
|
||||
57
app/db/models/transaction.py
Normal file
57
app/db/models/transaction.py
Normal 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
35
app/db/models/user.py
Normal 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})>"
|
||||
Reference in New Issue
Block a user