Files
thetool/application/ConstructionConsent/ConstructionConsentController.php
2025-06-18 14:51:51 +02:00

1443 lines
56 KiB
PHP

<?php
class ConstructionConsentController extends mfBaseController {
protected function init() : void
{
$this->needlogin = true;
$me = new User();
$me->loadMe();
$this->me = $me;
$this->layout()->set("me", $me);
if ($this->me->is("Admin")) $this->constructionConsentProjects = array_column(ConstructionConsentProject::getAll(), 'id');
else {
$constructionConsentProjects = json_decode((new WorkerFlag($this->me->id, "constructionConsent_projects"))->value() ?? '[]');
empty($constructionConsentProjects) ? $this->redirect("Dashboard") : $this->constructionConsentProjects = $constructionConsentProjects;
}
}
protected function indexAction() : void {
$this->layout()->setTemplate("ConstructionConsent/Index");
if($this->request->resetFilter) {
unset($_SESSION[MFAPPNAME . '-ConstructionConsent-filter']);
foreach($_SESSION as $key => $s) {
if(preg_match('/^' . MFAPPNAME . '-ConstructionConsent-filter-project-\d+$/', $key)) {
unset($_SESSION[$key]);
}
}
}
$filter = [];
$hasFaultyEntries = false;
if(is_array($this->request->filter)) {
$filter = $this->request->filter;
if(isset($this->request->filter["project_id"]) && $this->request->filter["project_id"]) {
$filter_p_id = $filter["project_id"];
if(count($this->request->filter) == 1 && isset($_SESSION[MFAPPNAME . '-ConstructionConsent-filter-project-' . $filter_p_id])) {
$filter = $_SESSION[MFAPPNAME . '-ConstructionConsent-filter-project-' . $filter_p_id];
} else {
$_SESSION[MFAPPNAME . '-ConstructionConsent-filter-project-' . $filter["project_id"]] = $filter;
}
$hasFaultyEntries = ConstructionConsentProject::hasFaultyOwnerEntries($filter["project_id"]);
} else {
$_SESSION[MFAPPNAME . '-ConstructionConsent-filter'] = $filter;
}
} else {
if(array_key_exists(MFAPPNAME . '-ConstructionConsent-filter', $_SESSION) && count($_SESSION[MFAPPNAME . '-ConstructionConsent-filter'])) {
$filter = $_SESSION[MFAPPNAME . '-ConstructionConsent-filter'];
if(isset($filter['project_id'])) {
$filter_p_id = $filter['project_id'];
if(isset($_SESSION[MFAPPNAME . '-ConstructionConsent-filter-project-' . $filter_p_id])) {
$filter = $_SESSION[MFAPPNAME . '-ConstructionConsent-filter-project-' . $filter_p_id];
}
}
}
}
//var_dump($_SESSION, $filter);exit;
$this->layout->set("allowed_projects", $this->constructionConsentProjects);
$this->layout->set("hasFaultyEntries", $hasFaultyEntries);
$this->layout->set("filter", $filter);
$filter = $this->getPreparedFilter($filter);
// pagination defaults
$pagination = [];
$pagination['start'] = 0;
$pagination['count'] = 20;
$pagination['maxItems'] = 0;
if (is_numeric($this->request->s)) {
$pagination['start'] = intval($this->request->s);
}
//var_dump($filter);exit;
$pagination['maxItems'] = ConstructionConsent::count($filter);
$this->layout()->set("pagination", $pagination);
$items = ConstructionConsent::search($filter, $pagination);
$this->layout->set("items", $items);
$this->layout()->set("stats", $this->generateStats($filter));
if(array_key_exists("project_id", $filter) && $filter["project_id"]) {
$project = new ConstructionConsentProject($filter["project_id"]);
if($project->id) {
$this->layout()->set("project", $project);
}
}
}
private function getPreparedFilter($filter) : array
{
$new_filter = [];
if (is_array($filter) && count($filter)) {
foreach ($filter as $name => $value) {
$new_filter[$name] = $value;
}
}
if (isset($new_filter['project_id']) && !in_array($new_filter['project_id'], $this->constructionConsentProjects) && strlen($new_filter['project_id']) > 0) {
$this->redirect("ConstructionConsent");
}
if (empty($new_filter['project_id'])) {
$new_filter['project_id'] = $this->constructionConsentProjects;
}
return $new_filter;
}
protected function viewAction() : void {
$this->layout()->setTemplate("ConstructionConsent/View");
$this->layout()->set("is_admin", $this->me->isAdmin());
$id = $this->request->id;
if(!is_numeric($id) || $id < 1) {
$this->layout()->setFlash("Zustimmungserklärung nicht gefunden", "error");
$this->redirect("ConstructionConsent");
}
$item = new ConstructionConsent($id);
if(!$item || !$item->id || !in_array($item->constructionconsentproject_id, $this->constructionConsentProjects)) {
$this->layout()->setFlash("Zustimmungserklärung nicht gefunden", "error");
$this->redirect("ConstructionConsent");
}
$this->layout()->set("item", $item);
}
protected function addAction() : void {
$this->layout()->setTemplate("ConstructionConsent/Form");
if($this->request->project_id) {
$this->layout()->set("project_id", $this->request->project_id);
}
$this->layout()->set("allowed_projects", $this->constructionConsentProjects);
}
protected function editAction() : void {
$id = $this->request->id;
if(!is_numeric($id) || $id < 1) {
$this->layout()->setFlash("Zustimmungserklärung nicht gefunden", "error");
$this->redirect("ConstructionConsent");
}
$item = new ConstructionConsent($id);
if(!$item || !$item->id) {
$this->layout()->setFlash("Zustimmungserklärung nicht gefunden", "error");
$this->redirect("ConstructionConsent");
}
$this->layout()->set("item", $item);
$this->addAction();
}
protected function generatePdf(array $owners, ConstructionConsent $consent): ?string
{
if (empty($owners)) return null;
$this->layout()->setTemplate("ConstructionConsent/Consentform.pdf");
$this->layout()->set("ressourcePathPrefix", MFFANCYBASEURL);
$this->layout()->set("owners", $owners);
$this->layout()->set("consent", $consent);
return $consent->createConsentFormPdf($owners); // Might need adjustment for multiple
}
protected function downloadAction()
{
$owner_id = $this->request->owner_id;
$open = $this->request->open;
if (!is_numeric($owner_id) || $owner_id < 1 || !($owner = new ConstructionConsentOwner($owner_id))->id) {
$this->layout()->setFlash("Besitzer nicht gefunden", "error");
$this->redirect("ConstructionConsent");
}
if (!($cc = $owner->consent)) {
$this->layout()->setFlash("Zustimmungserklärung nicht gefunden.", "error");
$this->redirect("ConstructionConsent");
}
if (!($filename = $this->generatePdf([$owner], $cc))) {
$this->layout()->setFlash("PDF-Erstellung fehlgeschlagen", "error");
$this->redirect("ConstructionConsent", "View", ["id" => $cc->id]);
}
$this->sendPdfResponse($filename, "Zustimmungserklärung-{$cc->id}-{$owner_id}.pdf", $open === 'true');
}
protected function downloadMultipleAction()
{
$owners = [];
foreach (ConstructionConsent::search($this->getPreparedFilter($this->request->filter), []) as $consent)
$owners = array_merge($owners, $consent->owners ?: []);
if (empty($owners)) {
$this->layout()->setFlash("Keine Besitzer gefunden", "error");
$this->redirect("ConstructionConsent");
}
if ($this->request->markAsSent === '1') {
foreach ($owners as $owner) {
$update = $owner->status === 'new' || $owner->status === null;
if ($owner->status === 'new') $owner->status = 'sent';
if ($owner->result === null) $owner->result = 'open';
if ($update) {
$owner->save();
$history = ConstructionConsentHistory::create([
"constructionconsent_id" => $owner->consent->id,
"key" => "status",
"old_value" => 'new',
"new_value" => 'sent'
]);
$history->save();
}
}
}
if (!($filename = $this->generatePdf($owners, $owners[0]->consent))) {
$this->layout()->setFlash("PDF-Erstellung fehlgeschlagen", "error");
$this->redirect("ConstructionConsent");
}
$this->sendPdfResponse($filename, "Zustimmungserklärungen-".date('Y-m-d').".pdf");
}
private function sendPdfResponse(string $filename, string $downloadName, bool $open = false): void
{
header('Content-Type: ' . mime_content_type($filename));
if ($open === true) header('Content-disposition: inline; filename="' . rawurlencode($downloadName) . '"');
else header('Content-disposition: attachment; filename="' . rawurlencode($downloadName) . '"');
header('Content-Length: ' . filesize($filename));
readfile($filename);
exit;
}
protected function saveAction() {
$r = $this->request;
$id = $r->id;
if(is_numeric($id) && $id > 0) {
//var_dump($id);exit;
$mode = "edit";
$item = new ConstructionConsent($id);
if(!$item->id) {
$this->layout()->setFlash("Zustimmungserklärung nicht gefunden", "error");
$this->redirect("ConstructionConsent");
}
} else {
$id = false;
$mode = "add";
}
if (!in_array($r->constructionconsentproject_id, $this->constructionConsentProjects)) {
$this->layout()->setFlash("Sie haben keine Berechtigung für dieses Projekt", "error");
$this->redirect("ConstructionConsent");
}
// if mode is add and object type is building check if adb_hausnummer_id is set and if object type is street check if adb_strasse_id is set
if($r->object_type == "building" && !$r->adb_hausnummer_id) {
$this->layout()->setFlash("Kein Gebäude ausgewählt", "error");
return $this->addAction();
}
if($r->object_type == "street" && !$r->adb_strasse_id) {
$this->layout()->setFlash("Keine Straße ausgewählt", "error");
return $this->addAction();
}
$data = [];
$data["constructionconsentproject_id"] = $r->constructionconsentproject_id;
$data["netzgebiet_id"] = (isset($r->netzgebiet_id) && is_int($r->netzgebiet_id)) ? $r->netzgebiet_id : null;
$data["object_type"] = $r->object_type;
$data["name"] = $r->name;
$data["adb_hausnummer_id"] = $r->adb_hausnummer_id;
$data["adb_strasse_id"] = $r->adb_strasse_id;
$data["ez"] = $r->ez;
$data["kg"] = $r->kg;
$data["gst"] = $r->gst;
$data["gstnr"] = $r->gstnr;
$data["rimo_gn"] = $r->rimo_gn;
$data["usage_length"] = $r->usage_length ?: null;
$data["usage_pipe_on_plot"] = $r->usage_pipe_on_plot ? 1 : 0;
$data["usage_pipe_in_building"] = $r->usage_pipe_in_building ? 1 : 0;
$data["usage_manhole"] = $r->usage_manhole ? 1 : 0;
$data["usage_owner"] = $r->usage_owner ? 1 : 0;
if($r->object_type == "building") {
$data["usage_pipe_on_plot"] = 1;
$data["usage_pipe_in_building"] = 1;
$data["usage_manhole"] = 1;
$data["usage_owner"] = 1;
}
if($mode == "add") {
$item = ConstructionConsent::create($data);
} else {
$item->update($data);
}
$this->layout()->set("item", $item);
$project = new ConstructionConsentProject($data["constructionconsentproject_id"]);
if(!$project->id) {
$this->layout()->setFlash("Projekt nicht gefunden", "error");
return $this->addAction();
}
if(!$data["object_type"]) {
$this->layout()->setFlash("Bitte alle benötigten Felder ausfüllen", "error");
return $this->addAction();
}
if($data["object_type"] == "address") {
if(!$data["adb_hausnummer_id"]) {
$this->layout()->setFlash("Kein Gebäude ausgewählt", "error");
return $this->addAction();
}
unset($data["adb_strasse_id"]);
}
if($data["object_type"] == "street") {
if(!$data["adb_strasse_id"]) {
$this->layout()->setFlash("Keine Straße ausgewählt", "error");
return $this->addAction();
}
unset($data["adb_hausnummer_id"]);
}
//var_dump($data, $item);exit;
if(!$item->save()) {
$this->layout()->setFlash("Fehler beim Speichern", "error");
return $this->addAction();
}
if(is_array($_FILES) && array_key_exists("consent_plan_image", $_FILES) && !$_FILES['consent_plan_image']['error']) {
try {
// returns File object or throws Exception on error
$file = mfUpload::handleFormUpload("consent_plan_image", false, TT_CONSTRUCTIONCONSENT_FILE_UPLOAD_SUBFOLDER);
} catch (Exception $ex) {
$this->layout()->setFlash("Fehler beim Hochladen: " . $ex->getMessage(), "warning");
return $this->editAction();
}
$history = ConstructionConsentHistory::create([
"constructionconsent_id" => $item->id,
"key" => "plan_upload",
"old_value" => ($item->file) ? $item->file->file->id : null,
"new_value" => $file->id
]);
} else {
if($r->submit_plan_file_id) {
$file = new File($r->submit_plan_file_id);
$history = ConstructionConsentHistory::create([
"constructionconsent_id" => $item->id,
"key" => "rimo_plan",
"old_value" => ($item->file) ? $item->file->file->id : null,
"new_value" => $file->id
]);
}
}
if($file && $file->id) {
$ccf = ConstructionConsentFile::create([
'constructionconsent_id' => $item->id,
'file_id' => $file->id,
'filename' => "zustimmungserklärung-" . $item->id . "-plan.png",
]);
// delete previous image
$img = ConstructionConsentFile::getFirst(["constructionconsent_id" => $item->id]);
if ($img) {
$img->file->delete();
$img->delete();
}
if (!$ccf->save()) {
$this->layout()->setFlash("Fehler beim Speichern des Plans", "warning");
}
$history->save();
}
$this->layout()->setFlash("Zustimmungserklärung erfolgreich gespeichert", "success");
$this->redirect("ConstructionConsent", "View", ["id" => $item->id]);
}
protected function deleteAction() {
$id = $this->request->id;
if(!is_numeric($id) || $id < 1) {
$this->layout()->setFlash("Zustimmungserklärung nicht gefunden", "error");
$this->redirect("ConstructionConsent");
}
$item = new ConstructionConsent($id);
if(!$item->id) {
$this->layout()->setFlash("Zustimmungserklärung nicht gefunden", "error");
$this->redirect("ConstructionConsent");
}
$item->delete();
$this->layout()->setFlash("Zustimmungserklärung erfolgreich gelöscht", "success");
$this->redirect("ConstructionConsent");
}
protected function saveDate() {
$r = $this->request;
$id = $r->consent_id;
if(is_numeric($id) && $id > 0) {
$mode = "edit";
$item = new ConstructionConsent($id);
if(!$item->id) {
$this->layout()->setFlash("Zustimmungserklärung nicht gefunden", "error");
$this->redirect("ConstructionConsent");
}
} else {
$this->layout()->setFlash("Zustimmungserklärung nicht gefunden", "error");
$this->redirect("ConstructionConsent");
}
$data = [];
if($r->inspection_date_planner) {
$data["inspection_date_planner"] = Layout::dateToInt($r->inspection_date_planner);
}
if($r->inspection_date_electrician) {
$data["inspection_date_electrician"] = Layout::dateToInt($r->inspection_date_electrician);
}
if(count($data)) {
$item->update($data);
if(!$item->save()) {
$this->layout()->setFlash("Fehler beim Speichern", "error");
return $this->addAction();
}
}
$this->layout()->setFlash("Zustimmungserklärung erfolgreich gespeichert", "success");
$this->redirect("ConstructionConsent", "View", ["id" => $item->id]);
}
protected function uploadFileAction() {
$id = $this->request->consent_id;
if(!is_numeric($id) || $id < 1) {
$this->layout()->setFlash("Zustimmungserklärung nicht gefunden", "error");
$this->redirect("ConstructionConsent");
}
$item = new ConstructionConsent($id);
if(!$item || !$item->id) {
$this->layout()->setFlash("Zustimmungserklärung nicht gefunden", "error");
$this->redirect("ConstructionConsent");
}
$upload_type = $this->request->upload_type;
if($upload_type != "planner" && $upload_type != "electrician") {
$this->layout()->setFlash("Falscher Dateityp", "error");
$this->redirect("ConstructionConsent", "View", ["id" => $item->id]);
}
if(is_array($_FILES) && array_key_exists("inspection_protocol_file", $_FILES) && !$_FILES['inspection_protocol_file']['error']) {
try {
// returns File object or throws Exception on error
$file = mfUpload::handleFormUpload("inspection_protocol_file", false, TT_CONSTRUCTIONCONSENT_FILE_UPLOAD_SUBFOLDER);
} catch (Exception $ex) {
$this->layout()->setFlash("Fehler beim Hochladen: " . $ex->getMessage(), "warning");
$this->redirect("ConstructionConsent", "View", ["id" => $item->id]);
}
$history = ConstructionConsentHistory::create([
"constructionconsent_id" => $item->id,
"key" => "inspection_protocol_$upload_type",
"old_value" => ($item->{"inspection_protocol_$upload_type"}) ? $item->{"inspection_protocol_$upload_type"}->file->id : null,
"new_value" => $file->id
]);
$ccf = ConstructionConsentFile::create([
'constructionconsent_id' => $item->id,
'file_id' => $file->id,
'filename' => "inspection_protocol_$upload_type",
]);
// delete previous image
if($item->{"inspection_protocol_$upload_type"}) {
$item->{"inspection_protocol_$upload_type"}->file->delete();
$item->{"inspection_protocol_$upload_type"}->delete();
}
if (!$ccf->save()) {
$this->layout()->setFlash("Fehler beim Speichern des Begehungsprotokolls", "warning");
}
$history->save();
$this->layout()->setFlash("Begehungsprotokoll erfolgreich hochgeladen", "success");
}
$this->redirect("ConstructionConsent", "View", ["id" => $item->id]);
}
protected function deleteFileAction()
{
$id = $this->request->id;
if (!is_numeric($id) || $id < 1) {
$this->layout()->setFlash("Zustimmungserklärung nicht gefunden", "error");
$this->redirect("ConstructionConsent");
}
$item = new ConstructionConsent($id);
if (!$item || !$item->id) {
$this->layout()->setFlash("Zustimmungserklärung nicht gefunden", "error");
$this->redirect("ConstructionConsent");
}
$file_id = $this->request->file_id;
$file = ConstructionConsentFile::getFirst(["constructionconsent_id" => $id, "file_id" => $file_id]);
if(!$file) {
$this->layout()->setFlash("Datei nicht gefunden", "error");
$this->redirect("ConstructionConsent", "View", ["id" => $id]);
}
$file->file->delete();
$file->delete();
$this->layout()->setFlash("Datei gelöscht", "success");
$this->redirect("ConstructionConsent", "View", ["id" => $id]);
}
protected function apiAction() {
$do = $this->request->do;
$data = [];
switch($do) {
case "findStreet":
$return = $this->findStreetApi();
break;
case "findAddress":
$return = $this->findAddressApi();
break;
case "getNetzgebieteApi":
$return = $this->getNetzgebieteApi();
break;
case "getRimoPlanPreview":
$return = $this->getRimoPlanPreviewApi();
break;
case "saveRimoPlanPreview":
$return = $this->saveRimoPlanPreviewApi();
break;
case "saveCheckbox":
$return = $this->saveCheckboxApi();
break;
case "deleteRimoPlan":
$return = $this->deleteRimoPlanApi();
break;
case "getEz":
$return = $this->getEzApi();
break;
default:
$this->log->warn(__METHOD__ . ": Called API function '$do' does not exist");
$return = false;
}
if(!is_array($return) || !count($return)) {
$data = ["status" => "error"];
$this->returnJson($data);
}
$data['status'] = "OK";
$data['result'] = $return;
$this->returnJson($data);
}
private function getEzApi() {
$kg = trim($this->request->kg);
$gst = trim($this->request->gst);
if(!$kg || !$gst) return false;
$ez = ADBGwrgst::getFirst(["kgnr" => $kg, "gstnr" => $gst]);
//var_dump($ez);exit;
if(!$ez) {
return ["ez" => ""];
}
return ["ez" => $ez->ez];
}
private function findStreetApi() {
$addresses = [];
$search = trim($this->request->q);
$project_id = $this->request->project_id;
$scluster_ids = [];
if($project_id) {
$project = new ConstructionConsentProject($project_id);
if(!$project->id) {
header("Content-Type: application/json");
echo json_encode(["results" => []]);
exit;
}
foreach($project->adb_networks as $network) {
$scluster_ids[] = $network->id;
}
} else {
// get all salesclusters
foreach(ADBNetzgebietModel::getAll() as $network) {
$scluster_ids[] = $network->id;
}
}
$results = [];
$search_parts = explode(" ", $search);
$ort_search = $strasse_search = $plz_search = $hausnummer_search = $gst_search = [];
foreach($search_parts as $p) {
$p = $this->db->escape(trim($p));
if(!$p) continue;
$ort_search[] = "ortschaft like '$p%'";
$strasse_search[] = "strasse like '$p%'";
$plz_search[] = "plz like '%$p%'";
$hausnummer_search[] = "hausnummer like '%$p%'";
$gst_search[] = "grund_nr like '%$p%'";
}
$where = "1=1";
if(count($scluster_ids)) {
$where .= " AND netzgebiet_id IN (".implode(', ',$scluster_ids).")";
}
/*if($include_gst) {
$sql = "SELECT * FROM view_hausnummer WHERE $where AND ((".implode(" OR ", $ort_search).") OR (".implode(" OR ", $strasse_search).") OR (".implode(" OR ", $plz_search).") OR (".implode(" OR ", $hausnummer_search).") OR (".implode(" OR ", $gst_search).") ) ORDER BY strasse, LENGTH(hausnummer), hausnummer";
} else {
$sql = "SELECT * FROM view_hausnummer WHERE $where AND ((".implode(" OR ", $ort_search).") OR (".implode(" OR ", $strasse_search).") OR (".implode(" OR ", $plz_search).") OR (".implode(" OR ", $hausnummer_search).")) ORDER BY strasse, LENGTH(hausnummer), hausnummer";
}*/
$sql = "SELECT strasse_id,plz,ortschaft,strasse FROM view_hausnummer
WHERE $where
AND ((".implode(" OR ", $ort_search).") OR (".implode(" OR ", $strasse_search).") OR (".implode(" OR ", $plz_search).") OR (".implode(" OR ", $hausnummer_search)."))
GROUP BY plz,ortschaft,strasse,strasse_id
ORDER BY strasse
";
$this->log->debug($sql);
$adb = FronkDB::singleton(ADDRESSDB_DBHOST, ADDRESSDB_DBUSER, ADDRESSDB_DBPASS, ADDRESSDB_DBNAME);
$res = $adb->query($sql);
if(!$adb->num_rows($res)) {
header("Content-Type: application/json");
echo json_encode(["results" => []]);
exit;
}
while($data = $adb->fetch_object($res)) {
//$address_string = $data->plz." ".$data->ortschaft.", ".$data->strasse." ".$data->hausnummer;
$address_string = $data->plz." ".$data->ortschaft.", ".$data->strasse;
/*if($include_gst) {
$address_string .= " | GST: ".$data->grund_nr;
}*/
$sort_key = $data->plz." ".$data->ortschaft." ".$data->strasse;
$address = [];
$address['id'] = $data->strasse_id;
$address["text"] = $address_string;
$address['sort_key'] = $sort_key;
$addresses[] = $address;
}
// sort results by most occurences of search strings
$sort = [];
foreach($addresses as $key => $address) {
$includes_int = false;
$count = 0;
foreach($search_parts as $p) {
$p = $this->db->escape(trim($p));
if(!$p) continue;
if(is_numeric(($p))) {
$includes_int = true;
if(substr_count(strtolower($address['text']), strtolower($p))) {
$count++;
}
} else {
$count += substr_count(strtolower($address['text']), strtolower($p));
}
}
unset($address['sort_key']);
//echo $address['text']." $p $count<br />\n";
if($includes_int && (($count + 1) - count($search_parts) ) < 1) {
continue;
}
if(!array_key_exists($count, $sort)) {
$sort[$count] = [];
}
$sort[$count][] = $address;
}
ksort($sort, SORT_NUMERIC);
$sort = array_reverse($sort, true);
//var_dump($sort);exit;
foreach($sort as $res) {
foreach($res as $a) {
$results[] = $a;
}
}
header("Content-Type: application/json");
echo json_encode(["results" => $results]);
exit;
}
private function findAddressApi() {
$addresses = [];
$search = trim($this->request->q);
$project_id = $this->request->project_id;
$include_gst = $this->request->include_gst ? $this->request->include_gst : false;
$scluster_ids = [];
if($project_id) {
$project = new ConstructionConsentProject($project_id);
if(!$project->id) {
header("Content-Type: application/json");
echo json_encode(["results" => []]);
exit;
}
foreach($project->adb_networks as $network) {
$scluster_ids[] = $network->id;
}
} else {
// get all salesclusters
foreach(ADBNetzgebietModel::getAll() as $network) {
$scluster_ids[] = $network->id;
}
}
$results = [];
$search_parts = explode(" ", $search);
$ort_search = $strasse_search = $plz_search = $hausnummer_search = $gst_search = [];
foreach($search_parts as $p) {
$p = $this->db->escape(trim($p));
if(!$p) continue;
$ort_search[] = "ortschaft like '$p%'";
$strasse_search[] = "strasse like '$p%'";
$plz_search[] = "plz like '%$p%'";
$hausnummer_search[] = "hausnummer like '%$p%'";
$gst_search[] = "grund_nr like '%$p%'";
}
$where = "1=1";
if(count($scluster_ids)) {
$where .= " AND netzgebiet_id IN (".implode(', ',$scluster_ids).")";
}
if($include_gst) {
$sql = "SELECT * FROM view_hausnummer WHERE $where AND ((".implode(" OR ", $ort_search).") OR (".implode(" OR ", $strasse_search).") OR (".implode(" OR ", $plz_search).") OR (".implode(" OR ", $hausnummer_search).") OR (".implode(" OR ", $gst_search).") ) ORDER BY strasse, LENGTH(hausnummer), hausnummer";
} else {
$sql = "SELECT * FROM view_hausnummer WHERE $where AND ((".implode(" OR ", $ort_search).") OR (".implode(" OR ", $strasse_search).") OR (".implode(" OR ", $plz_search).") OR (".implode(" OR ", $hausnummer_search).")) ORDER BY strasse, LENGTH(hausnummer), hausnummer";
}
$this->log->debug($sql);
$adb = FronkDB::singleton(ADDRESSDB_DBHOST, ADDRESSDB_DBUSER, ADDRESSDB_DBPASS, ADDRESSDB_DBNAME);
$res = $adb->query($sql);
$this->log->debug("done");
if(!$adb->num_rows($res)) {
header("Content-Type: application/json");
echo json_encode(["results" => []]);
exit;
}
while($data = $adb->fetch_object($res)) {
$address_string = $data->plz." ".$data->ortschaft.", ".$data->strasse." ".$data->hausnummer;
if($include_gst) {
$address_string .= " | GST: ".$data->grund_nr;
}
$sort_key = $data->plz." ".$data->ortschaft." ".$data->strasse;
$address = [];
$address['id'] = $data->hausnummer_id;
$address["text"] = $address_string;
$address['sort_key'] = $sort_key;
$addresses[] = $address;
}
// sort results by most occurences of search strings
$sort = [];
foreach($addresses as $key => $address) {
$includes_int = false;
$count = 0;
foreach($search_parts as $p) {
$p = $this->db->escape(trim($p));
if(!$p) continue;
if(is_numeric(($p))) {
$includes_int = true;
if(substr_count(strtolower($address['text']), strtolower($p))) {
$count++;
}
} else {
$count += substr_count(strtolower($address['text']), strtolower($p));
}
}
unset($address['sort_key']);
//echo $address['text']." $p $count<br />\n";
if($includes_int && (($count + 1) - count($search_parts) ) < 1) {
continue;
}
if(!array_key_exists($count, $sort)) {
$sort[$count] = [];
}
$sort[$count][] = $address;
}
ksort($sort, SORT_NUMERIC);
$sort = array_reverse($sort, true);
//var_dump($sort);exit;
foreach($sort as $res) {
foreach($res as $a) {
$results[] = $a;
}
}
header("Content-Type: application/json");
echo json_encode(["results" => $results]);
exit;
}
private function getRimoPlanPreviewApi() {
$adb_hausnummer_id = $this->request->building_id;
$include_home_trench = $this->request->include_home_trench ? true : false;
$hausnummer = new ADBHausnummer($adb_hausnummer_id);
if(!$hausnummer->id) {
return false;
}
$filename = "consent_plan_map_h{$adb_hausnummer_id}";
/*$bpi_file = PreorderFile::getFirst(["preorder_id" => $this->id, "filename" => $filename]);
if($bpi_file) {
return $bpi_file;
}*/
// get trenches from rimo
$geodataResponse = Rimoapi::getBuildingGeoJson($hausnummer->rimo_id);
$this->log->debug(__METHOD__.": ".print_r($geodataResponse, true));
//return false;
if (is_object($geodataResponse)) {
if (property_exists($geodataResponse, "homeSection")) {
if (property_exists($geodataResponse->homeSection, "features") && is_array($geodataResponse->homeSection->features)) {
foreach ($geodataResponse->homeSection->features as $feature) {
$home_trench = [];
foreach ($feature->geometry->coordinates as $coords) {
$long = $coords[0];
$lat = $coords[1];
$home_trench[] = [$lat, $long];
}
if ($hausnummer->home_trench != $home_trench) {
$hausnummer->home_trench = json_encode($home_trench);
$hausnummer->save();
}
}
}
}
if (property_exists($geodataResponse, "borderPoint")) {
if (property_exists($geodataResponse->borderPoint, "features") && is_array($geodataResponse->borderPoint->features)) {
foreach ($geodataResponse->borderPoint->features as $feature) {
$coords = $feature->geometry->coordinates;
$long = $coords[0];
$lat = $coords[1];
if ($hausnummer->borderpoint_lat != $lat || $hausnummer->borderpoint_long != $long) {
$hausnummer->borderpoint_lat = $lat;
$hausnummer->borderpoint_long = $long;
$hausnummer->save();
}
}
}
}
if (property_exists($geodataResponse, "trenches") && $geodataResponse->trenches->features) {
$trenches = [];
foreach ($geodataResponse->trenches->features as $feature) {
$feature_coords = [];
foreach ($feature->geometry->coordinates as $coords) {
$long = $coords[0];
$lat = $coords[1];
$feature_coords[] = [$lat, $long];
}
$trenches[] = $feature_coords;
}
if (count($trenches)) {
$hausnummer->trenches = json_encode($trenches);
$hausnummer->save();
}
}
}
// get new Borderpoint Image from Mapbox API
$params = [
"pin" => null,
"gps_lat" => $hausnummer->gps_lat,
"gps_long" => $hausnummer->gps_long,
"zoom" => 19,
"size_x" => 640,
"size_y" => 640,
"style" => "satellite-streets-v12",
"paths" => "",
"access_token" => TT_MAPBOX_TILE_API_TOKEN
];
if($hausnummer->trenches) {
$trenches = [];
if($include_home_trench && $hausnummer->home_trench) {
$trenches = [json_decode($hausnummer->home_trench)];
}
$street_trenches = json_decode($hausnummer->trenches);
foreach($street_trenches as $t) {
if(is_array($t) && count($t)) {
$trenches[] = $t;
}
}
$this->log->debug(print_r($trenches, true));
$params["paths"] = [
"line_width" => 5,
"line_color" => "ff0000",
"line_opacity" => 1,
"line_fill_color" => "ff0000",
"line_fill_opacity" => 1,
"coords" => $trenches
];
}
$image_content = Mapbox_StaticImageApi::getImageFileContent($params);
if(!$image_content) {
return false;
}
$fs_filename = "$filename.jpg";
if(!file_put_contents(MFUPLOAD_FILE_SAVE_PATH."/".TT_CONSTRUCTIONCONSENT_FILE_UPLOAD_SUBFOLDER."/$fs_filename", $image_content)) {
$this->log->error(__METHOD__.": Error saving Borderpoint Static Map Image File");
return false;
}
$file = FileModel::create([
"name" => "consent_plan_map",
"description" => $adb_hausnummer_id,
"filename" => "$filename.jpg",
"orig_filename" => "$filename.jpg",
"store_filename" => $fs_filename,
"subfolder" => TT_CONSTRUCTIONCONSENT_FILE_UPLOAD_SUBFOLDER,
]);
if(!$file->save()) {
$this->log->error(__METHOD__.": Error saving File Object");
return false;
}
$file->mimetype = $file->getMimetype();
$file->save();
return ["image_mimetype" => $file->mimetype, "image_base64" => base64_encode($image_content), "file_id" => $file->id];
}
private function deleteRimoPlanApi() {
$consent_id = $this->request->id;
$consent = new ConstructionConsent($consent_id);
if(!$consent->id) return false;
if($consent->file && $consent->file->file_id) {
$consent->file->delete();
}
return ["message" => "Plan deleted successfully"];
}
private function saveCheckboxApi() {
$r = $this->request;
$consent_id = $r->id;
$consent = new ConstructionConsent($consent_id);
if(!$consent->id) return false;
$data = [];
if($r->isset("inspection_planner")) {
$data["inspection_planner"] = ($r->inspection_planner > 0) ? date("U") : 0;
}
if($r->isset("inspection_electrician")) {
$data["inspection_electrician"] = ($r->inspection_electrician > 0) ? date("U") : 0;
}
if($r->isset("conduit_installed_building")) {
$data["conduit_installed_building"] = ($r->conduit_installed_building > 0) ? date("U") : 0;
}
if($r->isset("conduit_installed_ftu")) {
$data["conduit_installed_ftu"] = ($r->conduit_installed_ftu > 0) ? date("U") : 0;
}
if($r->isset("inhouse_cabling")) {
$data["inhouse_cabling"] = ($r->inhouse_cabling > 0) ? date("U") : 0;
}
if(count($data)) {
$consent->update($data);
$consent->save();
}
return ["message" => "ConstructionConsent saved successfully"];
}
private function generateStats($baseFilter = array()): array {
function getFilteredCount($wantedFilter, $filterValue, $baseFilter) {
if (!empty($baseFilter[$wantedFilter]) && $baseFilter[$wantedFilter] != $filterValue) return 0;
return ConstructionConsent::count(array_merge($baseFilter, [$wantedFilter => $filterValue]));
}
return [
"all" => ConstructionConsent::count($baseFilter),
"street" => getFilteredCount("object_type", "street", $baseFilter),
"building" => getFilteredCount("object_type", "building", $baseFilter),
"inspection_planner" => getFilteredCount("inspection_planner", "!NULL", $baseFilter),
"conduit_installed_building" => getFilteredCount("conduit_installed_building", "!NULL", $baseFilter),
"conduit_installed_ftu" => getFilteredCount("conduit_installed_ftu", "!NULL", $baseFilter),
"inhouse_cabling" => getFilteredCount("inhouse_cabling", "!NULL", $baseFilter),
"status_light_blue" => getFilteredCount("status_light", "blue", $baseFilter),
"status_light_red" => getFilteredCount("status_light", "red", $baseFilter),
"status_light_yellow" => getFilteredCount("status_light", "yellow", $baseFilter),
"status_light_green" => getFilteredCount("status_light", "green", $baseFilter)
];
}
protected function approveOverrideAction() {
$consent_id = $this->request->consent_id;
$checked = $this->request->checked;
$consent = new ConstructionConsent($consent_id);
if(!$consent->id) {
$this->returnJson(["status" => "error"]);
}
$consent->update(["approve_override" => $checked]);
$consent->save();
$this->returnJson(["status" => "OK"]);
}
protected function deferAction() {
$consent_id = $this->request->consent_id;
$checked = $this->request->checked;
$consent = new ConstructionConsent($consent_id);
if(!$consent->id) {
$this->returnJson(["status" => "error"]);
}
$consent->update(["deferred" => $checked]);
$consent->save();
$this->returnJson(["status" => "OK"]);
}
protected function prioritizeAction() {
$consent_id = $this->request->consent_id;
$checked = $this->request->checked;
$consent = new ConstructionConsent($consent_id);
if(!$consent->id) {
$this->returnJson(["status" => "error"]);
}
$consent->update(["prioritize" => $checked]);
$consent->save();
$this->returnJson(["status" => "OK"]);
}
protected function importConstructionConsentOwnersAction() {
$projectId = $this->request->project_id;
$importData = json_decode(file_get_contents('php://input'), true);
if (empty($importData) || !is_array($importData)) { $this->layout()->setFlash("Keine Daten gefunden", "error"); return $this->redirect("ConstructionConsent"); }
if (!is_numeric($projectId) || $projectId < 1) { $this->layout()->setFlash("Projekt nicht gefunden", "error"); return $this->redirect("ConstructionConsent"); }
if (!($consentProject = new ConstructionConsentProject($projectId))->id) { $this->layout()->setFlash("Projekt nicht gefunden", "error"); return $this->redirect("ConstructionConsent"); }
$counts = ["created" => 0, "updated" => 0, "skipped" => 0, "skippedBecauseNoConsent" => 0, "skippedBecauseAlreadyExists" => 0, "skippedBecauseFirstnameAndLastname" => 0];
foreach ($importData as $ownerData) {
$consentFilter = ["constructionconsentproject_id" => $consentProject->id, "kg" => $ownerData["KG-EZ"] ?? null, "ez" => $ownerData["EZ"] ?? null, "lnr" => $ownerData["LNR"] ?? null];
if (!($consentRecords = ConstructionConsent::search(array_filter($consentFilter)))) {
$counts["skippedBecauseNoConsent"]++;
$counts["skipped"]++; continue;
}
foreach ($consentRecords as $consentRecord) {
$birthdate = (isset($ownerData["GEB"]) && preg_match("/^\d{4}-\d{2}-\d{2}$/", $ownerData["GEB"])) ? $ownerData["GEB"] : null;
$ownerFilter = ["constructionconsent_id" => $consentRecord->id, "firstname" => $ownerData["VN"] ?? null, "lastname" => $ownerData["NN"] ?? null];
if ($birthdate) { $ownerFilter["birthdate"] = $birthdate; }
$isNew = !($ownerRecord = ConstructionConsentOwner::getFirst(array_filter($ownerFilter)));
if ($isNew) {
$ownerRecord = new ConstructionConsentOwner();
$ownerRecord->constructionconsent_id = $consentRecord->id;
$ownerRecord->create_by = $this->me->id;
} else {
$counts["skippedBecauseAlreadyExists"]++;
$counts["skipped"]++;
continue;
}
$addressKey = null; foreach(array_keys($ownerData) as $k) { if(strpos($k,"ADR") === 0) {$addressKey=$k; break;} }
$street = $city = $postcode = $country = null;
if ($addressKey && isset($ownerData[$addressKey])) {
$addressParts = explode(", ", str_replace('"', '', $ownerData[$addressKey]));
$street = $addressParts[0] ?? null;
if (isset($addressParts[1])) { list($postcode, $city) = array_pad(explode(" ", trim($addressParts[1]), 2), 2, null); }
$country = $addressParts[2] ?? null;
}
$ownerRecord->firstname = $ownerData["VN"] ?? null;
$ownerRecord->lastname = (($ownerData["JUR"] ?? 'nein') === "ja") ? ($ownerData['BEZ'] ?? $ownerData["NN"]) : ($ownerData["NN"] ?? null);
$ownerRecord->birthdate = $birthdate;
$ownerRecord->street = $street;
$ownerRecord->city = $city;
$ownerRecord->zip = $postcode;
$ownerRecord->country = $country;
$ownerRecord->edit_by = $this->me->id;
$firstname = $ownerRecord->firstname;
$lastname = $ownerRecord->lastname;
if (empty($firstname) && empty($lastname)) {
$counts["skippedBecauseFirstnameAndLastname"]++;
$counts["skipped"]++;
continue;
}
$ownerRecord->save();
$journal = ConstructionConsentJournal::create([
"constructionconsent_id" => $consentRecord->id,
"text" => "Import: Eigentümer $firstname $lastname wurde hinzugefügt"
]);
$journal->save();
$counts[$isNew ? "created" : "updated"]++;
}
}
self::returnJson(["status" => "OK", "counts" => $counts]);
return true;
}
private function getNetzgebieteApi() {
$project_id = $this->request->project_id;
if(!is_numeric($project_id) || $project_id < 1) self::sendError("Invalid project id");
$project = new ConstructionConsentProject($project_id);
if(!$project->id) self::sendError("Project not found");
$parsedNetworks = array_map(function($network) {
return [
"value" => $network->id,
"text" => $network->name,
];
}, array_values($project->adb_networks));
self::returnJson(["results" => $parsedNetworks]);
}
protected function downloadCSVAction() {
$filter = $this->request->filter;
if(!is_array($filter)) {
$this->layout()->setFlash("Fehler beim Erstellen der CSV", "error");
return $this->redirect("ConstructionConsent");
}
$filter = $this->getPreparedFilter($filter);
$items = ConstructionConsent::search($filter);
$filename = 'Zustimmungserklärungen_' . date('Ymd_His') . '.csv';
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
$output = fopen('php://output', 'w');
fwrite($output, chr(0xEF) . chr(0xBB) . chr(0xBF)); // UTF-8 BOM for Excel
$headers = [
'Projekt',
'Objekttyp',
'Objektadresse',
'Name',
'EZ',
'KG',
'GST',
'GSTNR',
'RIMO GN',
'Wohneinheiten',
'Bestellungen',
'Anfrageresultat',
'Nutzungslänge',
'Leerrohr am Grundstück',
'Leerrohr im Gebäude',
'Schacht',
'Eigentümer',
'Planer Besichtigung',
'Elektriker Besichtigung',
'Leerverrohrung im Gebäude installiert',
'Leerverrohrung bis FTU installiert',
'Inhouse Verkabelung',
'Anzahl Eigentümer',
'Notiz',
'Erstellt von',
'Geändert von',
'Erstellt am',
'Geändert am',
'Genehmigung überschrieben'
];
fputcsv($output, $headers, ';');
if ($items) {
foreach ($items as $item) {
$createUser = UserModel::getOne($item->create_by);
$editUser = UserModel::getOne($item->edit_by);
$address = "";
if($item->object_type == "street") {
$address = $item->adb_strasse->name . ", " . $item->adb_strasse->ortschaft->name . " " . $item->adb_strasse->gemeinde->name;
} else {
$address = $item->adb_hausnummer->strasse->name . " " . $item->adb_hausnummer->hausnummer . ($item->adb_hausnummer->stiege ? "/".$item->adb_hausnummer->stiege : "") . " " .
$item->adb_hausnummer->plz->plz . " " . $item->adb_hausnummer->ortschaft->name . " " .
$item->adb_hausnummer->strasse->gemeinde->name;
}
$status_class = 'Blau';
$approve_override = $item->approve_override;
if (isset($approve_override) && $approve_override) {
$status_class = 'Grün';
} elseif (isset($item->owner_result_counts['denied']) && $item->owner_result_counts['denied'] > 0) {
$status_class = 'Rot';
} elseif (
(isset($item->owner_result_counts['unresolvable']) && $item->owner_result_counts['unresolvable'] > 0) ||
(isset($item->owner_result_counts['moved']) && $item->owner_result_counts['moved'] > 0) ||
(isset($item->owner_result_counts['open']) && $item->owner_result_counts['open'] > 0)
) {
$status_class = 'Gelb'; // Yellow if at least one unresolvable or moved
} elseif (isset($item->owner_result_counts['accepted']) &&
$item->owner_result_counts['accepted'] === count($item->owners)) {
$status_class = 'Grün'; // Green if all accepted
}
$row = [
$item->project->name,
$item->object_type === 'street' ? 'Straße' : 'Gebäude',
$address,
$item->name,
$item->ez,
$item->kg,
$item->gst,
$item->gstnr,
$item->rimo_gn,
$item->adb_hausnummer->unit_count ?? '',
$item->preorder_count,
$status_class,
$item->usage_length,
$item->usage_pipe_on_plot == 1 ? 'Ja' : 'Nein',
$item->usage_pipe_in_building == 1 ? 'Ja' : 'Nein',
$item->usage_manhole == 1 ? 'Ja' : 'Nein',
$item->usage_owner == 1 ? 'Ja' : 'Nein',
$item->inspection_planner > 0 ? date('d.m.Y H:i:s', $item->inspection_planner) : '',
$item->inspection_electrician > 0 ? date('d.m.Y H:i:s', $item->inspection_electrician) : '',
$item->conduit_installed_building > 0 ? date('d.m.Y H:i:s', $item->conduit_installed_building) : '',
$item->conduit_installed_ftu > 0 ? date('d.m.Y H:i:s', $item->conduit_installed_ftu) : '',
$item->inhouse_cabling > 0 ? date('d.m.Y H:i:s', $item->inhouse_cabling) : '',
$item->owners !== null ? count($item->owners) : '',
$item->note,
$createUser ? $createUser->name : '',
$editUser ? $editUser->name : '',
date('d.m.Y H:i:s', $item->create),
date('d.m.Y H:i:s', $item->edit),
$item->approve_override,
];
fputcsv($output, $row, ';');
}
}
fclose($output);
exit();
}
protected function searchTabletAction() {
$name = '';
$filter = [];
// Handle POST data - support both form data and JSON input
if($_SERVER['REQUEST_METHOD'] === 'POST') {
$postData = [];
// Check for JSON input (Axios sends JSON)
$inputJSON = file_get_contents('php://input');
$jsonData = json_decode($inputJSON, true);
if($jsonData !== null) {
$postData = $jsonData;
} else {
$postData = $_POST;
}
if(isset($postData['name'])) {
$name = $postData['name'];
}
if(isset($postData['filter']) && is_array($postData['filter'])) {
$filter = $postData['filter'];
}
}
$results = ConstructionConsent::searchConstructionConsentBasedOnName($name, $filter);
self::returnJson([
'success' => true,
'count' => count($results),
'results' => $results
]);
}
protected function signTabletAction() {
$JSGlobals = ["BASE_PATH" => self::getUrl(""),
"DASHBOARD_URL" => self::getUrl("Dashboard"),
"MFAPPNAME" => MFAPPNAME_SLUG,
"PAGE_TITLE" => "Zustimmungserklärungen unterzeichnen",
"PATH" => [
["text" => MFAPPNAME_SLUG, "href" => self::getUrl("Dashboard")],
["text" => "Zustimmungserklärungen unterzeichnen", "href" => self::getUrl("ConstructionConsent", "SignTablet")],
],
"IS_ADMIN" => $this->me->is("Admin"),
];
$this->layout()->set("vueViewName", "ConstructionConsentSignTablet");
$this->layout()->set("JSGlobals", $JSGlobals);
$this->layout()->setTemplate("VueViews/Vue");
}
protected function signAction() {
$POST = json_decode(file_get_contents('php://input'), true);
$owner_id = $POST['owner_id'] ?? null;
$signature = $POST['signature'] ?? null;
$signature_name = $POST['signature_name'] ?? null;
$signature_date = $POST['signature_date'] ?? null;
if(!$owner_id || !$signature) {
self::sendError("Invalid request");
}
$owner = new ConstructionConsentOwner($owner_id);
if(!$owner->id) self::sendError("Owner not found");
if($signature_name) $owner->signature_name = $signature_name;
if($signature_date) $owner->signature_date = date("U", strtotime($signature_date));
if($signature) $owner->signature = $signature;
$owner->status = 'returned';
$owner->result = 'accepted';
if(!$owner->save()) self::sendError("Error saving signature");
self::returnJson(["success" => true]);
}
protected function faultyEntriesAction() {
// Get project ID from request if available
$projectId = isset($_GET['project_id']) ? intval($_GET['project_id']) : null;
if ($projectId === null) {
$this->layout()->setFlash("Projekt nicht gefunden", "error");
$this->redirect("ConstructionConsentProject");
return;
}
// Get faulty owner entries
$faultyEntries = ConstructionConsentProject::getFaultyOwnerEntries($projectId);
$JSGlobals = [
"BASE_URL" => self::getUrl(""),
"DASHBOARD_URL" => self::getUrl("Dashboard"),
"MFAPPNAME" => MFAPPNAME_SLUG,
"PAGE_TITLE" => "Fehlerhafte Einträge - Zustimmungserklärungen",
"PATH" => [
["text" => MFAPPNAME_SLUG, "href" => self::getUrl("Dashboard")],
["text" => "Zustimmungserklärungen", "href" => self::getUrl("ConstructionConsent")],
["text" => "Fehlerhafte Einträge", "href" => self::getUrl("ConstructionConsent", "faultyEntries")],
],
"FAULTY_ENTRIES" => $faultyEntries,
"SELECTED_PROJECT" => $projectId,
"IS_ADMIN" => $this->me->is("Admin"),
];
$this->layout()->set("vueViewName", "ConstructionConsentFaultyEntries");
$this->layout()->set("JSGlobals", $JSGlobals);
$this->layout()->setTemplate("VueViews/Vue");
}
}