129 lines
5.6 KiB
PHP
129 lines
5.6 KiB
PHP
<?php
|
|
namespace Espo\Custom\Hooks\CAkten;
|
|
|
|
use Espo\ORM\Entity;
|
|
use Espo\ORM\Repository\Option\SaveOptions;
|
|
use Espo\Core\Hook\Hook\BeforeSave;
|
|
|
|
/**
|
|
* Hook: Aktualisiert lastSync + syncStatus (Advoware) und aiLastSync + aiSyncStatus (AI)
|
|
* basierend auf dem neuesten Timestamp und schlechtesten Status der verknüpften Dokumente.
|
|
*/
|
|
class UpdateLastSyncFromDocuments implements BeforeSave
|
|
{
|
|
public function __construct(
|
|
private \Espo\ORM\EntityManager $entityManager
|
|
) {}
|
|
|
|
public function beforeSave(Entity $entity, SaveOptions $options): void
|
|
{
|
|
if ($options->get('skipHooks')) {
|
|
return;
|
|
}
|
|
|
|
if ($entity->isNew()) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$pdo = $this->entityManager->getPDO();
|
|
$aktenId = $entity->getId();
|
|
|
|
// Single aggregation row — CASE inside MAX() avoids the mixed-aggregate bug.
|
|
$stmt = $pdo->prepare(
|
|
"SELECT
|
|
MAX(last_sync_timestamp) AS maxAdvLastSync,
|
|
MAX(ai_last_sync) AS maxAiLastSync,
|
|
MAX(CASE
|
|
WHEN sync_status = 'failed' THEN 2
|
|
WHEN sync_status IN ('new','unclean')
|
|
OR sync_status IS NULL OR sync_status = '' THEN 1
|
|
ELSE 0
|
|
END) AS advWorstLevel,
|
|
MAX(CASE
|
|
WHEN ai_sync_status = 'failed' THEN 2
|
|
WHEN ai_sync_status IN ('new','unclean')
|
|
OR ai_sync_status IS NULL OR ai_sync_status = '' THEN 1
|
|
ELSE 0
|
|
END) AS aiWorstLevel,
|
|
-- ai_sync_status = unclean triggers graphParsingStatus
|
|
SUM(CASE WHEN ai_sync_status = 'unclean' THEN 1 ELSE 0 END) AS aiUncleanCount,
|
|
-- aiParsingStatus buckets
|
|
SUM(CASE WHEN ai_parsing_status = 'parsing' THEN 1 ELSE 0 END) AS parseParsingCount,
|
|
SUM(CASE WHEN ai_parsing_status = 'complete' THEN 1 ELSE 0 END) AS parseCompleteCount,
|
|
SUM(CASE WHEN ai_parsing_status = 'failed' THEN 1 ELSE 0 END) AS parseFailedCount,
|
|
COUNT(*) AS docCount
|
|
FROM c_dokumente
|
|
WHERE c_akten_id = :aktenId AND deleted = 0"
|
|
);
|
|
$stmt->execute([':aktenId' => $aktenId]);
|
|
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
|
|
|
if (!$row || (int)$row['docCount'] === 0) {
|
|
$entity->set('syncStatus', 'unclean');
|
|
$entity->set('aiSyncStatus', 'unclean');
|
|
$entity->set('aiParsingStatus', 'unknown');
|
|
return;
|
|
}
|
|
|
|
$docCount = (int)$row['docCount'];
|
|
|
|
// ── Timestamps ─────────────────────────────────────────────────
|
|
if (!empty($row['maxAdvLastSync'])) {
|
|
$entity->set('lastSync', $row['maxAdvLastSync']);
|
|
}
|
|
if (!empty($row['maxAiLastSync'])) {
|
|
$entity->set('aiLastSync', $row['maxAiLastSync']);
|
|
}
|
|
|
|
// ── Advoware Sync Status (worst-case) ──────────────────────────
|
|
$advLevel = (int)($row['advWorstLevel'] ?? 0);
|
|
if ($advLevel >= 2) {
|
|
$entity->set('syncStatus', 'failed');
|
|
} elseif ($advLevel === 1) {
|
|
$entity->set('syncStatus', 'unclean');
|
|
} else {
|
|
$entity->set('syncStatus', 'synced');
|
|
}
|
|
|
|
// ── AI Sync Status (worst-case) ────────────────────────────────
|
|
$aiLevel = (int)($row['aiWorstLevel'] ?? 0);
|
|
if ($aiLevel >= 2) {
|
|
$entity->set('aiSyncStatus', 'failed');
|
|
} elseif ($aiLevel === 1) {
|
|
$entity->set('aiSyncStatus', 'unclean');
|
|
} else {
|
|
$entity->set('aiSyncStatus', 'synced');
|
|
}
|
|
|
|
// ── AI Parsing Status (aggregated across all documents) ─────────
|
|
// Priority: parsing > unknown > complete_with_failures > complete
|
|
$parseParsing = (int)($row['parseParsingCount'] ?? 0);
|
|
$parseComplete = (int)($row['parseCompleteCount'] ?? 0);
|
|
$parseFailed = (int)($row['parseFailedCount'] ?? 0);
|
|
$parseUnknown = $docCount - $parseParsing - $parseComplete - $parseFailed;
|
|
|
|
if ($parseParsing > 0) {
|
|
$entity->set('aiParsingStatus', 'parsing');
|
|
} elseif ($parseUnknown > 0) {
|
|
$entity->set('aiParsingStatus', 'unknown');
|
|
} elseif ($parseFailed > 0) {
|
|
// No unknown/parsing left, but some failures
|
|
$entity->set('aiParsingStatus', 'complete_with_failures');
|
|
} else {
|
|
$entity->set('aiParsingStatus', 'complete');
|
|
}
|
|
|
|
// ── Graph Parsing Status (only auto-set to unclean, never reset) ─
|
|
// If any document's AI sync status is currently unclean → graph is stale.
|
|
// Any other transition (back to complete, etc.) must be done manually.
|
|
if ((int)($row['aiUncleanCount'] ?? 0) > 0) {
|
|
$entity->set('graphParsingStatus', 'unclean');
|
|
}
|
|
|
|
} catch (\Exception $e) {
|
|
$GLOBALS['log']->error('CAkten UpdateLastSyncFromDocuments Hook Error: ' . $e->getMessage());
|
|
}
|
|
}
|
|
}
|