loadMe(); $data["edit_by"] = $me->id; } return $data; } public function afterSave() { $this->adb_wohneinheit = null; $this->termination = null; // prevent potential infinite loop $nesting_level = mfValuecache::singleton()->get("rimoworkorder-save-nesting-level-".$this->id); if(!$nesting_level) { $nesting_level = 1; } else { $nesting_level++; } mfValuecache::singleton()->set("rimoworkorder-save-nesting-level-".$this->id, $nesting_level); if($nesting_level > 1) { return true; } // Statuschange from Rimo statuschange for all units $wohneinheit = $this->getProperty("adb_wohneinheit"); if($wohneinheit) { AddressDB::handleRimoStatusUpdate($wohneinheit->id); } } public function getAha() { if(!$this->id) return false; if(!$this->adb_wohneinheit_id) return false; $preorder = PreorderModel::getFirstActive(["adb_wohneinheit_id" => $this->adb_wohneinheit_id]); $this->log->debug(__METHOD__.": Wohneinheit: ".$this->adb_wohneinheit_id); $this->log->debug(__METHOD__.": Preorder: ".$preorder->id); if(!$preorder) return false; $api_creds = $preorder->getNetownerRimoApiCredentials(); $this->log->debug(__METHOD__.": Rimo Api Creds: ".print_r($api_creds, true)); if(!$api_creds) return false; $apikey = $api_creds["prod"]["key"]; $ah = Rimoapi::getWorkorderAhaReport($apikey, $this->rimo_id); return $ah; } public function parseAha(): array { if (!$this->id || !$this->adb_wohneinheit_id) return ['success' => false, 'message' => 'Missing ID']; $preorder = PreorderModel::getFirstActive(["adb_wohneinheit_id" => $this->adb_wohneinheit_id]); if (!$preorder?->id) return ['success' => false, 'message' => 'No active Preorder']; $workorder = WorkorderModel::getFirst(['preorderId' => $preorder->id]); if (!$workorder || !($pdf = $this->getAha())) return ['success' => false, 'message' => 'No Workorder or PDF']; try { $dropkabel = $this->parseDropkabelFromPdf($pdf); $map = $this->extractMapFromPdf($pdf); $meta = json_decode($workorder->metadata ?: '{}', true) ?: []; $mapFileId = null; if ($map) { if ($oldId = ($meta['dropcable']['map_file_id'] ?? null)) { $old = new File($oldId); if ($old->id) try { $old->delete(); } catch (Exception $e) {} } $fn = 'aha_lageplan_' . $this->id . '_' . time() . '.png'; $file = FileModel::create(['name' => 'AHA Lageplan ' . $this->rimo_name, 'filename' => $fn, 'store_filename' => $fn, 'orig_filename' => 'AHA_Lageplan_' . $this->rimo_name . '.png', 'mimetype' => 'image/png', 'subfolder' => 'aha_maps', 'create_by' => 1]); if ($file->save()) { $dir = MFUPLOAD_FILE_SAVE_PATH . '/aha_maps'; if (!is_dir($dir)) mkdir($dir, 0755, true); if (file_put_contents("$dir/$fn", $map)) $mapFileId = $file->id; } } $meta['dropcable'] = ['rimo_workorder_id' => $this->id, 'rimo_name' => $this->rimo_name, 'parsed_at' => time(), 'entries' => $dropkabel, 'map_file_id' => $mapFileId]; $workorder->metadata = json_encode($meta); WorkorderModel::update((array)$workorder); return ['success' => true, 'dropkabel_count' => count($dropkabel), 'has_map' => (bool)$mapFileId]; } catch (Exception $e) { $this->log->error(__METHOD__ . ": " . $e->getMessage()); return ['success' => false, 'message' => $e->getMessage()]; } } public static function autoParseForWorkorder(int $workorderId, bool $force = false): void { $wo = WorkorderModel::get($workorderId); if (!$wo) return; $meta = json_decode($wo->metadata ?? '{}', true); if (($force || empty($meta['dropcable']['parsed_at'])) && $wo->preorderId) { $pre = new Preorder($wo->preorderId); $rimos = $pre->adb_wohneinheit_id ? RimoWorkorderModel::search(['adb_wohneinheit_id' => $pre->adb_wohneinheit_id]) : []; if (!empty($rimos[0])) (new self($rimos[0]->id))->parseAha(); } } private function parseDropkabelFromPdf(string $pdf): array { $result = []; $text = (new Parser())->parseContent($pdf)->getPages()[0]?->getText() ?? ''; if (!preg_match('/Dropkabel:\s*\n(.+?)(?:Lage:|$)/s', $text, $m)) { // Try alternative pattern without strict newline requirement if (!preg_match('/Dropkabel[:\s]*(.+?)(?:Lage|Anschluss|$)/si', $text, $m)) { return $result; } } $started = false; foreach (explode("\n", $m[1]) as $line) { $line = trim($line); if (!$line) continue; // Check for header line (ID and Type columns) if (stripos($line, 'ID') !== false && (stripos($line, 'Type') !== false || stripos($line, 'Typ') !== false)) { $started = true; continue; } // Flexible cable ID pattern - matches F26-K009, F-ABC123-K01, F-XYZ(1)-K02, etc. if ($started && preg_match('#^([A-Z][A-Z0-9()/_-]*-K\d+)\s+(.+)$#i', $line, $p)) { $rest = $p[2]; $status = ''; foreach (['Planfreigabe', 'Plan released', 'Grobplanung', 'Executed', 'Ausgeführt', 'Detailed planning', 'Detailplanung'] as $s) if (preg_match('/\b' . preg_quote($s, '/') . '\s*$/i', $rest)) { $status = $s; $rest = trim(preg_replace('/\b' . preg_quote($s, '/') . '\s*$/i', '', $rest)); break; } $lp = $li = ''; if (preg_match_all('/(\d+)\s*m\b/', $rest, $lens, PREG_SET_ORDER)) { $lp = ($lens[0][1] ?? '') . ' m'; $li = ($lens[1][1] ?? '') . ' m'; $rest = preg_replace('/\d+\s*m\b/', '', $rest); } $result[] = ['cable_id' => trim($p[1]), 'type' => trim(preg_replace('/\s+/', ' ', $rest)), 'laenge_plan' => $lp, 'laenge_ist' => $li, 'status' => $status]; } } return $result; } private function extractMapFromPdf(string $pdf): ?string { $tmp = tempnam(sys_get_temp_dir(), 'aha_'); file_put_contents($tmp, $pdf); $out = tempnam(sys_get_temp_dir(), 'aha_img_'); unlink($out); exec(sprintf('pdftoppm -png -f 1 -l 1 -r 150 %s %s 2>&1', escapeshellarg($tmp), escapeshellarg($out)), $_, $ret); @unlink($tmp); $outFile = file_exists("$out-1.png") ? "$out-1.png" : "$out.png"; if ($ret !== 0 || !file_exists($outFile)) return null; $img = @imagecreatefromstring(file_get_contents($outFile)); @unlink($outFile); if (!$img) return null; $h = imagesy($img); $cropY = (int)($h * 0.42); $cropH = (int)($h * 0.84) - $cropY; $cropped = imagecrop($img, ['x' => 60, 'y' => $cropY, 'width' => imagesx($img) - 90, 'height' => $cropH]); imagedestroy($img); if (!$cropped) return null; ob_start(); imagepng($cropped, null, 6); $content = ob_get_clean(); imagedestroy($cropped); return $content; } public function getProperty($name) { if($this->$name == null) { if(!$this->id) { return null; } if($name == "adb_wohneinheit") { $this->adb_wohneinheit = new ADBWohneinheit($this->adb_wohneinheit_id); return $this->adb_wohneinheit; } $classname = ucfirst($name); $idfield = $name."_id"; $this->$name = new $classname($this->$idfield); if($this->$name->id) { return $this->$name; } else { return null; } } return $this->$name; } }