diff --git a/lib/employee-detail-data.ts b/lib/employee-detail-data.ts index c7cf5cf..1321552 100644 --- a/lib/employee-detail-data.ts +++ b/lib/employee-detail-data.ts @@ -33,10 +33,22 @@ export async function loadEmployeeDetail(tx: Tx, id: string, today: string = tod const g = await tx .selectNoFrom((eb) => [ // Vorgesetzte und direkte Berichte stehen nirgends als Spalte — sie - // kommen aus om_reporting_lines(). Beide schränken *in* der Funktion - // ein, es wandern also neun Zeilen über die Leitung und nicht achthundert. - jsonArrayFrom(berichtslinienAbfrage(eb, asOf, { employeeId: id })).as("ownLines"), - jsonArrayFrom(berichtslinienAbfrage(eb, asOf, { actingManagerId: id })).as("reports"), + // kommen aus om_reporting_lines(). + // + // **Einmal** gerufen, nicht zweimal. Hier standen zwei Teilabfragen mit + // je einem Filter, in der Annahme, der Filter schränke die Funktion + // ein. Das tut er nicht: wegen des `with recursive` in ihrem Körper + // lässt sie sich nicht einbetten, sie rechnet jedes Mal alle Zeilen aus + // und der Filter wirft sie danach weg. Auf dem Server gemessen: + // + // explain analyze select * from om_reporting_lines(current_date) + // where employee_id = …; + // → Rows Removed by Filter: 783, Execution Time: 69 ms + // + // Zwei Filter hiessen also zweimal dieselbe Rechnung. Jetzt wandern + // einmal alle Zeilen herüber — ein paar hundert Kilobyte über eine + // Verbindung im selben Netz — und die Auswahl trifft der Aufrufer. + jsonArrayFrom(berichtslinienAbfrage(eb, asOf)).as("lines"), jsonArrayFrom( eb @@ -110,14 +122,15 @@ export async function loadEmployeeDetail(tx: Tx, id: string, today: string = tod .executeTakeFirstOrThrow(); const orgMaps = buildOrgMaps(g.units as never, g.locations as never); - const line = g.ownLines[0] ?? null; + const line = g.lines.find((l) => l.employee_id === id) ?? null; + const reports = g.lines.filter((l) => l.acting_manager_id === id); const placement = pickPlacements(g.placementRows as never, asOf).get(id) ?? null; // Namen für die beteiligten Personen in einem Zug: die Vertretung, die // formal zuständige Leitung und die direkten Berichte. const relatedIds = Array.from( new Set( - [line?.acting_manager_id, line?.formal_manager_id, ...g.reports.map((r) => r.employee_id)].filter( + [line?.acting_manager_id, line?.formal_manager_id, ...reports.map((r) => r.employee_id)].filter( (x): x is string => Boolean(x) ) ) @@ -137,7 +150,7 @@ export async function loadEmployeeDetail(tx: Tx, id: string, today: string = tod return { employee, line, - reports: g.reports, + reports, history: g.history, dependents: g.dependents, notes: g.notes,