umsatz-wochenbericht
Baut den wöchentlichen "Umsatz-Maßnahmenbericht" (PDF + editierbares .docx) für die Filialen, die der Nutzer diese Woche im Fokus hat - mit echten Firestore-Zahlen (Ist-Umsatz vs. Tagesziel) als Chart
SKILL.md.claude/skills/umsatz-wochenbericht/SKILL.md
GitHubWöchentlicher Umsatz-Maßnahmenbericht
Warum dieses Skill existiert
Der Nutzer (Gebietsleiter) schreibt jeden Dienstag einen Bericht: was wurde diese Woche getan, um den Umsatz von 1-2 konkreten Filialen zu verbessern. Der Bericht braucht echte Zahlen (nicht nur die eigene Einschätzung) - daher zieht dieses Skill die tatsächlichen Firestore-Daten (tägliches Ist vs. das Live-Tagesziel aus Axonity) und baut daraus einen Chart, kombiniert mit dem Text, den nur der Nutzer liefern kann: was wurde diese Woche konkret gemacht, und warum.
Die Skripte liegen fest unter automation/src/wochenbericht/ (nicht in
scratch/ - sie sind wiederverwendbares Werkzeug, keine Wegwerf-Skripte).
Jeder Bericht bekommt einen eigenen, frischen Arbeitsordner im Scratchpad
(z.B. wochenbericht-kw41/) - die Skripte selbst kennen keine bestimmte
Woche, alle Pfade kommen als Argumente.
Feste Stil-Vorgaben des Nutzers (gelten für jeden Durchlauf)
- Keine Gedankenstriche „—" in jedem selbst geschriebenen Text (Rückmeldung
28.09.2026) - stattdessen einen normalen Bindestrich „-" verwenden. Gilt für
alle Textfelder in
week-config.json, nicht nur für einen Abschnitt. - Keine alten Charts wiederverwenden. Pro Filiale gibt es nur noch den
täglichen Ist-vs-Tagesziel-Chart (ändert sich jede Woche wirklich) - KEIN
Monatsumsatz-Balkendiagramm mehr, das über mehrere Wochenberichte hinweg
identisch aussehen würde, solange sich der Kalendermonat nicht ändert
(Rückmeldung 28.09.2026). Monats-Trends gehören als Zahl in den Analyse-Text
(
analysis), nicht als eigenes Chart. - Wenn ein
systemChange-Abschnitt Screenshots der App zeigen soll: siehe eigener Abschnitt unten.
Was NUR der Nutzer liefern kann (jede Woche neu erfragen)
Kein Skript kann erfinden, was diese Woche tatsächlich unternommen wurde - das MUSS im Gespräch mit dem Nutzer geklärt werden, bevor der Bericht gebaut wird:
- Welche Filiale(n) stehen diese Woche im Fokus (Kostenstellen-Nummer,
6-stellig, z.B.
402240- dieselbe Nummer wie infiliale_umsatz/filiale_produktion, nicht raten). - Was wurde konkret gemacht pro Filiale (die Maßnahme) - und warum (z.B. Personalmangel, Urlaubs-Häufung, geplanter Test).
- Ob es diese Woche ein gebietsweites Thema gibt, das alle Filialen
betrifft (optional - siehe
systemChangeunten). Die meisten Wochen NICHT, einfach weglassen. - Wenn eine Maßnahme von echten externen Fakten abhängt (z.B. wann an einem
bestimmten Ort am meisten los ist), das aktiv recherchieren statt zu raten
- siehe Beispiel Google-Maps-Stoßzeiten in der Historie dieses Skills (KW40: Verkostungs-Zeitfenster anhand echter "Stoßzeiten"-Daten des jeweiligen Marktes bei Google Maps festgelegt, inkl. Fund einer Feiertagsschließung, die die ursprüngliche Wochenplanung berührt hätte).
Ablauf
1. Rohdaten holen
cd C:\Users\Public\GLSC\automation
node src/wochenbericht/fetch-data.js <marktNr1,marktNr2,...> <scratchpad>/data.json
Holt pro Kostenstelle: filiale_umsatz (Monatswerte, nur noch für die
Analyse-Prosa relevant), tagesziel (tägliches Ziel) und filiale_produktion
(tägliches Ist, Axonity - inkl. produzierteWare/anzahlSorten, falls für
eine Maßnahme Produktionsmengen statt Umsatz gebraucht werden, siehe KW40
Bad Hersfeld). Fehlt für eine Filiale die monatliche Historie, das im
Analyse-Text kurz erwähnen (kein eigener Chart/Hinweisblock mehr nötig, seit
der Monats-Chart komplett entfällt).
2. Woche-Konfiguration schreiben
Eine week-config.json im selben Scratchpad-Ordner, mit dem Text aus dem
Gespräch mit dem Nutzer (Schema siehe unten). Ton/Stil: sachlich, konkrete
Zahlen aus data.json einbauen (z.B. "-7,0 % Monatsumsatz, aber Personal-
kosten -24,3 %"), keine Floskeln, keine „—".
3. Bericht bauen (PDF)
node src/wochenbericht/build-report.js <scratchpad>/data.json <scratchpad>/week-config.json <scratchpad>/report.html
node src/wochenbericht/render.js <scratchpad>/report.html <scratchpad>/report.pdf
4. Editierbares .docx (immer mitliefern, siehe frühere Rückmeldung des Nutzers)
node src/wochenbericht/render-chart-pngs.js <scratchpad>/data.json <scratchpad>/week-config.json <scratchpad>/chart-png
node src/wochenbericht/build-docx.js <scratchpad>/data.json <scratchpad>/week-config.json <scratchpad>/report.docx
Wichtig: der Ordnername für die Chart-PNGs muss exakt chart-png heißen
(nicht umbenennen) - build-docx.js sucht dort fest nach chart-png/ manifest.json, relativ zum Ordner von week-config.json.
5. Prüfen, bevor der Nutzer es sieht
- HTML/PDF: es gibt kein PDF-Vorschau-Tool auf diesem Rechner (kein
poppler) - stattdessen den Abschnitts-Aufbau imreport.htmldirekt gegenprüfen (z.B.grep "<h2>"- Nummerierung muss lückenlos sein) und die Dateigröße vonreport.pdfauf Plausibilität prüfen (wächst spürbar mit jedem eingebetteten Screenshot, das ist normal). - .docx: kein LibreOffice auf diesem Rechner - für eine schnelle
Textprüfung reicht, die XML direkt aus der
.docx(ZIP) zu entpacken und auf Stichworte zu grep(en); für eine echte visuelle Prüfung PowerShell + Word-COM-Automatisierung (New-Object -ComObject Word.Application,SaveAszu PDF), wie beim ersten Durchlauf gemacht. Auch prüfen, wie vieleword/media/*-Dateien in der.docx(ZIP) stecken - muss zur Anzahl Screenshots + Charts passen, sonst fehlt etwas beim Einbetten. - Prüfen, dass keine „—" im gerenderten
report.htmlvorkommen (z.B.grep -c "—" report.htmlsollte 0 sein, außer in Firmennamen o.Ä.).
6. Liefern
PDF und .docx per SendUserFile schicken (beide, nicht nur eins - der
Nutzer wollte in der Vergangenheit ausdrücklich beides). Kurze
Zusammenfassung: welche Filialen, auffällige Zahlen, ob eine Kostenstelle
ohne Monatshistorie dabei war.
Schema week-config.json
{
"title": "Umsatz-Maßnahmenbericht",
"subtitle": "Kalenderwoche 41 · Dienstag, 6. Oktober 2026",
"author": "Thang Duc Duong",
"created": "06.10.2026",
"systemChange": {
"heading": "Text OHNE führende Nummer - die wird automatisch vorangestellt",
"intro": "...",
"calloutLabel": "z.B. LÖSUNG oder HINWEIS",
"calloutText": "Absatz 1.\n\nAbsatz 2 (durch \\n\\n getrennt = eigener Absatz in PDF UND docx).",
"screenshots": [
{"file": "shot1.png", "caption": "Kurzer Bildtext 1."},
{"file": "shot2.png", "caption": "Kurzer Bildtext 2."}
],
"outro": "..."
},
"storesIntro": "Kurzer Einleitungssatz vor den Filial-Abschnitten.",
"stores": [
{
"marktNr": "402240",
"name": "Heilbad Heiligenstadt",
"measure": "Maßnahme diese Woche, in ganzen Sätzen.",
"measureShort": "Kurzfassung für die Zusammenfassungs-Tabelle.",
"analysis": "Kurzanalyse - konkrete Zahlen aus data.json, nicht nur Prozent."
}
],
"nextSteps": ["Nächster Schritt 1", "Nächster Schritt 2"]
}
systemChange ist komplett optional - Feld weglassen (nicht null
setzen, einfach den Key nicht schreiben), wenn es diese Woche kein
gebietsweites Thema gibt. Beide Renderer (build-report.js/build-docx.js)
lassen den Abschnitt dann automatisch weg und nummerieren "Filialen im
Fokus" (1.) und "Zusammenfassung" (2.) automatisch neu - nichts von Hand
anpassen. screenshots[].file-Pfade sind relativ zum Ordner von
week-config.json (dem Scratchpad-Ordner dieser Woche), nicht zum Skript.
screenshots ist ein Array - 0, 1 oder mehrere Bilder, werden paarweise
nebeneinander gesetzt (PDF: flex-wrap-Galerie, docx: 2-spaltige Tabelle).
region ist ebenfalls optional (Rückmeldung 28.09.2026: "Gebiet Ost"
sollte weg) - Feld weglassen, dann zeigt der Kopf nur noch "Gebietsleiter: ...
· Erstellt am ...". Nur wieder hinzufügen, wenn der Nutzer es für eine
bestimmte Woche ausdrücklich wieder haben will.
Screenshots der App für systemChange erzeugen
Wenn der systemChange-Abschnitt zeigen soll, wie eine Funktion in der App
tatsächlich aussieht, gibt es keinen echten Test-Login im Browser - aber es
gibt Admin-SDK-Zugriff auf Node-Ebene (automation/src/firestore-client.js).
Bevorzugte Technik (Rückmeldung 28.09.2026 - viel überzeugender als leere
Mock-Daten): echte, aktuelle Firestore-Daten per Admin-SDK abfragen und
direkt in die Render-Funktion der Seite injizieren, statt die Seite leer/
mit erfundenen Werten zu mocken. Vorgehen (siehe wochenbericht-kw40/ shot-inventur2.js in der Session-Historie als Vorlage):
- Zuerst prüfen, ob es echte, aktuell relevante Daten gibt - z.B. für
Inventur:
db.collection('inventur_period').get()nach einem Zeitraum durchsuchen, dessenstart/endHEUTE einschließt; danndb.collection('inventur_counts').where('month','==',...).get()für die echten Fortschritte, unddb.collection('inventur_photos').get()für echte Artikelfotos (Feldvariants[].img, base64-Data-URL). Ergebnis mitfs.writeFileSync(...)als JSON im Wochenordner ablegen. .claude/launch.json→glsc-staticstarten (lokaler Server fürmitarbeiter.html/index.html).- Ein Playwright-Skript im Wochenordner schreiben:
page.goto()auf die lokale Seite, dann perpage.evaluate()die echten Daten aus Schritt 1 als globale Variablen setzen (z.B. Inventur-Mitarbeiteransicht:ivCounts= echtescounts-Objekt,ivPhotos= echtes Foto-Objekt,maSessionmit echtem Filialnamen/Namen; Admin-Ansicht inindex.html:activeRegion,invCounts= alle echten Zählungen des Monats), dazu#login-screen/#update-box/#auth-gate/#global-refresh-btnausblenden,#app/#main-wrapeinblenden, passendes.panel/.app-icoaktiv setzen, danach die echte Render-Funktion selbst aufrufen (z.B.ivRender(),invRenderSubmissions()) - alles unveränderter Produktivcode, nur der Dateninput kommt vom Mock statt vom echten Firestore-Listener (der im gemockten Browser an den echten Security Rules scheitern würde). - Nur wenn keine echten aktuellen Daten existieren, auf leere Mock-Werte zurückfallen (Zählwerte 0, keine Fotos) - dann in der Bildunterschrift nicht als "reale Live-Daten" bezeichnen, sondern neutral als Ansicht der App beschriften.
- Für Elemente, die weit unten auf einer langen Seite liegen (z.B. die
Admin-Karte in
index.html):elementHandle.screenshot({path:...})verwenden, nicht manuellboundingBox()+page.screenshot({clip:...})- Playwright scrollt beim Element-Screenshot selbst korrekt hin; ein
manueller
clip()bezieht sich auf die aktuelle Viewport-Position und trifft daneben, wenn das Element außerhalb der Startansicht liegt (genau dieser Fehler kostete in KW40 einen Fehlversuch).
- Playwright scrollt beim Element-Screenshot selbst korrekt hin; ein
manueller
- Für eine sehr lange scrollbare Liste (z.B. alle Artikel einer Kategorie)
trotzdem mit
boundingBox()+page.screenshot({clip:...H begrenzt})auf eine realistische Bildschirmhöhe (~700-800px) zuschneiden - sonst wird das Bild absurd lang.
Kurzfassung für einen kompletten Durchlauf
cd C:\Users\Public\GLSC\automation
node src/wochenbericht/fetch-data.js <marktNrs> <scratchpad>/data.json
# week-config.json im Gespräch mit dem Nutzer füllen (siehe Schema oben, keine „—")
# bei Bedarf: Screenshots der App erzeugen (siehe Abschnitt oben)
node src/wochenbericht/build-report.js <scratchpad>/data.json <scratchpad>/week-config.json <scratchpad>/report.html
node src/wochenbericht/render.js <scratchpad>/report.html <scratchpad>/report.pdf
node src/wochenbericht/render-chart-pngs.js <scratchpad>/data.json <scratchpad>/week-config.json <scratchpad>/chart-png
node src/wochenbericht/build-docx.js <scratchpad>/data.json <scratchpad>/week-config.json <scratchpad>/report.docx
# Abschnitts-Nummerierung/„—"-Freiheit gegenprüfen, dann PDF + docx per SendUserFile liefern