Refactor CAdvowareAkten and CDokumente hooks; update localization and metadata; adjust microtime values in config and state files

This commit is contained in:
2026-03-25 22:01:58 +01:00
parent 7d55075490
commit 4a302542b7
14 changed files with 99 additions and 124 deletions

View File

@@ -1,75 +0,0 @@
<?php
namespace Espo\Custom\Hooks\CAdvowareAkten;
use Espo\ORM\Entity;
use Espo\ORM\Repository\Option\SaveOptions;
use Espo\Core\Hook\Hook\BeforeSave;
/**
* Hook: Prüft Junction-Table und aktualisiert globalen syncStatus
* basierend auf den syncstatus-Werten der verknüpften Dokumente
*/
class CheckGlobalSyncStatus 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 alle verknüpften Dokumente mit ihren syncstatus-Werten aus der Junction-Tabelle
$query = $this->entityManager->getQueryBuilder()
->select(['syncstatus'])
->from('CAdvowareAktenDokumente')
->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;
foreach ($rows as $row) {
$status = $row['syncstatus'] ?? null;
if ($status === 'new' || $status === 'unclean' || $status === null || $status === '') {
$hasUnsynced = true;
break;
}
}
// Setze globalen Status
if ($hasUnsynced) {
$entity->set('syncStatus', 'unclean');
} else {
// Alle Dokumente sind "synced"
$entity->set('syncStatus', 'synced');
$entity->set('lastSync', date('Y-m-d H:i:s'));
}
} catch (\Exception $e) {
// Bei Fehler loggen und Status auf "unclean" setzen
$GLOBALS['log']->error('CAdvowareAkten CheckGlobalSyncStatus Hook Error: ' . $e->getMessage());
$entity->set('syncStatus', 'unclean');
}
}
}

View File

@@ -3,10 +3,21 @@
namespace Espo\Custom\Hooks\CDokumente;
use Espo\ORM\Entity;
use Espo\ORM\Repository\Option\SaveOptions;
use Espo\Core\Hook\Hook\BeforeSave;
class CDokumente extends \Espo\Core\Hooks\Base
/**
* Hook: Berechnet Dokumenten-Hashes und setzt fileStatus
*
* Verwendet Blake3 als Hash-Algorithmus für optimale Performance
*/
class CDokumente implements BeforeSave
{
public function beforeSave(Entity $entity, array $options = [])
public function __construct(
private \Espo\ORM\EntityManager $entityManager
) {}
public function beforeSave(Entity $entity, SaveOptions $options): void
{
// Problem: isAttributeChanged('dokument') erkennt Datei-Änderungen nicht,
// da EspoCRM Datei-Uploads nicht als Feld-Änderung markiert.
@@ -14,23 +25,20 @@ class CDokumente extends \Espo\Core\Hooks\Base
// um sicherzustellen, dass Hashes bei Datei-Änderungen berechnet werden.
// Optimierung wäre wünschenswert, aber nicht möglich mit aktueller API.
$dokument = $entity->get('dokument');
$dokumentId = $entity->get('dokumentId');
if (!$dokument) {
if (!$dokumentId) {
return;
}
if (is_object($dokument)) {
$attachment = $dokument;
} else {
$attachment = $this->getEntityManager()->getEntity('Attachment', $dokument);
}
// Verwende EntityManager zur korrekten Relation-Verwaltung
$attachment = $this->entityManager->getEntityById('Attachment', $dokumentId);
if (!$attachment) {
return;
}
$filePath = 'data/upload/' . $attachment->get('id');
$filePath = 'data/upload/' . $attachment->getId();
if (!file_exists($filePath)) {
return;
}
@@ -41,7 +49,8 @@ class CDokumente extends \Espo\Core\Hooks\Base
return;
}
$newBlake3 = \blake3($fileContent);
// Blake3 Hashing - schneller als SHA3 und kryptographisch sicher
$newBlake3 = blake3($fileContent);
// Setze Hash
$entity->set('blake3hash', $newBlake3);

View File

@@ -15,7 +15,6 @@
"mietinkasso": "Mietinkasso",
"aktenzeichen": "Aktenzeichen",
"aktennummer": "Aktennummer",
"aktenpfad": "Aktenpfad (Windows)",
"syncStatus": "Sync-Status",
"lastSync": "Letzte Synchronisation",
"aktivierungsstatus": "Aktivierungsstatus",
@@ -43,7 +42,6 @@
"tooltips": {
"syncStatus": "Globaler Synchronisationsstatus: synced = Alle Dokumente synchronisiert, unclean = Mindestens ein Dokument ist neu oder hat Änderungen, pending_sync = Synchronisierung wurde gestartet aber noch nicht abgeschlossen, failed = Synchronisierung fehlgeschlagen. Wird automatisch basierend auf den Dokumenten-Status aktualisiert.",
"lastSync": "Zeitpunkt der letzten erfolgreichen Synchronisation aller Dokumente",
"aktivierungsstatus": "Aktivierungsstatus der Akte: new = Neu angelegt, import = Aus Advoware importiert, active = Aktiv synchronisiert, paused = Synchronisation pausiert, deactivated = Synchronisation deaktiviert",
"aktenpfad": "Windows-Dateipfad zur Akte in Advoware"
"aktivierungsstatus": "Aktivierungsstatus der Akte: new = Neu angelegt, import = Aus Advoware importiert, active = Aktiv synchronisiert, paused = Synchronisation pausiert, deactivated = Synchronisation deaktiviert"
}
}

View File

@@ -11,6 +11,9 @@
"syncedHash": "Sync-Hash",
"usn": "USN",
"dateipfad": "Dateipfad",
"lastSyncTimestamp": "Letzter Sync",
"advowareArt": "Advoware Art",
"advowareBemerkung": "Advoware Bemerkung",
"contactsvmhdokumente": "Freigegebene Nutzer",
"vmhMietverhltnisesDokumente": "Mietverhältnisse",
"vmhErstgespraechsdokumente": "Erstgespräche",
@@ -47,7 +50,10 @@
"syncStatus": "Status der Synchronisation mit Advoware: new=neu, unclean=geändert, synced=synchronisiert, failed=Fehler, unsupported=nicht unterstützt",
"syncedHash": "Hash-Wert bei letzter erfolgreicher Synchronisation",
"usn": "Update Sequence Number - Versionsnummer für Synchronisation",
"dateipfad": "Windows-Dateipfad des Dokuments in Advoware"
"dateipfad": "Windows-Dateipfad des Dokuments in Advoware",
"lastSyncTimestamp": "Zeitstempel der letzten erfolgreichen Synchronisation mit Advoware",
"advowareArt": "Dokumententyp/Art wie in Advoware klassifiziert",
"advowareBemerkung": "Bemerkungsfeld aus Advoware - wird bei Sync übernommen"
},
"options": {
"syncStatus": {

View File

@@ -4,7 +4,6 @@
"mietinkasso": "Mietinkasso",
"aktenzeichen": "Aktenzeichen",
"aktennummer": "Aktennummer",
"aktenpfad": "File Path (Windows)",
"syncStatus": "Sync Status",
"lastSync": "Last Synchronization",
"aktivierungsstatus": "Activation Status",
@@ -44,7 +43,6 @@
"tooltips": {
"syncStatus": "Global synchronization status: synced = All documents synchronized, unclean = At least one document is new or has changes, pending_sync = Synchronization started but not yet completed, failed = Synchronization failed. Updated automatically based on document status.",
"lastSync": "Timestamp of the last successful synchronization of all documents",
"aktivierungsstatus": "Activation status of the file: new = Newly created, import = Imported from Advoware, active = Actively synchronized, paused = Synchronization paused, deactivated = Synchronization deactivated",
"aktenpfad": "Windows file path to the file in Advoware"
"aktivierungsstatus": "Activation status of the file: new = Newly created, import = Imported from Advoware, active = Actively synchronized, paused = Synchronization paused, deactivated = Synchronization deactivated"
}
}

View File

@@ -10,6 +10,9 @@
"syncedHash": "Sync Hash",
"usn": "USN",
"dateipfad": "File Path",
"lastSyncTimestamp": "Last Sync",
"advowareArt": "Advoware Type",
"advowareBemerkung": "Advoware Remarks",
"contactsvmhdokumente": "Portal Users",
"vmhMietverhltnisesDokumente": "Tenancies",
"vmhErstgespraechsdokumente": "Initial Consultations",
@@ -52,7 +55,10 @@
"syncStatus": "Sync status with Advoware: new=new, unclean=changed, synced=synchronized, failed=error, unsupported=not supported",
"syncedHash": "Hash value at last successful synchronization",
"usn": "Update Sequence Number - Version number for synchronization",
"dateipfad": "Windows file path of the document in Advoware"
"dateipfad": "Windows file path of the document in Advoware",
"lastSyncTimestamp": "Timestamp of last successful synchronization with Advoware",
"advowareArt": "Document type/kind as classified in Advoware",
"advowareBemerkung": "Remarks field from Advoware - synced automatically"
},
"options": {
"syncStatus": {

View File

@@ -6,7 +6,7 @@
"name": "name"
},
{
"name": "aktenzeichen"
"name": "aktivierungsstatus"
}
],
[
@@ -14,7 +14,7 @@
"name": "aktennummer"
},
{
"name": "aktenpfad"
"name": "aktenzeichen"
}
],
[
@@ -24,12 +24,6 @@
{
"name": "lastSync"
}
],
[
{
"name": "aktivierungsstatus"
},
false
]
],
"style": "default",

View File

@@ -5,7 +5,9 @@
{
"name": "name"
},
{}
{
"name": "preview"
}
]
],
"dynamicLogicVisible": null,
@@ -20,8 +22,13 @@
"rows": [
[
{
"name": "preview"
"name": "advowareArt"
},
{
"name": "advowareBemerkung"
}
],
[
{
"name": "description"
}
@@ -60,7 +67,7 @@
"name": "cAdvowareAkten"
},
{
"name": "syncStatus"
"name": "dateipfad"
}
],
[
@@ -73,7 +80,10 @@
],
[
{
"name": "dateipfad"
"name": "syncStatus"
},
{
"name": "lastSyncTimestamp"
}
],
[

View File

@@ -2,26 +2,34 @@
{
"name": "name",
"link": true,
"width": 25
},
{
"name": "dokument",
"width": 20
},
{
"name": "dokument",
"width": 15
},
{
"name": "advowareArt",
"width": 10
},
{
"name": "syncStatus",
"width": 10
},
{
"name": "lastSyncTimestamp",
"width": 12
},
{
"name": "assignedUser",
"width": 15
"width": 12
},
{
"name": "createdAt",
"width": 14
"width": 11
},
{
"name": "modifiedAt",
"width": 14
"width": 10
}
]

View File

@@ -3,6 +3,10 @@
"name": "name",
"link": true
},
{
"name": "advowareArt",
"align": "left"
},
{
"name": "advowareAktenHnr",
"align": "left"
@@ -12,7 +16,7 @@
"align": "left"
},
{
"name": "advowareAktenLastSync",
"name": "lastSyncTimestamp",
"align": "left"
},
{

View File

@@ -57,12 +57,6 @@
"disableFormatting": true,
"isCustom": true
},
"aktenpfad": {
"type": "varchar",
"maxLength": 500,
"tooltip": true,
"isCustom": true
},
"syncStatus": {
"type": "enum",
"required": false,
@@ -118,7 +112,12 @@
},
"dokumenteSyncstatus": {
"type": "enum",
"options": ["new", "unclean", "synced", "failed"],
"options": [
"new",
"unclean",
"synced",
"failed"
],
"notStorable": true,
"utility": true,
"disabled": true

View File

@@ -118,6 +118,24 @@
"tooltip": true,
"isCustom": true
},
"lastSyncTimestamp": {
"type": "datetime",
"tooltip": true,
"readOnly": true,
"isCustom": true
},
"advowareArt": {
"type": "varchar",
"maxLength": 100,
"tooltip": true,
"isCustom": true
},
"advowareBemerkung": {
"type": "text",
"rows": 4,
"tooltip": true,
"isCustom": true
},
"puls": {
"type": "link",
"entity": "CPuls",

View File

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

View File

@@ -1,7 +1,7 @@
<?php
return [
'cacheTimestamp' => 1774447407,
'microtimeState' => 1774447407.027399,
'cacheTimestamp' => 1774472414,
'microtimeState' => 1774472414.878455,
'currencyRates' => [
'EUR' => 1.0
],