Enhance document synchronization hooks to handle linking and unlinking; update sync status for related entities; modify state file for cache and microtime values

This commit is contained in:
2026-03-27 09:52:28 +01:00
parent bd29d6e9b4
commit 2a18e62528
4 changed files with 107 additions and 20 deletions

View File

@@ -2,14 +2,22 @@
namespace Espo\Custom\Hooks\CDokumente; namespace Espo\Custom\Hooks\CDokumente;
use Espo\ORM\Entity; use Espo\ORM\Entity;
use Espo\ORM\Repository\Option\RemoveOptions;
use Espo\ORM\Repository\Option\SaveOptions; use Espo\ORM\Repository\Option\SaveOptions;
use Espo\Core\Hook\Hook\AfterRemove;
use Espo\Core\Hook\Hook\AfterSave; use Espo\Core\Hook\Hook\AfterSave;
/** /**
* Hook: Bei Änderung eines Dokuments syncStatus und aiSyncStatus auf "unclean" setzen * Hook: Bei Änderung, Verlinkung oder Löschung eines Dokuments syncStatus und
* und die verknüpfte CAkten-Entity aktualisieren. * aiSyncStatus auf "unclean" setzen und die verknüpfte CAkten-Entity aktualisieren.
*
* Auslöser:
* - afterSave: Feldänderungen (name, description, file, …)
* - afterSave: Neues Dokument mit cAktenId
* - afterSave: cAktenId-Änderung (Verlinkung / Entlinkung über CAkten-Panel)
* - afterRemove: Gelöschtes Dokument → verknüpfte Akte neu berechnen
*/ */
class UpdateJunctionSyncStatus implements AfterSave class UpdateJunctionSyncStatus implements AfterSave, AfterRemove
{ {
public function __construct( public function __construct(
private \Espo\ORM\EntityManager $entityManager private \Espo\ORM\EntityManager $entityManager
@@ -17,10 +25,41 @@ class UpdateJunctionSyncStatus implements AfterSave
public function afterSave(Entity $entity, SaveOptions $options): void public function afterSave(Entity $entity, SaveOptions $options): void
{ {
if ($entity->isNew()) { // Kein Re-Eintritt wenn dieser Hook selbst gespeichert hat
if ($options->get('skipHooks')) {
return; return;
} }
try {
// Fall 1: Neues Dokument → verknüpfte Akte neu berechnen
if ($entity->isNew()) {
if ($entity->get('cAktenId')) {
$this->triggerAkteUpdate($entity->get('cAktenId'));
}
return;
}
// Fall 2: cAktenId geändert (Dokument verlinkt oder entlinkt)
if ($entity->isAttributeChanged('cAktenId')) {
$oldAktenId = $entity->getFetched('cAktenId');
$newAktenId = $entity->get('cAktenId');
// Dokument der neuen Akte zugewiesen → als unclean markieren
if ($newAktenId) {
$entity->set('syncStatus', 'unclean');
$entity->set('aiSyncStatus', 'unclean');
$this->entityManager->saveEntity($entity, ['silent' => true, 'skipHooks' => true]);
$this->triggerAkteUpdate($newAktenId);
}
// Alte Akte ebenfalls neu berechnen (Dokument entfernt)
if ($oldAktenId && $oldAktenId !== $newAktenId) {
$this->triggerAkteUpdate($oldAktenId);
}
return;
}
// Fall 3: Relevante Feldänderungen (Datei, Name, Beschreibung, …)
if (!$this->hasRelevantChanges($entity)) { if (!$this->hasRelevantChanges($entity)) {
return; return;
} }
@@ -29,18 +68,40 @@ class UpdateJunctionSyncStatus implements AfterSave
return; return;
} }
try {
$entity->set('syncStatus', 'unclean'); $entity->set('syncStatus', 'unclean');
$entity->set('aiSyncStatus', 'unclean'); $entity->set('aiSyncStatus', 'unclean');
$this->entityManager->saveEntity($entity, ['silent' => true, 'skipHooks' => true]); $this->entityManager->saveEntity($entity, ['silent' => true, 'skipHooks' => true]);
$this->triggerAkteUpdate($entity->get('cAktenId'));
// Akte triggern → BeforeSave-Hook UpdateLastSyncFromDocuments aggregiert Status
$akte = $this->entityManager->getEntityById('CAkten', $entity->get('cAktenId'));
if ($akte) {
$this->entityManager->saveEntity($akte, ['silent' => true]);
}
} catch (\Exception $e) { } catch (\Exception $e) {
$GLOBALS['log']->error('CDokumente UpdateJunctionSyncStatus Hook Error: ' . $e->getMessage()); $GLOBALS['log']->error('CDokumente UpdateJunctionSyncStatus afterSave Error: ' . $e->getMessage());
}
}
public function afterRemove(Entity $entity, RemoveOptions $options): void
{
$akteId = $entity->get('cAktenId');
if (!$akteId) {
return;
}
try {
$this->triggerAkteUpdate($akteId);
} catch (\Exception $e) {
$GLOBALS['log']->error('CDokumente UpdateJunctionSyncStatus afterRemove Error: ' . $e->getMessage());
}
}
/**
* Speichert die CAkten-Entity neu, damit UpdateLastSyncFromDocuments
* den Sync-Status aller verknüpften Dokumente neu aggregiert.
*/
private function triggerAkteUpdate(string $akteId): void
{
$akte = $this->entityManager->getEntityById('CAkten', $akteId);
if ($akte) {
// silent=true → keine Audit-Einträge; kein skipHooks → UpdateLastSyncFromDocuments läuft
$this->entityManager->saveEntity($akte, ['silent' => true]);
} }
} }

View File

@@ -50,6 +50,12 @@ class PropagateDocuments implements AfterRelate, AfterUnrelate
$foreignEntity->set('cAktenId', $advowareAkten->getId()); $foreignEntity->set('cAktenId', $advowareAkten->getId());
$foreignEntity->set('syncStatus', 'new'); // Mark as new for Advoware sync $foreignEntity->set('syncStatus', 'new'); // Mark as new for Advoware sync
$this->entityManager->saveEntity($foreignEntity, ['silent' => true, 'skipHooks' => true]); $this->entityManager->saveEntity($foreignEntity, ['silent' => true, 'skipHooks' => true]);
// Akte über neue Verlinkung informieren → syncStatus auf unclean
$akte = $this->entityManager->getEntityById('CAkten', $advowareAkten->getId());
if ($akte) {
$this->entityManager->saveEntity($akte, ['silent' => true]);
}
} }
// Hole verbundene AIKnowledge // Hole verbundene AIKnowledge
@@ -97,8 +103,15 @@ class PropagateDocuments implements AfterRelate, AfterUnrelate
// Remove direct belongsTo relationship from document // Remove direct belongsTo relationship from document
if ($advowareAkten && $foreignEntity->get('cAktenId') === $advowareAkten->getId()) { if ($advowareAkten && $foreignEntity->get('cAktenId') === $advowareAkten->getId()) {
$akteId = $advowareAkten->getId(); // Vor dem Löschen merken
$foreignEntity->set('cAktenId', null); $foreignEntity->set('cAktenId', null);
$this->entityManager->saveEntity($foreignEntity, ['silent' => true, 'skipHooks' => true]); $this->entityManager->saveEntity($foreignEntity, ['silent' => true, 'skipHooks' => true]);
// Akte über Entlinkung informieren → syncStatus neu berechnen
$akte = $this->entityManager->getEntityById('CAkten', $akteId);
if ($akte) {
$this->entityManager->saveEntity($akte, ['silent' => true]);
}
} }
// Hole verbundene AIKnowledge // Hole verbundene AIKnowledge

View File

@@ -50,6 +50,12 @@ class PropagateDocuments implements AfterRelate, AfterUnrelate
$foreignEntity->set('cAktenId', $advowareAkten->getId()); $foreignEntity->set('cAktenId', $advowareAkten->getId());
$foreignEntity->set('syncStatus', 'new'); // Mark as new for Advoware sync $foreignEntity->set('syncStatus', 'new'); // Mark as new for Advoware sync
$this->entityManager->saveEntity($foreignEntity, ['silent' => true, 'skipHooks' => true]); $this->entityManager->saveEntity($foreignEntity, ['silent' => true, 'skipHooks' => true]);
// Akte über neue Verlinkung informieren → syncStatus auf unclean
$akte = $this->entityManager->getEntityById('CAkten', $advowareAkten->getId());
if ($akte) {
$this->entityManager->saveEntity($akte, ['silent' => true]);
}
} }
// Hole verbundene AIKnowledge // Hole verbundene AIKnowledge
@@ -97,8 +103,15 @@ class PropagateDocuments implements AfterRelate, AfterUnrelate
// Remove direct belongsTo relationship from document // Remove direct belongsTo relationship from document
if ($advowareAkten && $foreignEntity->get('cAktenId') === $advowareAkten->getId()) { if ($advowareAkten && $foreignEntity->get('cAktenId') === $advowareAkten->getId()) {
$akteId = $advowareAkten->getId(); // Vor dem Löschen merken
$foreignEntity->set('cAktenId', null); $foreignEntity->set('cAktenId', null);
$this->entityManager->saveEntity($foreignEntity, ['silent' => true, 'skipHooks' => true]); $this->entityManager->saveEntity($foreignEntity, ['silent' => true, 'skipHooks' => true]);
// Akte über Entlinkung informieren → syncStatus neu berechnen
$akte = $this->entityManager->getEntityById('CAkten', $akteId);
if ($akte) {
$this->entityManager->saveEntity($akte, ['silent' => true]);
}
} }
// Hole verbundene AIKnowledge // Hole verbundene AIKnowledge

View File

@@ -1,7 +1,7 @@
<?php <?php
return [ return [
'cacheTimestamp' => 1774562639, 'cacheTimestamp' => 1774566125,
'microtimeState' => 1774562639.79198, 'microtimeState' => 1774566125.048162,
'currencyRates' => [ 'currencyRates' => [
'EUR' => 1.0 'EUR' => 1.0
], ],