Add AdvowareAkte and AIKnowledge creation for Kündigungen; synchronize between Kündigungen and Räumungsklagen; update localization and metadata files
This commit is contained in:
148
custom/Espo/Custom/Hooks/CKuendigung/CreateAdvowareAkte.php
Normal file
148
custom/Espo/Custom/Hooks/CKuendigung/CreateAdvowareAkte.php
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
<?php
|
||||||
|
namespace Espo\Custom\Hooks\CKuendigung;
|
||||||
|
|
||||||
|
use Espo\ORM\Entity;
|
||||||
|
use Espo\Core\Hook\Hook\AfterSave;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook: Erstellt automatisch AdvowareAkte für Kündigung
|
||||||
|
*
|
||||||
|
* Wenn eine Kündigung erstellt/gespeichert wird:
|
||||||
|
* - Prüfe ob bereits eine AdvowareAkte vorhanden ist (über verknüpfte Räumungsklage)
|
||||||
|
* - Wenn nein: Erstelle neue AdvowareAkte und verknüpfe sie
|
||||||
|
*/
|
||||||
|
class CreateAdvowareAkte implements AfterSave
|
||||||
|
{
|
||||||
|
private static array $processing = [];
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private \Espo\ORM\EntityManager $entityManager,
|
||||||
|
private \Espo\Core\InjectableFactory $injectableFactory
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function afterSave(
|
||||||
|
Entity $entity,
|
||||||
|
\Espo\ORM\Repository\Option\SaveOptions $options
|
||||||
|
): void {
|
||||||
|
// Skip if silent or during hooks
|
||||||
|
if ($options->get('silent') || $options->get('skipHooks')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vermeide Loops
|
||||||
|
$key = $entity->getId() . '-create-akte';
|
||||||
|
if (isset(self::$processing[$key])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self::$processing[$key] = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Prüfe ob Kündigung bereits eine AdvowareAkte hat
|
||||||
|
$existingAkteId = $entity->get('advowareAktenId');
|
||||||
|
|
||||||
|
if ($existingAkteId) {
|
||||||
|
$GLOBALS['log']->info("CKuendigung CreateAdvowareAkte: Kündigung already has AdvowareAkte: {$existingAkteId}");
|
||||||
|
unset(self::$processing[$key]);
|
||||||
|
return; // Bereits vorhanden
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe ob verknüpfte Räumungsklagen eine Akte haben
|
||||||
|
$raeumungsklagen = $this->entityManager
|
||||||
|
->getRDBRepository('CKuendigung')
|
||||||
|
->getRelation($entity, 'vmhRumungsklages')
|
||||||
|
->find();
|
||||||
|
|
||||||
|
foreach ($raeumungsklagen as $rk) {
|
||||||
|
$rkAkteId = $rk->get('advowareAktenId');
|
||||||
|
if ($rkAkteId) {
|
||||||
|
// Übernehme Akte von Räumungsklage
|
||||||
|
$entity->set('advowareAktenId', $rkAkteId);
|
||||||
|
$this->entityManager->saveEntity($entity, ['silent' => true, 'skipHooks' => true]);
|
||||||
|
|
||||||
|
// Synchronisiere Aktennummer
|
||||||
|
$akte = $this->entityManager->getEntity('CAdvowareAkten', $rkAkteId);
|
||||||
|
if ($akte) {
|
||||||
|
$this->syncAktennummer($entity, $akte);
|
||||||
|
}
|
||||||
|
|
||||||
|
$GLOBALS['log']->info("CKuendigung CreateAdvowareAkte: Using AdvowareAkte from Räumungsklage: {$rkAkteId}");
|
||||||
|
unset(self::$processing[$key]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keine Akte gefunden -> Erstelle neue
|
||||||
|
$this->createNewAkte($entity);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$GLOBALS['log']->error('CKuendigung CreateAdvowareAkte Error: ' . $e->getMessage());
|
||||||
|
} finally {
|
||||||
|
unset(self::$processing[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createNewAkte(Entity $kuendigung): void
|
||||||
|
{
|
||||||
|
// Hole oder generiere Aktennummer
|
||||||
|
$aktennummer = $kuendigung->get('aktennr');
|
||||||
|
if (!$aktennummer) {
|
||||||
|
$aktennummer = time();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hole oder generiere Aktenzeichen
|
||||||
|
$aktenzeichen = $kuendigung->get('advowareAktenzeichen');
|
||||||
|
if (!$aktenzeichen) {
|
||||||
|
$aktenzeichen = 'AZ-' . date('Y-m-d-His');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erstelle AdvowareAkte
|
||||||
|
$akteData = [
|
||||||
|
'name' => 'Advoware Akte - ' . $kuendigung->get('name'),
|
||||||
|
'aktennummer' => $aktennummer,
|
||||||
|
'aktenzeichen' => $aktenzeichen,
|
||||||
|
'syncStatus' => 'unclean',
|
||||||
|
'assignedUserId' => $kuendigung->get('assignedUserId')
|
||||||
|
];
|
||||||
|
|
||||||
|
// Copy teams
|
||||||
|
$teamsIds = $kuendigung->getLinkMultipleIdList('teams');
|
||||||
|
if (!empty($teamsIds)) {
|
||||||
|
$akteData['teamsIds'] = $teamsIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
$akte = $this->entityManager->createEntity('CAdvowareAkten', $akteData);
|
||||||
|
|
||||||
|
if ($akte) {
|
||||||
|
// Verknüpfe mit Kündigung
|
||||||
|
$kuendigung->set('advowareAktenId', $akte->getId());
|
||||||
|
$this->entityManager->saveEntity($kuendigung, ['silent' => true, 'skipHooks' => true]);
|
||||||
|
|
||||||
|
// Synchronisiere Aktennummer zurück zur Kündigung
|
||||||
|
$this->syncAktennummer($kuendigung, $akte);
|
||||||
|
|
||||||
|
$GLOBALS['log']->info("CKuendigung CreateAdvowareAkte: Created new AdvowareAkte: {$akte->getId()}");
|
||||||
|
} else {
|
||||||
|
$GLOBALS['log']->error('CKuendigung CreateAdvowareAkte: Failed to create AdvowareAkte');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function syncAktennummer(Entity $kuendigung, Entity $akte): void
|
||||||
|
{
|
||||||
|
$needsUpdate = false;
|
||||||
|
|
||||||
|
if (!$kuendigung->get('aktennr') && $akte->get('aktennummer')) {
|
||||||
|
$kuendigung->set('aktennr', $akte->get('aktennummer'));
|
||||||
|
$needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$kuendigung->get('advowareAktenzeichen') && $akte->get('aktenzeichen')) {
|
||||||
|
$kuendigung->set('advowareAktenzeichen', $akte->get('aktenzeichen'));
|
||||||
|
$needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($needsUpdate) {
|
||||||
|
$this->entityManager->saveEntity($kuendigung, ['silent' => true, 'skipHooks' => true]);
|
||||||
|
$GLOBALS['log']->info("CKuendigung CreateAdvowareAkte: Synchronized Aktennummer/Aktenzeichen");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
92
custom/Espo/Custom/Hooks/CKuendigung/SyncAdvowareAkte.php
Normal file
92
custom/Espo/Custom/Hooks/CKuendigung/SyncAdvowareAkte.php
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
namespace Espo\Custom\Hooks\CKuendigung;
|
||||||
|
|
||||||
|
use Espo\ORM\Entity;
|
||||||
|
use Espo\Core\Hook\Hook\AfterRelate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook: Synchronisiert AdvowareAkte zwischen Kündigung und Räumungsklage
|
||||||
|
*
|
||||||
|
* Wenn eine Kündigung mit einer Räumungsklage verknüpft wird:
|
||||||
|
* - Prüfe ob Räumungsklage eine AdvowareAkte hat
|
||||||
|
* - Wenn ja, verknüpfe diese Akte auch mit der Kündigung
|
||||||
|
* - Übernehme/Synchronisiere Aktennummer und Aktenzeichen
|
||||||
|
*/
|
||||||
|
class SyncAdvowareAkte implements AfterRelate
|
||||||
|
{
|
||||||
|
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 vmhRumungsklages-Beziehung (wenn Räumungsklage zu Kündigung hinzugefügt wird)
|
||||||
|
if ($relationName !== 'vmhRumungsklages') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vermeide Loops
|
||||||
|
$key = $entity->getId() . '-' . $foreignEntity->getId() . '-sync-akte';
|
||||||
|
if (isset(self::$processing[$key])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self::$processing[$key] = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// $entity = CKuendigung
|
||||||
|
// $foreignEntity = CVmhRumungsklage
|
||||||
|
|
||||||
|
// Hole AdvowareAkte von der Räumungsklage (hasOne relationship - get via field)
|
||||||
|
$advowareAkteId = $foreignEntity->get('advowareAktenId');
|
||||||
|
|
||||||
|
if ($advowareAkteId) {
|
||||||
|
$advowareAkte = $this->entityManager->getEntity('CAdvowareAkten', $advowareAkteId);
|
||||||
|
|
||||||
|
if ($advowareAkte) {
|
||||||
|
$GLOBALS['log']->info("CKuendigung SyncAdvowareAkte: Found AdvowareAkte {$advowareAkte->getId()} on Räumungsklage {$foreignEntity->getId()}");
|
||||||
|
|
||||||
|
// Prüfe ob Kündigung bereits eine andere Akte hat
|
||||||
|
$existingAktenId = $entity->get('advowareAktenId');
|
||||||
|
|
||||||
|
if ($existingAktenId && $existingAktenId !== $advowareAkteId) {
|
||||||
|
$GLOBALS['log']->warning("CKuendigung SyncAdvowareAkte: Kündigung already has different AdvowareAkte {$existingAktenId}, will replace with {$advowareAkteId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verknüpfe AdvowareAkte mit Kündigung (belongsTo relationship - set field directly)
|
||||||
|
$entity->set('advowareAktenId', $advowareAkteId);
|
||||||
|
|
||||||
|
// Synchronisiere Aktennummer und Aktenzeichen
|
||||||
|
$needsUpdate = false;
|
||||||
|
|
||||||
|
if (!$entity->get('aktennr') && $advowareAkte->get('aktennummer')) {
|
||||||
|
$entity->set('aktennr', $advowareAkte->get('aktennummer'));
|
||||||
|
$needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$entity->get('advowareAktenzeichen') && $advowareAkte->get('aktenzeichen')) {
|
||||||
|
$entity->set('advowareAktenzeichen', $advowareAkte->get('aktenzeichen'));
|
||||||
|
$needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save once with all changes
|
||||||
|
$this->entityManager->saveEntity($entity, ['silent' => true, 'skipHooks' => true]);
|
||||||
|
$GLOBALS['log']->info("CKuendigung SyncAdvowareAkte: Successfully linked AdvowareAkte and synchronized fields to Kündigung");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$GLOBALS['log']->info("CKuendigung SyncAdvowareAkte: Räumungsklage {$foreignEntity->getId()} has no AdvowareAkte yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$GLOBALS['log']->error('CKuendigung SyncAdvowareAkte Error: ' . $e->getMessage());
|
||||||
|
} finally {
|
||||||
|
unset(self::$processing[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
"tasks": "Aufgaben",
|
"tasks": "Aufgaben",
|
||||||
"vmhRumungsklage": "Räumungsklagen",
|
"vmhRumungsklage": "Räumungsklagen",
|
||||||
"mietinkasso": "Mietinkasso",
|
"mietinkasso": "Mietinkasso",
|
||||||
|
"kuendigungen": "Kündigungen",
|
||||||
"dokumentes": "Dokumente"
|
"dokumentes": "Dokumente"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"hnr": "HNR (Advoware)",
|
"hnr": "HNR (Advoware)",
|
||||||
"syncStatus": "Sync-Status",
|
"syncStatus": "Sync-Status",
|
||||||
"syncedHash": "Sync-Hash",
|
"syncedHash": "Sync-Hash",
|
||||||
|
"usn": "USN",
|
||||||
"contactsvmhdokumente": "Freigegebene Nutzer",
|
"contactsvmhdokumente": "Freigegebene Nutzer",
|
||||||
"vmhMietverhltnisesDokumente": "Mietverhältnisse",
|
"vmhMietverhltnisesDokumente": "Mietverhältnisse",
|
||||||
"vmhErstgespraechsdokumente": "Erstgespräche",
|
"vmhErstgespraechsdokumente": "Erstgespräche",
|
||||||
@@ -43,7 +44,8 @@
|
|||||||
"blake3hash": "Kryptografischer Blake3-Hash der Datei (schneller und sicherer als MD5/SHA256)",
|
"blake3hash": "Kryptografischer Blake3-Hash der Datei (schneller und sicherer als MD5/SHA256)",
|
||||||
"hnr": "Hierarchische Referenznummer in Advoware",
|
"hnr": "Hierarchische Referenznummer in Advoware",
|
||||||
"syncStatus": "Status der Synchronisation mit Advoware: new=neu, unclean=geändert, synced=synchronisiert, failed=Fehler, unsupported=nicht unterstützt",
|
"syncStatus": "Status der Synchronisation mit Advoware: new=neu, unclean=geändert, synced=synchronisiert, failed=Fehler, unsupported=nicht unterstützt",
|
||||||
"syncedHash": "Hash-Wert bei letzter erfolgreicher Synchronisation"
|
"syncedHash": "Hash-Wert bei letzter erfolgreicher Synchronisation",
|
||||||
|
"usn": "Update Sequence Number - Versionsnummer für Synchronisation"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"syncStatus": {
|
"syncStatus": {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
"gekuendigte": "Mieter",
|
"gekuendigte": "Mieter",
|
||||||
"dokumenteskuendigung": "Dokumente",
|
"dokumenteskuendigung": "Dokumente",
|
||||||
"contactsKuendigung": "Portal-Freigaben",
|
"contactsKuendigung": "Portal-Freigaben",
|
||||||
|
"advowareAkten": "Advoware Akte",
|
||||||
|
"vmhRumungsklages": "Räumungsklagen",
|
||||||
"pulse": "Pulse"
|
"pulse": "Pulse"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
"klaeger": "Kläger",
|
"klaeger": "Kläger",
|
||||||
"beklagte": "Beklagte",
|
"beklagte": "Beklagte",
|
||||||
"vmhMietverhltnises": "Mietverhältnisse",
|
"vmhMietverhltnises": "Mietverhältnisse",
|
||||||
|
"kuendigungen": "Kündigungen",
|
||||||
"contactsRumungsklage": "Freigegebene Nutzer",
|
"contactsRumungsklage": "Freigegebene Nutzer",
|
||||||
"dokumentesvmhraumungsklage": "Dokumente",
|
"dokumentesvmhraumungsklage": "Dokumente",
|
||||||
"freigeschalteteNutzer": "Freigeschaltete Nutzer (veraltet)",
|
"freigeschalteteNutzer": "Freigeschaltete Nutzer (veraltet)",
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"tasks": "Tasks",
|
"tasks": "Tasks",
|
||||||
"vmhRumungsklage": "Räumungsklagen",
|
"vmhRumungsklage": "Räumungsklagen",
|
||||||
"mietinkasso": "Mietinkasso",
|
"mietinkasso": "Mietinkasso",
|
||||||
|
"kuendigungen": "Terminations",
|
||||||
"dokumentes": "Dokumente"
|
"dokumentes": "Dokumente"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"hnr": "HNR (Advoware)",
|
"hnr": "HNR (Advoware)",
|
||||||
"syncStatus": "Sync Status",
|
"syncStatus": "Sync Status",
|
||||||
"syncedHash": "Sync Hash",
|
"syncedHash": "Sync Hash",
|
||||||
|
"usn": "USN",
|
||||||
"contactsvmhdokumente": "Portal Users",
|
"contactsvmhdokumente": "Portal Users",
|
||||||
"vmhMietverhltnisesDokumente": "Tenancies",
|
"vmhMietverhltnisesDokumente": "Tenancies",
|
||||||
"vmhErstgespraechsdokumente": "Initial Consultations",
|
"vmhErstgespraechsdokumente": "Initial Consultations",
|
||||||
@@ -48,7 +49,8 @@
|
|||||||
"blake3hash": "Cryptographic Blake3 hash of the file (faster and more secure than MD5/SHA256)",
|
"blake3hash": "Cryptographic Blake3 hash of the file (faster and more secure than MD5/SHA256)",
|
||||||
"hnr": "Hierarchical reference number in Advoware",
|
"hnr": "Hierarchical reference number in Advoware",
|
||||||
"syncStatus": "Sync status with Advoware: new=new, unclean=changed, synced=synchronized, failed=error, unsupported=not supported",
|
"syncStatus": "Sync status with Advoware: new=new, unclean=changed, synced=synchronized, failed=error, unsupported=not supported",
|
||||||
"syncedHash": "Hash value at last successful synchronization"
|
"syncedHash": "Hash value at last successful synchronization",
|
||||||
|
"usn": "Update Sequence Number - Version number for synchronization"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"syncStatus": {
|
"syncStatus": {
|
||||||
|
|||||||
@@ -44,6 +44,8 @@
|
|||||||
"gekuendigte": "Tenant",
|
"gekuendigte": "Tenant",
|
||||||
"dokumenteskuendigung": "Documents",
|
"dokumenteskuendigung": "Documents",
|
||||||
"contactsKuendigung": "Portal Access",
|
"contactsKuendigung": "Portal Access",
|
||||||
|
"advowareAkten": "Advoware Case File",
|
||||||
|
"vmhRumungsklages": "Eviction Lawsuits",
|
||||||
"pulse": "Pulses"
|
"pulse": "Pulses"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"calls": "Calls",
|
"calls": "Calls",
|
||||||
"tasks": "Tasks",
|
"tasks": "Tasks",
|
||||||
"vmhMietverhltnises": "Tenancies",
|
"vmhMietverhltnises": "Tenancies",
|
||||||
|
"kuendigungen": "Terminations",
|
||||||
"freigeschalteteNutzer": "Activated Users",
|
"freigeschalteteNutzer": "Activated Users",
|
||||||
"collaborators": "Collaborators",
|
"collaborators": "Collaborators",
|
||||||
"vmhVermietersRKL": "Landlord",
|
"vmhVermietersRKL": "Landlord",
|
||||||
|
|||||||
@@ -195,6 +195,13 @@
|
|||||||
"entity": "CMietinkasso",
|
"entity": "CMietinkasso",
|
||||||
"isCustom": true
|
"isCustom": true
|
||||||
},
|
},
|
||||||
|
"kuendigungen": {
|
||||||
|
"type": "hasMany",
|
||||||
|
"foreign": "advowareAkten",
|
||||||
|
"entity": "CKuendigung",
|
||||||
|
"audited": false,
|
||||||
|
"isCustom": true
|
||||||
|
},
|
||||||
"dokumentes": {
|
"dokumentes": {
|
||||||
"type": "hasMany",
|
"type": "hasMany",
|
||||||
"foreign": "cAdvowareAkten",
|
"foreign": "cAdvowareAkten",
|
||||||
|
|||||||
@@ -102,6 +102,12 @@
|
|||||||
"tooltip": true,
|
"tooltip": true,
|
||||||
"isCustom": true
|
"isCustom": true
|
||||||
},
|
},
|
||||||
|
"usn": {
|
||||||
|
"type": "int",
|
||||||
|
"min": 0,
|
||||||
|
"tooltip": true,
|
||||||
|
"isCustom": true
|
||||||
|
},
|
||||||
"puls": {
|
"puls": {
|
||||||
"type": "link",
|
"type": "link",
|
||||||
"entity": "CPuls",
|
"entity": "CPuls",
|
||||||
|
|||||||
@@ -73,6 +73,9 @@
|
|||||||
"tooltip": true,
|
"tooltip": true,
|
||||||
"isCustom": true
|
"isCustom": true
|
||||||
},
|
},
|
||||||
|
"advowareAkten": {
|
||||||
|
"type": "link"
|
||||||
|
},
|
||||||
"syncStatus": {
|
"syncStatus": {
|
||||||
"type": "enum",
|
"type": "enum",
|
||||||
"required": false,
|
"required": false,
|
||||||
@@ -357,6 +360,21 @@
|
|||||||
"audited": false,
|
"audited": false,
|
||||||
"isCustom": true
|
"isCustom": true
|
||||||
},
|
},
|
||||||
|
"advowareAkten": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"foreign": "kuendigungen",
|
||||||
|
"entity": "CAdvowareAkten",
|
||||||
|
"audited": false,
|
||||||
|
"isCustom": true
|
||||||
|
},
|
||||||
|
"vmhRumungsklages": {
|
||||||
|
"type": "hasMany",
|
||||||
|
"relationName": "cKuendigungVmhRumungsklage",
|
||||||
|
"foreign": "kuendigungen",
|
||||||
|
"entity": "CVmhRumungsklage",
|
||||||
|
"audited": false,
|
||||||
|
"isCustom": true
|
||||||
|
},
|
||||||
"pulse": {
|
"pulse": {
|
||||||
"type": "hasMany",
|
"type": "hasMany",
|
||||||
"entity": "CPuls",
|
"entity": "CPuls",
|
||||||
|
|||||||
@@ -195,6 +195,14 @@
|
|||||||
"audited": false,
|
"audited": false,
|
||||||
"isCustom": true
|
"isCustom": true
|
||||||
},
|
},
|
||||||
|
"kuendigungen": {
|
||||||
|
"type": "hasMany",
|
||||||
|
"relationName": "cKuendigungVmhRumungsklage",
|
||||||
|
"foreign": "vmhRumungsklages",
|
||||||
|
"entity": "CKuendigung",
|
||||||
|
"audited": false,
|
||||||
|
"isCustom": true
|
||||||
|
},
|
||||||
"pulse": {
|
"pulse": {
|
||||||
"type": "hasMany",
|
"type": "hasMany",
|
||||||
"entity": "CPuls",
|
"entity": "CPuls",
|
||||||
|
|||||||
@@ -12,21 +12,20 @@ use Espo\Core\Utils\File\Manager as FileManager;
|
|||||||
*/
|
*/
|
||||||
class CDokumente extends Record
|
class CDokumente extends Record
|
||||||
{
|
{
|
||||||
private FileStorageManager $fileStorageManager;
|
|
||||||
private FileManager $fileManager;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject additional dependencies via setter methods
|
* Get FileStorageManager from container on-demand
|
||||||
* EspoCRM DI will automatically call these
|
|
||||||
*/
|
*/
|
||||||
public function injectFileStorageManager(FileStorageManager $fileStorageManager): void
|
private function getFileStorageManager(): FileStorageManager
|
||||||
{
|
{
|
||||||
$this->fileStorageManager = $fileStorageManager;
|
return $this->injectableFactory->create(FileStorageManager::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function injectFileManager(FileManager $fileManager): void
|
/**
|
||||||
|
* Get FileManager from container on-demand
|
||||||
|
*/
|
||||||
|
private function getFileManager(): FileManager
|
||||||
{
|
{
|
||||||
$this->fileManager = $fileManager;
|
return $this->injectableFactory->create(FileManager::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,14 +119,14 @@ class CDokumente extends Record
|
|||||||
private function duplicateAttachment(Entity $sourceAttachment): Entity
|
private function duplicateAttachment(Entity $sourceAttachment): Entity
|
||||||
{
|
{
|
||||||
// 1. Get source file path
|
// 1. Get source file path
|
||||||
$sourceFilePath = $this->fileStorageManager->getLocalFilePath($sourceAttachment);
|
$sourceFilePath = $this->getFileStorageManager()->getLocalFilePath($sourceAttachment);
|
||||||
|
|
||||||
if (!file_exists($sourceFilePath)) {
|
if (!file_exists($sourceFilePath)) {
|
||||||
throw new \RuntimeException('Source file not found: ' . $sourceFilePath);
|
throw new \RuntimeException('Source file not found: ' . $sourceFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Read source file content
|
// 2. Read source file content
|
||||||
$fileContent = $this->fileManager->getContents($sourceFilePath);
|
$fileContent = $this->getFileManager()->getContents($sourceFilePath);
|
||||||
|
|
||||||
// 3. Create new attachment entity
|
// 3. Create new attachment entity
|
||||||
$newAttachment = $this->entityManager->getEntity('Attachment');
|
$newAttachment = $this->entityManager->getEntity('Attachment');
|
||||||
@@ -142,7 +141,7 @@ class CDokumente extends Record
|
|||||||
$this->entityManager->saveEntity($newAttachment);
|
$this->entityManager->saveEntity($newAttachment);
|
||||||
|
|
||||||
// 4. Write file content to new location
|
// 4. Write file content to new location
|
||||||
$this->fileStorageManager->putContents($newAttachment, $fileContent);
|
$this->getFileStorageManager()->putContents($newAttachment, $fileContent);
|
||||||
|
|
||||||
// 5. Return new attachment
|
// 5. Return new attachment
|
||||||
return $newAttachment;
|
return $newAttachment;
|
||||||
|
|||||||
@@ -129,6 +129,9 @@ class CVmhMietverhltnis extends \Espo\Services\Record
|
|||||||
->relate($bewohner);
|
->relate($bewohner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 9c. Create AdvowareAkte and AIKnowledge (BEFORE document duplication!)
|
||||||
|
$this->createAdvowareAkteAndAIKnowledge($mietinkasso, $mietinkassoRepo);
|
||||||
|
|
||||||
// 10. Copy all documents from Mietverhältnis, Mietobjekt and Beteiligte
|
// 10. Copy all documents from Mietverhältnis, Mietobjekt and Beteiligte
|
||||||
// Get CDokumente service for duplication
|
// Get CDokumente service for duplication
|
||||||
$dokumenteService = $this->injectableFactory->create(\Espo\Custom\Services\CDokumente::class);
|
$dokumenteService = $this->injectableFactory->create(\Espo\Custom\Services\CDokumente::class);
|
||||||
@@ -367,6 +370,68 @@ class CVmhMietverhltnis extends \Espo\Services\Record
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create AdvowareAkte and AIKnowledge for Mietinkasso
|
||||||
|
*
|
||||||
|
* @param object $mietinkasso The created Mietinkasso entity
|
||||||
|
* @param object $mietinkassoRepo Repository for relations
|
||||||
|
*/
|
||||||
|
private function createAdvowareAkteAndAIKnowledge($mietinkasso, $mietinkassoRepo): void
|
||||||
|
{
|
||||||
|
// 1. Create AdvowareAkte
|
||||||
|
$aktennummer = time(); // Simple timestamp-based generation
|
||||||
|
$aktenzeichen = 'AZ-' . date('Y-m-d-His');
|
||||||
|
|
||||||
|
$advowareAkteData = [
|
||||||
|
'name' => 'Advoware Akte - ' . $mietinkasso->get('name'),
|
||||||
|
'aktennummer' => $aktennummer,
|
||||||
|
'aktenzeichen' => $aktenzeichen,
|
||||||
|
'syncStatus' => 'unclean',
|
||||||
|
'assignedUserId' => $mietinkasso->get('assignedUserId')
|
||||||
|
];
|
||||||
|
|
||||||
|
// Copy teams
|
||||||
|
$teamsIds = $mietinkasso->getLinkMultipleIdList('teams');
|
||||||
|
if (!empty($teamsIds)) {
|
||||||
|
$advowareAkteData['teamsIds'] = $teamsIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
$advowareAkte = $this->entityManager->createEntity('CAdvowareAkten', $advowareAkteData);
|
||||||
|
|
||||||
|
if ($advowareAkte) {
|
||||||
|
// Link AdvowareAkte to Mietinkasso (hasOne relationship - set field directly)
|
||||||
|
$mietinkasso->set('advowareAktenId', $advowareAkte->getId());
|
||||||
|
$this->entityManager->saveEntity($mietinkasso);
|
||||||
|
$GLOBALS['log']->info("CVmhMietverhltnis: Created and linked AdvowareAkte for Mietinkasso: {$advowareAkte->getId()}");
|
||||||
|
} else {
|
||||||
|
$GLOBALS['log']->error('CVmhMietverhltnis: Failed to create AdvowareAkte for Mietinkasso');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Create AIKnowledge
|
||||||
|
$aiKnowledgeData = [
|
||||||
|
'name' => 'AI Knowledge - ' . $mietinkasso->get('name'),
|
||||||
|
'aktivierungsstatus' => 'deactivated',
|
||||||
|
'syncStatus' => 'unclean',
|
||||||
|
'assignedUserId' => $mietinkasso->get('assignedUserId')
|
||||||
|
];
|
||||||
|
|
||||||
|
// Copy teams
|
||||||
|
if (!empty($teamsIds)) {
|
||||||
|
$aiKnowledgeData['teamsIds'] = $teamsIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
$aiKnowledge = $this->entityManager->createEntity('CAIKnowledge', $aiKnowledgeData);
|
||||||
|
|
||||||
|
if ($aiKnowledge) {
|
||||||
|
// Link AIKnowledge to Mietinkasso (hasOne relationship - set field directly)
|
||||||
|
$mietinkasso->set('aIKnowledgeId', $aiKnowledge->getId());
|
||||||
|
$this->entityManager->saveEntity($mietinkasso);
|
||||||
|
$GLOBALS['log']->info("CVmhMietverhltnis: Created and linked AIKnowledge for Mietinkasso: {$aiKnowledge->getId()}");
|
||||||
|
} else {
|
||||||
|
$GLOBALS['log']->error('CVmhMietverhltnis: Failed to create AIKnowledge for Mietinkasso');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log action to source entity stream
|
* Log action to source entity stream
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -264,7 +264,14 @@ class CVmhRumungsklage extends \Espo\Services\Record
|
|||||||
->relate($beklagter);
|
->relate($beklagter);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. Collect all documents from Mietverhältnisse, Kündigungen, Mietobjekte and Beteiligte
|
// 7. Create or link AdvowareAkte and AIKnowledge (BEFORE document duplication!)
|
||||||
|
$this->createOrLinkAdvowareAkteAndAIKnowledge(
|
||||||
|
$raeumungsklage,
|
||||||
|
$alleKuendigungen,
|
||||||
|
$raeumungsklagenRepo
|
||||||
|
);
|
||||||
|
|
||||||
|
// 8. Collect all documents from Mietverhältnisse, Kündigungen, Mietobjekte and Beteiligte
|
||||||
$alleLinkedDokumente = [];
|
$alleLinkedDokumente = [];
|
||||||
|
|
||||||
// Get CDokumente service for duplication
|
// Get CDokumente service for duplication
|
||||||
@@ -391,6 +398,128 @@ class CVmhRumungsklage extends \Espo\Services\Record
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create or link AdvowareAkte and create AIKnowledge for Räumungsklage
|
||||||
|
*
|
||||||
|
* @param object $raeumungsklage The created Räumungsklage entity
|
||||||
|
* @param array $alleKuendigungen All related Kündigungen
|
||||||
|
* @param object $raeumungsklagenRepo Repository for relations
|
||||||
|
*/
|
||||||
|
private function createOrLinkAdvowareAkteAndAIKnowledge(
|
||||||
|
$raeumungsklage,
|
||||||
|
array $alleKuendigungen,
|
||||||
|
$raeumungsklagenRepo
|
||||||
|
): void {
|
||||||
|
$advowareAkte = null;
|
||||||
|
|
||||||
|
// 1. Check if any Kündigung has an existing AdvowareAkte (belongsTo relationship - get via field)
|
||||||
|
foreach ($alleKuendigungen as $kuendigung) {
|
||||||
|
$existingAkteId = $kuendigung->get('advowareAktenId');
|
||||||
|
|
||||||
|
if ($existingAkteId) {
|
||||||
|
$existingAkte = $this->entityManager->getEntity('CAdvowareAkten', $existingAkteId);
|
||||||
|
if ($existingAkte) {
|
||||||
|
$advowareAkte = $existingAkte;
|
||||||
|
$GLOBALS['log']->info("CVmhRumungsklage: Using existing AdvowareAkte from Kündigung: {$existingAkte->getId()}");
|
||||||
|
break; // Use first found Akte
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. If no existing Akte found, create new one
|
||||||
|
if (!$advowareAkte) {
|
||||||
|
// Collect Aktennummer and Aktenzeichen from Kündigungen
|
||||||
|
$aktennummer = null;
|
||||||
|
$aktenzeichen = null;
|
||||||
|
|
||||||
|
foreach ($alleKuendigungen as $kuendigung) {
|
||||||
|
if (!$aktennummer && $kuendigung->get('aktennr')) {
|
||||||
|
$aktennummer = $kuendigung->get('aktennr');
|
||||||
|
}
|
||||||
|
if (!$aktenzeichen && $kuendigung->get('advowareAktenzeichen')) {
|
||||||
|
$aktenzeichen = $kuendigung->get('advowareAktenzeichen');
|
||||||
|
}
|
||||||
|
if ($aktennummer && $aktenzeichen) {
|
||||||
|
break; // Found both
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate if not found
|
||||||
|
if (!$aktennummer) {
|
||||||
|
$aktennummer = time(); // Simple timestamp-based generation
|
||||||
|
}
|
||||||
|
if (!$aktenzeichen) {
|
||||||
|
$aktenzeichen = 'AZ-' . date('Y-m-d-His');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new AdvowareAkte
|
||||||
|
$advowareAkteData = [
|
||||||
|
'name' => 'Advoware Akte - ' . $raeumungsklage->get('name'),
|
||||||
|
'aktennummer' => $aktennummer,
|
||||||
|
'aktenzeichen' => $aktenzeichen,
|
||||||
|
'syncStatus' => 'unclean',
|
||||||
|
'assignedUserId' => $raeumungsklage->get('assignedUserId')
|
||||||
|
];
|
||||||
|
|
||||||
|
// Copy teams
|
||||||
|
$teamsIds = $raeumungsklage->getLinkMultipleIdList('teams');
|
||||||
|
if (!empty($teamsIds)) {
|
||||||
|
$advowareAkteData['teamsIds'] = $teamsIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
$advowareAkte = $this->entityManager->createEntity('CAdvowareAkten', $advowareAkteData);
|
||||||
|
|
||||||
|
if ($advowareAkte) {
|
||||||
|
$GLOBALS['log']->info("CVmhRumungsklage: Created new AdvowareAkte: {$advowareAkte->getId()}");
|
||||||
|
|
||||||
|
// Link new Akte to ALL Kündigungen (belongsTo relationship - set field directly)
|
||||||
|
foreach ($alleKuendigungen as $kuendigung) {
|
||||||
|
try {
|
||||||
|
$kuendigung->set('advowareAktenId', $advowareAkte->getId());
|
||||||
|
$this->entityManager->saveEntity($kuendigung);
|
||||||
|
$GLOBALS['log']->info("CVmhRumungsklage: Linked new AdvowareAkte to Kündigung: {$kuendigung->getId()}");
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$GLOBALS['log']->warning("CVmhRumungsklage: Could not link AdvowareAkte to Kündigung: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$GLOBALS['log']->error('CVmhRumungsklage: Failed to create AdvowareAkte');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Link AdvowareAkte to Räumungsklage (hasOne relationship - set field directly)
|
||||||
|
if ($advowareAkte) {
|
||||||
|
$raeumungsklage->set('advowareAktenId', $advowareAkte->getId());
|
||||||
|
$this->entityManager->saveEntity($raeumungsklage);
|
||||||
|
$GLOBALS['log']->info("CVmhRumungsklage: Linked AdvowareAkte to Räumungsklage");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Create AIKnowledge
|
||||||
|
$aiKnowledgeData = [
|
||||||
|
'name' => 'AI Knowledge - ' . $raeumungsklage->get('name'),
|
||||||
|
'aktivierungsstatus' => 'deactivated',
|
||||||
|
'syncStatus' => 'unclean',
|
||||||
|
'assignedUserId' => $raeumungsklage->get('assignedUserId')
|
||||||
|
];
|
||||||
|
|
||||||
|
// Copy teams
|
||||||
|
$teamsIds = $raeumungsklage->getLinkMultipleIdList('teams');
|
||||||
|
if (!empty($teamsIds)) {
|
||||||
|
$aiKnowledgeData['teamsIds'] = $teamsIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
$aiKnowledge = $this->entityManager->createEntity('CAIKnowledge', $aiKnowledgeData);
|
||||||
|
|
||||||
|
if ($aiKnowledge) {
|
||||||
|
// Link AIKnowledge to Räumungsklage (hasOne relationship - set field directly)
|
||||||
|
$raeumungsklage->set('aIKnowledgeId', $aiKnowledge->getId());
|
||||||
|
$this->entityManager->saveEntity($raeumungsklage);
|
||||||
|
$GLOBALS['log']->info("CVmhRumungsklage: Created and linked AIKnowledge: {$aiKnowledge->getId()}");
|
||||||
|
} else {
|
||||||
|
$GLOBALS['log']->error('CVmhRumungsklage: Failed to create AIKnowledge');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log Räumungsklage creation to source entity stream
|
* Log Räumungsklage creation to source entity stream
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ return [
|
|||||||
0 => 'youtube.com',
|
0 => 'youtube.com',
|
||||||
1 => 'google.com'
|
1 => 'google.com'
|
||||||
],
|
],
|
||||||
'microtime' => 1774294800.115291,
|
'microtime' => 1774297547.393199,
|
||||||
'siteUrl' => 'https://crm.bitbylaw.com',
|
'siteUrl' => 'https://crm.bitbylaw.com',
|
||||||
'fullTextSearchMinLength' => 4,
|
'fullTextSearchMinLength' => 4,
|
||||||
'webSocketUrl' => 'ws://api.bitbylaw.com:5000/espocrm/ws',
|
'webSocketUrl' => 'ws://api.bitbylaw.com:5000/espocrm/ws',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
return [
|
return [
|
||||||
'cacheTimestamp' => 1774294800,
|
'cacheTimestamp' => 1774297547,
|
||||||
'microtimeState' => 1774294800.291216,
|
'microtimeState' => 1774297547.570899,
|
||||||
'currencyRates' => [
|
'currencyRates' => [
|
||||||
'EUR' => 1.0
|
'EUR' => 1.0
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user