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; namespace Espo\Custom\Hooks\CDokumente;
use Espo\ORM\Entity; 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, // Problem: isAttributeChanged('dokument') erkennt Datei-Änderungen nicht,
// da EspoCRM Datei-Uploads nicht als Feld-Änderung markiert. // 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. // um sicherzustellen, dass Hashes bei Datei-Änderungen berechnet werden.
// Optimierung wäre wünschenswert, aber nicht möglich mit aktueller API. // 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; return;
} }
if (is_object($dokument)) { // Verwende EntityManager zur korrekten Relation-Verwaltung
$attachment = $dokument; $attachment = $this->entityManager->getEntityById('Attachment', $dokumentId);
} else {
$attachment = $this->getEntityManager()->getEntity('Attachment', $dokument);
}
if (!$attachment) { if (!$attachment) {
return; return;
} }
$filePath = 'data/upload/' . $attachment->get('id'); $filePath = 'data/upload/' . $attachment->getId();
if (!file_exists($filePath)) { if (!file_exists($filePath)) {
return; return;
} }
@@ -41,7 +49,8 @@ class CDokumente extends \Espo\Core\Hooks\Base
return; return;
} }
$newBlake3 = \blake3($fileContent); // Blake3 Hashing - schneller als SHA3 und kryptographisch sicher
$newBlake3 = blake3($fileContent);
// Setze Hash // Setze Hash
$entity->set('blake3hash', $newBlake3); $entity->set('blake3hash', $newBlake3);

View File

@@ -15,7 +15,6 @@
"mietinkasso": "Mietinkasso", "mietinkasso": "Mietinkasso",
"aktenzeichen": "Aktenzeichen", "aktenzeichen": "Aktenzeichen",
"aktennummer": "Aktennummer", "aktennummer": "Aktennummer",
"aktenpfad": "Aktenpfad (Windows)",
"syncStatus": "Sync-Status", "syncStatus": "Sync-Status",
"lastSync": "Letzte Synchronisation", "lastSync": "Letzte Synchronisation",
"aktivierungsstatus": "Aktivierungsstatus", "aktivierungsstatus": "Aktivierungsstatus",
@@ -43,7 +42,6 @@
"tooltips": { "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.", "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", "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", "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"
} }
} }

View File

@@ -11,6 +11,9 @@
"syncedHash": "Sync-Hash", "syncedHash": "Sync-Hash",
"usn": "USN", "usn": "USN",
"dateipfad": "Dateipfad", "dateipfad": "Dateipfad",
"lastSyncTimestamp": "Letzter Sync",
"advowareArt": "Advoware Art",
"advowareBemerkung": "Advoware Bemerkung",
"contactsvmhdokumente": "Freigegebene Nutzer", "contactsvmhdokumente": "Freigegebene Nutzer",
"vmhMietverhltnisesDokumente": "Mietverhältnisse", "vmhMietverhltnisesDokumente": "Mietverhältnisse",
"vmhErstgespraechsdokumente": "Erstgespräche", "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", "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", "syncedHash": "Hash-Wert bei letzter erfolgreicher Synchronisation",
"usn": "Update Sequence Number - Versionsnummer für 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": { "options": {
"syncStatus": { "syncStatus": {

View File

@@ -4,7 +4,6 @@
"mietinkasso": "Mietinkasso", "mietinkasso": "Mietinkasso",
"aktenzeichen": "Aktenzeichen", "aktenzeichen": "Aktenzeichen",
"aktennummer": "Aktennummer", "aktennummer": "Aktennummer",
"aktenpfad": "File Path (Windows)",
"syncStatus": "Sync Status", "syncStatus": "Sync Status",
"lastSync": "Last Synchronization", "lastSync": "Last Synchronization",
"aktivierungsstatus": "Activation Status", "aktivierungsstatus": "Activation Status",
@@ -44,7 +43,6 @@
"tooltips": { "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.", "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", "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", "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"
} }
} }

View File

@@ -10,6 +10,9 @@
"syncedHash": "Sync Hash", "syncedHash": "Sync Hash",
"usn": "USN", "usn": "USN",
"dateipfad": "File Path", "dateipfad": "File Path",
"lastSyncTimestamp": "Last Sync",
"advowareArt": "Advoware Type",
"advowareBemerkung": "Advoware Remarks",
"contactsvmhdokumente": "Portal Users", "contactsvmhdokumente": "Portal Users",
"vmhMietverhltnisesDokumente": "Tenancies", "vmhMietverhltnisesDokumente": "Tenancies",
"vmhErstgespraechsdokumente": "Initial Consultations", "vmhErstgespraechsdokumente": "Initial Consultations",
@@ -52,7 +55,10 @@
"syncStatus": "Sync status with Advoware: new=new, unclean=changed, synced=synchronized, failed=error, unsupported=not supported", "syncStatus": "Sync status with Advoware: new=new, unclean=changed, synced=synchronized, failed=error, unsupported=not supported",
"syncedHash": "Hash value at last successful synchronization", "syncedHash": "Hash value at last successful synchronization",
"usn": "Update Sequence Number - Version number for 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": { "options": {
"syncStatus": { "syncStatus": {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -118,6 +118,24 @@
"tooltip": true, "tooltip": true,
"isCustom": 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": { "puls": {
"type": "link", "type": "link",
"entity": "CPuls", "entity": "CPuls",

View File

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