formularText("head"), "text" => formularText("text"), "typ" => formularGanzzahl("typ"), "done" => formularGanzzahl("done") ? 1 : 0, "user" => formularGanzzahl("user"), "user2" => formularGanzzahl("user2"), "user3" => formularGanzzahl("user3"), "user4" => formularGanzzahl("user4"), "folge" => formularGanzzahl("folge") ? 1 : 0, "folgetyp" => formularGanzzahl("folgetyp"), "folgedays" => formularGanzzahl("folgedays"), "start" => dispdate_to_mysql_date(formularText("datetime_start"), 0), "ende" => dispdate_to_mysql_date(formularText("datetime_end"), 1), ); } /* * Verteilt einen Termin auf Tage. Länger als zwölf Stunden oder Urlaub und * Krankheit werden zu ganzen Tagen: Rückgabe ist * array(erster Tag, Dauer je Tag, Anzahl Tage). */ function terminTageUndDauer($werte) { $start = $werte["start"]; $dauer = mysql_dauer($start, $werte["ende"]); $stunden = (int)strtok($dauer, ":"); if($stunden <= 12 && !terminIstGanztaegig($werte["typ"])) return array($start, $dauer, 1); // ceil() rechnet mit der Stundenzahl, nicht mit dem ganzen Zeitstring $tage = max(1, (int)ceil($stunden / 24)); if($start != TERMIN_KEIN_DATUM) $start = substr($start, 0, 11)."00:00:00"; return array($start, TERMIN_GANZTAG, $tage); } /* Der $i-te Tag einer Terminreihe, ohne Datum bleibt es ohne Datum */ function terminTag($start, $i) { if($start == TERMIN_KEIN_DATUM) return TERMIN_KEIN_DATUM; return date("Y-m-d H:i:s", strtotime("+".$i." day", strtotime($start))); } /* --------------------------------------------------------------------- * Überschneidungen * ------------------------------------------------------------------ */ /* * Sucht einen Termin, bei dem $benutzer schon zwischen $start und $ende * eingetragen ist. $ausserMain schließt die gerade bearbeitete Reihe aus. * Gibt die id des kollidierenden Termins zurück, sonst null (false bei * Fehlern). */ function terminUeberschneidung($dbconn, $benutzer, $start, $ende, $ausserMain) { if(!$benutzer || $start == TERMIN_KEIN_DATUM) return null; $params = array(TERMIN_KEIN_DATUM, $benutzer, $benutzer, $benutzer, $benutzer); // eine main_id von 0 gibt es nur bei Altdaten, dann darf nichts ausgenommen werden $eigene = ""; if($ausserMain > 0) { $eigene = " AND main_id != ?"; $params[] = (int)$ausserMain; } array_push($params, $start, $start, $ende, $ende, $start, $ende); $sql = "SELECT id FROM (SELECT id, main_id, user, user2, user3, user4, termtime, DATE_ADD(termtime, INTERVAL CONCAT(HOUR(termdauer),' ',MINUTE(termdauer)) HOUR_MINUTE) AS termend FROM termin WHERE termtime != ? AND done = 0) AS belegt WHERE (user = ? OR user2 = ? OR user3 = ? OR user4 = ?)$eigene AND ((? >= termtime AND ? < termend) OR (? > termtime AND ? <= termend) OR (? <= termtime AND ? >= termend)) LIMIT 1"; return db_wert($dbconn, $sql, $params); } /* * Prüft alle vier Mitarbeiter eines Termins für jeden Tag der Reihe. * Rückgabe 0, wenn nichts kollidiert, sonst die Fehlermeldung. */ function pruefeUeberschneidungen($dbconn, $werte, $start, $dauer, $tage, $ausserMain) { foreach(range(0, $tage - 1) as $i) { $tagStart = terminTag($start, $i); $tagEnde = dauer_to_date($tagStart, $dauer); foreach(array("user", "user2", "user3", "user4") as $feld) { $treffer = terminUeberschneidung($dbconn, $werte[$feld], $tagStart, $tagEnde, $ausserMain); if($treffer === false) return error("Fehler beim überprüfen von Überlappungen:
".mysqli_error($dbconn)); if($treffer !== null) return error("Dieser Mitarbeiter hat zu der Zeit bereits einen Termin, er kann nicht an zwei Orten gleichzeitig sein."); } } return 0; } /* --------------------------------------------------------------------- * Speichern * ------------------------------------------------------------------ */ /* * Legt einen Folgetermin an: gleicher Kopf und dieselben Mitarbeiter, aber * ohne Datum und mit einem Wiedervorlagedatum in $tage Tagen. Gibt dessen * id zurück oder false. */ function legeFolgeterminAn($dbconn, $werte, $adressid) { $sql = "INSERT INTO termin SET head = ?, text = '', adresse_id = ?, typ = ?, changetime = NOW(), datetime = NOW(), done = 0, termtime = ?, termdauer = ?, followed_by = 0, user = ?, user2 = ?, user3 = ?, user4 = ?, changeuser = ?, act_date = DATE_ADD(CURDATE(), INTERVAL ? DAY)"; $params = array($werte["head"], $adressid, $werte["folgetyp"], TERMIN_KEIN_DATUM, TERMIN_KEINE_DAUER, $werte["user"], $werte["user2"], $werte["user3"], $werte["user4"], $_SESSION["user"], $werte["folgedays"]); if(!db_abfrage($dbconn, $sql, $params)) { error("Fehler beim anlegen des Folgetermins:
".mysqli_error($dbconn)); return false; } $id = mysqli_insert_id($dbconn); // Ein Termin ohne eigene Reihe ist sein eigener Hauptsatz db_abfrage($dbconn, "UPDATE termin SET main_id = ? WHERE id = ?", array($id, $id)); if(saveLog($dbconn, db_sql_text($dbconn, $sql, $params)." [folgetermin, hat ID $id]", "DELETE FROM termin WHERE id = $id", "anlegen")) return false; return $id; } /* * Legt einen Termin an oder ändert ihn. $terminid ist die id der Reihe * (0 = neu), $adressid die Kundenadresse (0 bei Urlaub und Krankheit). * * Wird aus einem eintägigen Termin ein mehrtägiger, wird die alte Reihe * verworfen und neu angelegt - anders lässt sich die Reihe nicht umbauen. */ function speichereTermin($dbconn, $terminid, $adressid) { $werte = terminFormularwerte(); list($start, $dauer, $tage) = terminTageUndDauer($werte); beginneSpeichern($dbconn); $neuAnlegen = !$terminid; if($terminid && $tage > 1) { // aus einem Einzeltermin wird eine Reihe: alte Zeilen verwerfen $err = loescheTerminreihe($dbconn, $terminid); if($err) return beendeSpeichern($dbconn, $err); $neuAnlegen = true; $terminid = 0; } elseif($terminid) { // Zeilen der bisherigen Reihe bis auf die erste entfernen $sql = "DELETE FROM termin WHERE main_id = ? AND id != ?"; db_abfrage($dbconn, $sql, array($terminid, $terminid)) or $err = error("Konnte Nebentermine nicht löschen:
".mysqli_error($dbconn)); if(!empty($err)) return beendeSpeichern($dbconn, $err); } if(!$werte["done"]) { $err = pruefeUeberschneidungen($dbconn, $werte, $start, $dauer, $tage, $terminid); if($err) return beendeSpeichern($dbconn, $err); } $alt = null; if(!$neuAnlegen) { $alt = db_zeile($dbconn, "SELECT * FROM termin WHERE id = ?", array($terminid)); if($alt === false) return beendeSpeichern($dbconn, error("Fehler beim ermitteln des Termins:
".mysqli_error($dbconn))); if(!$alt) return beendeSpeichern($dbconn, error("Dieser Termin existiert nicht!")); } // Folgetermin nur einmal und nur, wenn der Termin gerade erledigt wird $folgeId = $alt ? (int)$alt["followed_by"] : 0; if($werte["done"] && $werte["folge"] && !$folgeId) { $folgeId = legeFolgeterminAn($dbconn, $werte, $adressid); if($folgeId === false) return beendeSpeichern($dbconn, 1); } $felder = array( "head" => $werte["head"], "text" => $werte["text"], "adresse_id" => $adressid, "typ" => $werte["typ"], "done" => $werte["done"], "termdauer" => $dauer, "changeuser" => $_SESSION["user"], "user" => $werte["user"], "user2" => $werte["user2"], "user3" => $werte["user3"], "user4" => $werte["user4"], "followed_by" => $folgeId, ); $zuweisung = array(); foreach(array_keys($felder) as $spalte) $zuweisung[] = "`$spalte` = ?"; $set = implode(", ", $zuweisung); $err = 0; if(!$neuAnlegen) { $sql = "UPDATE termin SET $set, termtime = ?, changetime = NOW() WHERE id = ?"; $params = array_merge(array_values($felder), array(terminTag($start, 0), $terminid)); if(!db_abfrage($dbconn, $sql, $params)) return beendeSpeichern($dbconn, error("Fehler beim ändern des Termins:
".mysqli_error($dbconn))); $altWerte = array(); foreach(array_keys($felder) as $spalte) $altWerte[] = $alt[$spalte]; $err = saveLog($dbconn, db_sql_text($dbconn, $sql, $params)." [termin change]", db_sql_text($dbconn, $sql, array_merge($altWerte, array($alt["termtime"], $terminid))), "ändern"); } else { $mainId = 0; foreach(range(0, $tage - 1) as $i) { if($err) break; $sql = "INSERT INTO termin SET $set, termtime = ?, main_id = ?, changetime = NOW(), datetime = NOW()"; $params = array_merge(array_values($felder), array(terminTag($start, $i), $mainId)); if(!db_abfrage($dbconn, $sql, $params)) { $err = error("Fehler beim anlegen des Termins:
".mysqli_error($dbconn)); break; } $id = mysqli_insert_id($dbconn); if(!$mainId) { // der erste Tag ist der Hauptsatz der Reihe $mainId = $id; db_abfrage($dbconn, "UPDATE termin SET main_id = ? WHERE id = ?", array($id, $id)) or $err = error("Fehler beim verknüpfen der Terminreihe:
".mysqli_error($dbconn)); } if(!$err) $err = saveLog($dbconn, db_sql_text($dbconn, $sql, $params)." [termin add, hat ID $id]", "DELETE FROM termin WHERE id = $id", "anlegen"); } } return beendeSpeichern($dbconn, $err); } /* Löscht eine Terminreihe samt Nebenzeilen */ function loescheTerminreihe($dbconn, $id) { beginneSpeichern($dbconn); $err = 0; $beschreibung = db_wert($dbconn, "SELECT head FROM termin WHERE id = ?", array($id)); if($beschreibung === false) return beendeSpeichern($dbconn, error("Fehler beim ermitteln des Termins:
".mysqli_error($dbconn))); if($beschreibung === null) return beendeSpeichern($dbconn, error("Dieser Termin existiert nicht!")); // erst die Nebenzeilen, dann der Hauptsatz - sonst verliert main_id den Bezug $sql = "DELETE FROM termin WHERE main_id = ? AND id != ?"; db_abfrage($dbconn, $sql, array($id, $id)) or $err = error("Konnte Nebentermine nicht löschen:
".mysqli_error($dbconn)); if(!$err) { $sql = "DELETE FROM termin WHERE id = ?"; db_abfrage($dbconn, $sql, array($id)) or $err = error("Konnte Haupttermin nicht löschen:
".mysqli_error($dbconn)); } if(!$err) $err = saveLog($dbconn, db_sql_text($dbconn, "DELETE FROM termin WHERE main_id = ?", array($id))." [$beschreibung]", "", "löschen"); return beendeSpeichern($dbconn, $err); } /* Markiert eine Terminreihe als erledigt ($erledigt = 1) oder offen (0) */ function setzeTerminErledigt($dbconn, $id, $erledigt) { $erledigt = $erledigt ? 1 : 0; $vorhanden = db_wert($dbconn, "SELECT id FROM termin WHERE id = ?", array($id)); if($vorhanden === false) return error("Fehler beim ermitteln des Termins:
".mysqli_error($dbconn)); if($vorhanden === null) return error("Dieser Termin existiert nicht!"); beginneSpeichern($dbconn); $err = 0; $sql = "UPDATE termin SET done = ?, changetime = NOW(), changeuser = ? WHERE main_id = ? OR id = ?"; $params = array($erledigt, $_SESSION["user"], $id, $id); db_abfrage($dbconn, $sql, $params) or $err = error("Konnte den Termin nicht umstellen:
".mysqli_error($dbconn)); if(!$err) $err = saveLog($dbconn, db_sql_text($dbconn, $sql, $params)." [termin erledigt]", "", "ändern"); return beendeSpeichern($dbconn, $err); } /* --------------------------------------------------------------------- * Lesen * ------------------------------------------------------------------ */ /* * Der Termin für das Formular: die Reihe zu einer Hauptsatz-id wird zu * einem Eintrag zusammengefasst, Anfang und Gesamtdauer eingeschlossen. */ function terminZumBearbeiten($dbconn, $id) { $res = db_zeile($dbconn, "SELECT MIN(termtime) AS termtime, SEC_TO_TIME(SUM(TIME_TO_SEC(termdauer))) AS termdauer, head, followed_by, MIN(id) AS id, main_id, typ, adresse_id, done, user, user2, user3, user4, changeuser, act_date, text FROM termin WHERE main_id = ? GROUP BY main_id", array($id)); if($res === false) { error("Fehler beim ermitteln des Termins:
".mysqli_error($dbconn)); return null; } if(!$res) return null; if($res["followed_by"] > 0) { $folge = db_zeile($dbconn, "SELECT DATE_FORMAT(act_date,'%Y-%m-%d') AS act_date, typ FROM termin WHERE id = ?", array($res["followed_by"])); if($folge) { $res["folgetyp"] = $folge["typ"]; $tage = (strtotime($folge["act_date"]) - strtotime(date("Y-m-d"))) / 86400; if($tage > 0) { $res["folge"] = 1; $res["folgedays"] = (int)$tage; } } } $res["termend"] = dauer_to_date($res["termtime"], $res["termdauer"]); if(str_contains($res["termend"], "00:00:00")) $res["termend"] = date("Y-m-d H:i:s", strtotime("-1 minutes", strtotime($res["termend"]))); return $res; } /* Die Auswahllisten des Terminformulars */ function terminAuswahllisten($dbconn, $smarty) { list($typids, $typs) = auswahlliste($dbconn, "SELECT id, typ FROM termtyp WHERE id > 0"); $smarty->assign("typids", $typids); $smarty->assign("typs", $typs); $benutzer = db_zeilen($dbconn, "SELECT id, vname, nname FROM user WHERE blocked = 0 AND id > 0 ORDER BY nname, vname"); $ids = array(); $namen = array(); foreach((array)$benutzer as $b) { $ids[] = $b["id"]; $namen[] = trim($b["vname"]." ".$b["nname"]); } $smarty->assign("userids", $ids); $smarty->assign("users", $namen); $ids[] = 0; $namen[] = "Keiner"; $smarty->assign("optuserids", $ids); $smarty->assign("optusers", $namen); $smarty->assign("days", range(0, 40)); list($urlaubIds, $urlaubNamen) = auswahlliste($dbconn, "SELECT id, typ FROM termtyp WHERE typ = 'Urlaub' OR typ = 'Krankheit'"); $smarty->assign("vac_types_id", $urlaubIds); $smarty->assign("vac_types_name", $urlaubNamen); } /* Die Adresse, an der ein Termin stattfindet - leer, wenn es keine gibt */ function terminAdresse($dbconn, $adressid) { $res = db_zeile($dbconn, "SELECT plz, ortsteil, ort, str, hnr, vname, partner_id, nname, vname2, nname2, firma, adresse.id, land, anrede_id, titel_id, anrede2_id, titel2_id FROM adresse LEFT JOIN strasse ON str_id = strasse.id LEFT JOIN ort ON ort_id = ort.id LEFT JOIN land ON land_id = land.id WHERE adresse.id = ?", array($adressid)); if($res === false) { error("Fehler beim abrufen der Daten:
".mysqli_error($dbconn)); return array(); } return $res ? $res : array(); } /* --------------------------------------------------------------------- * Kalender * ------------------------------------------------------------------ */ /* Deutsche Wochentagsnamen - unabhängig davon, ob die Locale vorhanden ist */ function wochentag($zeitpunkt) { $namen = array("Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"); return $namen[(int)date("w", $zeitpunkt)]; } /* * Die Termine eines Tages. $ganztags trennt die ganztägigen (Dauer über * zwölf Stunden) von den übrigen. */ function kalenderTermine($dbconn, $tag, $ganztags) { $vergleich = $ganztags ? ">" : "<"; $sql = "SELECT termin.id, termtyp.typ, termin.head, termtime, termdauer, user, user2, user3, user4, done, partner_id, main_id, str, ort, ortsteil, plz, hnr, adresse.vname AS vname, adresse.nname AS nname, user.vname AS uservname, user.nname AS usernname, user2.vname AS uservname2, user2.nname AS usernname2, user3.vname AS uservname3, user3.nname AS usernname3, user4.vname AS uservname4, user4.nname AS usernname4, firma, vname2, nname2 FROM termin LEFT JOIN termtyp ON termin.typ = termtyp.id LEFT JOIN adresse ON termin.adresse_id = adresse.id LEFT JOIN partner ON adresse.partner_id = partner.id LEFT JOIN strasse ON adresse.str_id = strasse.id LEFT JOIN ort ON strasse.ort_id = ort.id LEFT JOIN user ON termin.user = user.id LEFT JOIN user AS user2 ON termin.user2 = user2.id LEFT JOIN user AS user3 ON termin.user3 = user3.id LEFT JOIN user AS user4 ON termin.user4 = user4.id WHERE DATE(termtime) = ? AND termdauer $vergleich '12:00:00' ORDER BY termtime ASC"; return db_zeilen($dbconn, $sql, array($tag)) ?: array(); } /* Die an einem Tag gebuchten Arbeiten aus der Projektverwaltung */ function kalenderArbeiten($dbconn, $tag) { $sql = "SELECT work.id, project.id AS projectid, work.bezeichn AS head, work.rechn AS done, project.name AS projectname, work.datetime AS termtime, work.std AS termdauer, project.changeuser AS user, project.partner_id, adresse.vname AS vname, adresse.nname AS nname, adresse.firma AS firma FROM work LEFT JOIN project ON work.project_id = project.id LEFT JOIN partner ON project.partner_id = partner.id LEFT JOIN adresse ON adresse.partner_id = partner.id LEFT JOIN user ON work.changeuser = user.id WHERE adresse.typ_id = 1 AND DATE(work.datetime) = ? ORDER BY work.datetime ASC"; return db_zeilen($dbconn, $sql, array($tag)) ?: array(); } /* Wer an einem Tag gestempelt hat und ob der Tag abgeschlossen ist */ function kalenderAnwesenheit($dbconn, $tag) { $sql = "SELECT user.vname, user.nname, user.color, CASE MAX(zeiten.gehen) WHEN MAX(zeiten.kommen) THEN 0 ELSE 1 END AS complete FROM user LEFT JOIN zeiten ON user.id = zeiten.ma_id WHERE DATE(kommen) = ? GROUP BY user.id ORDER BY user.nname ASC"; return db_zeilen($dbconn, $sql, array($tag)) ?: array(); } /* * Die acht Tage einer Kalenderseite, beginnend einen Tag vor dem * Seitenanfang (so ist der Vortag immer sichtbar). */ function kalenderTage($dbconn, $seite) { $heute = strtotime("today"); $tage = array(); for($i = $seite * 8 - 1; $i < $seite * 8 + 7; $i++) { $zeitpunkt = strtotime("+".$i." day", $heute); $datum = date("Y-m-d", $zeitpunkt); $feiertag = feiertag_bayern((int)date("d", $zeitpunkt), (int)date("m", $zeitpunkt), (int)date("Y", $zeitpunkt)); $termine = array(); if(($_SESSION["firma"] ?? "") == "Ingenieurbüro Wagner") $termine = kalenderArbeiten($dbconn, $datum); $termine = array_merge($termine, kalenderTermine($dbconn, $datum, false)); $tage[] = array( // am Feiertag steht der Name des Feiertags statt des Wochentags "weekday" => ($feiertag ? date("d.m.Y", $zeitpunkt)."
" : wochentag($zeitpunkt)."
".date("d.m.Y", $zeitpunkt)), "date" => date("Ymd", $zeitpunkt), "termine" => $termine, "fulldaytermine" => kalenderTermine($dbconn, $datum, true), "numfulldaytermine" => 0, "ma_check" => kalenderAnwesenheit($dbconn, $datum), "feiertag" => $feiertag ? $feiertag : "", ); } return $tage; } /* Die Mitarbeiter der Kalenderspalte samt Resturlaub */ function kalenderBenutzer($dbconn) { $users = array(); $zeilen = db_zeilen($dbconn, "SELECT id, vname, nname, color FROM user WHERE blocked = 0 AND nick != 'ALL'"); foreach((array)$zeilen as $res) { $res["openvacation"] = urlaub_remaining($dbconn, $res["id"]); $users[$res["id"]] = $res; } return $users; } ?>