Commit Graph
13 Commits
Author SHA1 Message Date
schakal00783andClaude Opus 5 ff71cb40e1 docs: Sitzungsdauer und .env-Werte im README festhalten
Die Umstellung auf eine Woche stand bisher nur im Code und in der
Commit-Nachricht. Im README fehlte ueberhaupt eine Aussage dazu, wann
jemand ausgeloggt wird - eine Frage, die im Betrieb regelmaessig kommt.

Dazu die Werte, die nur in der Produktions-.env stehen und ein Deployment
ueberdauern, samt Hinweis, dass eine Aenderung dort einen Neustart des
Dienstes braucht.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 21:56:54 +02:00
schakal00783andClaude Opus 5 79bfd561bd 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>
2026-09-21 15:06:26 +02:00
schakal00783andClaude Opus 5 532b15ce99 feat(hrworks): Abwesenheiten taeglich aus HRworks uebernehmen
Urlaub, Krankheit, Elternzeit und Feiertage stehen in HRworks. Pulse holt
sie einmal taeglich und bucht sie auf das nicht abrechenbare Projekt
"Abwesenheiten". Nach HRworks geht nichts zurueck, es wird nur gelesen.

Die Stunden rechnet HRworks selbst: available-working-hours liefert je
Person und Tag das Soll und die verfuegbaren Stunden, die Differenz sind
die Abwesenheitsstunden. Damit stimmen Teilzeit und halbe Tage ohne
eigenen Kalender. relatedEvents nennt den Auslöser; Vorrang hat der
Feiertag, dann Krankheit, dann Abwesenheit.

Abgesprochene Regeln: nur genehmigte Abwesenheiten (validatedOk), bei
Krankmeldungen auch offene Bescheinigungen; nur bis heute buchen, der
Rest waechst taeglich nach; halbe Tage feste 4 Stunden; jeder aktive
Mitarbeiter wird dem Projekt zugewiesen, auch neue.

Jede Buchung traegt hrworks_ref = "<Nummer>@<Tag>" (unique). Ein zweiter
Lauf aendert nichts, Stornos und Verschiebungen verschwinden im naechsten
Lauf, DELETE /api/hrworks/entries entfernt genau diese Buchungen.

Nicht abrechenbare Projekte brauchen keinen Budgettopf mehr. Ein Topf
traegt Volumen, Laufzeit und Satz - das hat nur bei Billable und
Festpreis Sinn. Am Abwesenheitsprojekt war er nur ein Ablaufdatum, das
irgendwann Buchungen blockiert haette.

Taeglicher Lauf ueber systemd (perinco-pulse-hrworks.timer, 5:30 Uhr
Berliner Zeit, Persistent). Bedienung unter Einstellungen ->
Abwesenheiten (HRworks) mit Verbindungstest und Probelauf; der geheime
Schluessel liegt AES-verschluesselt in der Datenbank.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 14:50:54 +02:00
schakal00783andClaude Opus 5 7e7188d476 feat(saetze): Satz je Task plus Import-Endpunkt fuer Harvest
Satz je Task
  Ein Task kann einen eigenen Satz tragen; leer bedeutet weiter "Satz des
  Budgettopfs" bzw. bei Abruf "Satz des Projekts". Damit lassen sich
  "remote" und "vor Ort" im selben Projekt abrechnen, ohne dafuer getrennte
  Projekte anzulegen.

  Das Budget bleibt in Stunden - gemischte Saetze aendern nichts daran, wann
  ein Topf voll ist. Der Umsatz wird je Task-Satz aufsummiert: loadProjects
  gruppiert den Topfverbrauch nach Task-Satz und gibt das Ergebnis an
  potStats, Abruf rechnet je Task. Mitarbeiter sehen keine Saetze, auch
  nicht die am Task.

Import
  POST /api/imports/entries (nur Administrator) schreibt Buchungen fuer
  beliebige Personen - POST /api/entries bucht bewusst nur auf den
  angemeldeten Benutzer. Der Import laeuft durch dieselben Regeln: Projekt
  aktiv, Person zugewiesen, Topf passend zum Datum, maximal 24 h am Tag.
  dry_run schreibt nichts.

  Jede uebernommene Buchung traegt ihre harvest_id (unique). Ein zweiter
  Lauf legt nichts doppelt an. DELETE /api/imports/entries entfernt genau
  die uebernommenen Zeiten; handgetippte bleiben unberuehrt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 11:37:26 +02:00
schakal00783andClaude Opus 5 7606df9280 fix(export): "Summe" im PDF nicht mehr in der schmalen Spalte quetschen
Die Summenzeile des Stundennachweises trug ihre Beschriftung in der Spalte
"Tag". Die ist rund 40 Punkt breit, also brach "Summe" dort in drei Zeilen
um ("Su/mm/e"), waehrend die Zahlen rechts daneben in einer Zeile standen.

drawRow laesst in der Summenzeile eine Beschriftung jetzt in die leeren
Spalten dahinter laufen und setzt sie einzeilig. Gefuellte Spalten werden
nicht einverleibt, die Zahlen bleiben also an ihrem Platz. Das gilt fuer
alle PDF-Tabellen, nicht nur den Stundennachweis; Excel und CSV waren nie
betroffen.

Rueckbau: diesen Commit reverten - die Aenderung steckt allein in
export/render.js.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 10:11:27 +02:00
schakal00783andClaude Opus 5 9576d18abc feat(profil): eigenes Profilbild hochladen
Jeder setzt sein Bild unter "Mein Konto", Vorgesetzte und Administratoren
auch fuer die Benutzer, die sie verwalten. Wo bisher die Initialen standen,
steht jetzt das Bild - Seitenleiste, Kopfzeile, Team, Dashboard, Reports,
Projekte, Kunden und die Buchungsliste. Ohne Bild bleiben die Initialen.

Der Browser verkleinert das Bild vor dem Hochladen auf ein Quadrat von
256 px und schneidet mittig zu (lib/imageCompress.ts): aus einem 4-MB-Foto
werden wenige Kilobyte, und der Server muss kein Bild verarbeiten.

Gespeichert wird es als Data-URL in pulse_users.avatar (LONGTEXT). Kein
Upload-Verzeichnis, das gesichert und beim Deployment am Leben gehalten
werden muesste.

Geprueft wird serverseitig in lib/avatar.js: nur PNG, JPEG, GIF, WebP und
AVIF als Data-URL, hoechstens 2 MB. SVG ist bewusst nicht erlaubt, es kann
Skripte tragen. Externe http(s)-Adressen ebenso wenig - sonst laedt jeder
Seitenaufruf ein Bild von einem fremden Server und verraet, wer wann mit
Pulse arbeitet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 23:06:02 +02:00
schakal00783andClaude Opus 5 d9d46fea6a feat(pixel): 8-Bit-Handwerker laeuft einmal pro Minute durch die Seitenleiste
Im Theme "Pixel Art" laeuft eine kleine Figur ueber die Steinmauer der
Seitenleiste, macht einen Huepfer und verschwindet wieder nach links.
Auftritt alle 60 Sekunden, der erste nach 6 Sekunden.

Die Figur ist eine eigene Zeichnung - gruene Muetze, Schutzbrille,
Latzhose - und steht als Pixelraster 14 x 16 direkt im Code, ohne
Bilddatei. Bewusst kein Sprite aus einem kommerziellen Spiel: solche
Figuren und Grafiken sind geschuetzt und gehoeren nicht in eine
produktiv genutzte Anwendung.

Technik wie bei den Wetter-Ebenen: ein Canvas genau ueber der
Seitenleiste, ohne Klickfang, unter den Dialogen, per ResizeObserver an
die ein- und ausklappbare Breite gebunden. Zwischen zwei Auftritten
laeuft ein Timer statt eines Animationsframes. Bei "Bewegung reduzieren"
und unterhalb der Tabletbreite bleibt alles aus.

Rueckbau: diesen Commit reverten, oder die Datei loeschen und die eine
Zeile in lib/theme.tsx entfernen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 12:10:19 +02:00
schakal00783andClaude Opus 5 3b818bd1c6 fix(projekte): Kacheln als durchgehendes Raster statt Kundengruppen
Jede Kundenueberschrift begann eine neue Rasterzeile. Bei einem Projekt je
Kunde stand dadurch eine Kachel allein in der Zeile und rechts blieb der
Platz leer - auf der Produktion mit fuenf Kunden untereinander sichtbar.

Die Kacheln laufen jetzt in einem Raster, sortiert nach Kunde und Projekt.
Der Kundenname steht dafuer neben dem Projektcode auf der Kachel selbst.
Die Detailansicht steht wie in der Liste unter der Uebersicht.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 11:49:36 +02:00
schakal00783andClaude Opus 5 be37d89d53 feat(projekte): Listenansicht der Projektuebersicht
Umschalter Karten/Liste neben dem Statusfilter, die Wahl merkt sich der
Browser. Die Liste zeigt alle gefilterten Projekte flach ueber die Kunden
hinweg und sortierbar: Projekt mit Code, Toepfen und Tasks, Kunde, Typ,
gebuchte Stunden und Tage, Budget mit freiem Rest, Verbrauchsbalken,
offener Betrag mit erbrachtem Umsatz und das Team. Beträge nur ab
Vorgesetztem.

Ein Klick auf eine Zeile oeffnet dieselbe Detailansicht wie eine Kachel,
in der Liste unterhalb der Tabelle. Dafuer ist der Detailblock einmal
aufgebaut und wird in beiden Ansichten verwendet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 11:28:42 +02:00
schakal00783andClaude Opus 5 ef7171496f feat(ansichten): Listenansicht fuer Buchungen, Report und Kunden
Neue Komponente EntryList zeigt einzelne Buchungen als sortierbare Tabelle
mit Datum, Kunde, Projekt, Task und der Zeit getrennt nach billable und
non-billable, inklusive Summen.

- Buchung: Umschalter Kalender/Liste neben Woche/Monat/Jahr, Auswahl wird
  im Browser gemerkt. Klick auf eine Zeile oeffnet die Buchung.
- Reports: Klick auf einen Mitarbeiter oeffnet dessen Buchungen des
  Reportzeitraums als Liste; ein gesetzter Kundenfilter wird uebernommen.
- Kunden: Umschalter Karten/Liste mit Ort, Projekten, Stunden und Umsatz.
- GET /api/entries akzeptiert dafuer customer_id, Segmented nimmt Icons,
  Modal kennt die Groesse 2xl.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 10:14:48 +02:00
schakal00783andClaude Opus 5 8c93df11d9 feat(projekte): Projekttyp "Abruf" fuer Abrechnung nach Aufwand
Abruf braucht keinen Budgettopf: es gibt kein zugesagtes Volumen und kein
Ablaufdatum, gebucht wird, was anfaellt. Der Satz kann deshalb nicht am
Topf haengen und steht am Projekt (pulse_projects.rate, Stunde oder Tag).
Der Umsatz sind die abrechenbaren Stunden mal diesem Satz; offen ist
nichts.

- Migration ergaenzt den ENUM-Wert und die Spalte rate
- Anlegen verlangt einen Satz und nimmt keine Toepfe an; ein Topf
  nachtraeglich anzulegen wird abgewiesen
- Wechsel auf Abruf nur, solange auf den Toepfen nichts gebucht ist -
  sonst wuerden Buchungen ihr Budget verlieren; leere Toepfe fallen weg
- Buchungsdialog, Projektkarte, Projektdetail und Projektreport zeigen
  Satz und Umsatz statt Budgetbalken

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 00:13:35 +02:00
schakal00783andClaude Opus 5 413104ae73 docs: Themes und Gitea-Repos im README
Uebersicht der sieben Farbwelten mit ihren Animationen, dazu die vier
Stellen, die ein neues Theme braucht - die Pruefung im Backend wurde
beim Nachziehen von Rainy, Snow und Aura dreimal vergessen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 22:53:37 +02:00
schakal00783andClaude Opus 5 ef676a95f4 feat: perinco Pulse - Zeiterfassung fuer perinco
Erste Fassung, live unter https://pulse.perinco.de.

Frontend React 19 + TypeScript + Tailwind 4 (Vite), Backend Express mit
mysql2 auf MariaDB, Exporte als PDF (pdfkit), Excel (exceljs) und CSV.
Aufbau, Designsystem und die sieben Themes kommen aus dem perinco CRM.

Fachlich:
- Buchung mit Wochen-, Monats- und Jahresansicht, Zeiten als 1:23 oder 1,5
- Projekte mit n Budgettoepfen: Laufzeit, Volumen, Sperre bei Ablauf oder
  aufgebrauchtem Budget, Buchung nur auf den passenden Topf
- Kunden, Team mit Hierarchie, Reports im Harvest-Stil, Monatsexporte
- Rollen user/lead/management/admin, doppelt geprueft (Menue und Backend)
- SSO gegen Microsoft Entra ID, Uebernahme der AD-Benutzer per Drag & Drop

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 22:52:20 +02:00