diff --git a/custom/Espo/Custom/Hooks/CAIKnowledge/CheckGlobalSyncStatus.php b/custom/Espo/Custom/Hooks/CAIKnowledge/CheckGlobalSyncStatus.php new file mode 100644 index 00000000..0b8f5343 --- /dev/null +++ b/custom/Espo/Custom/Hooks/CAIKnowledge/CheckGlobalSyncStatus.php @@ -0,0 +1,75 @@ +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'); + } + } +} diff --git a/custom/Espo/Custom/Hooks/CAIKnowledge/DokumenteSyncStatus.php b/custom/Espo/Custom/Hooks/CAIKnowledge/DokumenteSyncStatus.php new file mode 100644 index 00000000..c54d0157 --- /dev/null +++ b/custom/Espo/Custom/Hooks/CAIKnowledge/DokumenteSyncStatus.php @@ -0,0 +1,47 @@ +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()); + } + } +} diff --git a/custom/Espo/Custom/Hooks/CAdvowareAkten/CheckGlobalSyncStatus.php b/custom/Espo/Custom/Hooks/CAdvowareAkten/CheckGlobalSyncStatus.php new file mode 100644 index 00000000..91643a1f --- /dev/null +++ b/custom/Espo/Custom/Hooks/CAdvowareAkten/CheckGlobalSyncStatus.php @@ -0,0 +1,75 @@ +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'); + } + } +} diff --git a/custom/Espo/Custom/Hooks/CAdvowareAkten/DokumenteSyncStatus.php b/custom/Espo/Custom/Hooks/CAdvowareAkten/DokumenteSyncStatus.php new file mode 100644 index 00000000..d20c0250 --- /dev/null +++ b/custom/Espo/Custom/Hooks/CAdvowareAkten/DokumenteSyncStatus.php @@ -0,0 +1,47 @@ +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()); + } + } +} diff --git a/custom/Espo/Custom/Resources/i18n/de_DE/CAIKnowledge.json b/custom/Espo/Custom/Resources/i18n/de_DE/CAIKnowledge.json index 8a936f68..29e38167 100644 --- a/custom/Espo/Custom/Resources/i18n/de_DE/CAIKnowledge.json +++ b/custom/Espo/Custom/Resources/i18n/de_DE/CAIKnowledge.json @@ -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" } } \ No newline at end of file diff --git a/custom/Espo/Custom/Resources/i18n/de_DE/CAdvowareAkten.json b/custom/Espo/Custom/Resources/i18n/de_DE/CAdvowareAkten.json index 31c7b193..db48801b 100644 --- a/custom/Espo/Custom/Resources/i18n/de_DE/CAdvowareAkten.json +++ b/custom/Espo/Custom/Resources/i18n/de_DE/CAdvowareAkten.json @@ -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" } } \ No newline at end of file diff --git a/custom/Espo/Custom/Resources/i18n/en_US/CAIKnowledge.json b/custom/Espo/Custom/Resources/i18n/en_US/CAIKnowledge.json index 49d3c0c0..d846d098 100644 --- a/custom/Espo/Custom/Resources/i18n/en_US/CAIKnowledge.json +++ b/custom/Espo/Custom/Resources/i18n/en_US/CAIKnowledge.json @@ -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" } } \ No newline at end of file diff --git a/custom/Espo/Custom/Resources/i18n/en_US/CAdvowareAkten.json b/custom/Espo/Custom/Resources/i18n/en_US/CAdvowareAkten.json index da4f262d..ad08983f 100644 --- a/custom/Espo/Custom/Resources/i18n/en_US/CAdvowareAkten.json +++ b/custom/Espo/Custom/Resources/i18n/en_US/CAdvowareAkten.json @@ -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" } } \ No newline at end of file diff --git a/custom/Espo/Custom/Resources/layouts/CAIKnowledge/bottomPanelsDetail.json b/custom/Espo/Custom/Resources/layouts/CAIKnowledge/bottomPanelsDetail.json index 07fa6852..30ab5c5b 100644 --- a/custom/Espo/Custom/Resources/layouts/CAIKnowledge/bottomPanelsDetail.json +++ b/custom/Espo/Custom/Resources/layouts/CAIKnowledge/bottomPanelsDetail.json @@ -9,5 +9,14 @@ }, "dokumentes": { "index": 1 + }, + "_tabBreak_1": { + "index": 2, + "tabBreak": true, + "tabLabel": "Ereignisse" + }, + "stream": { + "sticked": false, + "index": 3 } } diff --git a/custom/Espo/Custom/Resources/layouts/CAIKnowledge/detail.json b/custom/Espo/Custom/Resources/layouts/CAIKnowledge/detail.json new file mode 100644 index 00000000..1a106594 --- /dev/null +++ b/custom/Espo/Custom/Resources/layouts/CAIKnowledge/detail.json @@ -0,0 +1,24 @@ +[ + { + "rows": [ + [ + { + "name": "name" + }, + { + "name": "datenbankId" + } + ], + [ + { + "name": "syncStatus" + }, + { + "name": "lastSync" + } + ] + ], + "style": "default", + "label": "Overview" + } +] \ No newline at end of file diff --git a/custom/Espo/Custom/Resources/layouts/CAdvowareAkten/bottomPanelsDetail.json b/custom/Espo/Custom/Resources/layouts/CAdvowareAkten/bottomPanelsDetail.json index f482be2d..494101cc 100644 --- a/custom/Espo/Custom/Resources/layouts/CAdvowareAkten/bottomPanelsDetail.json +++ b/custom/Espo/Custom/Resources/layouts/CAdvowareAkten/bottomPanelsDetail.json @@ -15,5 +15,14 @@ }, "dokumentes": { "index": 1 + }, + "_tabBreak_1": { + "index": 2, + "tabBreak": true, + "tabLabel": "Ereignisse" + }, + "stream": { + "sticked": false, + "index": 3 } } diff --git a/custom/Espo/Custom/Resources/layouts/CAdvowareAkten/detail.json b/custom/Espo/Custom/Resources/layouts/CAdvowareAkten/detail.json index 9b5d4093..11d8ad59 100644 --- a/custom/Espo/Custom/Resources/layouts/CAdvowareAkten/detail.json +++ b/custom/Espo/Custom/Resources/layouts/CAdvowareAkten/detail.json @@ -5,11 +5,24 @@ { "name": "name" }, + { + "name": "aktenzeichen" + } + ], + [ { "name": "aktennummer" }, { - "name": "aktenzeichen" + "name": "aktenpfad" + } + ], + [ + { + "name": "syncStatus" + }, + { + "name": "lastSync" } ] ], diff --git a/custom/Espo/Custom/Resources/metadata/clientDefs/CAIKnowledge.json b/custom/Espo/Custom/Resources/metadata/clientDefs/CAIKnowledge.json index 29c8220a..99a170f8 100644 --- a/custom/Espo/Custom/Resources/metadata/clientDefs/CAIKnowledge.json +++ b/custom/Espo/Custom/Resources/metadata/clientDefs/CAIKnowledge.json @@ -3,6 +3,16 @@ "boolFilterList": [ "onlyMy" ], + "bottomPanels": { + "detail": [ + { + "name": "stream", + "label": "Ereignisse", + "reference": "stream", + "stacked": false + } + ] + }, "relationshipPanels": { "dokumentes": { "layout": "listForAIKnowledge", diff --git a/custom/Espo/Custom/Resources/metadata/clientDefs/CAdvowareAkten.json b/custom/Espo/Custom/Resources/metadata/clientDefs/CAdvowareAkten.json index df8814c2..aab352d3 100644 --- a/custom/Espo/Custom/Resources/metadata/clientDefs/CAdvowareAkten.json +++ b/custom/Espo/Custom/Resources/metadata/clientDefs/CAdvowareAkten.json @@ -30,6 +30,12 @@ "name": "history", "reference": "history", "disabled": true + }, + { + "name": "stream", + "label": "Ereignisse", + "reference": "stream", + "stacked": false } ] }, diff --git a/custom/Espo/Custom/Resources/metadata/entityDefs/CAIKnowledge.json b/custom/Espo/Custom/Resources/metadata/entityDefs/CAIKnowledge.json index d2480436..e5286fd5 100644 --- a/custom/Espo/Custom/Resources/metadata/entityDefs/CAIKnowledge.json +++ b/custom/Espo/Custom/Resources/metadata/entityDefs/CAIKnowledge.json @@ -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, diff --git a/custom/Espo/Custom/Resources/metadata/entityDefs/CAdvowareAkten.json b/custom/Espo/Custom/Resources/metadata/entityDefs/CAdvowareAkten.json index c3f687c8..2a4f0491 100644 --- a/custom/Espo/Custom/Resources/metadata/entityDefs/CAdvowareAkten.json +++ b/custom/Espo/Custom/Resources/metadata/entityDefs/CAdvowareAkten.json @@ -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, diff --git a/custom/Espo/Custom/Resources/metadata/scopes/CAdvowareAkten.json b/custom/Espo/Custom/Resources/metadata/scopes/CAdvowareAkten.json index fa0b9bf6..8546a14d 100644 --- a/custom/Espo/Custom/Resources/metadata/scopes/CAdvowareAkten.json +++ b/custom/Espo/Custom/Resources/metadata/scopes/CAdvowareAkten.json @@ -14,7 +14,7 @@ "customizable": true, "importable": true, "notifications": true, - "stream": false, + "stream": true, "disabled": false, "type": "BasePlus", "module": "Custom", diff --git a/data/config.php b/data/config.php index 202a4b88..438a1268 100644 --- a/data/config.php +++ b/data/config.php @@ -360,7 +360,7 @@ return [ 0 => 'youtube.com', 1 => 'google.com' ], - 'microtime' => 1773251210.764005, + 'microtime' => 1773253602.105787, 'siteUrl' => 'https://crm.bitbylaw.com', 'fullTextSearchMinLength' => 4, 'webSocketUrl' => 'ws://api.bitbylaw.com:5000/espocrm/ws', diff --git a/data/state.php b/data/state.php index 4eb973c6..2ea8a0e2 100644 --- a/data/state.php +++ b/data/state.php @@ -1,7 +1,7 @@ 1773251210, - 'microtimeState' => 1773251210.899627, + 'cacheTimestamp' => 1773253761, + 'microtimeState' => 1773253761.495155, 'currencyRates' => [ 'EUR' => 1.0 ],