Files
JQC_multi_tenant/app/models/tenant_settings.py
T

66 lines
2.6 KiB
Python

"""
app/models/tenant_settings.py
------------------------------
Per-tenant branding / self-service settings (MT-7).
One row per tenant DB. Created on first save; reads fall back to defaults
when no row exists so tenant-zero needs zero data migration.
"""
import sqlalchemy.exc
from app import db
from app.utils.time_utils import now_eastern
class TenantSettings(db.Model):
__tablename__ = 'tenant_settings'
id = db.Column(db.Integer, primary_key=True)
company_name = db.Column(db.String(150), nullable=True)
logo_url = db.Column(db.String(500), nullable=True)
primary_color = db.Column(db.String(7), nullable=False, default='#1a56db')
accent_color = db.Column(db.String(7), nullable=False, default='#16a34a')
support_email = db.Column(db.String(255), nullable=True)
updated_at = db.Column(db.DateTime, nullable=False, default=now_eastern)
updated_by = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='SET NULL'),
nullable=True)
# ── Defaults ──────────────────────────────────────────────────────────────
DEFAULTS = {
'company_name': 'Janitorial QC',
'logo_url': None,
'primary_color': '#1a56db',
'accent_color': '#16a34a',
'support_email': None,
}
@classmethod
def get_or_default(cls):
"""Return the settings row, a default-filled transient instance, or None.
Returns None when the tenant_settings table does not yet exist (i.e.
phase33 migration has not been run for this tenant DB). The caller
must handle None — routes redirect to the dashboard with a migration
prompt; the context processor silently returns no branding.
"""
try:
row = cls.query.first()
except (sqlalchemy.exc.ProgrammingError, sqlalchemy.exc.OperationalError):
# Table 'tenant_settings' doesn't exist — phase33 migration not yet applied.
# Any other DB error (connection failure, permission error) is re-raised.
return None
if row is not None:
return row
# Return a transient default so templates always get a real object.
obj = cls()
for k, v in cls.DEFAULTS.items():
setattr(obj, k, v)
return obj
@property
def display_name(self):
return self.company_name or self.DEFAULTS['company_name']
def __repr__(self):
return f'<TenantSettings company={self.company_name}>'