49 lines
2.3 KiB
Python
49 lines
2.3 KiB
Python
from app.extensions import db
|
|
from datetime import datetime
|
|
|
|
|
|
class SchwabConnection(db.Model):
|
|
"""OAuth connection to Schwab. One per user (single-user app)."""
|
|
__tablename__ = 'schwab_connections'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
access_token = db.Column(db.Text, nullable=False)
|
|
refresh_token = db.Column(db.Text, nullable=False)
|
|
token_expires_at = db.Column(db.DateTime, nullable=False) # UTC
|
|
is_active = db.Column(db.Boolean, default=True)
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
last_synced_at = db.Column(db.DateTime, nullable=True)
|
|
|
|
accounts = db.relationship('SchwabAccount', back_populates='connection',
|
|
cascade='all, delete-orphan', lazy='dynamic')
|
|
|
|
@property
|
|
def token_is_expired(self):
|
|
return datetime.utcnow() >= self.token_expires_at
|
|
|
|
def __repr__(self):
|
|
return f'<SchwabConnection id={self.id} expires={self.token_expires_at}>'
|
|
|
|
|
|
class SchwabAccount(db.Model):
|
|
"""Maps one Schwab account (identified by its encrypted hash) to a PFM account."""
|
|
__tablename__ = 'schwab_accounts'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
connection_id = db.Column(db.Integer, db.ForeignKey('schwab_connections.id'), nullable=False)
|
|
account_hash = db.Column(db.String(100), unique=True, nullable=False, index=True)
|
|
account_number_display = db.Column(db.String(20), nullable=True) # masked, e.g. "…4321"
|
|
account_type = db.Column(db.String(50), nullable=True) # CASH / MARGIN / etc.
|
|
account_name = db.Column(db.String(100), nullable=True) # display label
|
|
pfm_account_id = db.Column(db.Integer, db.ForeignKey('accounts.id'), nullable=True)
|
|
last_sync_date = db.Column(db.Date, nullable=True)
|
|
last_schwab_txn_id = db.Column(db.String(64), nullable=True)
|
|
is_active = db.Column(db.Boolean, default=True)
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
|
|
connection = db.relationship('SchwabConnection', back_populates='accounts')
|
|
pfm_account = db.relationship('Account')
|
|
|
|
def __repr__(self):
|
|
return f'<SchwabAccount {self.account_number_display} → PFM #{self.pfm_account_id}>'
|