Files
AgrarmonitorConnector/docs/zeitbuchung-analyse.md
T
bjoernpoettkerandClaude Opus 5.5 d2c5db7e22 Arbeitszeit: Analyse der Zeitbuchung in Agrarmonitor
Fundstellen fuer Taetigkeiten, Maschinen und Kostenstellen sowie die
Buchungsendpunkte unter module/arbeitszeit/api/ (Nachtragen, Verteilung
anlegen/aendern/loeschen, Schicht) mit Feldern, Antworten und offenen
Fragen fuer die Poettker-App.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 11:11:23 +02:00

132 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Zeitbuchung in Agrarmonitor – Analyse
Stand: 2026-10-14 · Grundlage: Live-Erkundung in `admin7.agrarmonitor.de` (Web-Oberfläche, eingeloggt im Browser) und das aktuelle Frontend-Bundle `assets/Admin.min.js`. Die Kopie `Fahrzeugübersicht/public/js/main.js` ist veraltet (alte Endpunkte `ajax-v3/arbeitszeit.php?id=…` liefern heute 404).
Schreibende Requests wurden nur für den Testmitarbeiter **3309** abgesetzt (Freigabe durch Björn) und danach vollständig wieder gelöscht.
## 1. Stammdaten
Alle Livesearch-Endpunkte liefern ein HTML-Fragment. Paging: Der generische Livesearch-Client liest `.pagination[data-pages]` aus der Antwort; fehlt das Element oder ist `data-pages` ≤ 1, gibt es nur eine Seite. Bei Tätigkeiten und Maschinen gibt es aktuell kein Paging (alle Zeilen auf Seite 1, `seite=2` liefert dieselben Zeilen).
### Tätigkeiten (Tätigkeitsarten)
- Seite: `/arbeitszeit/taetigkeiten` („Arbeiten auf dem Betrieb verwalten“)
- Livesearch: `GET /module/arbeitszeit/taetigkeiten-livesearch.php?seite=1&taetigkeiten_aktiv=1`
- `taetigkeiten_aktiv`: `0` = nur aktive (Standard der Seite), `1` = aktive und inaktive, `2` = nur inaktive
- aktuell 54 Tätigkeiten (33 aktiv)
- Aufbau: je „Bereich“ eine Überschrift `<h2><b class="bereich" data-id="17">100 Werkstatt (Mobil sichtbar)</b></h2>`, darauf folgend `table.taetigkeit_table`
- Zeile: `tr[data-id][data-activityuuid]`
- `data-id` = stabile Tätigkeits-ID (wird als `taetigkeit_id` gebucht)
- Spalten: 0 Nummer · 1 Bezeichnung · 2 Maschinen zuordnen (Ja/Nein) · 3 Abwesenheit · 4 Als Urlaub werten · 5 Notiz erzwingen · 6 Wartungsplan · 7 Erweiterte Effizienz · 8 Aktiv (`td.toggle` mit `.fa-check` = aktiv, `.fa-close` = inaktiv)
- Bereiche (aktuell): 100 Werkstatt, 200 Auftragsnebenzeiten, 300 Büro, 400 Hof-/Feldarbeit, 500 Gülle Mann solo, 600 LKW, 700 Rüstzeiten, 800 Pause, 900 Abwesenheit
- Hinweis: Bezeichnungen sind in Agrarmonitor teils abgeschnitten gespeichert (z. B. „Arbeiten, die keiner Maschine oder anderen Tätigkeit z“) – identisch in Liste und Buchungsformular.
### Maschinen
- Seite: `/maschinen`
- Livesearch: `GET /module/maschinen/livesearch.php?suchstring=&maschinen_aktiv=1&maschinengruppe=0&seite=1`
- `maschinen_aktiv`: `0` = nur aktive (Standard der Seite), **`1` = aktive und inaktive**, `2` = nur inaktive
- `maschinengruppe`: `0` = alle Kategorien, `-98` = alle Zugmaschinen, `-99` = alle Anbaumaschinen, sonst Kategorie-ID
- Aufbau: je Kategorie eine Überschrift `<h2>Schlepper</h2>` und `table.maschinen_table#maschinen_table_{kategorieId}`
- Zeile: `tr[data-id]`, Spalten: 0 Nummer · 1 Bezeichnung · 2 Kennzeichen · 3 Zähler · 4 Aktiv (`.fa-check`/`.fa-close`). Leere Werte werden als `-` angezeigt.
- **Eine Maschine kann in mehreren Kategorien stehen**: 382 Zeilen, aber nur 263 verschiedene Maschinen (99 Maschinen mehrfach).
- `getMaschinen(gruppenId)` sendet bereits `maschinen_aktiv: 1`, liefert also – anders als angenommen – aktive **und** inaktive Maschinen einer Kategorie. Paging ist für Maschinen nicht relevant (eine Seite).
- Das Buchungsformular bietet nur eine Teilmenge an (147 Maschinen im Verteilungs-Formular, 243 im Nachtragen-Formular). Welche Maschinen dort fehlen (inaktive? nicht mobil sichtbare?), ist nicht geprüft.
### Kostenstellen
- **Es gibt keine Kostenstellen-Stammdaten** (keine Seite, keine Livesearch, kein Auswahlfeld).
- „Kostenstelle“ ist nur ein Freitextfeld (max. 10 Zeichen) an Maschine (`/maschinen/detail/{id}`, Feld `kostenstelle`), Mitarbeiter und Artikel. Es wird beim DATEV-Export verwendet (Einstellung „Kostenstellen exportieren“).
- Das Modul „Betriebszweige“ (`/betriebszweige`) ist in diesem Mandanten nicht freigeschaltet („Diese Seite existiert nicht“).
- Eine Arbeitszeit-Buchung hat **kein Kostenstellen-Feld**. Am nächsten kommt das Feld **Projekt** (`projekt_id`, 16 Projekte unter `/projekte`).
### Mitarbeiter
- `getMitarbeiter()` nutzt `module/mitarbeiter/livesearch.php` mit `mitarbeiter_aktiv=1` (= aktive und inaktive) und `itemsperpage=100000` – reicht für die Zuordnung `am_mitarbeiter_id`.
## 2. Buchungsschnittstelle
**Gefunden: ja** (Web-Oberfläche, JSON-Endpunkte unter `module/arbeitszeit/api/`). Die Token-API `api.agrarmonitor.de/v1` ist nicht öffentlich dokumentiert (Weiterleitung auf `agrarmonitor.de/schnittstellen`, „Sprechen Sie uns an“); der Connector nutzt dort nur `/kunden`.
### Datenmodell in Agrarmonitor
- **Schicht** (Anwesenheit): ein Zeitraum pro Mitarbeiter und Tag (`schicht_id`), mehrere pro Tag möglich.
- **Verteilung**: ein Tätigkeitsblock innerhalb einer Schicht (`verteilung_id`), mit Tätigkeit, optional Maschine und Projekt, Notizen und Artikel-Ausgängen.
- **Pause**: eigener Datensatz in der Schicht (`pause_id`).
- Zeit ohne Verteilung erscheint in der Tagesansicht als „Nicht zugeordnet“.
- Tagesansicht: `GET /arbeitszeit/detail/{mitarbeiterId}/{YYYY-MM-DD}`; je Schicht `table.verteilung_table#verteilung_table_{schichtId}[data-schicht]`, Zeilen `tr.betrieb[id={verteilungId}]` (Spalten: Zeit „HH:MM - HH:MM“, Arbeit, Kunde, Notiz, Kontrolle, Dauer). Auftrags-Zeiten stehen als `tr.auftrag` daneben.
### Endpunkte (getestet mit Mitarbeiter 3309 am 14.10.2026)
Alle `POST` mit `Content-Type: application/json`, sofern nicht anders angegeben. Zeitformat in Formularen: `dd.mm.yy (HH:MM)` (z. B. `14.10.26 (08:00)`), Ortszeit Europe/Berlin, minutengenau.
| Zweck | Request | Antwort |
|---|---|---|
| Tag nachtragen (legt Schicht + Verteilung an) | `POST module/arbeitszeit/api/nachtragen.php`, **form-urlencoded** (jQuery-Kodierung): `form[…]`, `tage[]=YYYY-MM-DD`, `anzahl_datum=1`, `planungAusMitarbeiterId=0` | `{"action":"link","target":"arbeitszeit/plan"}` – **keine ID** |
| Formular einer bestehenden Verteilung laden (lesend) | `module/arbeitszeit/api/editVerteilungForm.php` `{employeeID, date, distributionID}` | HTML `form#verteilung_form[data-id]` + `form#notiz-form` |
| **Neue Verteilung in bestehender Schicht** | `editVerteilungForm.php` `{employeeID, date, shiftID}` | HTML-Formular; **legt sofort eine leere Verteilung an**, ihre ID steht in `#verteilung_form[data-id]`. Das UI löscht sie bei „Abbrechen“ wieder. |
| Verteilung speichern/ändern | `module/arbeitszeit/api/updateVerteilung.php` `{mitarbeiterId, verteilungId, verteilungForm, notizForm}` | `200` leerer Body; Validierungsfehler `422` mit JSON, z. B. `{"verteilungForm":"ganzertag must be present"}` |
| Verteilung löschen | `module/arbeitszeit/api/deleteVerteilung.php` `{distributionID, employeeID, date}` | `200` `null`; war es die letzte Verteilung: `{"action":"link","target":"arbeitszeit"}` und die Schicht wird mitgelöscht (`ist_deleted: 1`) |
| Schicht lesen | `GET module/arbeitszeit/api/getSchicht.php?schicht_id=` | JSON (`timestamp_start`, `timestamp_ende` als `YYYY-MM-DD HH:MM:SS`, `ist_deleted`, …) |
| Schicht ändern | `module/arbeitszeit/api/updateSchicht.php` `{shiftID, timestampStart, timestampEnd}` (Format `dd.mm.yy (HH:MM)`) | `200` `true` |
| Schicht löschen | `module/arbeitszeit/api/deleteSchicht.php` `{shiftID, employeeID, date}` | nicht getestet |
| Plausibilität prüfen | `GET module/arbeitszeit/api/checkPlausibelInSchicht.php?timestamp_start&timestamp_ende&mitarbeiterId` | `[]` oder `{timestamp_start:{code,typ,msg}, …}` – nicht getestet |
Weitere vorhandene Endpunkte (nicht getestet): `updatePause.php`/`editPauseForm.php`/`deletePause.php`, `addNotiz.php` (`{verteilungId, notizfeld, verfasser_id}` → `{id,…}`), `startTaetigkeit.php`/`startPause.php`/`beendePause.php`/`beende-schicht.php` (Stempeluhr des eingeloggten Benutzers).
### Felder
`verteilungForm` (alle Werte als String):
| Feld | Bedeutung |
|---|---|
| `timestamp_start`, `timestamp_ende` | `dd.mm.yy (HH:MM)` |
| `taetigkeit_id` | Tätigkeits-ID |
| `alte_taetigkeit_id` | bisherige Tätigkeit (`0` bei neuer Verteilung) |
| `maschine_id` | Maschinen-ID, `0` = keine |
| `projekt_id` | Projekt-ID, `0` = keines |
| `ganzertag` | **Pflicht** (`1` oder `0.5`; nur bei Abwesenheiten fachlich relevant) |
`notizForm`: `notizfeld` (Text; nicht leer → wird als **neue** Notiz an die Verteilung gehängt), `verfasser_id` (Mitarbeiter-ID des eingeloggten Benutzers, steht im Formular).
`nachtragen.php` → `form[…]`: `mitarbeitertyp=1`, `mitarbeiter_id`, `taetigkeit_id`, `maschine_id`, `projekt_id`, `arbeitszeit_start`/`arbeitszeit_ende` (`HH:MM`), `pause_start`/`pause_ende` (leer = keine Pause), `notiz`, `uebernehmen=1` („In Arbeitszeit mit gewählter Dauer übernehmen“; `2` = ohne Dauer, `0` = nur Planung), `kategorie_id=0`, `arbeitzeit_sollstunden=''`.
### Beobachtetes Verhalten
- Nachtragen legt eine Schicht exakt über den angegebenen Zeitraum und darin eine Verteilung an; die Notiz wird als Notizeintrag an der Verteilung gespeichert.
- Eine neue Verteilung über `editVerteilungForm`+`updateVerteilung` **verlängert die Schicht nicht** (Zugeordnet 1,50 h bei Anwesend 0,75 h). Die Schicht muss per `updateSchicht.php` angepasst werden.
- `updateVerteilung` und `deleteVerteilung` **verkürzen** die Schicht dagegen teils automatisch auf die verbleibenden Verteilungen.
- Ändern und Löschen sind möglich; die Referenz ist die `verteilung_id` (Zahl).
## 3. Vorschlag: App-Block → Agrarmonitor
| App | Agrarmonitor |
|---|---|
| Mitarbeiter (`users.am_mitarbeiter_id`) | `mitarbeiterId` / `mitarbeiter_id` / `employeeID` |
| Datum (Start des Arbeitstags) | `date` der Schicht (`YYYY-MM-DD`) |
| Start, Ende (`timestamptz`) | `timestamp_start`/`timestamp_ende` in Europe/Berlin, `dd.mm.yy (HH:MM)` – Sekunden gehen verloren |
| Tätigkeitsart (`activity_types.am_id`) | `taetigkeit_id` |
| Maschine (`machines.am_id`) | `maschine_id` (`0` = keine) |
| Kostenstelle | **kein Feld** – siehe offene Fragen |
| Notiz | `notizForm.notizfeld` (bzw. `form[notiz]` beim Nachtragen) |
| `am_ref` | `verteilung_id` |
Ablauf `createZeitbuchung`:
1. Tagesansicht `arbeitszeit/detail/{mitarbeiterId}/{datum}` laden und Schicht(en) ermitteln.
2. Keine Schicht → `nachtragen.php` für genau diesen Block; danach die Tagesansicht erneut laden und die neue Verteilung über Zeit (und Tätigkeit) finden → `ref`.
3. Schicht vorhanden → `editVerteilungForm.php` mit `shiftID` (liefert die neue ID) → `updateVerteilung.php` mit den Daten; schlägt das fehl, den Platzhalter per `deleteVerteilung.php` wieder entfernen. Danach die Schicht per `updateSchicht.php` auf [frühester Start, spätestes Ende] erweitern.
`updateZeitbuchung`: `updateVerteilung.php` mit vollständigem Formular (alte Tätigkeit aus `editVerteilungForm` mit `distributionID`), danach Schicht erweitern. Notiz nur senden, wenn sie sich geändert hat (sonst entsteht ein doppelter Notizeintrag).
`deleteZeitbuchung`: `deleteVerteilung.php`.
## 4. Offene Fragen
1. **Kostenstelle:** Agrarmonitor hat keine Kostenstellen-Stammdaten und kein Kostenstellen-Feld an der Buchung. Optionen: (a) Kostenstelle in der App streichen, (b) stattdessen **Projekte** (`projekt_id`) verwenden, (c) Kostenstelle nur in die Notiz schreiben. `getKostenstellen()` habe ich deshalb in Schritt 2 **nicht** umgesetzt.
2. **Pausen:** Die App überträgt keine Pausen. Die Lücke zwischen zwei Blöcken erscheint in Agrarmonitor dann als „Nicht zugeordnet“ innerhalb der Schicht und zählt als Anwesenheit/Arbeitszeit. Sollen Pausen als Agrarmonitor-Pause (`updatePause.php`) übertragen werden, oder soll jede Lücke eine eigene Schicht bekommen?
3. **Parallele Erfassung:** Stempeln Mitarbeiter zusätzlich in der Agrarmonitor-App, gibt es am selben Tag schon eine Schicht mit Verteilungen; App-Blöcke würden hineingebucht und können sich überschneiden. Vorher `checkPlausibelInSchicht.php` prüfen und bei Überschneidung ablehnen?
4. **Über Mitternacht:** Verteilungs-Zeitstempel enthalten das Datum, das Nachtragen nur `HH:MM`. Blöcke über Mitternacht sind ungetestet.
5. **Maschinenpflicht:** Tätigkeiten mit „Maschinen zuordnen = Ja“ verlangen im UI eine Maschine (clientseitige Prüfung über `data-extra`). Ob der Server das ebenfalls erzwingt, ist ungetestet – die App sollte es vorher prüfen (Feld `maschineZuordnen` in `getTaetigkeiten`).
6. **Tätigkeitsauswahl:** Das Buchungsformular bietet nur aktive Tätigkeiten an; Abwesenheiten (Bereich 900) und „800 Pause“ sollten in der App vermutlich ausgeblendet werden.
7. **Verfasser der Notiz:** `verfasser_id` ist der Agrarmonitor-Benutzer des Connectors, nicht der Mitarbeiter.