Neues crm-Paket mit Datenmodell, Geschäftslogik und REST-Endpunkten
für die Entität Kunde (/xhr/kunde/{list,get,create,update,delete}).
authorisation_required-Decorator: app-spezifische Rollenprüfung via
has_permission(appid, ...). SysAdmin systemweit, AppAdmin implizit in
seiner Anwendung, User (Mitarbeiter) als Basis-Rolle, Zusatz-Rollen
wie KundenManager additiv.
Flask CLI: init-db (Schema + Musterdaten + App-Rollen) und reset-db
(Bestätigung, behält SysAdmin).
config.ini: Neuer appid-Eintrag (KBPlusCRM), getrennt vom technischen
PROJEKTNAME. checkdb: appid aus config, Migration _db_V01_02 für
bestehende DBs.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
97 lines
3.3 KiB
Python
97 lines
3.3 KiB
Python
"""
|
|
SQLAlchemy-Datenmodelle für die CRM-Domain.
|
|
|
|
Alle Modelle verwenden den Bind-Key 'appdb' und liegen damit in der separaten
|
|
SQLite-Datenbank kbp_crm.db. Die Trennung zur Auth-Datenbank (Bind-Key 'auth')
|
|
erfolgt ausschließlich über __bind_key__.
|
|
|
|
:author: Mistral Vibe <vibe@mistral.ai>
|
|
:date: 2026-09-02
|
|
:version: 1.0
|
|
"""
|
|
import datetime
|
|
import decimal
|
|
|
|
from sqlalchemy import Column, DateTime, Integer, String
|
|
|
|
from db import dbapp
|
|
|
|
|
|
# Bind-Key für alle CRM-Modelle (entspricht dem Eintrag in app.config["SQLALCHEMY_BINDS"])
|
|
BINDKEY = 'appdb'
|
|
|
|
# App-Rollen (roleids, konsistent zu den Konstanten in auth.model). Die Rolle
|
|
# 'AppAdmin' ist bereits in auth.model definiert und wird von den CRM-Routes
|
|
# implizit über den Decorator berücksichtigt.
|
|
#
|
|
# Rollenmodell (additiv): Jeder User erhält zwingend die Basis-Rolle 'User'
|
|
# (MITARBEITER, entspricht APP_USER in auth.model). Damit passiert er das
|
|
# Login-Gate in auth/routes.checklogin. Zusätzliche Rollen wie 'KundenManager'
|
|
# werden ergänzend zugewiesen und gewähren weitergehende Berechtigungen, die
|
|
# über den Decorator authorisation_required geprüft werden.
|
|
MITARBEITER = "User"
|
|
KUNDEN_MANAGER = "KundenManager"
|
|
|
|
|
|
def columns2dict(record, excludefields=[]):
|
|
"""
|
|
Wandelt die Spalten eines SQLAlchemy-Records in ein Dictionary um.
|
|
|
|
Datums- und Decimal-Werte werden dabei in JSON-taugliche Strings konvertiert.
|
|
Über excludefields können Spalten ausgeschlossen werden (z.B. Passwort-Hashes).
|
|
"""
|
|
if not isinstance(excludefields, list):
|
|
excludefields = []
|
|
attribs = dict()
|
|
if hasattr(record, '__table__'):
|
|
if hasattr(record.__table__, 'columns'):
|
|
for col in record.__table__.columns:
|
|
if col.name in excludefields:
|
|
continue
|
|
value = getattr(record, col.name)
|
|
if isinstance(value, datetime.date):
|
|
value = value.isoformat()
|
|
if isinstance(value, decimal.Decimal):
|
|
value = "{0:.2f}".format(value)
|
|
attribs[col.name] = value
|
|
return attribs
|
|
|
|
|
|
class CrmSysteminfo(dbapp.Model):
|
|
"""
|
|
Systeminformationstabelle für die CRM-Datenbank.
|
|
|
|
Hält die DB-Versionsnummer, mit der checkdb feststellt, welche Migrationen
|
|
noch ausgeführt werden müssen. Der Klassenname unterscheidet sich bewusst
|
|
von auth.model.Systeminfo, da beide Klassen dieselbe SQLAlchemy-Registry
|
|
teilen und sich daher nicht namentlich doppeln dürfen.
|
|
"""
|
|
__tablename__ = 'systeminfo'
|
|
__bind_key__ = BINDKEY
|
|
id = Column(Integer, primary_key=True)
|
|
version = Column(String(20), nullable=False)
|
|
lastupdate = Column(DateTime, nullable=True)
|
|
initialinstall = Column(DateTime, nullable=True)
|
|
|
|
|
|
class Kunde(dbapp.Model):
|
|
"""
|
|
Kundenstamm: Verwaltung aller Kunden (Firmen oder Privatpersonen) mit den
|
|
relevanten Adressdaten.
|
|
"""
|
|
__tablename__ = 'kunden'
|
|
__bind_key__ = BINDKEY
|
|
id = Column(Integer, primary_key=True)
|
|
name = Column(String(200), nullable=False)
|
|
strasse = Column(String(200), nullable=True)
|
|
plz = Column(String(20), nullable=True)
|
|
ort = Column(String(100), nullable=True)
|
|
land = Column(String(80), nullable=True)
|
|
|
|
def to_dict(self):
|
|
"""Liefert die Kundendaten als Dictionary (für JSON-Serialisierung)."""
|
|
return columns2dict(self)
|
|
|
|
|
|
print("########## crm/model importiert")
|