import sqlalchemy as sa from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, relationship Base = declarative_base() class Account(Base): __tablename__ = 'accounts' id = sa.Column(sa.Integer, primary_key=True) name = sa.Column(sa.String(100), nullable=False) account_type = sa.Column(sa.String(50), nullable=False) # Asset, Liability, Equity, Revenue, Expense code = sa.Column(sa.String(20), unique=True, nullable=False) parent_id = sa.Column(sa.Integer, sa.ForeignKey('accounts.id')) balance = sa.Column(sa.Numeric(15, 2), default=0) parent = relationship('Account', remote_side=[id]) entries = relationship('JournalEntryLine', back_populates='account') def __repr__(self): return f"" class JournalEntry(Base): __tablename__ = 'journal_entries' id = sa.Column(sa.Integer, primary_key=True) date = sa.Column(sa.Date, nullable=False) reference = sa.Column(sa.String(100)) description = sa.Column(sa.String(200)) created_at = sa.Column(sa.DateTime, server_default=sa.func.now()) lines = relationship('JournalEntryLine', back_populates='journal_entry') class JournalEntryLine(Base): __tablename__ = 'journal_entry_lines' id = sa.Column(sa.Integer, primary_key=True) journal_entry_id = sa.Column(sa.Integer, sa.ForeignKey('journal_entries.id'), nullable=False) account_id = sa.Column(sa.Integer, sa.ForeignKey('accounts.id'), nullable=False) amount = sa.Column(sa.Numeric(15, 2), nullable=False) is_debit = sa.Column(sa.Boolean, nullable=False) # True for debit, False for credit journal_entry = relationship('JournalEntry', back_populates='lines') account = relationship('Account', back_populates='entries') class BankAccount(Base): __tablename__ = 'bank_accounts' id = sa.Column(sa.Integer, primary_key=True) name = sa.Column(sa.String(100), nullable=False) account_number = sa.Column(sa.String(50)) bank_name = sa.Column(sa.String(100)) currency = sa.Column(sa.String(3), default='USD') ledger_account_id = sa.Column(sa.Integer, sa.ForeignKey('accounts.id')) ledger_account = relationship('Account') transactions = relationship('BankTransaction', back_populates='bank_account') class BankTransaction(Base): __tablename__ = 'bank_transactions' id = sa.Column(sa.Integer, primary_key=True) bank_account_id = sa.Column(sa.Integer, sa.ForeignKey('bank_accounts.id'), nullable=False) date = sa.Column(sa.Date, nullable=False) amount = sa.Column(sa.Numeric(15, 2), nullable=False) description = sa.Column(sa.String(200)) reference = sa.Column(sa.String(100)) status = sa.Column(sa.String(20), default='unreconciled') # unreconciled, reconciled journal_entry_id = sa.Column(sa.Integer, sa.ForeignKey('journal_entries.id')) bank_account = relationship('BankAccount', back_populates='transactions') journal_entry = relationship('JournalEntry') class ReconciliationReport(Base): __tablename__ = 'reconciliation_reports' id = sa.Column(sa.Integer, primary_key=True) bank_account_id = sa.Column(sa.Integer, sa.ForeignKey('bank_accounts.id'), nullable=False) start_date = sa.Column(sa.Date, nullable=False) end_date = sa.Column(sa.Date, nullable=False) created_at = sa.Column(sa.DateTime, server_default=sa.func.now()) status = sa.Column(sa.String(20), default='draft') # draft, completed bank_account = relationship('BankAccount') matches = relationship('ReconciliationMatch', back_populates='report') class ReconciliationMatch(Base): __tablename__ = 'reconciliation_matches' id = sa.Column(sa.Integer, primary_key=True) report_id = sa.Column(sa.Integer, sa.ForeignKey('reconciliation_reports.id'), nullable=False) transaction_id = sa.Column(sa.Integer, sa.ForeignKey('bank_transactions.id'), nullable=False) journal_entry_id = sa.Column(sa.Integer, sa.ForeignKey('journal_entries.id'), nullable=False) match_score = sa.Column(sa.Numeric(5, 2)) # 0-100 score for match quality notes = sa.Column(sa.String(200)) report = relationship('ReconciliationReport', back_populates='matches') transaction = relationship('BankTransaction') journal_entry = relationship('JournalEntry')