194 lines
8.2 KiB
PHP
194 lines
8.2 KiB
PHP
<?php
|
|
|
|
use Smalot\PdfParser\Parser;
|
|
|
|
class RimoWorkorder extends mfBaseModel {
|
|
private $adb_wohneinheit;
|
|
private $termination;
|
|
|
|
protected function beforeUpdate($data) {
|
|
if(!array_key_exists("edit_by", $data)) {
|
|
$me = new User();
|
|
$me->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']);
|
|
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;
|
|
}
|
|
} |