from app import db from flask_login import UserMixin from datetime import datetime import json class User(db.Model, UserMixin): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) email = db.Column(db.String(120), unique=True, nullable=False) password_hash = db.Column(db.String(128), nullable=False) is_admin = db.Column(db.Boolean, default=False) totp_secret = db.Column(db.String(32), nullable=True) two_factor_enabled = db.Column(db.Boolean, default=False) backup_codes = db.Column(db.Text, nullable=True) # JSON array of hashed backup codes language = db.Column(db.String(5), default='en') currency = db.Column(db.String(3), default='USD') avatar = db.Column(db.String(255), default='icons/avatars/avatar-1.svg') created_at = db.Column(db.DateTime, default=datetime.utcnow) expenses = db.relationship('Expense', backref='user', lazy='dynamic', cascade='all, delete-orphan') categories = db.relationship('Category', backref='user', lazy='dynamic', cascade='all, delete-orphan') documents = db.relationship('Document', backref='user', lazy='dynamic', cascade='all, delete-orphan') def __repr__(self): return f'' class Category(db.Model): __tablename__ = 'categories' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(50), nullable=False) color = db.Column(db.String(7), default='#2b8cee') icon = db.Column(db.String(50), default='category') user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow) expenses = db.relationship('Expense', backref='category', lazy='dynamic') def __repr__(self): return f'' def to_dict(self): return { 'id': self.id, 'name': self.name, 'color': self.color, 'icon': self.icon, 'created_at': self.created_at.isoformat() } class Expense(db.Model): __tablename__ = 'expenses' id = db.Column(db.Integer, primary_key=True) amount = db.Column(db.Float, nullable=False) currency = db.Column(db.String(3), default='USD') description = db.Column(db.String(200), nullable=False) category_id = db.Column(db.Integer, db.ForeignKey('categories.id'), nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) tags = db.Column(db.Text, default='[]') # JSON array of tags receipt_path = db.Column(db.String(255), nullable=True) date = db.Column(db.DateTime, default=datetime.utcnow) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) def __repr__(self): return f'' def get_tags(self): try: return json.loads(self.tags) except: return [] def set_tags(self, tags_list): self.tags = json.dumps(tags_list) def to_dict(self): return { 'id': self.id, 'amount': self.amount, 'currency': self.currency, 'description': self.description, 'category_id': self.category_id, 'category_name': self.category.name if self.category else None, 'category_color': self.category.color if self.category else None, 'tags': self.get_tags(), 'receipt_path': self.receipt_path, 'date': self.date.isoformat(), 'created_at': self.created_at.isoformat() } class Document(db.Model): """ Model for storing user documents (bank statements, receipts, invoices, etc.) Security: All queries filtered by user_id to ensure users only see their own documents """ __tablename__ = 'documents' id = db.Column(db.Integer, primary_key=True) filename = db.Column(db.String(255), nullable=False) original_filename = db.Column(db.String(255), nullable=False) file_path = db.Column(db.String(500), nullable=False) file_size = db.Column(db.Integer, nullable=False) # in bytes file_type = db.Column(db.String(50), nullable=False) # PDF, CSV, XLSX, etc. mime_type = db.Column(db.String(100), nullable=False) document_category = db.Column(db.String(100), nullable=True) # Bank Statement, Invoice, Receipt, Contract, etc. status = db.Column(db.String(50), default='uploaded') # uploaded, processing, analyzed, error user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) def __repr__(self): return f'' def to_dict(self): return { 'id': self.id, 'filename': self.original_filename, 'file_size': self.file_size, 'file_type': self.file_type, 'document_category': self.document_category, 'status': self.status, 'created_at': self.created_at.isoformat(), 'updated_at': self.updated_at.isoformat() }