createZeitbuchung, updateZeitbuchung und deleteZeitbuchung buchen Taetigkeitsbloecke und Pausen ueber module/arbeitszeit/api/. Die erste Buchung eines Tages legt per nachtragen.php die Schicht an, weitere Buchungen kommen in die vorhandene Schicht, die bei Bedarf verlaengert wird. Die Kostenstelle wird an die Notiz gehaengt. Unbekannte Taetigkeiten werden vorher abgelehnt, weil Agrarmonitor sie ohne Fehler speichert. Ablehnungen kommen als ZeitbuchungError mit Grund. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
13 KiB
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=1taetigkeiten_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 folgendtable.taetigkeit_table - Zeile:
tr[data-id][data-activityuuid]data-id= stabile Tätigkeits-ID (wird alstaetigkeit_idgebucht)- 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.togglemit.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=1maschinen_aktiv:0= nur aktive (Standard der Seite),1= aktive und inaktive,2= nur inaktivemaschinengruppe:0= alle Kategorien,-98= alle Zugmaschinen,-99= alle Anbaumaschinen, sonst Kategorie-ID
- Aufbau: je Kategorie eine Überschrift
<h2>Schlepper</h2>undtable.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 bereitsmaschinen_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}, Feldkostenstelle), 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()nutztmodule/mitarbeiter/livesearch.phpmitmitarbeiter_aktiv=1(= aktive und inaktive) unditemsperpage=100000– reicht für die Zuordnungam_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 Schichttable.verteilung_table#verteilung_table_{schichtId}[data-schicht], Zeilentr.betrieb[id={verteilungId}](Spalten: Zeit „HH:MM - HH:MM“, Arbeit, Kunde, Notiz, Kontrolle, Dauer). Auftrags-Zeiten stehen alstr.auftragdaneben.
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+updateVerteilungverlängert die Schicht nicht (Zugeordnet 1,50 h bei Anwesend 0,75 h). Die Schicht muss perupdateSchicht.phpangepasst werden. updateVerteilungunddeleteVerteilungverkü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:
- Tagesansicht
arbeitszeit/detail/{mitarbeiterId}/{datum}laden und Schicht(en) ermitteln. - Keine Schicht →
nachtragen.phpfür genau diesen Block; danach die Tagesansicht erneut laden und die neue Verteilung über Zeit (und Tätigkeit) finden →ref. - Schicht vorhanden →
editVerteilungForm.phpmitshiftID(liefert die neue ID) →updateVerteilung.phpmit den Daten; schlägt das fehl, den Platzhalter perdeleteVerteilung.phpwieder entfernen. Danach die Schicht perupdateSchicht.phpauf [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. Entscheidungen (14.10.2026)
- Kostenstelle: wird als Zeile
Kostenstelle: …an die Notiz gehängt. - Pausen: werden als Agrarmonitor-Pause übertragen (
editPauseForm.phpmitshiftIDlegt die Pause an,updatePause.phpmitneu: 1speichert sie,deletePause.php{pauseID, employeeID, date}löscht). Eine Pause braucht eine vorhandene Schicht. - Überschneidungen: Das Buchen in Agrarmonitor selbst wird für die Mitarbeiter abgeschaltet; der Connector prüft keine Überschneidungen.
- Umsetzung:
createZeitbuchung,updateZeitbuchung,deleteZeitbuchungmitref=verteilung:<mitarbeiterId>:<datum>:<id>bzw.pause:…. - Notiz ändern: Notizen löscht
module/notiz/api/delete.php{noteID}(Antworttrue). Beim Ändern wird die neue Notiz gespeichert und danach die alte gelöscht.
Echttest (14.10.2026, Mitarbeiter 3309, Tag 11.10.2026)
Über den Connector (eigenes, freigeschaltetes Gerät) angelegt: Block 08:00–09:00 mit Notiz und Kostenstelle (per Nachtragen), Pause 09:00–09:15, Block 09:15–10:00 mit Maschine 5033; danach Block 1 auf 08:55 und Pause auf 09:10 geändert, Notiz ersetzt; alles wieder gelöscht, der Tag ist danach leer. Ergebnis wie erwartet.
Fund: Agrarmonitor speichert eine unbekannte taetigkeit_id (999999) ohne Fehler – es entsteht eine Verteilung ohne Tätigkeit. Der Connector prüft die Tätigkeit deshalb gegen die Auswahl im Buchungsformular (nachtragen-form.php bzw. editVerteilungForm.php) und wirft sonst ZeitbuchungError (grund: 'taetigkeit nicht buchbar'). Maschinen-IDs werden nicht geprüft, weil das Formular nur eine Teilmenge der Maschinen anbietet.
5. Offene Fragen
- Pause als erste Buchung des Tages: Ohne Schicht kann keine Pause gebucht werden (
ZeitbuchungError). Die App überträgt Blöcke beim Beenden; eine Pause vor dem ersten Tätigkeitsblock landet dann beineeds_manual. - Über Mitternacht: Der Connector trägt bis 23:59 nach und korrigiert Verteilung und Schicht danach auf die echten Zeiten – im Mock getestet, im echten System nicht.
- 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 (FeldmaschineZuordneningetTaetigkeiten). - Tätigkeitsauswahl: Das Buchungsformular bietet nur aktive Tätigkeiten an; Abwesenheiten (Bereich 900) und „800 Pause“ sollten in der App vermutlich ausgeblendet werden.
- Verfasser der Notiz:
verfasser_idist der Agrarmonitor-Benutzer des Connectors, nicht der Mitarbeiter.