feat(mehrere): Auslastung bis heute, Satz je Task bearbeitbar, Bestellnummer, Rolle Externe

Auslastung
  Verglichen werden jetzt die bis heute gebuchten Stunden mit der Kapazitaet
  bis heute. Die Kapazitaet endete schon immer am heutigen Tag; wurde im
  Voraus gebucht - eine ganze Woche Urlaub am Montag - standen 40 Stunden
  gegen 8 Stunden Kapazitaet und die Wochenansicht zeigte 500 Prozent. Die
  Spalte "Stunden" zeigt weiter den ganzen Zeitraum.

Tasks
  Das Feld zum Hinzufuegen lief aus der Karte heraus: die Zeile darf jetzt
  umbrechen, und die Breitenangaben tragen "!", weil inputClass w-full
  mitbringt und sonst gewinnt. Der Satz eines Tasks steht als schmales Feld
  direkt in der Zeile und wird beim Verlassen gespeichert - vorher lag er
  hinter einem Abfragedialog und war praktisch nicht zu finden.

Export der Buchungen
  Kunde und Projekt kommen nur noch aus den eigenen Zuweisungen, auch fuer
  Vorgesetzte. Eine Liste aller Projekte des Hauses war dort nur Suchaufwand.

Budgettopf
  Neues Feld Bestellnummer (optional). Es erscheint im Stundennachweis als
  eigene Kopfzeile, wenn die gebuchten Toepfe eine tragen.

Rolle "Externe"
  Gleiche Rechte wie "Mitarbeiter" und derselbe Rang, die Rolle trennt nur,
  wer zum Haus gehoert. Sie setzt is_external automatisch, damit Liste und
  Kennzeichnung nicht widersprechen. Vorgesetzte duerfen sie vergeben.
  Stellen, die bisher role === 'user' pruefen, fragen nun hasRole(lead) ab -
  sonst haetten Externe alle Projekte gesehen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
schakal00783
2026-09-21 15:06:26 +02:00
co-authored by Claude Opus 5
parent 532b15ce99
commit 79bfd561bd
21 changed files with 246 additions and 72 deletions
+34 -2
View File
@@ -55,6 +55,15 @@ export async function buildTimeReport(viewer, query) {
);
const capTo = to > today() ? today() : to;
/*
* Auslastung nur bis heute. Die Kapazitaet endet ohnehin am heutigen Tag
* (capTo) - werden Zeiten im Voraus gebucht, etwa eine ganze Woche Urlaub am
* Montag, stehen sonst 40 gebuchte Stunden gegen 8 Stunden Kapazitaet und die
* Auslastung springt auf 500 Prozent. Die Spalte "Stunden" zeigt weiter
* alles, was im Zeitraum gebucht ist.
*/
const bisHeute = entries.filter((e) => e.entry_date <= capTo);
const minutes = entries.reduce((s, e) => s + e.minutes, 0);
const billable = entries.filter((e) => e.billable).reduce((s, e) => s + e.minutes, 0);
const amount = entries.reduce((s, e) => s + e.amount, 0);
@@ -70,10 +79,13 @@ export async function buildTimeReport(viewer, query) {
}
}
const byId = new Map(users.map((u) => [u.id, u]));
const minutenBisHeute = new Map();
for (const e of bisHeute) minutenBisHeute.set(e.user_id, (minutenBisHeute.get(e.user_id) || 0) + e.minutes);
for (const row of userRows) {
const u = byId.get(row.user_id);
row.capacity_minutes = u && from <= capTo ? capacityMinutes(u, settings, from, capTo) : 0;
row.utilization = row.capacity_minutes ? Math.round((row.minutes / row.capacity_minutes) * 100) : null;
row.minutes_until_today = minutenBisHeute.get(row.user_id) || 0;
row.utilization = row.capacity_minutes ? Math.round((row.minutes_until_today / row.capacity_minutes) * 100) : null;
}
return {
@@ -87,6 +99,8 @@ export async function buildTimeReport(viewer, query) {
amount_without_fixed: r2(amount),
fixed_amount: r2(fixedAmount),
entries: entries.length,
// Fuer die Auslastung: nur was bis heute gebucht ist.
minutes_until_today: bisHeute.reduce((s, e) => s + e.minutes, 0),
},
clients: group(entries, (e) => e.customer_id, (e) => ({ customer_id: e.customer_id, name: e.customer_name, color: e.customer_color }), includeFixed),
projects: group(
@@ -153,6 +167,15 @@ function buildProjectDetail(project, entries) {
};
});
/*
* Auslastung nur bis heute. Die Kapazitaet endet ohnehin am heutigen Tag
* (capTo) - werden Zeiten im Voraus gebucht, etwa eine ganze Woche Urlaub am
* Montag, stehen sonst 40 gebuchte Stunden gegen 8 Stunden Kapazitaet und die
* Auslastung springt auf 500 Prozent. Die Spalte "Stunden" zeigt weiter
* alles, was im Zeitraum gebucht ist.
*/
const bisHeute = entries.filter((e) => e.entry_date <= capTo);
const minutes = entries.reduce((s, e) => s + e.minutes, 0);
const billable = entries.filter((e) => e.billable).reduce((s, e) => s + e.minutes, 0);
return {
@@ -205,7 +228,16 @@ router.get('/customer/:id', async (req, res, next) => {
cursor.setUTCMonth(cursor.getUTCMonth() + 1);
}
const minutes = entries.reduce((s, e) => s + e.minutes, 0);
/*
* Auslastung nur bis heute. Die Kapazitaet endet ohnehin am heutigen Tag
* (capTo) - werden Zeiten im Voraus gebucht, etwa eine ganze Woche Urlaub am
* Montag, stehen sonst 40 gebuchte Stunden gegen 8 Stunden Kapazitaet und die
* Auslastung springt auf 500 Prozent. Die Spalte "Stunden" zeigt weiter
* alles, was im Zeitraum gebucht ist.
*/
const bisHeute = entries.filter((e) => e.entry_date <= capTo);
const minutes = entries.reduce((s, e) => s + e.minutes, 0);
const billable = entries.filter((e) => e.billable).reduce((s, e) => s + e.minutes, 0);
res.json({
customer,