Add sync status and last sync fields to CAIKnowledge and CAdvowareAkten entities, update layouts and metadata. Implement hooks for global sync status management.
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
namespace Espo\Custom\Hooks\CAIKnowledge;
|
||||
|
||||
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('CAIKnowledgeDokumente')
|
||||
->where([
|
||||
'cAIKnowledgeId' => $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('CAIKnowledge CheckGlobalSyncStatus Hook Error: ' . $e->getMessage());
|
||||
$entity->set('syncStatus', 'unclean');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace Espo\Custom\Hooks\CAIKnowledge;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\Hook\Hook\AfterRelate;
|
||||
|
||||
/**
|
||||
* Hook: Setzt Dokument-Sync-Status auf "new" beim Verknüpfen und
|
||||
* globalen syncStatus auf "unclean"
|
||||
*/
|
||||
class DokumenteSyncStatus implements AfterRelate
|
||||
{
|
||||
public function __construct(
|
||||
private \Espo\ORM\EntityManager $entityManager
|
||||
) {}
|
||||
|
||||
public function afterRelate(
|
||||
Entity $entity,
|
||||
string $relationName,
|
||||
Entity $foreignEntity,
|
||||
array $columnData,
|
||||
\Espo\ORM\Repository\Option\RelateOptions $options
|
||||
): void {
|
||||
// Nur für dokumentes-Beziehung
|
||||
if ($relationName !== 'dokumentes') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Setze Sync-Status des Dokuments in der Junction-Tabelle auf "new"
|
||||
$repository = $this->entityManager->getRDBRepository('CAIKnowledge');
|
||||
|
||||
try {
|
||||
$repository->getRelation($entity, 'dokumentes')->updateColumns(
|
||||
$foreignEntity,
|
||||
['syncstatus' => 'new']
|
||||
);
|
||||
|
||||
// Setze globalen syncStatus auf "unclean"
|
||||
$entity->set('syncStatus', 'unclean');
|
||||
$this->entityManager->saveEntity($entity, ['silent' => true, 'skipHooks' => true]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// Fehler loggen, aber nicht werfen (um Verknüpfung nicht zu blockieren)
|
||||
$GLOBALS['log']->error('CAIKnowledge DokumenteSyncStatus Hook Error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?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');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace Espo\Custom\Hooks\CAdvowareAkten;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\Hook\Hook\AfterRelate;
|
||||
|
||||
/**
|
||||
* Hook: Setzt Dokument-Sync-Status auf "new" beim Verknüpfen und
|
||||
* globalen syncStatus auf "unclean"
|
||||
*/
|
||||
class DokumenteSyncStatus implements AfterRelate
|
||||
{
|
||||
public function __construct(
|
||||
private \Espo\ORM\EntityManager $entityManager
|
||||
) {}
|
||||
|
||||
public function afterRelate(
|
||||
Entity $entity,
|
||||
string $relationName,
|
||||
Entity $foreignEntity,
|
||||
array $columnData,
|
||||
\Espo\ORM\Repository\Option\RelateOptions $options
|
||||
): void {
|
||||
// Nur für dokumentes-Beziehung
|
||||
if ($relationName !== 'dokumentes') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Setze Sync-Status des Dokuments in der Junction-Tabelle auf "new"
|
||||
$repository = $this->entityManager->getRDBRepository('CAdvowareAkten');
|
||||
|
||||
try {
|
||||
$repository->getRelation($entity, 'dokumentes')->updateColumns(
|
||||
$foreignEntity,
|
||||
['syncstatus' => 'new']
|
||||
);
|
||||
|
||||
// Setze globalen syncStatus auf "unclean"
|
||||
$entity->set('syncStatus', 'unclean');
|
||||
$this->entityManager->saveEntity($entity, ['silent' => true, 'skipHooks' => true]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// Fehler loggen, aber nicht werfen (um Verknüpfung nicht zu blockieren)
|
||||
$GLOBALS['log']->error('CAdvowareAkten DokumenteSyncStatus Hook Error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@
|
||||
"vmhRumungsklage": "Räumungsklage",
|
||||
"mietinkasso": "Mietinkasso",
|
||||
"datenbankId": "Datenbank-ID",
|
||||
"syncStatus": "Sync-Status",
|
||||
"lastSync": "Letzte Synchronisation",
|
||||
"dokumenteAiDocumentId": "AI Document ID",
|
||||
"dokumenteSyncstatus": "Sync-Status",
|
||||
"dokumenteLastSync": "Letzter Sync"
|
||||
@@ -15,5 +17,16 @@
|
||||
"dokumentes": "Dokumente",
|
||||
"vmhRumungsklage": "Räumungsklage",
|
||||
"mietinkasso": "Mietinkasso"
|
||||
},
|
||||
"options": {
|
||||
"syncStatus": {
|
||||
"synced": "Synchronisiert",
|
||||
"unclean": "Nicht synchronisiert"
|
||||
}
|
||||
},
|
||||
"tooltips": {
|
||||
"syncStatus": "Globaler Synchronisationsstatus: synced = Alle Dokumente synchronisiert, unclean = Mindestens ein Dokument ist neu oder hat Änderungen. Wird automatisch basierend auf den Dokumenten-Status aktualisiert.",
|
||||
"lastSync": "Zeitpunkt der letzten erfolgreichen Synchronisation aller Dokumente",
|
||||
"datenbankId": "Eindeutige ID in der AI-Datenbank"
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,23 @@
|
||||
"mietinkasso": "Mietinkasso",
|
||||
"aktenzeichen": "Aktenzeichen",
|
||||
"aktennummer": "Aktennummer",
|
||||
"pfad": "Dateipfad",
|
||||
"aktenpfad": "Aktenpfad (Windows)",
|
||||
"syncStatus": "Sync-Status",
|
||||
"lastSync": "Letzte Synchronisation",
|
||||
"dokumentes": "Dokumente",
|
||||
"dokumenteHnr": "HNR",
|
||||
"dokumenteSyncstatus": "Sync-Status",
|
||||
"dokumenteLastSync": "Letzter Sync"
|
||||
},
|
||||
"options": {
|
||||
"syncStatus": {
|
||||
"synced": "Synchronisiert",
|
||||
"unclean": "Nicht synchronisiert"
|
||||
}
|
||||
},
|
||||
"tooltips": {
|
||||
"syncStatus": "Globaler Synchronisationsstatus: synced = Alle Dokumente synchronisiert, unclean = Mindestens ein Dokument ist neu oder hat Änderungen. Wird automatisch basierend auf den Dokumenten-Status aktualisiert.",
|
||||
"lastSync": "Zeitpunkt der letzten erfolgreichen Synchronisation aller Dokumente",
|
||||
"aktenpfad": "Windows-Dateipfad zur Akte in Advoware"
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
"vmhRumungsklage": "Räumungsklage",
|
||||
"mietinkasso": "Mietinkasso",
|
||||
"datenbankId": "Database ID",
|
||||
"syncStatus": "Sync Status",
|
||||
"lastSync": "Last Synchronization",
|
||||
"dokumenteAiDocumentId": "AI Document ID",
|
||||
"dokumenteSyncstatus": "Sync Status",
|
||||
"dokumenteLastSync": "Last Sync"
|
||||
@@ -15,5 +17,16 @@
|
||||
},
|
||||
"labels": {
|
||||
"Create CAIKnowledge": "Create AI Knowledge"
|
||||
},
|
||||
"options": {
|
||||
"syncStatus": {
|
||||
"synced": "Synchronized",
|
||||
"unclean": "Not Synchronized"
|
||||
}
|
||||
},
|
||||
"tooltips": {
|
||||
"syncStatus": "Global synchronization status: synced = All documents synchronized, unclean = At least one document is new or has changes. Updated automatically based on document status.",
|
||||
"lastSync": "Timestamp of the last successful synchronization of all documents",
|
||||
"datenbankId": "Unique ID in the AI database"
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,9 @@
|
||||
"mietinkasso": "Mietinkasso",
|
||||
"aktenzeichen": "Aktenzeichen",
|
||||
"aktennummer": "Aktennummer",
|
||||
"pfad": "File Path",
|
||||
"aktenpfad": "File Path (Windows)",
|
||||
"syncStatus": "Sync Status",
|
||||
"lastSync": "Last Synchronization",
|
||||
"dokumentes": "Dokumente",
|
||||
"dokumenteHnr": "HNR",
|
||||
"dokumenteSyncstatus": "Sync Status",
|
||||
@@ -20,5 +22,16 @@
|
||||
},
|
||||
"labels": {
|
||||
"Create CAdvowareAkten": "Create Advoware Akten"
|
||||
},
|
||||
"options": {
|
||||
"syncStatus": {
|
||||
"synced": "Synchronized",
|
||||
"unclean": "Not Synchronized"
|
||||
}
|
||||
},
|
||||
"tooltips": {
|
||||
"syncStatus": "Global synchronization status: synced = All documents synchronized, unclean = At least one document is new or has changes. Updated automatically based on document status.",
|
||||
"lastSync": "Timestamp of the last successful synchronization of all documents",
|
||||
"aktenpfad": "Windows file path to the file in Advoware"
|
||||
}
|
||||
}
|
||||
@@ -9,5 +9,14 @@
|
||||
},
|
||||
"dokumentes": {
|
||||
"index": 1
|
||||
},
|
||||
"_tabBreak_1": {
|
||||
"index": 2,
|
||||
"tabBreak": true,
|
||||
"tabLabel": "Ereignisse"
|
||||
},
|
||||
"stream": {
|
||||
"sticked": false,
|
||||
"index": 3
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
[
|
||||
{
|
||||
"rows": [
|
||||
[
|
||||
{
|
||||
"name": "name"
|
||||
},
|
||||
{
|
||||
"name": "datenbankId"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "syncStatus"
|
||||
},
|
||||
{
|
||||
"name": "lastSync"
|
||||
}
|
||||
]
|
||||
],
|
||||
"style": "default",
|
||||
"label": "Overview"
|
||||
}
|
||||
]
|
||||
@@ -15,5 +15,14 @@
|
||||
},
|
||||
"dokumentes": {
|
||||
"index": 1
|
||||
},
|
||||
"_tabBreak_1": {
|
||||
"index": 2,
|
||||
"tabBreak": true,
|
||||
"tabLabel": "Ereignisse"
|
||||
},
|
||||
"stream": {
|
||||
"sticked": false,
|
||||
"index": 3
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,24 @@
|
||||
{
|
||||
"name": "name"
|
||||
},
|
||||
{
|
||||
"name": "aktenzeichen"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "aktennummer"
|
||||
},
|
||||
{
|
||||
"name": "aktenzeichen"
|
||||
"name": "aktenpfad"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "syncStatus"
|
||||
},
|
||||
{
|
||||
"name": "lastSync"
|
||||
}
|
||||
]
|
||||
],
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
"boolFilterList": [
|
||||
"onlyMy"
|
||||
],
|
||||
"bottomPanels": {
|
||||
"detail": [
|
||||
{
|
||||
"name": "stream",
|
||||
"label": "Ereignisse",
|
||||
"reference": "stream",
|
||||
"stacked": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"relationshipPanels": {
|
||||
"dokumentes": {
|
||||
"layout": "listForAIKnowledge",
|
||||
|
||||
@@ -30,6 +30,12 @@
|
||||
"name": "history",
|
||||
"reference": "history",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"name": "stream",
|
||||
"label": "Ereignisse",
|
||||
"reference": "stream",
|
||||
"stacked": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -47,6 +47,28 @@
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"syncStatus": {
|
||||
"type": "enum",
|
||||
"required": false,
|
||||
"options": [
|
||||
"synced",
|
||||
"unclean"
|
||||
],
|
||||
"style": {
|
||||
"synced": "success",
|
||||
"unclean": "warning"
|
||||
},
|
||||
"default": "unclean",
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"lastSync": {
|
||||
"type": "datetime",
|
||||
"required": false,
|
||||
"readOnly": true,
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"dokumenteAiDocumentId": {
|
||||
"type": "varchar",
|
||||
"notStorable": true,
|
||||
|
||||
@@ -56,12 +56,34 @@
|
||||
"readOnlyAfterCreate": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"pfad": {
|
||||
"aktenpfad": {
|
||||
"type": "varchar",
|
||||
"maxLength": 500,
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"syncStatus": {
|
||||
"type": "enum",
|
||||
"required": false,
|
||||
"options": [
|
||||
"synced",
|
||||
"unclean"
|
||||
],
|
||||
"style": {
|
||||
"synced": "success",
|
||||
"unclean": "warning"
|
||||
},
|
||||
"default": "unclean",
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"lastSync": {
|
||||
"type": "datetime",
|
||||
"required": false,
|
||||
"readOnly": true,
|
||||
"tooltip": true,
|
||||
"isCustom": true
|
||||
},
|
||||
"dokumenteHnr": {
|
||||
"type": "int",
|
||||
"notStorable": true,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"customizable": true,
|
||||
"importable": true,
|
||||
"notifications": true,
|
||||
"stream": false,
|
||||
"stream": true,
|
||||
"disabled": false,
|
||||
"type": "BasePlus",
|
||||
"module": "Custom",
|
||||
|
||||
Reference in New Issue
Block a user