From 93827dde0f926b270a5dfa928735e5a2123c0c1f Mon Sep 17 00:00:00 2001 From: Gerd Date: Fri, 4 Sep 2026 15:43:43 +0200 Subject: [PATCH] =?UTF-8?q?Serverseitige=20Filterung=20f=C3=BCr=20Listen-E?= =?UTF-8?q?ndpunkte=20und=20Einzelabrufe=20in=20Listen-Komponenten?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Server: Die list-Endpunkte für Kontakt, Projekt, Kontaktadresse und CallLog akzeptieren nun einen optionalen Query-Parameter (kunden_id bzw. projekt_id) und filtern im ORM-Query. Neue Migration V01.05 legt Indizes auf den FK-Spalten an. Client: Die fetch-Funktionen erhalten einen optionalen Parent-ID-Parameter. Die Listen-Komponenten übergeben die Parent-ID aus den Route-Params an den Server und laden nur noch benötigte Daten. Statt kompletter Eltern-Listen wird per fetchXxxById ein einzelner Datensatz für die Namensauflösung geladen. Die CallLog-Bearbeitungsmaske behält ihre ungesfilterten Dropdown-Abrufe. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- App_Server/root/crm/model/__init__.py | 14 ++++++- App_Server/root/crm/model/call_log.py | 13 ++++-- App_Server/root/crm/model/checkdb.py | 17 ++++++++ App_Server/root/crm/model/kontakt.py | 13 ++++-- App_Server/root/crm/model/kontaktadresse.py | 13 ++++-- App_Server/root/crm/model/projekt.py | 13 ++++-- App_Server/root/crm/routes.py | 20 +++++---- Client/Components/call_log.ts | 46 ++++++++++----------- Client/Components/kontakt.ts | 46 +++++++-------------- Client/Components/kontaktadresse.ts | 44 +++++++------------- Client/Components/projekt.ts | 43 +++++++------------ Client/models/call_log.ts | 12 +++++- Client/models/kontakt.ts | 12 +++++- Client/models/kontaktadresse.ts | 12 +++++- Client/models/projekt.ts | 12 +++++- 15 files changed, 189 insertions(+), 141 deletions(-) diff --git a/App_Server/root/crm/model/__init__.py b/App_Server/root/crm/model/__init__.py index b5286d2..ec7fde3 100644 --- a/App_Server/root/crm/model/__init__.py +++ b/App_Server/root/crm/model/__init__.py @@ -12,7 +12,7 @@ erfolgt ausschließlich über __bind_key__. import datetime import decimal -from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String +from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Index, Integer, String from sqlalchemy.orm import relationship from db import dbapp @@ -103,6 +103,9 @@ class Kontakt(dbapp.Model): """ __tablename__ = 'kontakte' __bind_key__ = BINDKEY + __table_args__ = ( + Index('ix_kontakte_kunden_id', 'kunden_id'), + ) id = Column(Integer, primary_key=True) kunden_id = Column(Integer, ForeignKey('kunden.id'), nullable=False) anrede = Column(String(30), nullable=True) @@ -134,6 +137,9 @@ class Projekt(dbapp.Model): """ __tablename__ = 'projekte' __bind_key__ = BINDKEY + __table_args__ = ( + Index('ix_projekte_kunden_id', 'kunden_id'), + ) id = Column(Integer, primary_key=True) kunden_id = Column(Integer, ForeignKey('kunden.id'), nullable=False) projektname = Column(String(200), nullable=False) @@ -163,6 +169,9 @@ class Kontaktadresse(dbapp.Model): """ __tablename__ = 'kontaktadressen' __bind_key__ = BINDKEY + __table_args__ = ( + Index('ix_kontaktadressen_projekt_id', 'projekt_id'), + ) id = Column(Integer, primary_key=True) projekt_id = Column(Integer, ForeignKey('projekte.id'), nullable=False) anrede = Column(String(30), nullable=True) @@ -200,6 +209,9 @@ class CallLog(dbapp.Model): """ __tablename__ = 'call_logs' __bind_key__ = BINDKEY + __table_args__ = ( + Index('ix_call_logs_projekt_id', 'projekt_id'), + ) id = Column(Integer, primary_key=True) projekt_id = Column(Integer, ForeignKey('projekte.id'), nullable=False) datum = Column(String(20), nullable=True) diff --git a/App_Server/root/crm/model/call_log.py b/App_Server/root/crm/model/call_log.py index 088a2b3..f60280f 100644 --- a/App_Server/root/crm/model/call_log.py +++ b/App_Server/root/crm/model/call_log.py @@ -17,11 +17,18 @@ from auth.model.errors import ApplicationError, HtmlStatusCode session = dbapp.session -def get_call_logs(): +def get_call_logs(projekt_id: int | None = None): """ - Liefert alle Gesprächsnotizen, sortiert nach Datum absteigend. + Liefert Gesprächsnotizen, sortiert nach Datum absteigend. + + Ist projekt_id gesetzt, werden nur die Notizen dieses Projekts + zurückgegeben (serverseitige Filterung). Ohne projekt_id werden + alle Notizen geliefert. """ - return CallLog.query.order_by(CallLog.datum.desc()).all() + query = CallLog.query + if projekt_id is not None: + query = query.filter(CallLog.projekt_id == projekt_id) + return query.order_by(CallLog.datum.desc()).all() def get_call_log(call_log_id: int) -> CallLog: diff --git a/App_Server/root/crm/model/checkdb.py b/App_Server/root/crm/model/checkdb.py index a3704f4..5f4d87e 100644 --- a/App_Server/root/crm/model/checkdb.py +++ b/App_Server/root/crm/model/checkdb.py @@ -91,6 +91,23 @@ def _db_V01_04(): session.commit() +def _db_V01_05(): + """ + Legt Indizes auf den Fremdschlüsselspalten an, die für die + serverseitige Filterung der Listen-Endpunkte genutzt werden. + CREATE INDEX IF NOT EXISTS ist idempotent und überspringet bereits + vorhandene Indizes. + """ + from sqlalchemy import text + + session.execute(text("CREATE INDEX IF NOT EXISTS ix_kontakte_kunden_id ON kontakte (kunden_id)")) + session.execute(text("CREATE INDEX IF NOT EXISTS ix_projekte_kunden_id ON projekte (kunden_id)")) + session.execute(text("CREATE INDEX IF NOT EXISTS ix_kontaktadressen_projekt_id ON kontaktadressen (projekt_id)")) + session.execute(text("CREATE INDEX IF NOT EXISTS ix_call_logs_projekt_id ON call_logs (projekt_id)")) + _updatesysteminfo('01.05') + session.commit() + + def checkupdates(): """ Ermittelt die aktuelle DB-Version aus der Systeminfo-Tabelle und führt alle diff --git a/App_Server/root/crm/model/kontakt.py b/App_Server/root/crm/model/kontakt.py index f80665c..bef73f5 100644 --- a/App_Server/root/crm/model/kontakt.py +++ b/App_Server/root/crm/model/kontakt.py @@ -17,11 +17,18 @@ from auth.model.errors import ApplicationError, HtmlStatusCode session = dbapp.session -def get_kontakte(): +def get_kontakte(kunden_id: int | None = None): """ - Liefert alle Kontakte, sortiert nach Name. + Liefert Kontakte, sortiert nach Name. + + Ist kunden_id gesetzt, werden nur die Kontakte dieses Kunden + zurückgegeben (serverseitige Filterung). Ohne kunden_id werden + alle Kontakte geliefert. """ - return Kontakt.query.order_by(Kontakt.name).all() + query = Kontakt.query + if kunden_id is not None: + query = query.filter(Kontakt.kunden_id == kunden_id) + return query.order_by(Kontakt.name).all() def get_kontakt(kontakt_id: int) -> Kontakt: diff --git a/App_Server/root/crm/model/kontaktadresse.py b/App_Server/root/crm/model/kontaktadresse.py index 5ac0669..eb520c8 100644 --- a/App_Server/root/crm/model/kontaktadresse.py +++ b/App_Server/root/crm/model/kontaktadresse.py @@ -17,11 +17,18 @@ from auth.model.errors import ApplicationError, HtmlStatusCode session = dbapp.session -def get_kontaktadressen(): +def get_kontaktadressen(projekt_id: int | None = None): """ - Liefert alle Kontaktadressen, sortiert nach Name. + Liefert Kontaktadressen, sortiert nach Name. + + Ist projekt_id gesetzt, werden nur die Kontaktadressen dieses + Projekts zurückgegeben (serverseitige Filterung). Ohne projekt_id + werden alle Kontaktadressen geliefert. """ - return Kontaktadresse.query.order_by(Kontaktadresse.name).all() + query = Kontaktadresse.query + if projekt_id is not None: + query = query.filter(Kontaktadresse.projekt_id == projekt_id) + return query.order_by(Kontaktadresse.name).all() def get_kontaktadresse(kontaktadresse_id: int) -> Kontaktadresse: diff --git a/App_Server/root/crm/model/projekt.py b/App_Server/root/crm/model/projekt.py index c6942b5..0f4ad87 100644 --- a/App_Server/root/crm/model/projekt.py +++ b/App_Server/root/crm/model/projekt.py @@ -17,11 +17,18 @@ from auth.model.errors import ApplicationError, HtmlStatusCode session = dbapp.session -def get_projekte(): +def get_projekte(kunden_id: int | None = None): """ - Liefert alle Projekte, sortiert nach projektname. + Liefert Projekte, sortiert nach projektname. + + Ist kunden_id gesetzt, werden nur die Projekte dieses Kunden + zurückgegeben (serverseitige Filterung). Ohne kunden_id werden + alle Projekte geliefert. """ - return Projekt.query.order_by(Projekt.projektname).all() + query = Projekt.query + if kunden_id is not None: + query = query.filter(Projekt.kunden_id == kunden_id) + return query.order_by(Projekt.projektname).all() def get_projekt(projekt_id: int) -> Projekt: diff --git a/App_Server/root/crm/routes.py b/App_Server/root/crm/routes.py index 1937dda..fc623e5 100644 --- a/App_Server/root/crm/routes.py +++ b/App_Server/root/crm/routes.py @@ -80,8 +80,9 @@ def kunde_delete(): @app.get("/xhr/kontakt/list") @authorisation_required([MITARBEITER, KUNDEN_MANAGER]) def kontakt_list(): - """Liefert alle Kontakte als Liste.""" - result = [k.to_dict() for k in kontakte.get_kontakte()] + """Liefert Kontakte als Liste. Optional nach kunden_id gefiltert.""" + kunden_id = request.args.get("kunden_id", type=int) + result = [k.to_dict() for k in kontakte.get_kontakte(kunden_id)] return jsonify(result), 200 @@ -125,8 +126,9 @@ def kontakt_delete(): @app.get("/xhr/projekt/list") @authorisation_required([MITARBEITER, KUNDEN_MANAGER]) def projekt_list(): - """Liefert alle Projekte als Liste.""" - result = [p.to_dict() for p in projekte.get_projekte()] + """Liefert Projekte als Liste. Optional nach kunden_id gefiltert.""" + kunden_id = request.args.get("kunden_id", type=int) + result = [p.to_dict() for p in projekte.get_projekte(kunden_id)] return jsonify(result), 200 @@ -170,8 +172,9 @@ def projekt_delete(): @app.get("/xhr/kontaktadresse/list") @authorisation_required([MITARBEITER, KUNDEN_MANAGER]) def kontaktadresse_list(): - """Liefert alle Kontaktadressen als Liste.""" - result = [k.to_dict() for k in kontaktadressen.get_kontaktadressen()] + """Liefert Kontaktadressen als Liste. Optional nach projekt_id gefiltert.""" + projekt_id = request.args.get("projekt_id", type=int) + result = [k.to_dict() for k in kontaktadressen.get_kontaktadressen(projekt_id)] return jsonify(result), 200 @@ -215,8 +218,9 @@ def kontaktadresse_delete(): @app.get("/xhr/calllog/list") @authorisation_required([MITARBEITER, KUNDEN_MANAGER]) def calllog_list(): - """Liefert alle Gesprächsnotizen als Liste.""" - result = [l.to_dict() for l in call_logs.get_call_logs()] + """Liefert Gesprächsnotizen als Liste. Optional nach projekt_id gefiltert.""" + projekt_id = request.args.get("projekt_id", type=int) + result = [l.to_dict() for l in call_logs.get_call_logs(projekt_id)] return jsonify(result), 200 diff --git a/Client/Components/call_log.ts b/Client/Components/call_log.ts index ba8a893..c6df511 100644 --- a/Client/Components/call_log.ts +++ b/Client/Components/call_log.ts @@ -5,7 +5,7 @@ import {Select, iSelect, iSelectOption} from "./widgets/select" import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} from "./widgets/Treetable_V2" import {iCallLog, fetchCallLogs, fetchCallLogById, createCallLogREST, updateCallLogREST} from "../models/call_log" import {iKunde, fetchKunden} from "../models/kunde" -import {iProjekt, fetchProjekte} from "../models/projekt" +import {iProjekt, fetchProjekte, fetchProjektById} from "../models/projekt" import {iKontaktadresse, fetchKontaktadressen} from "../models/kontaktadresse" // Aktualisierungsintervall für die Dauer-Anzeige in Millisekunden. @@ -15,10 +15,11 @@ export const call_log_liste: m.ClosureComponent = (vnode) => { let treenodes: iTreenode[] = [] let selectedNode: iTreenode | null = null let filterProjektId: number | null = null - // Projektname für die Überschrift, asynchron aufgelöst. + // Projektname für die Überschrift, asynchron per fetchProjektById + // aufgelöst. kunden_name wird ebenfalls aus dem Projekt bezogen, + // da es nicht in den Route-Params enthalten ist. let projektName: string = "" - // Projektliste asynchron laden, für Projektauflösung beim Neuanlegen. - let projektListe: iProjekt[] = [] + let kundenName: string = "" const handleRowMarked = (node: iTreenode) => { selectedNode = node @@ -41,12 +42,9 @@ export const call_log_liste: m.ClosureComponent = (vnode) => { erledigt: false } if (filterProjektId !== null) { - const projekt = projektListe.find((p: iProjekt) => p.id === filterProjektId) - if (projekt) { - params.projekt_id = projekt.id - params.projekt_name = projekt.projektname - params.kunden_name = projekt.kunden_name - } + params.projekt_id = filterProjektId + params.projekt_name = projektName + params.kunden_name = kundenName } m.route.set("/call_log", params) } @@ -94,24 +92,22 @@ export const call_log_liste: m.ClosureComponent = (vnode) => { filterProjektId = null projektName = "" } - // Gesprächsnotizen asynchron vom Server laden. - fetchCallLogs().then((result) => { - let filtered = result - if (filterProjektId !== null) { - filtered = result.filter((l: iCallLog) => l.projekt_id === filterProjektId) - } - treenodes = filtered.map((log: iCallLog) => new Treenode(log, true, [])) + // Gesprächsnotizen asynchron vom Server laden. Die Filterung + // nach projekt_id erfolgt serverseitig, sofern ein Filter + // gesetzt ist. + fetchCallLogs(filterProjektId !== null ? filterProjektId : undefined).then((result) => { + treenodes = result.map((log: iCallLog) => new Treenode(log, true, [])) }) - // Projektliste asynchron laden, für Projektnamen-Auflösung. - fetchProjekte().then((result) => { - projektListe = result - if (filterProjektId !== null && projektName === "") { - const projekt = projektListe.find((p: iProjekt) => p.id === filterProjektId) - if (projekt) { + // Projektnamen und Kundennamen asynchron auflösen, falls + // nicht in den Route-Params enthalten. + if (filterProjektId !== null) { + fetchProjektById(filterProjektId).then((projekt: iProjekt) => { + if (projektName === "") { projektName = projekt.projektname } - } - }) + kundenName = projekt.kunden_name + }) + } }, view: (vnode) => { return m("div.w3-container.w3-auto", { diff --git a/Client/Components/kontakt.ts b/Client/Components/kontakt.ts index f9afc87..5b14d61 100644 --- a/Client/Components/kontakt.ts +++ b/Client/Components/kontakt.ts @@ -3,18 +3,15 @@ import {Form, iForm} from "./widgets/form" import { Input, iInput, Textarea, iTextarea} from "./widgets/inputs" import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} from "./widgets/Treetable_V2" import {iKontakt, fetchKontakte, fetchKontaktById, createKontaktREST, updateKontaktREST} from "../models/kontakt" -import {iKunde, fetchKunden} from "../models/kunde" +import {iKunde, fetchKundeById} from "../models/kunde" export const kontaktliste: m.ClosureComponent = (vnode) => { let treenodes: iTreenode[] = [] let selectedNode: iTreenode | null = null let filterKundenId: number | null = null - // Kundenname für die Überschrift, asynchron aus der Kundenliste - // aufgelöst. + // Kundenname für die Überschrift, asynchron per fetchKundeById + // aufgelöst, falls nicht in den Route-Params enthalten. let kundenName: string = "" - // Kundenliste asynchron vom Server laden, für die Kundenauflösung - // beim Anlegen eines neuen Kontakts. - let kundenListe: iKunde[] = [] const handleRowMarked = (node: iTreenode) => { selectedNode = node @@ -24,11 +21,8 @@ export const kontaktliste: m.ClosureComponent = (vnode) => { const params: any = {id: -1} // Nur bei kundengefilterter Liste den Kunden vorbelegen. if (filterKundenId !== null) { - const kunde = kundenListe.find((k: iKunde) => k.id === filterKundenId) - if (kunde) { - params.kunden_id = kunde.id - params.kunden_name = kunde.name - } + params.kunden_id = filterKundenId + params.kunden_name = kundenName } m.route.set("/kontakt", params) } @@ -71,27 +65,17 @@ export const kontaktliste: m.ClosureComponent = (vnode) => { filterKundenId = null kundenName = "" } - // Kontakte asynchron vom Server laden. m.request löst nach - // Promise-Auflösung automatisch ein Redraw aus. - fetchKontakte().then((result) => { - let filtered = result - if (filterKundenId !== null) { - filtered = result.filter((k: iKontakt) => k.kunden_id === filterKundenId) - } - treenodes = filtered.map((kontakt: iKontakt) => new Treenode(kontakt, true, [])) - }) - // Kundenliste asynchron vom Server laden. - fetchKunden().then((result) => { - kundenListe = result - // Fallback: Falls kunden_name nicht in den Route-Params - // enthalten war, aus der Kundenliste auflösen. - if (filterKundenId !== null && kundenName === "") { - const kunde = kundenListe.find((k: iKunde) => k.id === filterKundenId) - if (kunde) { - kundenName = kunde.name - } - } + // Kontakte asynchron vom Server laden. Die Filterung nach + // kunden_id erfolgt serverseitig, sofern ein Filter gesetzt ist. + fetchKontakte(filterKundenId !== null ? filterKundenId : undefined).then((result) => { + treenodes = result.map((kontakt: iKontakt) => new Treenode(kontakt, true, [])) }) + // Kundennamen asynchron auflösen, falls nicht in Route-Params. + if (filterKundenId !== null && kundenName === "") { + fetchKundeById(filterKundenId).then((kunde: iKunde) => { + kundenName = kunde.name + }) + } }, view: (vnode) => { return m("div.w3-container.w3-auto", { diff --git a/Client/Components/kontaktadresse.ts b/Client/Components/kontaktadresse.ts index b66b56c..25e079b 100644 --- a/Client/Components/kontaktadresse.ts +++ b/Client/Components/kontaktadresse.ts @@ -3,18 +3,15 @@ import {Form, iForm} from "./widgets/form" import { Input, iInput, Textarea, iTextarea} from "./widgets/inputs" import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} from "./widgets/Treetable_V2" import {iKontaktadresse, fetchKontaktadressen, fetchKontaktadresseById, createKontaktadresseREST, updateKontaktadresseREST} from "../models/kontaktadresse" -import {iProjekt, fetchProjekte} from "../models/projekt" +import {iProjekt, fetchProjektById} from "../models/projekt" export const kontaktadresseliste: m.ClosureComponent = (vnode) => { let treenodes: iTreenode[] = [] let selectedNode: iTreenode | null = null let filterProjektId: number | null = null - // Projektname für die Überschrift, asynchron aus der Projektliste - // aufgelöst. + // Projektname für die Überschrift, asynchron per fetchProjektById + // aufgelöst, falls nicht in den Route-Params enthalten. let projektName: string = "" - // Projektliste asynchron vom Server laden, für die Projektauflösung - // beim Anlegen einer neuen Kontaktadresse. - let projektListe: iProjekt[] = [] const handleRowMarked = (node: iTreenode) => { selectedNode = node @@ -23,11 +20,8 @@ export const kontaktadresseliste: m.ClosureComponent = (vnode) => { const addKontaktadresseRoute = () => { const params: any = {id: -1} if (filterProjektId !== null) { - const projekt = projektListe.find((p: iProjekt) => p.id === filterProjektId) - if (projekt) { - params.projekt_id = projekt.id - params.projekt_name = projekt.projektname - } + params.projekt_id = filterProjektId + params.projekt_name = projektName } m.route.set("/kontaktadresse", params) } @@ -97,24 +91,18 @@ export const kontaktadresseliste: m.ClosureComponent = (vnode) => { filterProjektId = null projektName = "" } - // Kontaktadressen asynchron vom Server laden. - fetchKontaktadressen().then((result) => { - let filtered = result - if (filterProjektId !== null) { - filtered = result.filter((k: iKontaktadresse) => k.projekt_id === filterProjektId) - } - treenodes = filtered.map((kontaktadresse: iKontaktadresse) => new Treenode(kontaktadresse, true, [])) - }) - // Projektliste asynchron vom Server laden. - fetchProjekte().then((result) => { - projektListe = result - if (filterProjektId !== null && projektName === "") { - const projekt = projektListe.find((p: iProjekt) => p.id === filterProjektId) - if (projekt) { - projektName = projekt.projektname - } - } + // Kontaktadressen asynchron vom Server laden. Die Filterung + // nach projekt_id erfolgt serverseitig, sofern ein Filter + // gesetzt ist. + fetchKontaktadressen(filterProjektId !== null ? filterProjektId : undefined).then((result) => { + treenodes = result.map((kontaktadresse: iKontaktadresse) => new Treenode(kontaktadresse, true, [])) }) + // Projektnamen asynchron auflösen, falls nicht in Route-Params. + if (filterProjektId !== null && projektName === "") { + fetchProjektById(filterProjektId).then((projekt: iProjekt) => { + projektName = projekt.projektname + }) + } }, view: (vnode) => { return m("div.w3-container.w3-auto", { diff --git a/Client/Components/projekt.ts b/Client/Components/projekt.ts index 9c909fa..1d21235 100644 --- a/Client/Components/projekt.ts +++ b/Client/Components/projekt.ts @@ -3,18 +3,15 @@ import {Form, iForm} from "./widgets/form" import { Input, iInput} from "./widgets/inputs" import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} from "./widgets/Treetable_V2" import {iProjekt, fetchProjekte, fetchProjektById, createProjektREST, updateProjektREST} from "../models/projekt" -import {iKunde, fetchKunden} from "../models/kunde" +import {iKunde, fetchKundeById} from "../models/kunde" export const projektliste: m.ClosureComponent = (vnode) => { let treenodes: iTreenode[] = [] let selectedNode: iTreenode | null = null let filterKundenId: number | null = null - // Kundenname für die Überschrift, asynchron aus der Kundenliste - // aufgelöst. + // Kundenname für die Überschrift, asynchron per fetchKundeById + // aufgelöst, falls nicht in den Route-Params enthalten. let kundenName: string = "" - // Kundenliste asynchron vom Server laden, für die Kundenauflösung - // beim Anlegen eines neuen Projekts. - let kundenListe: iKunde[] = [] const handleRowMarked = (node: iTreenode) => { selectedNode = node @@ -24,11 +21,8 @@ export const projektliste: m.ClosureComponent = (vnode) => { const params: any = {id: -1} // Nur bei kundengefilterter Liste den Kunden vorbelegen. if (filterKundenId !== null) { - const kunde = kundenListe.find((k: iKunde) => k.id === filterKundenId) - if (kunde) { - params.kunden_id = kunde.id - params.kunden_name = kunde.name - } + params.kunden_id = filterKundenId + params.kunden_name = kundenName } m.route.set("/projekt", params) } @@ -108,24 +102,17 @@ export const projektliste: m.ClosureComponent = (vnode) => { filterKundenId = null kundenName = "" } - // Projekte asynchron vom Server laden. - fetchProjekte().then((result) => { - let filtered = result - if (filterKundenId !== null) { - filtered = result.filter((p: iProjekt) => p.kunden_id === filterKundenId) - } - treenodes = filtered.map((projekt: iProjekt) => new Treenode(projekt, true, [])) - }) - // Kundenliste asynchron vom Server laden. - fetchKunden().then((result) => { - kundenListe = result - if (filterKundenId !== null && kundenName === "") { - const kunde = kundenListe.find((k: iKunde) => k.id === filterKundenId) - if (kunde) { - kundenName = kunde.name - } - } + // Projekte asynchron vom Server laden. Die Filterung nach + // kunden_id erfolgt serverseitig, sofern ein Filter gesetzt ist. + fetchProjekte(filterKundenId !== null ? filterKundenId : undefined).then((result) => { + treenodes = result.map((projekt: iProjekt) => new Treenode(projekt, true, [])) }) + // Kundennamen asynchron auflösen, falls nicht in Route-Params. + if (filterKundenId !== null && kundenName === "") { + fetchKundeById(filterKundenId).then((kunde: iKunde) => { + kundenName = kunde.name + }) + } }, view: (vnode) => { return m("div.w3-container.w3-auto", { diff --git a/Client/models/call_log.ts b/Client/models/call_log.ts index 75b2c75..91baa7c 100644 --- a/Client/models/call_log.ts +++ b/Client/models/call_log.ts @@ -36,11 +36,19 @@ const CALLLOG_REST_BASE = "/xhr/calllog" /** * Lädt die Liste der Gesprächsnotizen vom Server. + * Ist projektId gesetzt (>= 0), werden nur die Notizen dieses Projekts + * geliefert (serverseitige Filterung). Ohne Parameter werden alle + * Notizen geliefert. */ -export function fetchCallLogs(): Promise { +export function fetchCallLogs(projektId?: number): Promise { + const params: Record = {} + if (projektId !== undefined && projektId >= 0) { + params.projekt_id = projektId + } return m.request({ method: "GET", - url: CALLLOG_REST_BASE + "/list" + url: CALLLOG_REST_BASE + "/list", + params: params }).then((result) => result as iCallLog[]) } diff --git a/Client/models/kontakt.ts b/Client/models/kontakt.ts index 5f2bd58..70b085c 100644 --- a/Client/models/kontakt.ts +++ b/Client/models/kontakt.ts @@ -33,11 +33,19 @@ const KONTAKT_REST_BASE = "/xhr/kontakt" /** * Lädt die Kontaktliste vom Server. + * Ist kundenId gesetzt (>= 0), werden nur die Kontakte dieses Kunden + * geliefert (serverseitige Filterung). Ohne Parameter werden alle + * Kontakte geliefert. */ -export function fetchKontakte(): Promise { +export function fetchKontakte(kundenId?: number): Promise { + const params: Record = {} + if (kundenId !== undefined && kundenId >= 0) { + params.kunden_id = kundenId + } return m.request({ method: "GET", - url: KONTAKT_REST_BASE + "/list" + url: KONTAKT_REST_BASE + "/list", + params: params }).then((result) => result as iKontakt[]) } diff --git a/Client/models/kontaktadresse.ts b/Client/models/kontaktadresse.ts index 247ddbf..c6b1884 100644 --- a/Client/models/kontaktadresse.ts +++ b/Client/models/kontaktadresse.ts @@ -38,11 +38,19 @@ const KONTAKTADRESSE_REST_BASE = "/xhr/kontaktadresse" /** * Lädt die Kontaktadressenliste vom Server. + * Ist projektId gesetzt (>= 0), werden nur die Kontaktadressen dieses + * Projekts geliefert (serverseitige Filterung). Ohne Parameter werden + * alle Kontaktadressen geliefert. */ -export function fetchKontaktadressen(): Promise { +export function fetchKontaktadressen(projektId?: number): Promise { + const params: Record = {} + if (projektId !== undefined && projektId >= 0) { + params.projekt_id = projektId + } return m.request({ method: "GET", - url: KONTAKTADRESSE_REST_BASE + "/list" + url: KONTAKTADRESSE_REST_BASE + "/list", + params: params }).then((result) => result as iKontaktadresse[]) } diff --git a/Client/models/projekt.ts b/Client/models/projekt.ts index 9776345..0dd60fe 100644 --- a/Client/models/projekt.ts +++ b/Client/models/projekt.ts @@ -30,11 +30,19 @@ const PROJEKT_REST_BASE = "/xhr/projekt" /** * Lädt die Projektliste vom Server. + * Ist kundenId gesetzt (>= 0), werden nur die Projekte dieses Kunden + * geliefert (serverseitige Filterung). Ohne Parameter werden alle + * Projekte geliefert. */ -export function fetchProjekte(): Promise { +export function fetchProjekte(kundenId?: number): Promise { + const params: Record = {} + if (kundenId !== undefined && kundenId >= 0) { + params.kunden_id = kundenId + } return m.request({ method: "GET", - url: PROJEKT_REST_BASE + "/list" + url: PROJEKT_REST_BASE + "/list", + params: params }).then((result) => result as iProjekt[]) }