Compare commits

..

28 Commits

Author SHA1 Message Date
faffe3d874 Update EspoCRM Best Practices to version 2.4 with new features and implementation patterns for Custom API Endpoints; remove outdated TESTERGEBNISSE_JUNCTION_TABLE.md file. 2026-03-12 22:52:42 +01:00
bf0f596ad4 Update configuration and localization files for CAIKnowledgeCDokumente; add metadata and scopes 2026-03-12 22:40:38 +01:00
3ecc6275bc Implement CAIKnowledge and CDokumente junction functionality; add API routes, metadata, and localization files 2026-03-12 22:39:33 +01:00
d0397e475e Remove unused CAICollectionCDokumente localization, metadata, and service files; update CAIKnowledge layout and CAdvowareAkten scope type 2026-03-12 21:50:02 +01:00
51d9f7fa22 Refactor Blake3 hashing implementation in CDokumente; update localization and metadata files 2026-03-11 22:45:42 +01:00
80dc3b40d3 Implement Blake3 hashing in CDokumente; update related metadata and localization files 2026-03-11 22:20:21 +01:00
e15dd14cab Add 'pending_sync' status to syncStatus in CAIKnowledge and CAdvowareAkten entities; update tooltips and API documentation 2026-03-11 22:05:27 +01:00
54d66da52d Add 'syncedHash' field to CAIKnowledge and CAdvowareAkten entities; update API documentation and configuration timestamps 2026-03-11 21:53:35 +01:00
ae359048af Add 'aktivierungsstatus' field to CAIKnowledge and CAdvowareAkten entities; update API documentation and configuration timestamps 2026-03-11 21:49:02 +01:00
c678660ad6 Add 'unsupported' status to dokumenteSyncstatus in CAIKnowledge and CAIKnowledgeCDokumente; update API documentation and configuration timestamps 2026-03-11 21:19:56 +01:00
c952fc40bc Update configuration and state files; adjust microtime values and add CAIKnowledgeCDokumente junction entity with metadata and API documentation 2026-03-11 20:48:35 +01:00
b2c391539d Update documentation for Junction Table UI-Integration and document propagation patterns; include new features and best practices for sync status management 2026-03-11 20:38:45 +01:00
e7b14406fb Update detail layouts for CMietinkasso and CVmhRumungsklage to include AI Knowledge and Advoware Akten; update cache and microtime state in state.php 2026-03-11 20:09:13 +01:00
4707925917 Update timestamps in config and state files; add hooks for document relation propagation in CAIKnowledge, CAdvowareAkten, CMietinkasso, and CVmhRumungsklage 2026-03-11 20:07:34 +01:00
c2c9cfe709 Remove Advoware and x.AI related fields, tooltips, and options from CDokumente JSON files. Add UpdateJunctionSyncStatus hook to manage sync status for related entities on document updates. Update configuration timestamps in state and config files. 2026-03-11 19:55:29 +01:00
9411337939 Add sync status and last sync fields to CAIKnowledge and CAdvowareAkten entities, update layouts and metadata. Implement hooks for global sync status management. 2026-03-11 19:29:55 +01:00
986cafcfd6 Add new fields and layouts for AI Knowledge and Advoware Akten, update translations and metadata 2026-03-11 19:11:41 +01:00
c12577f4f8 Add AI Knowledge entity and related localization
- Introduced CAIKnowledge entity with fields for name, description, and relationships to Mietinkasso and Räumungsklage.
- Added localization files for CAIKnowledge in multiple languages including English, German, Spanish, and more.
- Updated existing JSON files to include references to AI Knowledge where applicable.
- Created necessary metadata definitions for client, entity, and record definitions for CAIKnowledge.
- Enhanced existing documents and mietinkasso resources to support new AI Knowledge functionality.
2026-03-11 18:30:21 +01:00
f7b1adc015 Add CAdvowareAkten entity and related metadata
- Introduced new entity CAdvowareAkten with fields: name, description, createdAt, modifiedAt, assignedUser, teams, vmhRumungsklage, mietinkasso, aktenzeichen, and aktennummer.
- Defined relationships for CAdvowareAkten with existing entities: CVmhRumungsklage and CMietinkasso.
- Created client definitions, layouts, and ACL for CAdvowareAkten.
- Added internationalization support for CAdvowareAkten in multiple languages.
- Updated existing entities and metadata to include references to CAdvowareAkten.
2026-03-11 18:21:20 +01:00
0f307c7eca Remove CAICollection and CAdvowareAkten entities and their associated metadata, layouts, services, and translations. Update configuration and state files accordingly. Add side panel configuration for CMietinkasso. 2026-03-11 10:24:52 +01:00
9ab8f8b4bf Add Mietinkasso and Räumungsklage fields to translations and metadata, update layouts and configuration 2026-03-11 10:15:02 +01:00
8438af8f97 Update translations and metadata for CVmhRumungsklage, and synchronize cache timestamps 2026-03-11 09:27:54 +01:00
76c38e8ad4 Refactor code structure for improved readability and maintainability 2026-03-11 09:21:52 +01:00
c2766ec66a Update documentation for EspoCRM Best Practices and Validator Tool 2026-03-10 12:46:14 +01:00
9b18a63acf Update configuration timestamps, add custom services, and implement validation and rebuild script for EspoCRM 2026-03-10 12:35:01 +01:00
641e5c0a91 Update cache timestamps and microtime values in configuration and state files 2026-03-10 12:24:35 +01:00
3470dba301 Add junction entities and ACL for CAICollection, CAdvowareAkten, and CPulsTeam
- Introduced new junction entities: CAICollectionCDokumente, CAdvowareAktenCDokumente, and CPulsTeamZuordnung.
- Implemented respective controllers and services for handling CRUD operations.
- Added metadata definitions for new entities, including fields, links, and indexes.
- Created language files for English and German translations for the new entities.
- Developed a script to set ACL permissions for the new junction entities across existing roles.
- Updated existing entity definitions to include new relationships and fields related to Advoware and AI Collections.
2026-03-10 00:10:57 +01:00
0340c59e5c Add CAICollection and Advoware Akten entity and related resources 2026-03-09 23:57:48 +01:00
224 changed files with 175645 additions and 2544 deletions

View File

@@ -32,6 +32,7 @@ namespace Espo\Classes\RecordHooks\CurrencyRecordRate;
use Espo\Core\Exceptions\Conflict;
use Espo\Core\Record\DeleteParams;
use Espo\Core\Record\Hook\DeleteHook;
use Espo\Core\Utils\Currency\DatabasePopulator;
use Espo\Core\WebSocket\Submission;
use Espo\Entities\CurrencyRecordRate;
use Espo\ORM\Entity;
@@ -46,6 +47,7 @@ class AfterDelete implements DeleteHook
public function __construct(
private SyncManager $syncManager,
private Submission $submission,
private DatabasePopulator $databasePopulator,
) {}
public function process(Entity $entity, DeleteParams $params): void
@@ -58,6 +60,7 @@ class AfterDelete implements DeleteHook
throw new Conflict($e->getMessage(), previous: $e);
}
$this->databasePopulator->process();
$this->submission->submit('appParamsUpdate');
}
}

View File

@@ -31,6 +31,7 @@ namespace Espo\Classes\RecordHooks\CurrencyRecordRate;
use Espo\Core\Exceptions\Conflict;
use Espo\Core\Record\Hook\SaveHook;
use Espo\Core\Utils\Currency\DatabasePopulator;
use Espo\Core\WebSocket\Submission;
use Espo\Entities\CurrencyRecordRate;
use Espo\ORM\Entity;
@@ -45,6 +46,7 @@ class AfterSave implements SaveHook
public function __construct(
private SyncManager $syncManager,
private Submission $submission,
private DatabasePopulator $databasePopulator,
) {}
public function process(Entity $entity): void
@@ -57,6 +59,7 @@ class AfterSave implements SaveHook
throw new Conflict($e->getMessage(), previous: $e);
}
$this->databasePopulator->process();
$this->submission->submit('appParamsUpdate');
}
}

View File

@@ -141,7 +141,7 @@ class RecordService
];
if ($this->user->isPortal()) {
$where[] = ['isInternal' => true];
$where[] = ['isInternal' => false];
}
$this->applyPortalAccess($builder, $where);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
/*! espocrm 2026-03-06 */
/*! espocrm 2026-03-10 */
define("modules/crm/views/scheduler/scheduler",["exports","view","vis-data","vis-timeline","moment","jquery"],function(t,e,a,s,n,r){Object.defineProperty(t,"__esModule",{value:!0});t.default=void 0;e=i(e);n=i(n);r=i(r);function i(t){return t&&t.__esModule?t:{default:t}}class o extends e.default{templateContent=`
<div class="timeline"></div>
<link href="{{basePath}}client/modules/crm/css/vis.css" rel="stylesheet">

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,71 @@
<?php
namespace Espo\Custom\Api\JunctionData;
use Espo\Core\Api\Action;
use Espo\Core\Api\Request;
use Espo\Core\Api\Response;
use Espo\Core\Api\ResponseComposer;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\NotFound;
use Espo\ORM\EntityManager;
/**
* GET /api/v1/JunctionData/CAIKnowledge/:knowledgeId/dokumentes
*
* Returns all documents linked to a knowledge entry with junction table data
*/
class GetDokumentes implements Action
{
public function __construct(
private EntityManager $entityManager
) {}
public function process(Request $request): Response
{
$knowledgeId = $request->getRouteParam('knowledgeId');
if (!$knowledgeId) {
throw new BadRequest('Knowledge ID is required');
}
// Verify knowledge exists
$knowledge = $this->entityManager->getEntityById('CAIKnowledge', $knowledgeId);
if (!$knowledge) {
throw new NotFound('Knowledge entry not found');
}
$pdo = $this->entityManager->getPDO();
$sql = "
SELECT
j.id as junctionId,
j.c_a_i_knowledge_id as cAIKnowledgeId,
j.c_dokumente_id as cDokumenteId,
j.ai_document_id as aiDocumentId,
j.syncstatus,
j.last_sync as lastSync,
d.id as documentId,
d.name as documentName,
d.blake3hash as blake3hash,
d.created_at as documentCreatedAt,
d.modified_at as documentModifiedAt
FROM c_a_i_knowledge_dokumente j
INNER JOIN c_dokumente d ON j.c_dokumente_id = d.id
WHERE j.c_a_i_knowledge_id = :knowledgeId
AND j.deleted = 0
AND d.deleted = 0
ORDER BY j.id DESC
";
$sth = $pdo->prepare($sql);
$sth->execute(['knowledgeId' => $knowledgeId]);
$results = $sth->fetchAll(\PDO::FETCH_ASSOC);
return ResponseComposer::json([
'total' => count($results),
'list' => $results
]);
}
}

View File

@@ -0,0 +1,178 @@
<?php
namespace Espo\Custom\Api\JunctionData;
use Espo\Core\Api\Action;
use Espo\Core\Api\Request;
use Espo\Core\Api\Response;
use Espo\Core\Api\ResponseComposer;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\Conflict;
use Espo\ORM\EntityManager;
/**
* POST /api/v1/JunctionData/CAIKnowledge/:knowledgeId/dokumentes/:documentId
*
* Creates or updates relationship with junction table data
* This endpoint links the entities AND sets junction columns in one call
*/
class LinkDokument implements Action
{
public function __construct(
private EntityManager $entityManager
) {}
public function process(Request $request): Response
{
$knowledgeId = $request->getRouteParam('knowledgeId');
$documentId = $request->getRouteParam('documentId');
$data = $request->getParsedBody();
if (!$knowledgeId || !$documentId) {
throw new BadRequest('Knowledge ID and Document ID are required');
}
// Verify entities exist
$knowledge = $this->entityManager->getEntityById('CAIKnowledge', $knowledgeId);
if (!$knowledge) {
throw new NotFound('Knowledge entry not found');
}
$document = $this->entityManager->getEntityById('CDokumente', $documentId);
if (!$document) {
throw new NotFound('Document not found');
}
$pdo = $this->entityManager->getPDO();
// Check if link already exists
$existing = $this->checkIfLinked($knowledgeId, $documentId);
if ($existing) {
// Link exists - update junction columns
return $this->updateExisting($knowledgeId, $documentId, $data);
}
// Create new link via ORM (triggers hooks like DokumenteSyncStatus)
$this->entityManager->getRDBRepository('CAIKnowledge')
->getRelation($knowledge, 'dokumentes')
->relate($document);
// Now set junction columns if provided
if (!empty((array)$data)) {
return $this->updateExisting($knowledgeId, $documentId, $data);
}
// Return created entry
$result = $this->getJunctionEntry($knowledgeId, $documentId);
return ResponseComposer::json($result);
}
private function checkIfLinked(string $knowledgeId, string $documentId): bool
{
$pdo = $this->entityManager->getPDO();
$sql = "
SELECT COUNT(*) as count
FROM c_a_i_knowledge_dokumente
WHERE c_a_i_knowledge_id = :knowledgeId
AND c_dokumente_id = :documentId
AND deleted = 0
";
$sth = $pdo->prepare($sql);
$sth->execute([
'knowledgeId' => $knowledgeId,
'documentId' => $documentId
]);
$result = $sth->fetch(\PDO::FETCH_ASSOC);
return $result['count'] > 0;
}
private function updateExisting(string $knowledgeId, string $documentId, \stdClass $data): Response
{
$pdo = $this->entityManager->getPDO();
// Build dynamic UPDATE SET clause
$setClauses = [];
$params = [
'knowledgeId' => $knowledgeId,
'documentId' => $documentId
];
if (isset($data->aiDocumentId)) {
$setClauses[] = "ai_document_id = :aiDocumentId";
$params['aiDocumentId'] = $data->aiDocumentId;
}
if (isset($data->syncstatus)) {
$allowedStatuses = ['new', 'unclean', 'synced', 'failed', 'unsupported'];
if (!in_array($data->syncstatus, $allowedStatuses)) {
throw new BadRequest('Invalid syncstatus value. Allowed: ' . implode(', ', $allowedStatuses));
}
$setClauses[] = "syncstatus = :syncstatus";
$params['syncstatus'] = $data->syncstatus;
}
if (isset($data->lastSync)) {
$setClauses[] = "last_sync = :lastSync";
$params['lastSync'] = $data->lastSync;
} elseif (isset($data->updateLastSync) && $data->updateLastSync === true) {
$setClauses[] = "last_sync = NOW()";
}
if (!empty($setClauses)) {
$sql = "
UPDATE c_a_i_knowledge_dokumente
SET " . implode(', ', $setClauses) . "
WHERE c_a_i_knowledge_id = :knowledgeId
AND c_dokumente_id = :documentId
AND deleted = 0
";
$sth = $pdo->prepare($sql);
$sth->execute($params);
}
// Return updated data
$result = $this->getJunctionEntry($knowledgeId, $documentId);
return ResponseComposer::json($result);
}
private function getJunctionEntry(string $knowledgeId, string $documentId): array
{
$pdo = $this->entityManager->getPDO();
$sql = "
SELECT
id as junctionId,
c_a_i_knowledge_id as cAIKnowledgeId,
c_dokumente_id as cDokumenteId,
ai_document_id as aiDocumentId,
syncstatus,
last_sync as lastSync
FROM c_a_i_knowledge_dokumente
WHERE c_a_i_knowledge_id = :knowledgeId
AND c_dokumente_id = :documentId
AND deleted = 0
";
$sth = $pdo->prepare($sql);
$sth->execute([
'knowledgeId' => $knowledgeId,
'documentId' => $documentId
]);
$result = $sth->fetch(\PDO::FETCH_ASSOC);
if (!$result) {
throw new NotFound('Junction entry not found');
}
return $result;
}
}

View File

@@ -0,0 +1,123 @@
<?php
namespace Espo\Custom\Api\JunctionData;
use Espo\Core\Api\Action;
use Espo\Core\Api\Request;
use Espo\Core\Api\Response;
use Espo\Core\Api\ResponseComposer;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\NotFound;
use Espo\ORM\EntityManager;
/**
* PUT /api/v1/JunctionData/CAIKnowledge/:knowledgeId/dokumentes/:documentId
*
* Updates junction table columns for an existing relationship
*/
class UpdateJunction implements Action
{
public function __construct(
private EntityManager $entityManager
) {}
public function process(Request $request): Response
{
$knowledgeId = $request->getRouteParam('knowledgeId');
$documentId = $request->getRouteParam('documentId');
$data = $request->getParsedBody();
if (!$knowledgeId || !$documentId) {
throw new BadRequest('Knowledge ID and Document ID are required');
}
$pdo = $this->entityManager->getPDO();
// Build dynamic UPDATE SET clause
$setClauses = [];
$params = [
'knowledgeId' => $knowledgeId,
'documentId' => $documentId
];
if (isset($data->aiDocumentId)) {
$setClauses[] = "ai_document_id = :aiDocumentId";
$params['aiDocumentId'] = $data->aiDocumentId;
}
if (isset($data->syncstatus)) {
$allowedStatuses = ['new', 'unclean', 'synced', 'failed', 'unsupported'];
if (!in_array($data->syncstatus, $allowedStatuses)) {
throw new BadRequest('Invalid syncstatus value. Allowed: ' . implode(', ', $allowedStatuses));
}
$setClauses[] = "syncstatus = :syncstatus";
$params['syncstatus'] = $data->syncstatus;
}
if (isset($data->lastSync)) {
$setClauses[] = "last_sync = :lastSync";
$params['lastSync'] = $data->lastSync;
} elseif (isset($data->updateLastSync) && $data->updateLastSync === true) {
$setClauses[] = "last_sync = NOW()";
}
if (empty($setClauses)) {
throw new BadRequest('No fields to update. Provide at least one of: aiDocumentId, syncstatus, lastSync');
}
$sql = "
UPDATE c_a_i_knowledge_dokumente
SET " . implode(', ', $setClauses) . "
WHERE c_a_i_knowledge_id = :knowledgeId
AND c_dokumente_id = :documentId
AND deleted = 0
";
$sth = $pdo->prepare($sql);
$sth->execute($params);
$affectedRows = $sth->rowCount();
if ($affectedRows === 0) {
throw new NotFound('Junction entry not found or no changes made');
}
// Return updated data
$result = $this->getJunctionEntry($knowledgeId, $documentId);
return ResponseComposer::json($result);
}
private function getJunctionEntry(string $knowledgeId, string $documentId): array
{
$pdo = $this->entityManager->getPDO();
$sql = "
SELECT
id as junctionId,
c_a_i_knowledge_id as cAIKnowledgeId,
c_dokumente_id as cDokumenteId,
ai_document_id as aiDocumentId,
syncstatus,
last_sync as lastSync
FROM c_a_i_knowledge_dokumente
WHERE c_a_i_knowledge_id = :knowledgeId
AND c_dokumente_id = :documentId
AND deleted = 0
";
$sth = $pdo->prepare($sql);
$sth->execute([
'knowledgeId' => $knowledgeId,
'documentId' => $documentId
]);
$result = $sth->fetch(\PDO::FETCH_ASSOC);
if (!$result) {
throw new NotFound('Junction entry not found');
}
return $result;
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Espo\Custom\Controllers;
use Espo\Core\Controllers\Record;
/**
* Junction Controller: CAICollection ↔ CDokumente
*
* Provides REST API access to the junction table with additionalColumns:
* - xaifileid: XAI external file ID
* - syncStatus: Sync state tracking
*/
class CAICollectionCDokumente extends Record
{
// Inherits all CRUD operations from Record controller
//
// Available endpoints:
// GET /api/v1/CAICollectionCDokumente
// GET /api/v1/CAICollectionCDokumente/{id}
// POST /api/v1/CAICollectionCDokumente
// PUT /api/v1/CAICollectionCDokumente/{id}
// DELETE /api/v1/CAICollectionCDokumente/{id}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Espo\Custom\Controllers;
class CAIKnowledge extends \Espo\Core\Templates\Controllers\Base
{
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Espo\Custom\Controllers;
use Espo\Core\Controllers\Record;
/**
* Junction Controller: CAIKnowledge ↔ CDokumente
*
* Provides REST API access to the junction table with additionalColumns:
* - aiDocumentId: External AI document reference
* - syncstatus: Sync state tracking (new, unclean, synced, failed)
* - lastSync: Last synchronization timestamp
*/
class CAIKnowledgeCDokumente extends Record
{
// Inherits all CRUD operations from Record controller
//
// Available endpoints:
// GET /api/v1/CAIKnowledgeCDokumente
// GET /api/v1/CAIKnowledgeCDokumente/{id}
// POST /api/v1/CAIKnowledgeCDokumente
// PUT /api/v1/CAIKnowledgeCDokumente/{id}
// DELETE /api/v1/CAIKnowledgeCDokumente/{id}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Espo\Custom\Controllers;
class CAdvowareAkten extends \Espo\Core\Templates\Controllers\BasePlus
{
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Espo\Custom\Controllers;
use Espo\Core\Controllers\Record;
/**
* Junction Controller: CAdvowareAkten ↔ CDokumente
*
* Provides REST API access to the junction table with additionalColumns:
* - hnr: Advoware HNR reference
* - syncStatus: Sync state tracking
*/
class CAdvowareAktenCDokumente extends Record
{
// Inherits all CRUD operations from Record controller
//
// Available endpoints:
// GET /api/v1/CAdvowareAktenCDokumente
// GET /api/v1/CAdvowareAktenCDokumente/{id}
// POST /api/v1/CAdvowareAktenCDokumente
// PUT /api/v1/CAdvowareAktenCDokumente/{id}
// DELETE /api/v1/CAdvowareAktenCDokumente/{id}
}

View File

@@ -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');
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -0,0 +1,155 @@
<?php
namespace Espo\Custom\Hooks\CAIKnowledge;
use Espo\ORM\Entity;
use Espo\Core\Hook\Hook\AfterRelate;
use Espo\Core\Hook\Hook\AfterUnrelate;
/**
* Hook: Propagiert Dokumenten-Verknüpfungen von AIKnowledge nach oben zu Räumungsklage/Mietinkasso
*
* Wenn Dokument mit AIKnowledge verknüpft wird:
* → verknüpfe mit verbundener Räumungsklage/Mietinkasso
* → von dort propagiert es automatisch zu AdvowareAkten (via deren Hooks)
*
* Wenn Dokument von AIKnowledge entknüpft wird:
* → entknüpfe von verbundener Räumungsklage/Mietinkasso
* → von dort propagiert es automatisch von AdvowareAkten (via deren Hooks)
*/
class PropagateDocumentsUp implements AfterRelate, AfterUnrelate
{
private static array $processing = [];
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;
}
// Vermeide Loops
$key = $entity->getId() . '-' . $foreignEntity->getId() . '-relate';
if (isset(self::$processing[$key])) {
return;
}
self::$processing[$key] = true;
try {
// Prüfe ob Räumungsklage verknüpft ist
$raumungsklage = $this->entityManager
->getRDBRepository('CAIKnowledge')
->getRelation($entity, 'vmhRumungsklage')
->findOne();
if ($raumungsklage) {
$this->relateDocument($raumungsklage, 'dokumentesvmhraumungsklage', $foreignEntity);
}
// Prüfe ob Mietinkasso verknüpft ist
$mietinkasso = $this->entityManager
->getRDBRepository('CAIKnowledge')
->getRelation($entity, 'mietinkasso')
->findOne();
if ($mietinkasso) {
$this->relateDocument($mietinkasso, 'dokumentesmietinkasso', $foreignEntity);
}
} catch (\Exception $e) {
$GLOBALS['log']->error('CAIKnowledge PropagateDocumentsUp (relate) Error: ' . $e->getMessage());
} finally {
unset(self::$processing[$key]);
}
}
public function afterUnrelate(
Entity $entity,
string $relationName,
Entity $foreignEntity,
\Espo\ORM\Repository\Option\UnrelateOptions $options
): void {
// Nur für dokumentes-Beziehung
if ($relationName !== 'dokumentes') {
return;
}
// Vermeide Loops
$key = $entity->getId() . '-' . $foreignEntity->getId() . '-unrelate';
if (isset(self::$processing[$key])) {
return;
}
self::$processing[$key] = true;
try {
// Prüfe ob Räumungsklage verknüpft ist
$raumungsklage = $this->entityManager
->getRDBRepository('CAIKnowledge')
->getRelation($entity, 'vmhRumungsklage')
->findOne();
if ($raumungsklage) {
$this->unrelateDocument($raumungsklage, 'dokumentesvmhraumungsklage', $foreignEntity);
}
// Prüfe ob Mietinkasso verknüpft ist
$mietinkasso = $this->entityManager
->getRDBRepository('CAIKnowledge')
->getRelation($entity, 'mietinkasso')
->findOne();
if ($mietinkasso) {
$this->unrelateDocument($mietinkasso, 'dokumentesmietinkasso', $foreignEntity);
}
} catch (\Exception $e) {
$GLOBALS['log']->error('CAIKnowledge PropagateDocumentsUp (unrelate) Error: ' . $e->getMessage());
} finally {
unset(self::$processing[$key]);
}
}
/**
* Hilfsfunktion: Verknüpfe Dokument (nur wenn nicht bereits verknüpft)
*/
private function relateDocument(Entity $parentEntity, string $relationName, Entity $document): void
{
$repository = $this->entityManager->getRDBRepository($parentEntity->getEntityType());
$relation = $repository->getRelation($parentEntity, $relationName);
// Prüfe ob bereits verknüpft
$isRelated = $relation
->where(['id' => $document->getId()])
->findOne();
if (!$isRelated) {
$relation->relate($document);
}
}
/**
* Hilfsfunktion: Entknüpfe Dokument
*/
private function unrelateDocument(Entity $parentEntity, string $relationName, Entity $document): void
{
$repository = $this->entityManager->getRDBRepository($parentEntity->getEntityType());
$relation = $repository->getRelation($parentEntity, $relationName);
// Prüfe ob verknüpft
$isRelated = $relation
->where(['id' => $document->getId()])
->findOne();
if ($isRelated) {
$relation->unrelate($document);
}
}
}

View File

@@ -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');
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -0,0 +1,155 @@
<?php
namespace Espo\Custom\Hooks\CAdvowareAkten;
use Espo\ORM\Entity;
use Espo\Core\Hook\Hook\AfterRelate;
use Espo\Core\Hook\Hook\AfterUnrelate;
/**
* Hook: Propagiert Dokumenten-Verknüpfungen von AdvowareAkten nach oben zu Räumungsklage/Mietinkasso
*
* Wenn Dokument mit AdvowareAkten verknüpft wird:
* → verknüpfe mit verbundener Räumungsklage/Mietinkasso
* → von dort propagiert es automatisch zu AIKnowledge (via deren Hooks)
*
* Wenn Dokument von AdvowareAkten entknüpft wird:
* → entknüpfe von verbundener Räumungsklage/Mietinkasso
* → von dort propagiert es automatisch von AIKnowledge (via deren Hooks)
*/
class PropagateDocumentsUp implements AfterRelate, AfterUnrelate
{
private static array $processing = [];
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;
}
// Vermeide Loops
$key = $entity->getId() . '-' . $foreignEntity->getId() . '-relate';
if (isset(self::$processing[$key])) {
return;
}
self::$processing[$key] = true;
try {
// Prüfe ob Räumungsklage verknüpft ist
$raumungsklage = $this->entityManager
->getRDBRepository('CAdvowareAkten')
->getRelation($entity, 'vmhRumungsklage')
->findOne();
if ($raumungsklage) {
$this->relateDocument($raumungsklage, 'dokumentesvmhraumungsklage', $foreignEntity);
}
// Prüfe ob Mietinkasso verknüpft ist
$mietinkasso = $this->entityManager
->getRDBRepository('CAdvowareAkten')
->getRelation($entity, 'mietinkasso')
->findOne();
if ($mietinkasso) {
$this->relateDocument($mietinkasso, 'dokumentesmietinkasso', $foreignEntity);
}
} catch (\Exception $e) {
$GLOBALS['log']->error('CAdvowareAkten PropagateDocumentsUp (relate) Error: ' . $e->getMessage());
} finally {
unset(self::$processing[$key]);
}
}
public function afterUnrelate(
Entity $entity,
string $relationName,
Entity $foreignEntity,
\Espo\ORM\Repository\Option\UnrelateOptions $options
): void {
// Nur für dokumentes-Beziehung
if ($relationName !== 'dokumentes') {
return;
}
// Vermeide Loops
$key = $entity->getId() . '-' . $foreignEntity->getId() . '-unrelate';
if (isset(self::$processing[$key])) {
return;
}
self::$processing[$key] = true;
try {
// Prüfe ob Räumungsklage verknüpft ist
$raumungsklage = $this->entityManager
->getRDBRepository('CAdvowareAkten')
->getRelation($entity, 'vmhRumungsklage')
->findOne();
if ($raumungsklage) {
$this->unrelateDocument($raumungsklage, 'dokumentesvmhraumungsklage', $foreignEntity);
}
// Prüfe ob Mietinkasso verknüpft ist
$mietinkasso = $this->entityManager
->getRDBRepository('CAdvowareAkten')
->getRelation($entity, 'mietinkasso')
->findOne();
if ($mietinkasso) {
$this->unrelateDocument($mietinkasso, 'dokumentesmietinkasso', $foreignEntity);
}
} catch (\Exception $e) {
$GLOBALS['log']->error('CAdvowareAkten PropagateDocumentsUp (unrelate) Error: ' . $e->getMessage());
} finally {
unset(self::$processing[$key]);
}
}
/**
* Hilfsfunktion: Verknüpfe Dokument (nur wenn nicht bereits verknüpft)
*/
private function relateDocument(Entity $parentEntity, string $relationName, Entity $document): void
{
$repository = $this->entityManager->getRDBRepository($parentEntity->getEntityType());
$relation = $repository->getRelation($parentEntity, $relationName);
// Prüfe ob bereits verknüpft
$isRelated = $relation
->where(['id' => $document->getId()])
->findOne();
if (!$isRelated) {
$relation->relate($document);
}
}
/**
* Hilfsfunktion: Entknüpfe Dokument
*/
private function unrelateDocument(Entity $parentEntity, string $relationName, Entity $document): void
{
$repository = $this->entityManager->getRDBRepository($parentEntity->getEntityType());
$relation = $repository->getRelation($parentEntity, $relationName);
// Prüfe ob verknüpft
$isRelated = $relation
->where(['id' => $document->getId()])
->findOne();
if ($isRelated) {
$relation->unrelate($document);
}
}
}

View File

@@ -35,22 +35,24 @@ class CDokumente extends \Espo\Core\Hooks\Base
return;
}
// Berechne neue Hashes
$newMd5 = hash_file('md5', $filePath);
$newSha256 = hash_file('sha256', $filePath);
// Berechne Blake3 Hash
$fileContent = file_get_contents($filePath);
if ($fileContent === false) {
return;
}
$newBlake3 = \blake3($fileContent);
// Setze Hashes
$entity->set('md5sum', $newMd5);
$entity->set('sha256', $newSha256);
// Setze Hash
$entity->set('blake3hash', $newBlake3);
// Bestimme Status
if ($entity->isNew()) {
$entity->set('fileStatus', 'new');
} else {
$oldMd5 = $entity->getFetched('md5sum');
$oldSha256 = $entity->getFetched('sha256');
$oldBlake3 = $entity->getFetched('blake3hash');
if ($oldMd5 !== $newMd5 || $oldSha256 !== $newSha256) {
if ($oldBlake3 !== $newBlake3) {
$entity->set('fileStatus', 'changed');
} else {
$entity->set('fileStatus', 'synced');

View File

@@ -0,0 +1,155 @@
<?php
namespace Espo\Custom\Hooks\CDokumente;
use Espo\ORM\Entity;
use Espo\ORM\Repository\Option\SaveOptions;
use Espo\Core\Hook\Hook\AfterSave;
/**
* Hook: Bei Änderung eines Dokuments werden alle verknüpften
* AdvowareAkten und AIKnowledge Junction-Table-Einträge auf "unclean" gesetzt
*/
class UpdateJunctionSyncStatus implements AfterSave
{
public function __construct(
private \Espo\ORM\EntityManager $entityManager
) {}
public function afterSave(Entity $entity, SaveOptions $options): void
{
// Überspringe bei Create (nur bei Update)
if ($entity->isNew()) {
return;
}
// Überspringe, wenn keine relevanten Felder geändert wurden
if (!$this->hasRelevantChanges($entity)) {
return;
}
try {
// Update AdvowareAkten Junction-Tables
$this->updateAdvowareAktenJunctions($entity);
// Update AIKnowledge Junction-Tables
$this->updateAIKnowledgeJunctions($entity);
} catch (\Exception $e) {
// Fehler loggen, aber nicht werfen (um Save nicht zu blockieren)
$GLOBALS['log']->error('CDokumente UpdateJunctionSyncStatus Hook Error: ' . $e->getMessage());
}
}
/**
* Prüft ob relevante Felder geändert wurden
*/
private function hasRelevantChanges(Entity $entity): bool
{
// Relevante Felder für Sync-Status
$relevantFields = [
'name',
'description',
'dokument',
'dokumentId',
'preview',
'previewId',
'fileStatus'
];
foreach ($relevantFields as $field) {
if ($entity->isAttributeChanged($field)) {
return true;
}
}
return false;
}
/**
* Update AdvowareAkten Junction-Tables
*/
private function updateAdvowareAktenJunctions(Entity $entity): void
{
$updateQuery = $this->entityManager->getQueryBuilder()
->update()
->in('CAdvowareAktenDokumente')
->set(['syncstatus' => 'unclean'])
->where([
'cDokumenteId' => $entity->getId(),
'deleted' => false
])
->build();
$this->entityManager->getQueryExecutor()->execute($updateQuery);
// Hole alle betroffenen AdvowareAkten IDs
$selectQuery = $this->entityManager->getQueryBuilder()
->select(['cAdvowareAktenId'])
->from('CAdvowareAktenDokumente')
->where([
'cDokumenteId' => $entity->getId(),
'deleted' => false
])
->build();
$pdoStatement = $this->entityManager->getQueryExecutor()->execute($selectQuery);
$rows = $pdoStatement->fetchAll(\PDO::FETCH_ASSOC);
// Trigger Update auf jeder AdvowareAkte (um CheckGlobalSyncStatus Hook auszulösen)
foreach ($rows as $row) {
$aktenId = $row['cAdvowareAktenId'] ?? null;
if ($aktenId) {
$akte = $this->entityManager->getEntity('CAdvowareAkten', $aktenId);
if ($akte) {
// Force Update ohne Hook-Loop
$akte->set('syncStatus', 'unclean');
$this->entityManager->saveEntity($akte);
}
}
}
}
/**
* Update AIKnowledge Junction-Tables
*/
private function updateAIKnowledgeJunctions(Entity $entity): void
{
$updateQuery = $this->entityManager->getQueryBuilder()
->update()
->in('CAIKnowledgeDokumente')
->set(['syncstatus' => 'unclean'])
->where([
'cDokumenteId' => $entity->getId(),
'deleted' => false
])
->build();
$this->entityManager->getQueryExecutor()->execute($updateQuery);
// Hole alle betroffenen AIKnowledge IDs
$selectQuery = $this->entityManager->getQueryBuilder()
->select(['cAIKnowledgeId'])
->from('CAIKnowledgeDokumente')
->where([
'cDokumenteId' => $entity->getId(),
'deleted' => false
])
->build();
$pdoStatement = $this->entityManager->getQueryExecutor()->execute($selectQuery);
$rows = $pdoStatement->fetchAll(\PDO::FETCH_ASSOC);
// Trigger Update auf jeder AIKnowledge (um CheckGlobalSyncStatus Hook auszulösen)
foreach ($rows as $row) {
$knowledgeId = $row['cAIKnowledgeId'] ?? null;
if ($knowledgeId) {
$knowledge = $this->entityManager->getEntity('CAIKnowledge', $knowledgeId);
if ($knowledge) {
// Force Update ohne Hook-Loop
$knowledge->set('syncStatus', 'unclean');
$this->entityManager->saveEntity($knowledge);
}
}
}
}
}

View File

@@ -0,0 +1,154 @@
<?php
namespace Espo\Custom\Hooks\CMietinkasso;
use Espo\ORM\Entity;
use Espo\Core\Hook\Hook\AfterRelate;
use Espo\Core\Hook\Hook\AfterUnrelate;
/**
* Hook: Propagiert Dokumenten-Verknüpfungen von Mietinkasso zu AdvowareAkten und AIKnowledge
*
* - Wenn Dokument mit Mietinkasso verknüpft wird → verknüpfe auch mit AdvowareAkten + AIKnowledge
* - Wenn Dokument von Mietinkasso entknüpft wird → entknüpfe auch von AdvowareAkten + AIKnowledge
*/
class PropagateDocuments implements AfterRelate, AfterUnrelate
{
private static array $processing = [];
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 dokumentesmietinkasso-Beziehung
if ($relationName !== 'dokumentesmietinkasso') {
return;
}
// Vermeide Loops
$key = $entity->getId() . '-' . $foreignEntity->getId() . '-relate';
if (isset(self::$processing[$key])) {
return;
}
self::$processing[$key] = true;
try {
// Hole verbundene AdvowareAkten
$advowareAkten = $this->entityManager
->getRDBRepository('CMietinkasso')
->getRelation($entity, 'advowareAkten')
->findOne();
// Verknüpfe Dokument mit AdvowareAkten
if ($advowareAkten) {
$this->relateDocument($advowareAkten, 'dokumentes', $foreignEntity);
}
// Hole verbundene AIKnowledge
$aIKnowledge = $this->entityManager
->getRDBRepository('CMietinkasso')
->getRelation($entity, 'aIKnowledge')
->findOne();
// Verknüpfe Dokument mit AIKnowledge
if ($aIKnowledge) {
$this->relateDocument($aIKnowledge, 'dokumentes', $foreignEntity);
}
} catch (\Exception $e) {
$GLOBALS['log']->error('CMietinkasso PropagateDocuments (relate) Error: ' . $e->getMessage());
} finally {
unset(self::$processing[$key]);
}
}
public function afterUnrelate(
Entity $entity,
string $relationName,
Entity $foreignEntity,
\Espo\ORM\Repository\Option\UnrelateOptions $options
): void {
// Nur für dokumentesmietinkasso-Beziehung
if ($relationName !== 'dokumentesmietinkasso') {
return;
}
// Vermeide Loops
$key = $entity->getId() . '-' . $foreignEntity->getId() . '-unrelate';
if (isset(self::$processing[$key])) {
return;
}
self::$processing[$key] = true;
try {
// Hole verbundene AdvowareAkten
$advowareAkten = $this->entityManager
->getRDBRepository('CMietinkasso')
->getRelation($entity, 'advowareAkten')
->findOne();
// Entknüpfe Dokument von AdvowareAkten
if ($advowareAkten) {
$this->unrelateDocument($advowareAkten, 'dokumentes', $foreignEntity);
}
// Hole verbundene AIKnowledge
$aIKnowledge = $this->entityManager
->getRDBRepository('CMietinkasso')
->getRelation($entity, 'aIKnowledge')
->findOne();
// Entknüpfe Dokument von AIKnowledge
if ($aIKnowledge) {
$this->unrelateDocument($aIKnowledge, 'dokumentes', $foreignEntity);
}
} catch (\Exception $e) {
$GLOBALS['log']->error('CMietinkasso PropagateDocuments (unrelate) Error: ' . $e->getMessage());
} finally {
unset(self::$processing[$key]);
}
}
/**
* Hilfsfunktion: Verknüpfe Dokument (nur wenn nicht bereits verknüpft)
*/
private function relateDocument(Entity $parentEntity, string $relationName, Entity $document): void
{
$repository = $this->entityManager->getRDBRepository($parentEntity->getEntityType());
$relation = $repository->getRelation($parentEntity, $relationName);
// Prüfe ob bereits verknüpft
$isRelated = $relation
->where(['id' => $document->getId()])
->findOne();
if (!$isRelated) {
$relation->relate($document);
}
}
/**
* Hilfsfunktion: Entknüpfe Dokument
*/
private function unrelateDocument(Entity $parentEntity, string $relationName, Entity $document): void
{
$repository = $this->entityManager->getRDBRepository($parentEntity->getEntityType());
$relation = $repository->getRelation($parentEntity, $relationName);
// Prüfe ob verknüpft
$isRelated = $relation
->where(['id' => $document->getId()])
->findOne();
if ($isRelated) {
$relation->unrelate($document);
}
}
}

View File

@@ -0,0 +1,154 @@
<?php
namespace Espo\Custom\Hooks\CVmhRumungsklage;
use Espo\ORM\Entity;
use Espo\Core\Hook\Hook\AfterRelate;
use Espo\Core\Hook\Hook\AfterUnrelate;
/**
* Hook: Propagiert Dokumenten-Verknüpfungen von Räumungsklage zu AdvowareAkten und AIKnowledge
*
* - Wenn Dokument mit Räumungsklage verknüpft wird → verknüpfe auch mit AdvowareAkten + AIKnowledge
* - Wenn Dokument von Räumungsklage entknüpft wird → entknüpfe auch von AdvowareAkten + AIKnowledge
*/
class PropagateDocuments implements AfterRelate, AfterUnrelate
{
private static array $processing = [];
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 dokumentesvmhraumungsklage-Beziehung
if ($relationName !== 'dokumentesvmhraumungsklage') {
return;
}
// Vermeide Loops
$key = $entity->getId() . '-' . $foreignEntity->getId() . '-relate';
if (isset(self::$processing[$key])) {
return;
}
self::$processing[$key] = true;
try {
// Hole verbundene AdvowareAkten
$advowareAkten = $this->entityManager
->getRDBRepository('CVmhRumungsklage')
->getRelation($entity, 'advowareAkten')
->findOne();
// Verknüpfe Dokument mit AdvowareAkten
if ($advowareAkten) {
$this->relateDocument($advowareAkten, 'dokumentes', $foreignEntity);
}
// Hole verbundene AIKnowledge
$aIKnowledge = $this->entityManager
->getRDBRepository('CVmhRumungsklage')
->getRelation($entity, 'aIKnowledge')
->findOne();
// Verknüpfe Dokument mit AIKnowledge
if ($aIKnowledge) {
$this->relateDocument($aIKnowledge, 'dokumentes', $foreignEntity);
}
} catch (\Exception $e) {
$GLOBALS['log']->error('CVmhRumungsklage PropagateDocuments (relate) Error: ' . $e->getMessage());
} finally {
unset(self::$processing[$key]);
}
}
public function afterUnrelate(
Entity $entity,
string $relationName,
Entity $foreignEntity,
\Espo\ORM\Repository\Option\UnrelateOptions $options
): void {
// Nur für dokumentesvmhraumungsklage-Beziehung
if ($relationName !== 'dokumentesvmhraumungsklage') {
return;
}
// Vermeide Loops
$key = $entity->getId() . '-' . $foreignEntity->getId() . '-unrelate';
if (isset(self::$processing[$key])) {
return;
}
self::$processing[$key] = true;
try {
// Hole verbundene AdvowareAkten
$advowareAkten = $this->entityManager
->getRDBRepository('CVmhRumungsklage')
->getRelation($entity, 'advowareAkten')
->findOne();
// Entknüpfe Dokument von AdvowareAkten
if ($advowareAkten) {
$this->unrelateDocument($advowareAkten, 'dokumentes', $foreignEntity);
}
// Hole verbundene AIKnowledge
$aIKnowledge = $this->entityManager
->getRDBRepository('CVmhRumungsklage')
->getRelation($entity, 'aIKnowledge')
->findOne();
// Entknüpfe Dokument von AIKnowledge
if ($aIKnowledge) {
$this->unrelateDocument($aIKnowledge, 'dokumentes', $foreignEntity);
}
} catch (\Exception $e) {
$GLOBALS['log']->error('CVmhRumungsklage PropagateDocuments (unrelate) Error: ' . $e->getMessage());
} finally {
unset(self::$processing[$key]);
}
}
/**
* Hilfsfunktion: Verknüpfe Dokument (nur wenn nicht bereits verknüpft)
*/
private function relateDocument(Entity $parentEntity, string $relationName, Entity $document): void
{
$repository = $this->entityManager->getRDBRepository($parentEntity->getEntityType());
$relation = $repository->getRelation($parentEntity, $relationName);
// Prüfe ob bereits verknüpft
$isRelated = $relation
->where(['id' => $document->getId()])
->findOne();
if (!$isRelated) {
$relation->relate($document);
}
}
/**
* Hilfsfunktion: Entknüpfe Dokument
*/
private function unrelateDocument(Entity $parentEntity, string $relationName, Entity $document): void
{
$repository = $this->entityManager->getRDBRepository($parentEntity->getEntityType());
$relation = $repository->getRelation($parentEntity, $relationName);
// Prüfe ob verknüpft
$isRelated = $relation
->where(['id' => $document->getId()])
->findOne();
if ($isRelated) {
$relation->unrelate($document);
}
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "إنشاء {الكيانTypeTranslated}"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "الاجتماعات",
"calls": "المكالمات",
"tasks": "مهام"
},
"labels": {
"Create CAdvowareAkten": "إنشاء {الكيانTypeTranslated}"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Създаване на AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Срещи",
"calls": "Разговори",
"tasks": "Задачи"
},
"labels": {
"Create CAdvowareAkten": "Създаване на Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Vytvořit AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Schůzky",
"calls": "Hovory",
"tasks": "Úkoly"
},
"labels": {
"Create CAdvowareAkten": "Vytvořit Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Opret AI Knowledge "
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Møder",
"calls": "Opkald",
"tasks": "Opgaver"
},
"labels": {
"Create CAdvowareAkten": "Opret Advoware Akten "
}
}

View File

@@ -0,0 +1,49 @@
{
"labels": {
"Create CAIKnowledge": "AI Knowledge erstellen"
},
"fields": {
"dokumentes": "Dokumente",
"vmhRumungsklage": "Räumungsklage",
"mietinkasso": "Mietinkasso",
"datenbankId": "Datenbank-ID",
"syncStatus": "Sync-Status",
"lastSync": "Letzte Synchronisation",
"aktivierungsstatus": "Aktivierungsstatus",
"dokumenteAiDocumentId": "AI Document ID",
"dokumenteSyncstatus": "Sync-Status",
"dokumenteLastSync": "Letzter Sync",
"dokumenteSyncedHash": "Sync-Hash"
},
"links": {
"dokumentes": "Dokumente",
"vmhRumungsklage": "Räumungsklage",
"mietinkasso": "Mietinkasso"
},
"options": {
"syncStatus": {
"synced": "Synchronisiert",
"unclean": "Nicht synchronisiert",
"pending_sync": "Synchronisierung ausstehend"
},
"aktivierungsstatus": {
"new": "Neu",
"active": "Aktiv",
"paused": "Pausiert",
"deactivated": "Deaktiviert"
},
"dokumenteSyncstatus": {
"new": "Neu",
"unclean": "Nicht synchronisiert",
"synced": "Synchronisiert",
"failed": "Fehlgeschlagen",
"unsupported": "Nicht unterstützt"
}
},
"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. Wird automatisch basierend auf den Dokumenten-Status aktualisiert.",
"lastSync": "Zeitpunkt der letzten erfolgreichen Synchronisation aller Dokumente",
"aktivierungsstatus": "Aktivierungsstatus des AI Knowledge Entries: new = Neu angelegt, active = Aktiv synchronisiert, paused = Synchronisation pausiert, deactivated = Synchronisation deaktiviert",
"datenbankId": "Eindeutige ID in der AI-Datenbank"
}
}

View File

@@ -0,0 +1,30 @@
{
"fields": {
"cAIKnowledge": "AI Knowledge",
"cDokumente": "Dokument",
"aiDocumentId": "AI Dokument-ID",
"syncstatus": "Sync-Status",
"lastSync": "Letzte Synchronisation",
"syncedHash": "Sync-Hash"
},
"links": {
"cAIKnowledge": "AI Knowledge",
"cDokumente": "Dokument"
},
"labels": {
"Create CAIKnowledgeCDokumente": "Verknüpfung erstellen"
},
"options": {
"syncstatus": {
"new": "Neu",
"unclean": "Geändert",
"synced": "Synchronisiert",
"failed": "Fehler",
"unsupported": "Nicht unterstützt"
}
},
"tooltips": {
"aiDocumentId": "Externe AI-Dokument-Referenz-ID",
"syncedHash": "Hash-Wert des zuletzt synchronisierten Dokument-Zustands"
}
}

View File

@@ -0,0 +1,30 @@
{
"fields": {
"cAIKnowledge": "AI Knowledge",
"cDokumente": "Dokument",
"aiDocumentId": "AI Dokument-ID",
"syncstatus": "Sync-Status",
"lastSync": "Letzte Synchronisation",
"syncedHash": "Sync-Hash"
},
"links": {
"cAIKnowledge": "AI Knowledge",
"cDokumente": "Dokument"
},
"labels": {
"Create CAIKnowledgeCDokumente": "Verknüpfung erstellen"
},
"options": {
"syncstatus": {
"new": "Neu",
"unclean": "Geändert",
"synced": "Synchronisiert",
"failed": "Fehler",
"unsupported": "Nicht unterstützt"
}
},
"tooltips": {
"aiDocumentId": "Externe AI-Dokument-Referenz-ID",
"syncedHash": "Hash-Wert des zuletzt synchronisierten Dokument-Zustands"
}
}

View File

@@ -0,0 +1,46 @@
{
"links": {
"calls": "Anrufe",
"tasks": "Aufgaben",
"vmhRumungsklage": "Räumungsklagen",
"mietinkasso": "Mietinkasso",
"dokumentes": "Dokumente"
},
"labels": {
"Create CAdvowareAkten": "Advoware Akten erstellen"
},
"fields": {
"vmhRumungsklage": "Räumungsklagen",
"mietinkasso": "Mietinkasso",
"aktenzeichen": "Aktenzeichen",
"aktennummer": "Aktennummer",
"aktenpfad": "Aktenpfad (Windows)",
"syncStatus": "Sync-Status",
"lastSync": "Letzte Synchronisation",
"aktivierungsstatus": "Aktivierungsstatus",
"dokumentes": "Dokumente",
"dokumenteHnr": "HNR",
"dokumenteSyncstatus": "Sync-Status",
"dokumenteLastSync": "Letzter Sync",
"dokumenteSyncedHash": "Sync-Hash"
},
"options": {
"syncStatus": {
"synced": "Synchronisiert",
"unclean": "Nicht synchronisiert",
"pending_sync": "Synchronisierung ausstehend"
},
"aktivierungsstatus": {
"new": "Neu",
"active": "Aktiv",
"paused": "Pausiert",
"deactivated": "Deaktiviert"
}
},
"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. Wird automatisch basierend auf den Dokumenten-Status aktualisiert.",
"lastSync": "Zeitpunkt der letzten erfolgreichen Synchronisation aller Dokumente",
"aktivierungsstatus": "Aktivierungsstatus der Akte: new = Neu angelegt, active = Aktiv synchronisiert, paused = Synchronisation pausiert, deactivated = Synchronisation deaktiviert",
"aktenpfad": "Windows-Dateipfad zur Akte in Advoware"
}
}

View File

@@ -0,0 +1,33 @@
{
"labels": {
"Create CAdvowareAktenCDokumente": "Advoware-Dokument-Verknüpfung erstellen",
"CAdvowareAktenCDokumente": "Advoware-Dokument-Verknüpfungen"
},
"fields": {
"cAdvowareAkten": "Advoware-Akte",
"cAdvowareAktenId": "Advoware-Akte ID",
"cDokumente": "Dokument",
"cDokumenteId": "Dokument ID",
"hnr": "HNR",
"syncStatus": "Sync-Status",
"syncedHash": "Sync-Hash",
"deleted": "Gelöscht"
},
"links": {
"cAdvowareAkten": "Advoware-Akte",
"cDokumente": "Dokument"
},
"options": {
"syncStatus": {
"new": "Neu",
"changed": "Geändert",
"synced": "Synchronisiert",
"deleted": "Gelöscht"
}
},
"tooltips": {
"hnr": "Advoware HNR Referenz für dieses Dokument",
"syncStatus": "Synchronisierungsstatus mit Advoware",
"syncedHash": "Hash-Wert des zuletzt synchronisierten Dokument-Zustands (zur Änderungserkennung)"
}
}

View File

@@ -2,16 +2,7 @@
"fields": {
"dokument": "Download",
"preview": "Vorschau",
"ydocumentuuid": "Y-Document-UUID",
"md5sum": "MD5-Prüfsumme",
"sha256": "SHA256-Prüfsumme",
"aktennr": "Advoware Identifikator",
"advowareLastSync": "Advoware letzte Synchronisation",
"syncStatus": "Sync-Status",
"xaiId": "x.AI ID",
"xaiCollections": "x.AI Collections",
"xaiSyncStatus": "Sync-Status",
"fileStatus": "Datei-Status",
"blake3hash": "Blake3-Hash",
"contactsvmhdokumente": "Freigegebene Nutzer",
"vmhMietverhltnisesDokumente": "Mietverhältnisse",
"vmhErstgespraechsdokumente": "Erstgespräche",
@@ -20,7 +11,15 @@
"beteiligte2dokumente": "Beteiligte",
"mietobjekt2dokumente": "Mietobjekte",
"mietinkassosdokumente": "Mietinkasso",
"kndigungensdokumente": "Kündigungen"
"kndigungensdokumente": "Kündigungen",
"advowareAktens": "Advoware Akten",
"aIKnowledges": "AI Knowledge",
"advowareAktenHnr": "Advoware HNR",
"advowareAktenSyncstatus": "Advoware Sync-Status",
"advowareAktenLastSync": "Advoware Letzter Sync",
"aiKnowledgeAiDocumentId": "AI Document ID",
"aiKnowledgeSyncstatus": "AI Sync-Status",
"aiKnowledgeLastSync": "AI Letzter Sync"
},
"links": {
"contactsvmhdokumente": "Freigegebene Nutzer",
@@ -31,39 +30,14 @@
"beteiligte2dokumente": "Beteiligte",
"mietobjekt2dokumente": "Mietobjekte",
"mietinkassosdokumente": "Mietinkasso",
"kndigungensdokumente": "Kündigungen"
"kndigungensdokumente": "Kündigungen",
"advowareAktens": "Advoware Akten",
"aIKnowledges": "AI Knowledge"
},
"labels": {
"Create CDokumente": "Dokument erstellen"
},
"tooltips": {
"aktennr": "Eindeutige Dokument-Nummer aus Advoware",
"advowareLastSync": "Zeitpunkt der letzten Synchronisation mit Advoware",
"syncStatus": "Status der Advoware-Synchronisation: pending_sync = Warte auf Sync, clean = erfolgreich synchronisiert, unclean = Änderungen ausstehend, failed = Fehler, no_sync = Nicht synchronisiert",
"xaiId": "Eindeutige ID für x.AI Synchronisation",
"xaiCollections": "Liste der x.AI Collections für dieses Dokument",
"xaiSyncStatus": "Status der x.AI Synchronisation: pending_sync = Warte auf Sync, clean = erfolgreich synchronisiert, unclean = Änderungen ausstehend, failed = Fehler, no_sync = Nicht synchronisiert",
"fileStatus": "Status der Datei: new = neu hochgeladen, changed = geändert, synced = synchronisiert"
},
"options": {
"syncStatus": {
"pending_sync": "Warte auf Sync",
"clean": "Synchronisiert",
"unclean": "Änderungen ausstehend",
"failed": "Fehlgeschlagen",
"no_sync": "Kein Sync"
},
"xaiSyncStatus": {
"pending_sync": "Warte auf Sync",
"clean": "Synchronisiert",
"unclean": "Abweichungen",
"failed": "Fehlgeschlagen",
"no_sync": "Kein Sync"
},
"fileStatus": {
"new": "Neu",
"changed": "Geändert",
"synced": "Synchronisiert"
}
"blake3hash": "Kryptografischer Blake3-Hash der Datei (schneller und sicherer als MD5/SHA256)"
}
}

View File

@@ -1,54 +1,42 @@
{
"fields": {
"advowareAktenzeichen": "Advoware Aktenzeichen",
"aktennr": "Advoware Identifikator",
"advowareLastSync": "Advoware letzte Synchronisation",
"syncStatus": "Sync-Status",
"klaeger": "Kläger",
"beklagte": "Beklagte",
"vmhMietverhltnises": "Mietverhältnisse",
"contactsMietinkasso": "Freigegebene Nutzer",
"dokumentesmietinkasso": "Dokumente",
"gerichtsrubrum": "Gerichtsrubrum",
"gegenstandswert": "Gegenstandswert",
"kuendigungsservice": "Kündigungsservice",
"aussergerichtlicheGebuehren13": "Außergerichtliche Gebühren 1,3",
"gerichtskosten1Instanz": "Gerichtskosten 1. Instanz",
"anwaltskosten1Instanz": "Anwaltskosten 1. Instanz",
"freigeschalteteNutzer": "Freigeschaltete Nutzer (veraltet)",
"collaborators": "Mitarbeiter",
"vmhVermietersMIK": "Vermieter"
},
"links": {
"meetings": "Termine",
"calls": "Anrufe",
"tasks": "Aufgaben",
"klaeger": "Kläger",
"beklagte": "Beklagte",
"vmhMietverhltnises": "Mietverhältnisse",
"contactsMietinkasso": "Freigegebene Nutzer",
"dokumentesmietinkasso": "Dokumente",
"freigeschalteteNutzer": "Freigeschaltete Nutzer (veraltet)",
"collaborators": "Mitarbeiter",
"vmhVermietersMIK": "Vermieter",
"pulse": "Pulse"
},
"labels": {
"Create CMietinkasso": "Mietinkasso erstellen"
},
"tooltips": {
"advowareAktenzeichen": "Aktenzeichen aus dem Advoware-System",
"aktennr": "Eindeutige Inkasso-Nummer aus Advoware",
"advowareLastSync": "Zeitpunkt der letzten Synchronisation mit Advoware",
"syncStatus": "Status der Advoware-Synchronisation: pending_sync = Warte auf Sync, clean = erfolgreich synchronisiert, unclean = Änderungen ausstehend, failed = Fehler, no_sync = Nicht synchronisiert"
},
"options": {
"syncStatus": {
"pending_sync": "Warte auf Sync",
"clean": "Synchronisiert",
"unclean": "Änderungen ausstehend",
"failed": "Fehlgeschlagen",
"no_sync": "Kein Sync"
"fields": {
"klaeger": "Kläger",
"beklagte": "Beklagte",
"vmhMietverhltnises": "Mietverhältnisse",
"contactsMietinkasso": "Freigegebene Nutzer",
"dokumentesmietinkasso": "Dokumente",
"gegenstandswert": "Gegenstandswert",
"kuendigungsservice": "Kündigungsservice",
"aussergerichtlicheGebuehren13": "Außergerichtliche Gebühren 1,3",
"gerichtskosten1Instanz": "Gerichtskosten 1. Instanz",
"anwaltskosten1Instanz": "Anwaltskosten 1. Instanz",
"freigeschalteteNutzer": "Freigeschaltete Nutzer (veraltet)",
"collaborators": "Mitarbeiter",
"vmhVermietersMIK": "Vermieter",
"advowareAkten": "Advoware Akten",
"aIKnowledge": "AI Knowledge"
},
"links": {
"meetings": "Termine",
"calls": "Anrufe",
"tasks": "Aufgaben",
"klaeger": "Kläger",
"beklagte": "Beklagte",
"vmhMietverhltnises": "Mietverhältnisse",
"contactsMietinkasso": "Freigegebene Nutzer",
"dokumentesmietinkasso": "Dokumente",
"freigeschalteteNutzer": "Freigeschaltete Nutzer (veraltet)",
"collaborators": "Mitarbeiter",
"vmhVermietersMIK": "Vermieter",
"pulse": "Pulse",
"advowareAkten": "Advoware Akten",
"aIKnowledge": "AI Knowledge"
},
"labels": {
"Create CMietinkasso": "Mietinkasso erstellen"
},
"tooltips": {
"gegenstandswert": "Wert des Streitgegenstands",
"kuendigungsservice": "Kündigungsservice aktiviert"
}
}
}

View File

@@ -0,0 +1,27 @@
{
"labels": {
"Create CPulsTeamZuordnung": "Puls-Team-Zuordnung erstellen",
"CPulsTeamZuordnung": "Puls-Team-Zuordnungen"
},
"fields": {
"puls": "Puls",
"pulsId": "Puls ID",
"team": "Team",
"teamId": "Team ID",
"aktiv": "Aktiv",
"abgeschlossen": "Abgeschlossen",
"prioritaet": "Priorität"
},
"links": {
"puls": "Puls",
"team": "Team"
},
"options": {
"prioritaet": {
"Niedrig": "Niedrig",
"Normal": "Normal",
"Hoch": "Hoch",
"Dringend": "Dringend"
}
}
}

View File

@@ -1,57 +1,45 @@
{
"fields": {
"portalUser": "Portalnutzer",
"advowareAktenzeichen": "Advoware Aktenzeichen",
"aktennr": "Advoware Identifikator",
"advowareLastSync": "Advoware letzte Synchronisation",
"syncStatus": "Sync-Status",
"xaiCollectionId": "x.AI Collection ID",
"klaeger": "Kläger",
"beklagte": "Beklagte",
"vmhMietverhltnises": "Mietverhältnisse",
"contactsRumungsklage": "Freigegebene Nutzer",
"dokumentesvmhraumungsklage": "Dokumente",
"gerichtsrubrum": "Gerichtsrubrum",
"gegenstandswert": "Gegenstandswert",
"kuendigungsservice": "Kündigungsservice",
"aussergerichtlicheGebuehren13": "Außergerichtliche Gebühren 1,3",
"gerichtskosten1Instanz": "Gerichtskosten 1. Instanz",
"anwaltskosten1Instanz": "Anwaltskosten 1. Instanz",
"freigeschalteteNutzer": "Freigeschaltete Nutzer (veraltet)",
"collaborators": "Mitarbeiter",
"vmhVermietersRKL": "Vermieter"
},
"links": {
"meetings": "Termine",
"calls": "Anrufe",
"tasks": "Aufgaben",
"klaeger": "Kläger",
"beklagte": "Beklagte",
"vmhMietverhltnises": "Mietverhältnisse",
"contactsRumungsklage": "Freigegebene Nutzer",
"dokumentesvmhraumungsklage": "Dokumente",
"freigeschalteteNutzer": "Freigeschaltete Nutzer (veraltet)",
"collaborators": "Mitarbeiter",
"vmhVermietersRKL": "Vermieter",
"pulse": "Pulse"
},
"labels": {
"Create CVmhRumungsklage": "Räumungsklage erstellen"
},
"tooltips": {
"advowareAktenzeichen": "Aktenzeichen aus dem Advoware-System",
"aktennr": "Eindeutige Klage-Nummer aus Advoware",
"advowareLastSync": "Zeitpunkt der letzten Synchronisation mit Advoware",
"syncStatus": "Status der Advoware-Synchronisation: pending_sync = Warte auf Sync, clean = erfolgreich synchronisiert, unclean = Änderungen ausstehend, failed = Fehler, no_sync = Nicht synchronisiert",
"xaiCollectionId": "Collection ID für x.AI Synchronisation"
},
"options": {
"syncStatus": {
"pending_sync": "Warte auf Sync",
"clean": "Synchronisiert",
"unclean": "Änderungen ausstehend",
"failed": "Fehlgeschlagen",
"no_sync": "Kein Sync"
"fields": {
"portalUser": "Portalnutzer",
"klaeger": "Kläger",
"beklagte": "Beklagte",
"vmhMietverhltnises": "Mietverhältnisse",
"contactsRumungsklage": "Freigegebene Nutzer",
"dokumentesvmhraumungsklage": "Dokumente",
"gerichtsrubrum": "Gerichtsrubrum",
"gegenstandswert": "Gegenstandswert",
"kuendigungsservice": "Kündigungsservice",
"aussergerichtlicheGebuehren13": "Außergerichtliche Gebühren 1,3",
"gerichtskosten1Instanz": "Gerichtskosten 1. Instanz",
"anwaltskosten1Instanz": "Anwaltskosten 1. Instanz",
"freigeschalteteNutzer": "Freigeschaltete Nutzer (veraltet)",
"collaborators": "Mitarbeiter",
"vmhVermietersRKL": "Vermieter",
"advowareAkten": "Advoware Akten",
"aIKnowledge": "AI Knowledge"
},
"links": {
"meetings": "Termine",
"calls": "Anrufe",
"tasks": "Aufgaben",
"klaeger": "Kläger",
"beklagte": "Beklagte",
"vmhMietverhltnises": "Mietverhältnisse",
"contactsRumungsklage": "Freigegebene Nutzer",
"dokumentesvmhraumungsklage": "Dokumente",
"freigeschalteteNutzer": "Freigeschaltete Nutzer (veraltet)",
"collaborators": "Mitarbeiter",
"vmhVermietersRKL": "Vermieter",
"pulse": "Pulse",
"advowareAkten": "Advoware Akten",
"aIKnowledge": "AI Knowledge"
},
"labels": {
"Create CVmhRumungsklage": "Räumungsklage erstellen"
},
"tooltips": {
"gerichtsrubrum": "Rubrum des Gerichtsverfahrens",
"gegenstandswert": "Wert des Streitgegenstands",
"kuendigungsservice": "Kündigungsservice aktiviert"
}
}
}

View File

@@ -4,5 +4,8 @@
},
"labels": {
"Log": "Log"
},
"scopeNamesPlural": {
"CAdvowareAkten": "Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Δημιουργία AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Συναντήσεις",
"calls": "Κλήσεις",
"tasks": "Εργασίες"
},
"labels": {
"Create CAdvowareAkten": "Δημιουργία Advoware Akten"
}
}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1,49 @@
{
"fields": {
"dokumentes": "Dokumente",
"vmhRumungsklage": "Räumungsklage",
"mietinkasso": "Mietinkasso",
"datenbankId": "Database ID",
"syncStatus": "Sync Status",
"lastSync": "Last Synchronization",
"aktivierungsstatus": "Activation Status",
"dokumenteAiDocumentId": "AI Document ID",
"dokumenteSyncstatus": "Sync Status",
"dokumenteLastSync": "Last Sync",
"dokumenteSyncedHash": "Sync Hash"
},
"links": {
"dokumentes": "Dokumente",
"vmhRumungsklage": "Räumungsklage",
"mietinkasso": "Mietinkasso"
},
"labels": {
"Create CAIKnowledge": "Create AI Knowledge"
},
"options": {
"syncStatus": {
"synced": "Synchronized",
"unclean": "Not Synchronized",
"pending_sync": "Synchronization Pending"
},
"aktivierungsstatus": {
"new": "New",
"active": "Active",
"paused": "Paused",
"deactivated": "Deactivated"
},
"dokumenteSyncstatus": {
"new": "New",
"unclean": "Not Synchronized",
"synced": "Synchronized",
"failed": "Failed",
"unsupported": "Unsupported"
}
},
"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. Updated automatically based on document status.",
"lastSync": "Timestamp of the last successful synchronization of all documents",
"aktivierungsstatus": "Activation status of the AI Knowledge entry: new = Newly created, active = Actively synchronized, paused = Synchronization paused, deactivated = Synchronization deactivated",
"datenbankId": "Unique ID in the AI database"
}
}

View File

@@ -0,0 +1,30 @@
{
"fields": {
"cAIKnowledge": "AI Knowledge",
"cDokumente": "Document",
"aiDocumentId": "AI Document ID",
"syncstatus": "Sync Status",
"lastSync": "Last Sync",
"syncedHash": "Synced Hash"
},
"links": {
"cAIKnowledge": "AI Knowledge",
"cDokumente": "Document"
},
"labels": {
"Create CAIKnowledgeCDokumente": "Create Link"
},
"options": {
"syncstatus": {
"new": "New",
"unclean": "Changed",
"synced": "Synced",
"failed": "Failed",
"unsupported": "Unsupported"
}
},
"tooltips": {
"aiDocumentId": "External AI document reference ID",
"syncedHash": "Hash value of last synced document state"
}
}

View File

@@ -0,0 +1,30 @@
{
"fields": {
"cAIKnowledge": "AI Knowledge",
"cDokumente": "Document",
"aiDocumentId": "AI Document ID",
"syncstatus": "Sync Status",
"lastSync": "Last Sync",
"syncedHash": "Synced Hash"
},
"links": {
"cAIKnowledge": "AI Knowledge",
"cDokumente": "Document"
},
"labels": {
"Create CAIKnowledgeCDokumente": "Create Link"
},
"options": {
"syncstatus": {
"new": "New",
"unclean": "Changed",
"synced": "Synced",
"failed": "Failed",
"unsupported": "Unsupported"
}
},
"tooltips": {
"aiDocumentId": "External AI document reference ID",
"syncedHash": "Hash value of last synced document state"
}
}

View File

@@ -0,0 +1,47 @@
{
"fields": {
"vmhRumungsklage": "Räumungsklagen",
"mietinkasso": "Mietinkasso",
"aktenzeichen": "Aktenzeichen",
"aktennummer": "Aktennummer",
"aktenpfad": "File Path (Windows)",
"syncStatus": "Sync Status",
"lastSync": "Last Synchronization",
"aktivierungsstatus": "Activation Status",
"dokumentes": "Dokumente",
"dokumenteHnr": "HNR",
"dokumenteSyncstatus": "Sync Status",
"dokumenteLastSync": "Last Sync",
"dokumenteSyncedHash": "Sync Hash"
},
"links": {
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks",
"vmhRumungsklage": "Räumungsklagen",
"mietinkasso": "Mietinkasso",
"dokumentes": "Dokumente"
},
"labels": {
"Create CAdvowareAkten": "Create Advoware Akten"
},
"options": {
"syncStatus": {
"synced": "Synchronized",
"unclean": "Not Synchronized",
"pending_sync": "Synchronization Pending"
},
"aktivierungsstatus": {
"new": "New",
"active": "Active",
"paused": "Paused",
"deactivated": "Deactivated"
}
},
"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. 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, active = Actively synchronized, paused = Synchronization paused, deactivated = Synchronization deactivated",
"aktenpfad": "Windows file path to the file in Advoware"
}
}

View File

@@ -0,0 +1,33 @@
{
"labels": {
"Create CAdvowareAktenCDokumente": "Create Advoware Document Link",
"CAdvowareAktenCDokumente": "Advoware Document Links"
},
"fields": {
"cAdvowareAkten": "Advoware File",
"cAdvowareAktenId": "Advoware File ID",
"cDokumente": "Document",
"cDokumenteId": "Document ID",
"hnr": "HNR",
"syncStatus": "Sync Status",
"syncedHash": "Sync Hash",
"deleted": "Deleted"
},
"links": {
"cAdvowareAkten": "Advoware File",
"cDokumente": "Document"
},
"options": {
"syncStatus": {
"new": "New",
"changed": "Changed",
"synced": "Synced",
"deleted": "Deleted"
}
},
"tooltips": {
"hnr": "Advoware HNR reference for this document",
"syncStatus": "Synchronization status with Advoware",
"syncedHash": "Hash value of the last synchronized document state (for change detection)"
}
}

View File

@@ -1,26 +1,25 @@
{
"fields": {
"dokument": "Download",
"ydocumentuuid": "Y-Document-UUID",
"preview": "Preview",
"contactsvmhdokumente": "Portal Users",
"vmhMietverhltnisesDokumente": "Tenancies",
"vmhErstgespraechsdokumente": "Initial Consultations",
"vmhRumungsklagesdokumente": "Eviction Lawsuits",
"kuendigungDokumente": "Terminations",
"md5sum": "MD5 Checksum",
"sha256": "SHA256 Checksum",
"blake3hash": "Blake3 Hash",
"beteiligte2dokumente": "Parties",
"mietobjekt2dokumente": "Properties",
"mietinkassosdokumente": "Rent Collection",
"kndigungensdokumente": "Terminations",
"aktennr": "Advoware Identifier",
"advowareLastSync": "Advoware Last Sync",
"syncStatus": "Sync Status",
"xaiId": "x.AI ID",
"xaiCollections": "x.AI Collections",
"xaiSyncStatus": "Sync Status",
"fileStatus": "File Status"
"advowareAktens": "Advoware Akten",
"aIKnowledges": "AI Knowledge",
"advowareAktenHnr": "Advoware HNR",
"advowareAktenSyncstatus": "Advoware Sync Status",
"advowareAktenLastSync": "Advoware Last Sync",
"aiKnowledgeAiDocumentId": "AI Document ID",
"aiKnowledgeSyncstatus": "AI Sync Status",
"aiKnowledgeLastSync": "AI Last Sync"
},
"links": {
"contactsvmhdokumente": "Portal Users",
@@ -31,42 +30,19 @@
"beteiligte2dokumente": "Parties",
"mietobjekt2dokumente": "Properties",
"mietinkassosdokumente": "Rent Collection",
"kndigungensdokumente": "Terminations"
"kndigungensdokumente": "Terminations",
"advowareAktens": "Advoware Akten",
"aIKnowledges": "AI Knowledge"
},
"labels": {
"Create CDokumente": "Create Dokument"
},
"layouts": {
"listRaeumungsKl": "List (RaeumungsKl)"
"listRaeumungsKl": "List (RaeumungsKl)",
"listForAdvowareAkten": "List for Advoware Akten",
"listForAIKnowledge": "List for AI Knowledge"
},
"tooltips": {
"aktennr": "Unique document number from Advoware",
"advowareLastSync": "Time of last synchronization with Advoware",
"syncStatus": "Advoware synchronization status: pending_sync = Waiting for sync, clean = successfully synchronized, unclean = changes pending, failed = error, no_sync = Not synchronized",
"xaiId": "Unique ID for x.AI synchronization",
"xaiCollections": "List of x.AI collections for this document",
"xaiSyncStatus": "x.AI synchronization status: pending_sync = Waiting for sync, clean = successfully synchronized, unclean = changes pending, failed = error, no_sync = Not synchronized",
"fileStatus": "File status: new = newly uploaded, changed = modified, synced = synchronized"
},
"options": {
"syncStatus": {
"pending_sync": "Waiting for Sync",
"clean": "Synchronized",
"unclean": "Changes Pending",
"failed": "Failed",
"no_sync": "No Sync"
},
"xaiSyncStatus": {
"pending_sync": "Waiting for Sync",
"clean": "Synchronized",
"unclean": "Changes Pending",
"failed": "Failed",
"no_sync": "No Sync"
},
"fileStatus": {
"new": "New",
"changed": "Changed",
"synced": "Synchronized"
}
"blake3hash": "Cryptographic Blake3 hash of the file (faster and more secure than MD5/SHA256)"
}
}

View File

@@ -1,54 +1,42 @@
{
"fields": {
"advowareAktenzeichen": "Advoware Case Number",
"aktennr": "Advoware Identifier",
"advowareLastSync": "Advoware Last Sync",
"syncStatus": "Sync Status",
"klaeger": "Plaintiff",
"beklagte": "Defendant",
"vmhMietverhltnises": "Tenancies",
"contactsMietinkasso": "Portal Users",
"dokumentesmietinkasso": "Documents",
"gerichtsrubrum": "Court Rubrum",
"gegenstandswert": "Claim Value",
"kuendigungsservice": "Termination Service",
"aussergerichtlicheGebuehren13": "Out-of-court Fees 1.3",
"gerichtskosten1Instanz": "Court Costs 1st Instance",
"anwaltskosten1Instanz": "Attorney Fees 1st Instance",
"freigeschalteteNutzer": "Activated Users (deprecated)",
"collaborators": "Collaborators",
"vmhVermietersMIK": "Landlord"
},
"links": {
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks",
"klaeger": "Plaintiff",
"beklagte": "Defendant",
"vmhMietverhltnises": "Tenancies",
"contactsMietinkasso": "Portal Users",
"dokumentesmietinkasso": "Documents",
"freigeschalteteNutzer": "Activated Users (deprecated)",
"collaborators": "Collaborators",
"vmhVermietersMIK": "Landlord",
"pulse": "Pulses"
},
"labels": {
"Create CMietinkasso": "Create Mietinkasso"
},
"tooltips": {
"advowareAktenzeichen": "Case number from Advoware system",
"aktennr": "Unique collection number from Advoware",
"advowareLastSync": "Time of last synchronization with Advoware",
"syncStatus": "Advoware synchronization status: pending_sync = Waiting for sync, clean = successfully synchronized, unclean = changes pending, failed = error, no_sync = Not synchronized"
},
"options": {
"syncStatus": {
"pending_sync": "Waiting for Sync",
"clean": "Synchronized",
"unclean": "Changes Pending",
"failed": "Failed",
"no_sync": "No Sync"
}
}
}
{
"fields": {
"klaeger": "Plaintiff",
"beklagte": "Defendant",
"vmhMietverhltnises": "Tenancies",
"contactsMietinkasso": "Portal Users",
"dokumentesmietinkasso": "Documents",
"gegenstandswert": "Claim Value",
"kuendigungsservice": "Termination Service",
"aussergerichtlicheGebuehren13": "Out-of-court Fees 1.3",
"gerichtskosten1Instanz": "Court Costs 1st Instance",
"anwaltskosten1Instanz": "Attorney Fees 1st Instance",
"freigeschalteteNutzer": "Activated Users (deprecated)",
"collaborators": "Collaborators",
"vmhVermietersMIK": "Landlord",
"advowareAkten": "Advoware Akten",
"aIKnowledge": "AI Knowledge"
},
"links": {
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks",
"klaeger": "Plaintiff",
"beklagte": "Defendant",
"vmhMietverhltnises": "Tenancies",
"contactsMietinkasso": "Portal Users",
"dokumentesmietinkasso": "Documents",
"freigeschalteteNutzer": "Activated Users (deprecated)",
"collaborators": "Collaborators",
"vmhVermietersMIK": "Landlord",
"pulse": "Pulses",
"advowareAkten": "Advoware Akten",
"aIKnowledge": "AI Knowledge"
},
"labels": {
"Create CMietinkasso": "Create Mietinkasso"
},
"tooltips": {
"gegenstandswert": "Value of the disputed matter",
"kuendigungsservice": "Termination service enabled"
}
}

View File

@@ -0,0 +1,27 @@
{
"labels": {
"Create CPulsTeamZuordnung": "Create Puls Team Assignment",
"CPulsTeamZuordnung": "Puls Team Assignments"
},
"fields": {
"puls": "Puls",
"pulsId": "Puls ID",
"team": "Team",
"teamId": "Team ID",
"aktiv": "Active",
"abgeschlossen": "Completed",
"prioritaet": "Priority"
},
"links": {
"puls": "Puls",
"team": "Team"
},
"options": {
"prioritaet": {
"Niedrig": "Low",
"Normal": "Normal",
"Hoch": "High",
"Dringend": "Urgent"
}
}
}

View File

@@ -12,7 +12,9 @@
"kuendigungsservice": "Termination Service",
"aussergerichtlicheGebuehren13": "Out-of-Court Fees 1.3",
"gerichtskosten1Instanz": "Court Costs 1st Instance",
"anwaltskosten1Instanz": "Attorney Fees 1st Instance"
"anwaltskosten1Instanz": "Attorney Fees 1st Instance",
"advowareAkten": "Advoware Akten",
"aIKnowledge": "AI Knowledge"
},
"links": {
"meetings": "Meetings",
@@ -26,21 +28,16 @@
"beklagte": "Defendant",
"klaeger": "Plaintiff",
"contactsRumungsklage": "Portal Users",
"pulse": "Pulses"
"pulse": "Pulses",
"advowareAkten": "Advoware Akten",
"aIKnowledge": "AI Knowledge"
},
"labels": {
"Create CVmhRumungsklage": "Create Räumungsklage"
},
"tooltips": {
"syncStatus": "Advoware synchronization status: pending_sync = Waiting for sync, clean = successfully synchronized, unclean = changes pending, failed = error, no_sync = Not synchronized"
},
"options": {
"syncStatus": {
"pending_sync": "Waiting for Sync",
"clean": "Synchronized",
"unclean": "Changes Pending",
"failed": "Failed",
"no_sync": "No Sync"
}
"gerichtsrubrum": "Court proceeding rubrum",
"gegenstandswert": "Value of the disputed matter",
"kuendigungsservice": "Termination service enabled"
}
}

View File

@@ -14,7 +14,9 @@
"CBankverbindungen": "Bankverbindung",
"CMietinkasso": "Mietinkasso",
"CKuendigung": "Kündigung",
"CPuls": "Puls"
"CPuls": "Puls",
"CAdvowareAkten": "Advoware Akten",
"CAIKnowledge": "AI Knowledge"
},
"scopeNamesPlural": {
"CVmhMietverhltnis": "Mietverhältnisse",
@@ -31,6 +33,8 @@
"CBankverbindungen": "Bankverbindungen",
"CMietinkasso": "Mietinkassa",
"CKuendigung": "Kündigungen",
"CPuls": "Pulse"
"CPuls": "Pulse",
"CAdvowareAkten": "Advoware Akten",
"CAIKnowledge": "AI Knowledge"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Crear AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Reuniones",
"calls": "Llamadas",
"tasks": "Tareas"
},
"labels": {
"Create CAdvowareAkten": "Crear Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Crear AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Presentaciones",
"calls": "Llamadas",
"tasks": "Tareas"
},
"labels": {
"Create CAdvowareAkten": "Crear Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "ایجاد AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "جلسات",
"calls": "تماس ها",
"tasks": "وظایف"
},
"labels": {
"Create CAdvowareAkten": "ایجاد Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Créer un AI Knowledge"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAdvowareAkten": "Créer un Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Napravi AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Sastanci",
"calls": "Pozivi",
"tasks": "Zadaci"
},
"labels": {
"Create CAdvowareAkten": "Kreiraj Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "{EntityTypeTranslated} létrehozása"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "találkozók",
"calls": "felhívja",
"tasks": "Feladatok"
},
"labels": {
"Create CAdvowareAkten": "{EntityTypeTranslated} létrehozása"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Buat AI Knowledge"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAdvowareAkten": "Buat Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Crea AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Riunioni",
"calls": "Chiamate",
"tasks": "Compiti"
},
"labels": {
"Create CAdvowareAkten": "Crea Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "AI Knowledge を作成する"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "会議",
"calls": "通話",
"tasks": "タスク"
},
"labels": {
"Create CAdvowareAkten": "Advoware Akten を作成する"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Sukurti AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Susitikimai",
"calls": "Skambučiai",
"tasks": "Užduotys"
},
"labels": {
"Create CAdvowareAkten": "Sukurti Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Izveidot AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Tikšanās",
"calls": "Zvani",
"tasks": "Uzdevumi"
},
"labels": {
"Create CAdvowareAkten": "Izveidot Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Opprett AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Møter",
"calls": "Samtaler",
"tasks": "Oppgaver"
},
"labels": {
"Create CAdvowareAkten": "Opprett Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Creëer AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Vergaderingen",
"calls": "Gesprekken",
"tasks": "Taken"
},
"labels": {
"Create CAdvowareAkten": "Creëer Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Utwórz AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Spotkania",
"calls": "Połączenia",
"tasks": "Zadania"
},
"labels": {
"Create CAdvowareAkten": "Utwórz Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Criar AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Reuniões",
"calls": "Ligações",
"tasks": "Tarefas"
},
"labels": {
"Create CAdvowareAkten": "Criar Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Criar AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Reuniões",
"calls": "Chamadas",
"tasks": "Tarefas"
},
"labels": {
"Create CAdvowareAkten": "Criar Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Creare AI Knowledge"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAdvowareAkten": "Creare Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Создать AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Встречи",
"calls": "Звонки",
"tasks": "Задачи"
},
"labels": {
"Create CAdvowareAkten": "Создать Advoware Akten"
}
}

View File

@@ -0,0 +1,5 @@
{
"labels": {
"Create CAIKnowledge": "Vytvoriť AI Knowledge"
}
}

View File

@@ -0,0 +1,10 @@
{
"links": {
"meetings": "Stretnutia",
"calls": "Hovory",
"tasks": "Úlohy"
},
"labels": {
"Create CAdvowareAkten": "Vytvoriť Advoware Akten"
}
}

Some files were not shown because too many files have changed in this diff Show More