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>
This commit is contained in:
1 parent
e6ce1d254b
commit
d2c5db7e22
1 file changed
+131
@@ -0,0 +1,131 @@
|
|||||||
|
# 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×tamp_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.
|
||||||
Reference in new issue
Block a user