Notiz column now has max 5 lines with expand/collapse animation and auto-resizing edit textarea. Moved raw SQL query to WorkorderModel::getPreorderIdsByCampaigns() to fix OOM in archiveWorkorders(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
272 lines
14 KiB
PHP
272 lines
14 KiB
PHP
<?php
|
|
// WorkorderModel.php
|
|
|
|
class WorkorderModel extends TTCrudBaseModel
|
|
{
|
|
public int $id;
|
|
public int $preorderId;
|
|
public ?int $companyId;
|
|
public ?int $civilEngineeringCompanyId;
|
|
public ?int $originalCompanyId;
|
|
public ?int $clusterId;
|
|
public string $status;
|
|
public ?int $assignmentDate;
|
|
public ?int $deadlineDate;
|
|
public ?int $appointmentDate;
|
|
public ?string $additionalInfo;
|
|
public ?string $cableLength;
|
|
public ?string $cableType;
|
|
public ?string $metadata;
|
|
public int $create;
|
|
public int $createBy;
|
|
|
|
private static function buildWhereClause(array $filters, array $allowedCampaignIds): string
|
|
{
|
|
if (empty($allowedCampaignIds)) return " WHERE 1=0";
|
|
|
|
$sql = Helper::generateFilterCondition(array_map('intval', $allowedCampaignIds), 'p.preordercampaign_id');
|
|
|
|
if (empty($filters['status'])) {
|
|
$sql .= " AND w.status NOT IN ('completed', 'cancelled', 'charged', 'archived')";
|
|
} else {
|
|
$sql .= Helper::generateFilterCondition($filters['status'], 'w.status', true);
|
|
}
|
|
|
|
if (!empty($filters['id'])) $sql .= Helper::generateFilterCondition($filters['id'], 'w.id', true);
|
|
if (!empty($filters['preordercampaign_id'])) $sql .= Helper::generateFilterCondition($filters['preordercampaign_id'], 'p.preordercampaign_id');
|
|
if (!empty($filters['companyName'])) $sql .= Helper::generateFilterCondition($filters['companyName'], 'c.name');
|
|
if (!empty($filters['netOwnerId'])) $sql .= Helper::generateFilterCondition($filters['netOwnerId'], 'n.owner_id');
|
|
if (!empty($filters['networkOwnerName'])) $sql .= Helper::generateFilterCondition($filters['networkOwnerName'], 'owner_addr.company');
|
|
if (!empty($filters['deadlineDate'])) $sql .= Helper::generateFilterCondition($filters['deadlineDate'], 'w.deadlineDate');
|
|
if (!empty($filters['preorderInfo'])) {
|
|
$searchColumns = "p.firstname|p.lastname|p.company|p.oaid|p.street|p.housenumber|p.zip|p.city|str.name|ort.name|w.additionalInfo";
|
|
$sql .= Helper::generateFilterCondition($filters['preorderInfo'], $searchColumns);
|
|
}
|
|
if (!empty($filters['rimo_fcp_name'])) $sql .= Helper::generateFilterCondition($filters['rimo_fcp_name'], "hn.rimo_fcp_name");
|
|
if (!empty($filters['additionalInfo'])) $sql .= Helper::generateFilterCondition($filters['additionalInfo'], 'w.additionalInfo');
|
|
|
|
return "WHERE " . ltrim(trim($sql), 'AND');
|
|
}
|
|
|
|
public static function getAdminWorkorders(array $filters, ?int $limit, int $offset, array $order, array $allowedCampaignIds): array
|
|
{
|
|
$db = self::getDB();
|
|
$fronkDbName = FRONKDB_DBNAME;
|
|
$addressDbName = defined('ADDRESSDB_DBNAME') ? ADDRESSDB_DBNAME : 'addressdb';
|
|
|
|
$sql = "
|
|
SELECT
|
|
w.id, w.status, w.deadlineDate, w.appointmentDate, w.companyId, w.additionalInfo, p.preordercampaign_id, hn.rimo_fcp_name,
|
|
n.owner_id as tenantId, p.ucode, CONCAT_WS(' ', p.firstname, p.lastname) as customerName,
|
|
p.company as customerCompany, p.oaid, IFNULL(c_civil.name, c.name) as companyName, str.name as street, hn.hausnummer,
|
|
hn.stiege, we.bezeichner as apartment, plz.plz, ort.name as city,
|
|
n.owner_id as netOwnerId
|
|
FROM `$fronkDbName`.`Workorder` w
|
|
JOIN `$fronkDbName`.`Preorder` p ON w.preorderId = p.id
|
|
LEFT JOIN `$fronkDbName`.`Preordercampaign` pc ON p.preordercampaign_id = pc.id
|
|
LEFT JOIN `$fronkDbName`.`Network` n ON pc.network_id = n.id
|
|
LEFT JOIN `$fronkDbName`.`WorkorderCompany` c ON w.companyId = c.id
|
|
LEFT JOIN `$fronkDbName`.`WorkorderCompany` c_civil ON w.civilEngineeringCompanyId = c_civil.id
|
|
LEFT JOIN `$addressDbName`.`Hausnummer` hn ON p.adb_hausnummer_id = hn.id
|
|
LEFT JOIN `$addressDbName`.`Strasse` str ON hn.strasse_id = str.id
|
|
LEFT JOIN `$addressDbName`.`Plz` plz ON hn.plz_id = plz.id
|
|
LEFT JOIN `$addressDbName`.`Ortschaft` ort ON hn.ortschaft_id = ort.id
|
|
LEFT JOIN `$addressDbName`.`Wohneinheit` we ON p.adb_wohneinheit_id = we.id
|
|
";
|
|
|
|
$sql .= self::buildWhereClause($filters, $allowedCampaignIds);
|
|
|
|
$orderBy = "";
|
|
if (!empty($order['key'])) {
|
|
$sortableColumns = ['id', 'status', 'deadlineDate', 'companyName', 'rimo_fcp_name', 'additionalInfo'];
|
|
if (in_array($order['key'], $sortableColumns)) {
|
|
$sortOrder = (strtoupper($order['order']) === 'DESC') ? 'DESC' : 'ASC';
|
|
$orderBy = " ORDER BY " . $db->real_escape_string($order['key']) . " " . $sortOrder;
|
|
}
|
|
}
|
|
if (empty($orderBy)) $orderBy = " ORDER BY CASE WHEN w.deadlineDate IS NULL THEN 1 ELSE 0 END, w.deadlineDate ASC";
|
|
|
|
$sql .= $orderBy;
|
|
|
|
if ($limit !== null) $sql .= " LIMIT " . intval($limit) . " OFFSET " . intval($offset);
|
|
$result = $db->query($sql);
|
|
return $result ? $result->fetch_all(MYSQLI_ASSOC) : [];
|
|
}
|
|
|
|
public static function countAdminWorkorders(array $filters, array $allowedCampaignIds): int
|
|
{
|
|
$db = self::getDB();
|
|
$fronkDbName = FRONKDB_DBNAME;
|
|
$addressDbName = defined('ADDRESSDB_DBNAME') ? ADDRESSDB_DBNAME : 'addressdb';
|
|
$sql = "
|
|
SELECT COUNT(w.id) as count FROM `$fronkDbName`.`Workorder` w
|
|
JOIN `$fronkDbName`.`Preorder` p ON w.preorderId = p.id
|
|
LEFT JOIN `$fronkDbName`.`WorkorderCompany` c ON w.companyId = c.id
|
|
LEFT JOIN `$fronkDbName`.`WorkorderCompany` c_civil ON w.civilEngineeringCompanyId = c_civil.id
|
|
LEFT JOIN `$fronkDbName`.`Preordercampaign` pc ON p.preordercampaign_id = pc.id
|
|
LEFT JOIN `$fronkDbName`.`Network` n ON pc.network_id = n.id
|
|
LEFT JOIN `$addressDbName`.`Hausnummer` hn ON p.adb_hausnummer_id = hn.id
|
|
LEFT JOIN `$addressDbName`.`Strasse` str ON hn.strasse_id = str.id
|
|
LEFT JOIN `$addressDbName`.`Plz` plz ON hn.plz_id = plz.id
|
|
LEFT JOIN `$addressDbName`.`Ortschaft` ort ON hn.ortschaft_id = ort.id
|
|
";
|
|
$sql .= self::buildWhereClause($filters, $allowedCampaignIds);
|
|
$result = $db->query($sql);
|
|
return $result ? $result->fetch_assoc()['count'] : 0;
|
|
}
|
|
|
|
private static function buildCompanyWhereClause(array $filters, int $companyId): string
|
|
{
|
|
$sql = "(w.companyId = " . $companyId . " OR w.civilEngineeringCompanyId = " . $companyId . ") AND w.status != 'charged'";
|
|
|
|
if (empty($filters['status'])) {
|
|
$sql .= " AND w.status NOT IN ('completed', 'cancelled', 'archived')";
|
|
} else {
|
|
$sql .= Helper::generateFilterCondition($filters['status'], 'w.status', true);
|
|
}
|
|
|
|
if (!empty($filters['id'])) $sql .= Helper::generateFilterCondition($filters['id'], 'w.id', true);
|
|
if (!empty($filters['preordercampaign_id'])) $sql .= Helper::generateFilterCondition($filters['preordercampaign_id'], 'p.preordercampaign_id');
|
|
if (!empty($filters['deadlineDate'])) $sql .= Helper::generateFilterCondition($filters['deadlineDate'], 'w.deadlineDate');
|
|
if (!empty($filters['networkOwnerName'])) $sql .= Helper::generateFilterCondition($filters['networkOwnerName'], 'owner_addr.company');
|
|
if (!empty($filters['appointmentDate'])) $sql .= Helper::generateFilterCondition($filters['appointmentDate'], 'w.appointmentDate');
|
|
if (!empty($filters['preorderInfo'])) {
|
|
$searchColumns = "p.firstname|p.lastname|p.company|p.oaid|p.street|p.housenumber|p.zip|p.city|str.name|ort.name|p.phone|p.email|w.additionalInfo";
|
|
$sql .= Helper::generateFilterCondition($filters['preorderInfo'], $searchColumns);
|
|
}
|
|
if (!empty($filters['rimo_fcp_name'])) $sql .= Helper::generateFilterCondition($filters['rimo_fcp_name'], "hn.rimo_fcp_name");
|
|
if (!empty($filters['additionalInfo'])) $sql .= Helper::generateFilterCondition($filters['additionalInfo'], 'w.additionalInfo');
|
|
return "WHERE " . $sql;
|
|
}
|
|
|
|
public static function getCompanyWorkorders(array $filters, ?int $limit, int $offset, array $order, int $companyId): array
|
|
{
|
|
$db = self::getDB();
|
|
$fronkDbName = FRONKDB_DBNAME;
|
|
$addressDbName = defined('ADDRESSDB_DBNAME') ? ADDRESSDB_DBNAME : 'addressdb';
|
|
$sql = "
|
|
SELECT w.id, w.status, w.deadlineDate, w.appointmentDate, w.additionalInfo, hn.rimo_fcp_name,
|
|
owner_addr.company as networkOwnerName, p.preordercampaign_id,
|
|
CONCAT_WS(' ', p.firstname, p.lastname) as customerName, p.company as customerCompany, p.oaid,
|
|
p.phone, p.email, str.name as street, hn.hausnummer, hn.stiege, we.bezeichner as apartment, plz.plz, ort.name as city
|
|
FROM `$fronkDbName`.`Workorder` w
|
|
JOIN `$fronkDbName`.`Preorder` p ON w.preorderId = p.id
|
|
LEFT JOIN `$fronkDbName`.`Preordercampaign` pc ON p.preordercampaign_id = pc.id
|
|
LEFT JOIN `$fronkDbName`.`Network` n ON pc.network_id = n.id
|
|
LEFT JOIN `$fronkDbName`.`Address` owner_addr ON n.owner_id = owner_addr.id
|
|
LEFT JOIN `$addressDbName`.`Hausnummer` hn ON p.adb_hausnummer_id = hn.id
|
|
LEFT JOIN `$addressDbName`.`Strasse` str ON hn.strasse_id = str.id
|
|
LEFT JOIN `$addressDbName`.`Plz` plz ON hn.plz_id = plz.id
|
|
LEFT JOIN `$addressDbName`.`Ortschaft` ort ON hn.ortschaft_id = ort.id
|
|
LEFT JOIN `$addressDbName`.`Wohneinheit` we ON p.adb_wohneinheit_id = we.id
|
|
";
|
|
$sql .= self::buildCompanyWhereClause($filters, $companyId);
|
|
|
|
$orderBy = "";
|
|
if (!empty($order['key'])) {
|
|
$sortableColumns = ['id', 'status', 'deadlineDate', 'rimo_fcp_name', 'appointmentDate', 'additionalInfo', 'preordercampaign_id'];
|
|
if (in_array($order['key'], $sortableColumns)) {
|
|
$sortOrder = (strtoupper($order['order']) === 'DESC') ? 'DESC' : 'ASC';
|
|
$orderBy = " ORDER BY " . $db->real_escape_string($order['key']) . " " . $sortOrder;
|
|
}
|
|
}
|
|
if (empty($orderBy)) $orderBy = " ORDER BY CASE WHEN w.deadlineDate IS NULL THEN 1 ELSE 0 END, w.deadlineDate ASC";
|
|
|
|
$sql .= $orderBy;
|
|
|
|
if ($limit !== null) $sql .= " LIMIT " . intval($limit) . " OFFSET " . intval($offset);
|
|
|
|
$result = $db->query($sql);
|
|
return $result ? $result->fetch_all(MYSQLI_ASSOC) : [];
|
|
}
|
|
|
|
public static function countCompanyWorkorders(array $filters, int $companyId): int
|
|
{
|
|
$db = self::getDB();
|
|
$fronkDbName = FRONKDB_DBNAME;
|
|
$addressDbName = defined('ADDRESSDB_DBNAME') ? ADDRESSDB_DBNAME : 'addressdb';
|
|
$sql = "
|
|
SELECT COUNT(w.id) as count FROM `$fronkDbName`.`Workorder` w
|
|
JOIN `$fronkDbName`.`Preorder` p ON w.preorderId = p.id
|
|
LEFT JOIN `$addressDbName`.`Hausnummer` hn ON p.adb_hausnummer_id = hn.id
|
|
LEFT JOIN `$fronkDbName`.`Preordercampaign` pc ON p.preordercampaign_id = pc.id
|
|
LEFT JOIN `$fronkDbName`.`Network` n ON pc.network_id = n.id
|
|
LEFT JOIN `$fronkDbName`.`Address` owner_addr ON n.owner_id = owner_addr.id
|
|
LEFT JOIN `$addressDbName`.`Strasse` str ON hn.strasse_id = str.id
|
|
LEFT JOIN `$addressDbName`.`Plz` plz ON hn.plz_id = plz.id
|
|
LEFT JOIN `$addressDbName`.`Ortschaft` ort ON hn.ortschaft_id = ort.id
|
|
";
|
|
$sql .= self::buildCompanyWhereClause($filters, $companyId);
|
|
$result = $db->query($sql);
|
|
return $result ? $result->fetch_assoc()['count'] : 0;
|
|
}
|
|
|
|
public static function getPreorderIdsByCampaigns(array $campaignIds): array {
|
|
if (empty($campaignIds)) return [];
|
|
$db = self::getDB();
|
|
$campaignIdList = implode(',', array_map('intval', $campaignIds));
|
|
$result = $db->query("SELECT id FROM `" . FRONKDB_DBNAME . "`.`Preorder` WHERE preordercampaign_id IN ($campaignIdList)");
|
|
if (!$result || $result->num_rows === 0) return [];
|
|
$ids = array_column($result->fetch_all(MYSQLI_ASSOC), 'id');
|
|
$result->free();
|
|
return $ids;
|
|
}
|
|
|
|
public static function getTechnicalData(int $workorderId): ?array {
|
|
$workorder = self::get($workorderId);
|
|
if (!$workorder || !$workorder->preorderId) return null;
|
|
|
|
$preorder = new Preorder($workorder->preorderId);
|
|
if (!$preorder->id || !$preorder->adb_wohneinheit_id) return null;
|
|
|
|
$wohneinheit = $preorder->adb_wohneinheit;
|
|
if (!$wohneinheit) return null;
|
|
|
|
$defaultCluster = '';
|
|
if ($preorder->adb_hausnummer && $preorder->adb_hausnummer->netzgebiet) {
|
|
$defaultCluster = $preorder->adb_hausnummer->netzgebiet->extref ?? '';
|
|
}
|
|
|
|
$patchposition = [
|
|
'equipmentName' => $wohneinheit->getPatchEqString(),
|
|
'equipmentPort' => $wohneinheit->patch_port,
|
|
'cluster' => $wohneinheit->patch_cluster ?: $defaultCluster,
|
|
'shelf' => $wohneinheit->patch_shelf,
|
|
'module' => $wohneinheit->patch_module,
|
|
];
|
|
|
|
// Get dropcable data from metadata
|
|
$dropkabelData = [];
|
|
$ahaParsed = null;
|
|
$mapFile = null;
|
|
if (!empty($workorder->metadata)) {
|
|
$metadata = json_decode($workorder->metadata, true);
|
|
if (!empty($metadata['dropcable'])) {
|
|
$ahaParsed = $metadata['dropcable']['parsed_at'] ?? null;
|
|
$dropkabelData = $metadata['dropcable']['entries'] ?? [];
|
|
if ($mapFileId = $metadata['dropcable']['map_file_id'] ?? null) {
|
|
$file = new File($mapFileId);
|
|
if ($file->id) {
|
|
$mapFile = ['id' => $file->id, 'name' => $file->name, 'download_url' => '/File/show?id=' . $file->id];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$rimoWorkorders = [];
|
|
if (is_array($wohneinheit->rimo_workorders) && count($wohneinheit->rimo_workorders)) {
|
|
foreach ($wohneinheit->rimo_workorders as $wo) {
|
|
$rimoWorkorders[] = [
|
|
'id' => $wo->id, 'rimoName' => $wo->rimo_name, 'rimoId' => $wo->rimo_id,
|
|
'rimoStatus' => $wo->rimo_status, 'downloadUrl' => "/RimoWorkorder/downloadAha?id=" . $wo->id,
|
|
];
|
|
}
|
|
}
|
|
|
|
return [
|
|
'patchposition' => $patchposition,
|
|
'rimoWorkorders' => $rimoWorkorders,
|
|
'dropcable' => ['parsed_at' => $ahaParsed, 'entries' => $dropkabelData, 'map_file' => $mapFile],
|
|
];
|
|
}
|
|
} |