Implement UpdateLastSyncFromDocuments hook; adjust syncStatus logic; update layout and metadata for dokumentes relationship

This commit is contained in:
2026-03-25 22:26:49 +01:00
parent 4a302542b7
commit 35d165a4b6
8 changed files with 163 additions and 39 deletions

View File

@@ -0,0 +1,111 @@
<?php
namespace Espo\Custom\Hooks\CAdvowareAkten;
use Espo\ORM\Entity;
use Espo\ORM\Repository\Option\SaveOptions;
use Espo\Core\Hook\Hook\BeforeSave;
/**
* Hook: Aktualisiert lastSync basierend auf dem neuesten lastSyncTimestamp
* der verknüpften Dokumente
*/
class UpdateLastSyncFromDocuments implements BeforeSave
{
public function __construct(
private \Espo\ORM\EntityManager $entityManager
) {}
public function beforeSave(Entity $entity, SaveOptions $options): void
{
// Überspringe, wenn skipHooks gesetzt ist (verhindert Loops)
if ($options->get('skipHooks')) {
return;
}
// Nur wenn Entity bereits existiert (nicht bei Create)
if ($entity->isNew()) {
return;
}
try {
// Hole das neueste lastSyncTimestamp aller verknüpften Dokumente
$query = $this->entityManager->getQueryBuilder()
->select([
'MAX:lastSyncTimestamp' => 'maxLastSync',
'COUNT:id' => 'dokumentCount'
])
->from('CDokumente')
->where([
'cAdvowareAktenId' => $entity->getId(),
'deleted' => false,
'lastSyncTimestamp!=' => null
])
->build();
$pdoStatement = $this->entityManager->getQueryExecutor()->execute($query);
$result = $pdoStatement->fetch(\PDO::FETCH_ASSOC);
if ($result && $result['maxLastSync']) {
// Setze lastSync auf den neuesten Sync-Timestamp
$entity->set('lastSync', $result['maxLastSync']);
}
// Berechne auch syncStatus basierend auf verknüpften Dokumenten
$this->updateSyncStatus($entity);
} catch (\Exception $e) {
// Fehler loggen, aber nicht werfen (um Save nicht zu blockieren)
$GLOBALS['log']->error('CAdvowareAkten UpdateLastSyncFromDocuments Hook Error: ' . $e->getMessage());
}
}
/**
* Aktualisiert syncStatus basierend auf den Status der verknüpften Dokumente
*/
private function updateSyncStatus(Entity $entity): void
{
$query = $this->entityManager->getQueryBuilder()
->select(['syncStatus'])
->from('CDokumente')
->where([
'cAdvowareAktenId' => $entity->getId(),
'deleted' => false
])
->build();
$pdoStatement = $this->entityManager->getQueryExecutor()->execute($query);
$rows = $pdoStatement->fetchAll(\PDO::FETCH_ASSOC);
// Wenn keine Dokumente verknüpft, setze auf "unclean"
if (empty($rows)) {
$entity->set('syncStatus', 'unclean');
return;
}
// Prüfe, ob irgendein Dokument "new" oder "unclean" ist
$hasUnsynced = false;
$hasFailed = false;
foreach ($rows as $row) {
$status = $row['syncStatus'] ?? null;
if ($status === 'failed') {
$hasFailed = true;
}
if ($status === 'new' || $status === 'unclean' || $status === null || $status === '') {
$hasUnsynced = true;
}
}
// Setze globalen Status
if ($hasFailed) {
$entity->set('syncStatus', 'failed');
} elseif ($hasUnsynced) {
$entity->set('syncStatus', 'unclean');
} else {
// Alle Dokumente sind "synced"
$entity->set('syncStatus', 'synced');
}
}
}

View File

@@ -34,11 +34,11 @@ class UpdateJunctionSyncStatus implements AfterSave
$entity->set('syncStatus', 'unclean'); $entity->set('syncStatus', 'unclean');
$this->entityManager->saveEntity($entity, ['silent' => true, 'skipHooks' => true]); $this->entityManager->saveEntity($entity, ['silent' => true, 'skipHooks' => true]);
// Also update the parent AdvowareAkte // Trigger parent AdvowareAkte update (will update syncStatus and lastSync via its own hook)
$akte = $this->entityManager->getEntity('CAdvowareAkten', $entity->get('cAdvowareAktenId')); $akte = $this->entityManager->getEntityById('CAdvowareAkten', $entity->get('cAdvowareAktenId'));
if ($akte) { if ($akte) {
$akte->set('syncStatus', 'unclean'); // Just touch the entity to trigger beforeSave hook
$this->entityManager->saveEntity($akte, ['silent' => true, 'skipHooks' => true]); $this->entityManager->saveEntity($akte, ['silent' => true]);
} }
} }

View File

@@ -14,7 +14,9 @@
"tabLabel": "Dokumente" "tabLabel": "Dokumente"
}, },
"dokumentes": { "dokumentes": {
"index": 1 "index": 1,
"view": "views/record/panels/relationship",
"layout": "listForAdvowareAkten"
}, },
"_tabBreak_1": { "_tabBreak_1": {
"index": 2, "index": 2,

View File

@@ -0,0 +1,39 @@
[
{
"name": "name",
"link": true,
"width": 20
},
{
"name": "advowareArt",
"align": "left",
"width": 12
},
{
"name": "advowareBemerkung",
"align": "left",
"notSortable": true,
"width": 18
},
{
"name": "syncStatus",
"align": "left",
"width": 12
},
{
"name": "lastSyncTimestamp",
"align": "left",
"width": 13
},
{
"name": "dokument",
"notSortable": true,
"align": "left",
"width": 12
},
{
"name": "createdAt",
"align": "left",
"width": 13
}
]

View File

@@ -104,36 +104,6 @@
"tooltip": true, "tooltip": true,
"isCustom": true "isCustom": true
}, },
"dokumenteHnr": {
"type": "int",
"notStorable": true,
"utility": true,
"disabled": true
},
"dokumenteSyncstatus": {
"type": "enum",
"options": [
"new",
"unclean",
"synced",
"failed"
],
"notStorable": true,
"utility": true,
"disabled": true
},
"dokumenteLastSync": {
"type": "datetime",
"notStorable": true,
"utility": true,
"disabled": true
},
"dokumenteSyncedHash": {
"type": "varchar",
"notStorable": true,
"utility": true,
"disabled": true
},
"dokumentes": { "dokumentes": {
"type": "linkMultiple", "type": "linkMultiple",
"layoutDetailDisabled": false, "layoutDetailDisabled": false,
@@ -143,7 +113,6 @@
"importDisabled": false, "importDisabled": false,
"exportDisabled": false, "exportDisabled": false,
"customizationDisabled": false, "customizationDisabled": false,
"disabled": true,
"isCustom": true "isCustom": true
} }
}, },

View File

@@ -78,6 +78,7 @@
"hnr": { "hnr": {
"type": "int", "type": "int",
"tooltip": true, "tooltip": true,
"disableFormatting": true,
"isCustom": true "isCustom": true
}, },
"syncStatus": { "syncStatus": {
@@ -108,8 +109,10 @@
}, },
"usn": { "usn": {
"type": "int", "type": "int",
"dbType": "bigint",
"min": 0, "min": 0,
"tooltip": true, "tooltip": true,
"disableFormatting": true,
"isCustom": true "isCustom": true
}, },
"dateipfad": { "dateipfad": {

View File

@@ -360,7 +360,7 @@ return [
0 => 'youtube.com', 0 => 'youtube.com',
1 => 'google.com' 1 => 'google.com'
], ],
'microtime' => 1774472414.650919, 'microtime' => 1774473866.020977,
'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' => 1774472414, 'cacheTimestamp' => 1774473866,
'microtimeState' => 1774472414.878455, 'microtimeState' => 1774473866.194744,
'currencyRates' => [ 'currencyRates' => [
'EUR' => 1.0 'EUR' => 1.0
], ],