Remove Advoware and x.AI related fields, tooltips, and options from CDokumente JSON files. Add UpdateJunctionSyncStatus hook to manage sync status for related entities on document updates. Update configuration timestamps in state and config files.

This commit is contained in:
2026-03-11 19:55:29 +01:00
parent 9411337939
commit c2c9cfe709
6 changed files with 159 additions and 124 deletions

View File

@@ -0,0 +1,155 @@
<?php
namespace Espo\Custom\Hooks\CDokumente;
use Espo\ORM\Entity;
use Espo\ORM\Repository\Option\SaveOptions;
use Espo\Core\Hook\Hook\AfterSave;
/**
* Hook: Bei Änderung eines Dokuments werden alle verknüpften
* AdvowareAkten und AIKnowledge Junction-Table-Einträge auf "unclean" gesetzt
*/
class UpdateJunctionSyncStatus implements AfterSave
{
public function __construct(
private \Espo\ORM\EntityManager $entityManager
) {}
public function afterSave(Entity $entity, SaveOptions $options): void
{
// Überspringe bei Create (nur bei Update)
if ($entity->isNew()) {
return;
}
// Überspringe, wenn keine relevanten Felder geändert wurden
if (!$this->hasRelevantChanges($entity)) {
return;
}
try {
// Update AdvowareAkten Junction-Tables
$this->updateAdvowareAktenJunctions($entity);
// Update AIKnowledge Junction-Tables
$this->updateAIKnowledgeJunctions($entity);
} catch (\Exception $e) {
// Fehler loggen, aber nicht werfen (um Save nicht zu blockieren)
$GLOBALS['log']->error('CDokumente UpdateJunctionSyncStatus Hook Error: ' . $e->getMessage());
}
}
/**
* Prüft ob relevante Felder geändert wurden
*/
private function hasRelevantChanges(Entity $entity): bool
{
// Relevante Felder für Sync-Status
$relevantFields = [
'name',
'description',
'dokument',
'dokumentId',
'preview',
'previewId',
'fileStatus'
];
foreach ($relevantFields as $field) {
if ($entity->isAttributeChanged($field)) {
return true;
}
}
return false;
}
/**
* Update AdvowareAkten Junction-Tables
*/
private function updateAdvowareAktenJunctions(Entity $entity): void
{
$updateQuery = $this->entityManager->getQueryBuilder()
->update()
->in('CAdvowareAktenDokumente')
->set(['syncstatus' => 'unclean'])
->where([
'cDokumenteId' => $entity->getId(),
'deleted' => false
])
->build();
$this->entityManager->getQueryExecutor()->execute($updateQuery);
// Hole alle betroffenen AdvowareAkten IDs
$selectQuery = $this->entityManager->getQueryBuilder()
->select(['cAdvowareAktenId'])
->from('CAdvowareAktenDokumente')
->where([
'cDokumenteId' => $entity->getId(),
'deleted' => false
])
->build();
$pdoStatement = $this->entityManager->getQueryExecutor()->execute($selectQuery);
$rows = $pdoStatement->fetchAll(\PDO::FETCH_ASSOC);
// Trigger Update auf jeder AdvowareAkte (um CheckGlobalSyncStatus Hook auszulösen)
foreach ($rows as $row) {
$aktenId = $row['cAdvowareAktenId'] ?? null;
if ($aktenId) {
$akte = $this->entityManager->getEntity('CAdvowareAkten', $aktenId);
if ($akte) {
// Force Update ohne Hook-Loop
$akte->set('syncStatus', 'unclean');
$this->entityManager->saveEntity($akte);
}
}
}
}
/**
* Update AIKnowledge Junction-Tables
*/
private function updateAIKnowledgeJunctions(Entity $entity): void
{
$updateQuery = $this->entityManager->getQueryBuilder()
->update()
->in('CAIKnowledgeDokumente')
->set(['syncstatus' => 'unclean'])
->where([
'cDokumenteId' => $entity->getId(),
'deleted' => false
])
->build();
$this->entityManager->getQueryExecutor()->execute($updateQuery);
// Hole alle betroffenen AIKnowledge IDs
$selectQuery = $this->entityManager->getQueryBuilder()
->select(['cAIKnowledgeId'])
->from('CAIKnowledgeDokumente')
->where([
'cDokumenteId' => $entity->getId(),
'deleted' => false
])
->build();
$pdoStatement = $this->entityManager->getQueryExecutor()->execute($selectQuery);
$rows = $pdoStatement->fetchAll(\PDO::FETCH_ASSOC);
// Trigger Update auf jeder AIKnowledge (um CheckGlobalSyncStatus Hook auszulösen)
foreach ($rows as $row) {
$knowledgeId = $row['cAIKnowledgeId'] ?? null;
if ($knowledgeId) {
$knowledge = $this->entityManager->getEntity('CAIKnowledge', $knowledgeId);
if ($knowledge) {
// Force Update ohne Hook-Loop
$knowledge->set('syncStatus', 'unclean');
$this->entityManager->saveEntity($knowledge);
}
}
}
}
}

View File

@@ -5,12 +5,6 @@
"ydocumentuuid": "Y-Document-UUID", "ydocumentuuid": "Y-Document-UUID",
"md5sum": "MD5-Prüfsumme", "md5sum": "MD5-Prüfsumme",
"sha256": "SHA256-Prüfsumme", "sha256": "SHA256-Prüfsumme",
"aktennr": "Advoware Identifikator",
"advowareLastSync": "Advoware letzte Synchronisation",
"syncStatus": "Sync-Status",
"xaiId": "x.AI ID",
"xaiCollections": "x.AI Collections",
"xaiSyncStatus": "Sync-Status",
"fileStatus": "Datei-Status", "fileStatus": "Datei-Status",
"contactsvmhdokumente": "Freigegebene Nutzer", "contactsvmhdokumente": "Freigegebene Nutzer",
"vmhMietverhltnisesDokumente": "Mietverhältnisse", "vmhMietverhltnisesDokumente": "Mietverhältnisse",
@@ -47,29 +41,9 @@
"Create CDokumente": "Dokument erstellen" "Create CDokumente": "Dokument erstellen"
}, },
"tooltips": { "tooltips": {
"aktennr": "Eindeutige Dokument-Nummer aus Advoware",
"advowareLastSync": "Zeitpunkt der letzten Synchronisation mit Advoware",
"syncStatus": "Status der Advoware-Synchronisation: pending_sync = Warte auf Sync, clean = erfolgreich synchronisiert, unclean = Änderungen ausstehend, failed = Fehler, no_sync = Nicht synchronisiert",
"xaiId": "Eindeutige ID für x.AI Synchronisation",
"xaiCollections": "Liste der x.AI Collections für dieses Dokument",
"xaiSyncStatus": "Status der x.AI Synchronisation: pending_sync = Warte auf Sync, clean = erfolgreich synchronisiert, unclean = Änderungen ausstehend, failed = Fehler, no_sync = Nicht synchronisiert",
"fileStatus": "Status der Datei: new = neu hochgeladen, changed = geändert, synced = synchronisiert" "fileStatus": "Status der Datei: new = neu hochgeladen, changed = geändert, synced = synchronisiert"
}, },
"options": { "options": {
"syncStatus": {
"pending_sync": "Warte auf Sync",
"clean": "Synchronisiert",
"unclean": "Änderungen ausstehend",
"failed": "Fehlgeschlagen",
"no_sync": "Kein Sync"
},
"xaiSyncStatus": {
"pending_sync": "Warte auf Sync",
"clean": "Synchronisiert",
"unclean": "Abweichungen",
"failed": "Fehlgeschlagen",
"no_sync": "Kein Sync"
},
"fileStatus": { "fileStatus": {
"new": "Neu", "new": "Neu",
"changed": "Geändert", "changed": "Geändert",

View File

@@ -14,14 +14,9 @@
"mietobjekt2dokumente": "Properties", "mietobjekt2dokumente": "Properties",
"mietinkassosdokumente": "Rent Collection", "mietinkassosdokumente": "Rent Collection",
"kndigungensdokumente": "Terminations", "kndigungensdokumente": "Terminations",
"aktennr": "Advoware Identifier",
"advowareLastSync": "Advoware Last Sync",
"syncStatus": "Sync Status",
"xaiId": "x.AI ID",
"xaiCollections": "x.AI Collections",
"xaiSyncStatus": "Sync Status",
"fileStatus": "File Status", "fileStatus": "File Status",
"advowareAktens": "Advoware Akten", "advowareAktens": "Advoware Akten",
"advowareAktens": "Advoware Akten",
"aIKnowledges": "AI Knowledge", "aIKnowledges": "AI Knowledge",
"advowareAktenHnr": "Advoware HNR", "advowareAktenHnr": "Advoware HNR",
"advowareAktenSyncstatus": "Advoware Sync Status", "advowareAktenSyncstatus": "Advoware Sync Status",
@@ -52,29 +47,9 @@
"listForAIKnowledge": "List for AI Knowledge" "listForAIKnowledge": "List for AI Knowledge"
}, },
"tooltips": { "tooltips": {
"aktennr": "Unique document number from Advoware",
"advowareLastSync": "Time of last synchronization with Advoware",
"syncStatus": "Advoware synchronization status: pending_sync = Waiting for sync, clean = successfully synchronized, unclean = changes pending, failed = error, no_sync = Not synchronized",
"xaiId": "Unique ID for x.AI synchronization",
"xaiCollections": "List of x.AI collections for this document",
"xaiSyncStatus": "x.AI synchronization status: pending_sync = Waiting for sync, clean = successfully synchronized, unclean = changes pending, failed = error, no_sync = Not synchronized",
"fileStatus": "File status: new = newly uploaded, changed = modified, synced = synchronized" "fileStatus": "File status: new = newly uploaded, changed = modified, synced = synchronized"
}, },
"options": { "options": {
"syncStatus": {
"pending_sync": "Waiting for Sync",
"clean": "Synchronized",
"unclean": "Changes Pending",
"failed": "Failed",
"no_sync": "No Sync"
},
"xaiSyncStatus": {
"pending_sync": "Waiting for Sync",
"clean": "Synchronized",
"unclean": "Changes Pending",
"failed": "Failed",
"no_sync": "No Sync"
},
"fileStatus": { "fileStatus": {
"new": "New", "new": "New",
"changed": "Changed", "changed": "Changed",

View File

@@ -75,75 +75,11 @@
"isCustom": true, "isCustom": true,
"copyToClipboard": true "copyToClipboard": true
}, },
"aktennr": {
"type": "int",
"required": false,
"tooltip": true,
"isCustom": true
},
"advowareLastSync": {
"type": "datetime",
"required": false,
"readOnly": true,
"tooltip": true,
"isCustom": true
},
"syncStatus": {
"type": "enum",
"required": false,
"options": [
"pending_sync",
"clean",
"unclean",
"failed",
"no_sync"
],
"style": {
"pending_sync": "default",
"clean": "success",
"unclean": "warning",
"failed": "danger",
"no_sync": null
},
"default": "no_sync",
"tooltip": true,
"isCustom": true
},
"puls": { "puls": {
"type": "link", "type": "link",
"entity": "CPuls", "entity": "CPuls",
"isCustom": true "isCustom": true
}, },
"xaiId": {
"type": "varchar",
"maxLength": 255,
"isCustom": true
},
"xaiCollections": {
"type": "array",
"isCustom": true
},
"xaiSyncStatus": {
"type": "enum",
"required": false,
"options": [
"pending_sync",
"clean",
"unclean",
"failed",
"no_sync"
],
"style": {
"pending_sync": "default",
"clean": "success",
"unclean": "warning",
"failed": "danger",
"no_sync": null
},
"default": "no_sync",
"tooltip": true,
"isCustom": true
},
"fileStatus": { "fileStatus": {
"type": "enum", "type": "enum",
"required": false, "required": false,
@@ -362,11 +298,6 @@
"id" "id"
] ]
}, },
"aktennr": {
"columns": [
"aktennr"
]
},
"md5sum": { "md5sum": {
"columns": [ "columns": [
"md5sum" "md5sum"

View File

@@ -360,7 +360,7 @@ return [
0 => 'youtube.com', 0 => 'youtube.com',
1 => 'google.com' 1 => 'google.com'
], ],
'microtime' => 1773253602.105787, 'microtime' => 1773255038.319449,
'siteUrl' => 'https://crm.bitbylaw.com', 'siteUrl' => 'https://crm.bitbylaw.com',
'fullTextSearchMinLength' => 4, 'fullTextSearchMinLength' => 4,
'webSocketUrl' => 'ws://api.bitbylaw.com:5000/espocrm/ws', 'webSocketUrl' => 'ws://api.bitbylaw.com:5000/espocrm/ws',

View File

@@ -1,7 +1,7 @@
<?php <?php
return [ return [
'cacheTimestamp' => 1773253761, 'cacheTimestamp' => 1773255038,
'microtimeState' => 1773253761.495155, 'microtimeState' => 1773255038.505417,
'currencyRates' => [ 'currencyRates' => [
'EUR' => 1.0 'EUR' => 1.0
], ],