diff --git a/client/custom/src/handlers/mietobjekt/eviction-action.js b/client/custom/src/handlers/mietobjekt/eviction-action.js
deleted file mode 100644
index 2cc1628e..00000000
--- a/client/custom/src/handlers/mietobjekt/eviction-action.js
+++ /dev/null
@@ -1,57 +0,0 @@
-define('custom:handlers/mietobjekt/eviction-action', [], function () {
-
- /**
- * Handler for initiating eviction lawsuit from Mietobjekt
- */
- return class {
-
- constructor(view) {
- this.view = view;
- }
-
- /**
- * Initialize the handler
- */
- initEvictionAction() {
- // Initialization if needed
- }
-
- /**
- * Action to initiate eviction (Räumungsklage)
- */
- initiateEviction() {
- const model = this.view.model;
-
- // Confirm dialog
- this.view.confirm(
- this.view.translate('initiateEvictionConfirmation', 'messages', 'CMietobjekt'),
- () => {
- Espo.Ui.notify(this.view.translate('pleaseWait', 'messages'));
-
- // POST request to backend
- Espo.Ajax.postRequest('CMietobjekt/action/initiateEviction', {
- id: model.id
- }).then(response => {
- Espo.Ui.success(
- this.view.translate('evictionCreatedSuccess', 'messages', 'CMietobjekt')
- );
-
- // Navigate to the newly created Räumungsklage
- this.view.getRouter().navigate(
- '#CVmhRumungsklage/view/' + response.id,
- {trigger: true}
- );
- }).catch(xhr => {
- let message = this.view.translate('evictionCreatedError', 'messages', 'CMietobjekt');
-
- if (xhr.responseJSON && xhr.responseJSON.message) {
- message = xhr.responseJSON.message;
- }
-
- Espo.Ui.error(message);
- });
- }
- );
- }
- };
-});
diff --git a/client/custom/src/handlers/mietverhaeltnis/eviction-action.js b/client/custom/src/handlers/mietverhaeltnis/eviction-action.js
new file mode 100644
index 00000000..cc761314
--- /dev/null
+++ b/client/custom/src/handlers/mietverhaeltnis/eviction-action.js
@@ -0,0 +1,70 @@
+define('custom:handlers/mietverhaeltnis/eviction-action', [], function () {
+
+ class EvictionActionHandler {
+
+ constructor(view) {
+ this.view = view;
+ }
+
+ /**
+ * Setup-Methode wird automatisch aufgerufen
+ */
+ initInitiateEviction() {
+ // Optional: Button-Logik nach Render
+ }
+
+ /**
+ * Action-Handler (wird bei Button-Click aufgerufen)
+ */
+ actionInitiateEviction() {
+ console.log('actionInitiateEviction called');
+ const model = this.view.model;
+
+ // Confirmation Dialog
+ this.view.confirm(
+ this.view.translate('confirmEviction', 'messages', 'CVmhMietverhltnis'),
+ () => {
+ console.log('Confirmation accepted, initiating eviction');
+ this.initiateEviction(model.id);
+ }
+ );
+ }
+
+ /**
+ * AJAX Request zum Backend
+ */
+ initiateEviction(mietverhaeltnisId) {
+ Espo.Ui.notify(this.view.translate('pleaseWait', 'messages'));
+
+ Espo.Ajax.postRequest('CVmhMietverhltnis/action/initiateEviction', {
+ id: mietverhaeltnisId
+ })
+ .then(response => {
+ Espo.Ui.success(
+ this.view.translate('evictionCreated', 'messages', 'CVmhMietverhltnis')
+ );
+
+ // Navigation zur erstellten Räumungsklage
+ this.view.getRouter().navigate(
+ '#CVmhRumungsklage/view/' + response.id,
+ {trigger: true}
+ );
+ })
+ .catch(xhr => {
+ console.error('Eviction initiation failed:', xhr);
+
+ let errorMessage = this.view.translate('evictionError', 'messages', 'CVmhMietverhltnis');
+
+ if (xhr.status === 403) {
+ errorMessage = this.view.translate('Access denied', 'messages');
+ } else if (xhr.status === 404) {
+ errorMessage = this.view.translate('Not found', 'messages');
+ }
+
+ Espo.Ui.error(errorMessage);
+ });
+ }
+ }
+
+ return EvictionActionHandler;
+});
diff --git a/client/custom/src/handlers/mietverhaeltnis/rent-collection-action.js b/client/custom/src/handlers/mietverhaeltnis/rent-collection-action.js
new file mode 100644
index 00000000..1ea00160
--- /dev/null
+++ b/client/custom/src/handlers/mietverhaeltnis/rent-collection-action.js
@@ -0,0 +1,70 @@
+define('custom:handlers/mietverhaeltnis/rent-collection-action', [], function () {
+
+ class RentCollectionActionHandler {
+
+ constructor(view) {
+ this.view = view;
+ }
+
+ /**
+ * Setup-Methode wird automatisch aufgerufen
+ */
+ initInitiateRentCollection() {
+ // Optional: Button-Logik nach Render
+ }
+
+ /**
+ * Action-Handler (wird bei Button-Click aufgerufen)
+ */
+ actionInitiateRentCollection() {
+ console.log('actionInitiateRentCollection called');
+ const model = this.view.model;
+
+ // Confirmation Dialog
+ this.view.confirm(
+ this.view.translate('confirmRentCollection', 'messages', 'CVmhMietverhltnis'),
+ () => {
+ console.log('Confirmation accepted, initiating rent collection');
+ this.initiateRentCollection(model.id);
+ }
+ );
+ }
+
+ /**
+ * AJAX Request zum Backend
+ */
+ initiateRentCollection(mietverhaeltnisId) {
+ Espo.Ui.notify(this.view.translate('pleaseWait', 'messages'));
+
+ Espo.Ajax.postRequest('CVmhMietverhltnis/action/initiateRentCollection', {
+ id: mietverhaeltnisId
+ })
+ .then(response => {
+ Espo.Ui.success(
+ this.view.translate('rentCollectionCreated', 'messages', 'CVmhMietverhltnis')
+ );
+
+ // Navigation zum erstellten Mietinkasso
+ this.view.getRouter().navigate(
+ '#CMietinkasso/view/' + response.id,
+ {trigger: true}
+ );
+ })
+ .catch(xhr => {
+ console.error('Rent collection initiation failed:', xhr);
+
+ let errorMessage = this.view.translate('rentCollectionError', 'messages', 'CVmhMietverhltnis');
+
+ if (xhr.status === 403) {
+ errorMessage = this.view.translate('Access denied', 'messages');
+ } else if (xhr.status === 404) {
+ errorMessage = this.view.translate('Not found', 'messages');
+ }
+
+ Espo.Ui.error(errorMessage);
+ });
+ }
+ }
+
+ return RentCollectionActionHandler;
+});
diff --git a/custom/Espo/Custom/Controllers/CMietobjekt.php b/custom/Espo/Custom/Controllers/CMietobjekt.php
index d904faaa..1a4f5380 100644
--- a/custom/Espo/Custom/Controllers/CMietobjekt.php
+++ b/custom/Espo/Custom/Controllers/CMietobjekt.php
@@ -2,27 +2,6 @@
namespace Espo\Custom\Controllers;
-use Espo\Core\Exceptions\BadRequest;
-use Espo\Core\Exceptions\Forbidden;
-use Espo\Core\Api\Request;
-
class CMietobjekt extends \Espo\Core\Templates\Controllers\Base
{
- /**
- * POST Action: Initiate eviction lawsuit from Mietobjekt
- */
- public function postActionInitiateEviction(Request $request): array
- {
- $data = $request->getParsedBody();
-
- $id = $data->id ?? null;
- if (!$id) {
- throw new BadRequest('No Mietobjekt ID provided');
- }
-
- $service = $this->getRecordService();
- $result = $service->initiateEviction($id);
-
- return $result;
- }
}
diff --git a/custom/Espo/Custom/Controllers/CVmhMietverhltnis.php b/custom/Espo/Custom/Controllers/CVmhMietverhltnis.php
index 6d64cea1..78b1a241 100644
--- a/custom/Espo/Custom/Controllers/CVmhMietverhltnis.php
+++ b/custom/Espo/Custom/Controllers/CVmhMietverhltnis.php
@@ -2,6 +2,44 @@
namespace Espo\Custom\Controllers;
+use Espo\Core\Exceptions\BadRequest;
+use Espo\Core\Api\Request;
+
class CVmhMietverhltnis extends \Espo\Core\Templates\Controllers\BasePlus
{
+ /**
+ * POST Action: Initiate eviction lawsuit from Mietverhältnis
+ */
+ public function postActionInitiateEviction(Request $request): array
+ {
+ $data = $request->getParsedBody();
+
+ $id = $data->id ?? null;
+ if (!$id) {
+ throw new BadRequest('No Mietverhältnis ID provided');
+ }
+
+ $service = $this->getRecordService();
+ $result = $service->initiateEviction($id);
+
+ return $result;
+ }
+
+ /**
+ * POST Action: Initiate rent collection from Mietverhältnis
+ */
+ public function postActionInitiateRentCollection(Request $request): array
+ {
+ $data = $request->getParsedBody();
+
+ $id = $data->id ?? null;
+ if (!$id) {
+ throw new BadRequest('No Mietverhältnis ID provided');
+ }
+
+ $service = $this->getRecordService();
+ $result = $service->initiateRentCollection($id);
+
+ return $result;
+ }
}
diff --git a/custom/Espo/Custom/Resources/i18n/de_DE/CVmhMietverhltnis.json b/custom/Espo/Custom/Resources/i18n/de_DE/CVmhMietverhltnis.json
index 16f1c1f3..b95acc7a 100644
--- a/custom/Espo/Custom/Resources/i18n/de_DE/CVmhMietverhltnis.json
+++ b/custom/Espo/Custom/Resources/i18n/de_DE/CVmhMietverhltnis.json
@@ -42,6 +42,16 @@
"vmhMietobjekt": "Mietobjekt"
},
"labels": {
- "Create CVmhMietverhltnis": "Mietverhältnis erstellen"
+ "Create CVmhMietverhltnis": "Mietverhältnis erstellen",
+ "Initiate Eviction": "Räumungsklage einleiten",
+ "Initiate Rent Collection": "Mietinkasso einleiten"
+ },
+ "messages": {
+ "confirmEviction": "Möchten Sie wirklich eine Räumungsklage aus diesem Mietverhältnis einleiten?",
+ "evictionCreated": "Räumungsklage wurde erfolgreich erstellt",
+ "evictionError": "Fehler beim Erstellen der Räumungsklage",
+ "confirmRentCollection": "Möchten Sie wirklich ein Mietinkasso aus diesem Mietverhältnis einleiten?",
+ "rentCollectionCreated": "Mietinkasso wurde erfolgreich erstellt",
+ "rentCollectionError": "Fehler beim Erstellen des Mietinkassos"
}
}
\ No newline at end of file
diff --git a/custom/Espo/Custom/Resources/i18n/en_US/CVmhMietverhltnis.json b/custom/Espo/Custom/Resources/i18n/en_US/CVmhMietverhltnis.json
index f47e5b7a..9b4b1962 100644
--- a/custom/Espo/Custom/Resources/i18n/en_US/CVmhMietverhltnis.json
+++ b/custom/Espo/Custom/Resources/i18n/en_US/CVmhMietverhltnis.json
@@ -45,7 +45,17 @@
"vmhMietobjekt": "Property"
},
"labels": {
- "Create CVmhMietverhltnis": "Create Mietverhältnis"
+ "Create CVmhMietverhltnis": "Create Mietverhältnis",
+ "Initiate Eviction": "Initiate Eviction Lawsuit",
+ "Initiate Rent Collection": "Initiate Rent Collection"
+ },
+ "messages": {
+ "confirmEviction": "Do you really want to initiate an eviction lawsuit from this tenancy?",
+ "evictionCreated": "Eviction lawsuit has been created successfully",
+ "evictionError": "Error creating eviction lawsuit",
+ "confirmRentCollection": "Do you really want to initiate rent collection from this tenancy?",
+ "rentCollectionCreated": "Rent collection has been created successfully",
+ "rentCollectionError": "Error creating rent collection"
},
"options": {
"status": {
diff --git a/custom/Espo/Custom/Resources/metadata/clientDefs/CMietobjekt.json b/custom/Espo/Custom/Resources/metadata/clientDefs/CMietobjekt.json
index d56ba627..21d6326f 100644
--- a/custom/Espo/Custom/Resources/metadata/clientDefs/CMietobjekt.json
+++ b/custom/Espo/Custom/Resources/metadata/clientDefs/CMietobjekt.json
@@ -5,22 +5,6 @@
],
"color": "#dea185",
"iconClass": "fas fa-house",
- "menu": {
- "detail": {
- "buttons": [
- {
- "label": "Räumungsklage einleiten",
- "name": "initiateEviction",
- "iconHtml": "",
- "style": "danger",
- "acl": "edit",
- "handler": "custom:handlers/mietobjekt/eviction-action",
- "initFunction": "initEvictionAction",
- "actionFunction": "initiateEviction"
- }
- ]
- }
- },
"relationshipPanels": {
"vmhMietverhltnises2Mietobjekt": {
"layout": null,
diff --git a/custom/Espo/Custom/Resources/metadata/clientDefs/CVmhMietverhltnis.json b/custom/Espo/Custom/Resources/metadata/clientDefs/CVmhMietverhltnis.json
index fa2ebc38..d23a758e 100644
--- a/custom/Espo/Custom/Resources/metadata/clientDefs/CVmhMietverhltnis.json
+++ b/custom/Espo/Custom/Resources/metadata/clientDefs/CVmhMietverhltnis.json
@@ -3,6 +3,32 @@
"boolFilterList": [
"onlyMy"
],
+ "menu": {
+ "detail": {
+ "buttons": [
+ {
+ "name": "initiateEviction",
+ "label": "Initiate Eviction",
+ "handler": "custom:handlers/mietverhaeltnis/eviction-action",
+ "initFunction": "initInitiateEviction",
+ "actionFunction": "actionInitiateEviction",
+ "iconHtml": "",
+ "style": "danger",
+ "acl": "edit"
+ },
+ {
+ "name": "initiateRentCollection",
+ "label": "Initiate Rent Collection",
+ "handler": "custom:handlers/mietverhaeltnis/rent-collection-action",
+ "initFunction": "initInitiateRentCollection",
+ "actionFunction": "actionInitiateRentCollection",
+ "iconHtml": "",
+ "style": "warning",
+ "acl": "edit"
+ }
+ ]
+ }
+ },
"sidePanels": {
"detail": [
{
diff --git a/custom/Espo/Custom/Services/CMietobjekt.php b/custom/Espo/Custom/Services/CMietobjekt.php
deleted file mode 100644
index de1cd882..00000000
--- a/custom/Espo/Custom/Services/CMietobjekt.php
+++ /dev/null
@@ -1,132 +0,0 @@
-entityManager->getEntity('CMietobjekt', $mietobjektId);
- if (!$mietobjekt) {
- throw new NotFound('Mietobjekt not found');
- }
-
- // Check ACL - Read and Create permissions
- if (!$this->acl->check($mietobjekt, 'read')) {
- throw new Forbidden('No read access to Mietobjekt');
- }
-
- if (!$this->acl->checkScope('CVmhRumungsklage', 'create')) {
- throw new Forbidden('No create access to Räumungsklage');
- }
-
- // Start transaction to ensure atomicity
- $this->entityManager->getTransactionManager()->start();
-
- try {
- // Prepare data for new Räumungsklage
- $data = new \stdClass();
- $data->name = 'Räumungsklage - ' . $mietobjekt->get('name');
-
- // Copy assignedUser and teams
- if ($mietobjekt->get('assignedUserId')) {
- $data->assignedUserId = $mietobjekt->get('assignedUserId');
- }
-
- $teamsIds = $mietobjekt->getLinkMultipleIdList('teams');
- if (!empty($teamsIds)) {
- $data->teamsIds = $teamsIds;
- }
-
- // Create Räumungsklage entity
- $raeumungsklage = $this->entityManager->createEntity('CVmhRumungsklage', (array)$data);
-
- if (!$raeumungsklage) {
- throw new \RuntimeException('Failed to create Räumungsklage');
- }
-
- $raeumungsklagenRepo = $this->entityManager->getRepository('CVmhRumungsklage');
-
- // Link Mietobjekt to Räumungsklage
- $raeumungsklagenRepo
- ->getRelation($raeumungsklage, 'mietobjekte')
- ->relate($mietobjekt);
-
- // Get and link Mietverhältnisse
- $mietverhaeltnisse = $this->entityManager
- ->getRepository('CMietobjekt')
- ->getRelation($mietobjekt, 'vmhMietverhltnises')
- ->find();
-
- foreach ($mietverhaeltnisse as $mietverhaeltnis) {
- // Link Mietverhältnis to Räumungsklage
- $raeumungsklagenRepo
- ->getRelation($raeumungsklage, 'vmhMietverhltnises')
- ->relate($mietverhaeltnis);
-
- // Get Vermieter (Kläger) from Mietverhältnis
- $vermieterBeteiligte = $this->entityManager
- ->getRepository('CVmhMietverhltnis')
- ->getRelation($mietverhaeltnis, 'vmhbeteiligtevermieter')
- ->find();
-
- foreach ($vermieterBeteiligte as $vermieter) {
- // Link as Kläger
- $raeumungsklagenRepo
- ->getRelation($raeumungsklage, 'klaeger')
- ->relate($vermieter);
- }
-
- // Get Mieter (Beklagte) from Mietverhältnis
- $mieterBeteiligte = $this->entityManager
- ->getRepository('CVmhMietverhltnis')
- ->getRelation($mietverhaeltnis, 'vmhbeteiligtemieter')
- ->find();
-
- foreach ($mieterBeteiligte as $mieter) {
- // Link as Beklagte
- $raeumungsklagenRepo
- ->getRelation($raeumungsklage, 'beklagte')
- ->relate($mieter);
- }
- }
-
- // Copy portal contacts from Mietobjekt
- $portalContacts = $this->entityManager
- ->getRepository('CMietobjekt')
- ->getRelation($mietobjekt, 'contactsMietobjekt')
- ->find();
-
- foreach ($portalContacts as $contact) {
- $raeumungsklagenRepo
- ->getRelation($raeumungsklage, 'contactsRumungsklage')
- ->relate($contact);
- }
-
- // Commit transaction
- $this->entityManager->getTransactionManager()->commit();
-
- return [
- 'id' => $raeumungsklage->getId(),
- 'name' => $raeumungsklage->get('name')
- ];
- } catch (\Exception $e) {
- // Rollback on any error
- $this->entityManager->getTransactionManager()->rollback();
- throw $e;
- }
- }
-}
diff --git a/custom/Espo/Custom/Services/CVmhMietverhltnis.php b/custom/Espo/Custom/Services/CVmhMietverhltnis.php
new file mode 100644
index 00000000..b28c3081
--- /dev/null
+++ b/custom/Espo/Custom/Services/CVmhMietverhltnis.php
@@ -0,0 +1,371 @@
+entityManager->getEntity('CVmhMietverhltnis', $mietverhaeltnisId);
+ if (!$mietverhaeltnis) {
+ throw new NotFound('Mietverhältnis not found');
+ }
+
+ // 2. ACL Checks
+ if (!$this->acl->check($mietverhaeltnis, 'read')) {
+ throw new Forbidden('No read access to Mietverhältnis');
+ }
+
+ if (!$this->acl->checkScope('CVmhRumungsklage', 'create')) {
+ throw new Forbidden('No create access to Räumungsklage');
+ }
+
+ // 3. Start Transaction
+ $this->entityManager->getTransactionManager()->start();
+
+ try {
+ // 4. Prepare data for new Räumungsklage
+ $data = new \stdClass();
+ $data->name = 'Räumungsklage - ' . $mietverhaeltnis->get('name');
+
+ // Copy assignedUser and teams
+ if ($mietverhaeltnis->get('assignedUserId')) {
+ $data->assignedUserId = $mietverhaeltnis->get('assignedUserId');
+ }
+
+ $teamsIds = $mietverhaeltnis->getLinkMultipleIdList('teams');
+ if (!empty($teamsIds)) {
+ $data->teamsIds = $teamsIds;
+ }
+
+ // 5. Create Räumungsklage entity
+ $raeumungsklage = $this->entityManager->createEntity('CVmhRumungsklage', (array)$data);
+
+ if (!$raeumungsklage) {
+ throw new \RuntimeException('Failed to create Räumungsklage');
+ }
+
+ $raeumungsklagenRepo = $this->entityManager->getRepository('CVmhRumungsklage');
+
+ // 6. Link Mietverhältnis to Räumungsklage
+ $raeumungsklagenRepo
+ ->getRelation($raeumungsklage, 'vmhMietverhltnises')
+ ->relate($mietverhaeltnis);
+
+ // 7. Get and link Mietobjekt
+ $mietobjekt = $this->entityManager
+ ->getRepository('CVmhMietverhltnis')
+ ->getRelation($mietverhaeltnis, 'vmhMietobjekt')
+ ->findOne();
+
+ if ($mietobjekt) {
+ $raeumungsklagenRepo
+ ->getRelation($raeumungsklage, 'mietobjekte')
+ ->relate($mietobjekt);
+ }
+
+ // 8. Get Vermieter (Kläger) from Mietverhältnis
+ $vermieterBeteiligte = $this->entityManager
+ ->getRepository('CVmhMietverhltnis')
+ ->getRelation($mietverhaeltnis, 'vmhbeteiligtevermieter')
+ ->find();
+
+ foreach ($vermieterBeteiligte as $vermieter) {
+ // Link as Kläger
+ $raeumungsklagenRepo
+ ->getRelation($raeumungsklage, 'klaeger')
+ ->relate($vermieter);
+ }
+
+ // 9. Get Mieter (Beklagte) from Mietverhältnis
+ $mieterBeteiligte = $this->entityManager
+ ->getRepository('CVmhMietverhltnis')
+ ->getRelation($mietverhaeltnis, 'vmhbeteiligtemieter')
+ ->find();
+
+ foreach ($mieterBeteiligte as $mieter) {
+ // Link as Beklagte
+ $raeumungsklagenRepo
+ ->getRelation($raeumungsklage, 'beklagte')
+ ->relate($mieter);
+ }
+
+ // 9b. Get Sonstige Bewohner (auch Beklagte) from Mietverhältnis
+ $sonstigeBewohner = $this->entityManager
+ ->getRepository('CVmhMietverhltnis')
+ ->getRelation($mietverhaeltnis, 'sonstigebesitzervmhmietverhltnis')
+ ->find();
+
+ foreach ($sonstigeBewohner as $bewohner) {
+ // Link as Beklagte
+ $raeumungsklagenRepo
+ ->getRelation($raeumungsklage, 'beklagte')
+ ->relate($bewohner);
+ }
+
+ // 10. Copy all documents from Mietverhältnis, Mietobjekt and Beteiligte
+ // 10a. Dokumente vom Mietverhältnis
+ $dokumenteMV = $this->entityManager
+ ->getRepository('CVmhMietverhltnis')
+ ->getRelation($mietverhaeltnis, 'dokumentesvmhMietverhltnisse')
+ ->find();
+
+ foreach ($dokumenteMV as $dokument) {
+ $raeumungsklagenRepo
+ ->getRelation($raeumungsklage, 'dokumentesvmhraumungsklage')
+ ->relate($dokument);
+ }
+
+ // 10b. Dokumente vom Mietobjekt
+ if ($mietobjekt) {
+ $dokumenteMO = $this->entityManager
+ ->getRepository('CMietobjekt')
+ ->getRelation($mietobjekt, 'dokumentesMietobjekt')
+ ->find();
+
+ foreach ($dokumenteMO as $dokument) {
+ $raeumungsklagenRepo
+ ->getRelation($raeumungsklage, 'dokumentesvmhraumungsklage')
+ ->relate($dokument);
+ }
+ }
+
+ // 10c. Dokumente von allen Beteiligten (Vermieter + Mieter + Sonstige)
+ $alleBeteiligte = array_merge(
+ iterator_to_array($vermieterBeteiligte),
+ iterator_to_array($mieterBeteiligte),
+ iterator_to_array($sonstigeBewohner)
+ );
+
+ foreach ($alleBeteiligte as $beteiligter) {
+ $dokumenteBet = $this->entityManager
+ ->getRepository('CBeteiligte')
+ ->getRelation($beteiligter, 'dokumentesBeteiligte')
+ ->find();
+
+ foreach ($dokumenteBet as $dokument) {
+ $raeumungsklagenRepo
+ ->getRelation($raeumungsklage, 'dokumentesvmhraumungsklage')
+ ->relate($dokument);
+ }
+ }
+
+ // 11. Copy portal contacts from Mietverhältnis (nur vom Mietverhältnis!)
+ $portalContacts = $this->entityManager
+ ->getRepository('CVmhMietverhltnis')
+ ->getRelation($mietverhaeltnis, 'contactsMietverhltnis')
+ ->find();
+
+ foreach ($portalContacts as $contact) {
+ $raeumungsklagenRepo
+ ->getRelation($raeumungsklage, 'contactsRumungsklage')
+ ->relate($contact);
+ }
+
+ // 12. Commit transaction
+ $this->entityManager->getTransactionManager()->commit();
+
+ return [
+ 'id' => $raeumungsklage->getId(),
+ 'name' => $raeumungsklage->get('name')
+ ];
+ } catch (\Exception $e) {
+ // Rollback on any error
+ $this->entityManager->getTransactionManager()->rollback();
+ throw $e;
+ }
+ }
+
+ /**
+ * Initiate rent collection (Mietinkasso) from Mietverhältnis
+ *
+ * @param string $mietverhaeltnisId
+ * @return array
+ * @throws NotFound
+ * @throws Forbidden
+ */
+ public function initiateRentCollection(string $mietverhaeltnisId): array
+ {
+ // 1. Load Mietverhältnis
+ $mietverhaeltnis = $this->entityManager->getEntity('CVmhMietverhltnis', $mietverhaeltnisId);
+ if (!$mietverhaeltnis) {
+ throw new NotFound('Mietverhältnis not found');
+ }
+
+ // 2. ACL Checks
+ if (!$this->acl->check($mietverhaeltnis, 'read')) {
+ throw new Forbidden('No read access to Mietverhältnis');
+ }
+
+ if (!$this->acl->checkScope('CMietinkasso', 'create')) {
+ throw new Forbidden('No create access to Mietinkasso');
+ }
+
+ // 3. Start Transaction
+ $this->entityManager->getTransactionManager()->start();
+
+ try {
+ // 4. Prepare data for new Mietinkasso
+ $data = new \stdClass();
+ $data->name = 'Mietinkasso - ' . $mietverhaeltnis->get('name');
+
+ // Copy assignedUser and teams
+ if ($mietverhaeltnis->get('assignedUserId')) {
+ $data->assignedUserId = $mietverhaeltnis->get('assignedUserId');
+ }
+
+ $teamsIds = $mietverhaeltnis->getLinkMultipleIdList('teams');
+ if (!empty($teamsIds)) {
+ $data->teamsIds = $teamsIds;
+ }
+
+ // 5. Create Mietinkasso entity
+ $mietinkasso = $this->entityManager->createEntity('CMietinkasso', (array)$data);
+
+ if (!$mietinkasso) {
+ throw new \RuntimeException('Failed to create Mietinkasso');
+ }
+
+ $mietinkassoRepo = $this->entityManager->getRepository('CMietinkasso');
+
+ // 6. Link Mietverhältnis to Mietinkasso
+ $mietinkassoRepo
+ ->getRelation($mietinkasso, 'vmhMietverhltnises')
+ ->relate($mietverhaeltnis);
+
+ // 7. Get and link Mietobjekt
+ $mietobjekt = $this->entityManager
+ ->getRepository('CVmhMietverhltnis')
+ ->getRelation($mietverhaeltnis, 'vmhMietobjekt')
+ ->findOne();
+
+ if ($mietobjekt) {
+ $mietinkassoRepo
+ ->getRelation($mietinkasso, 'mietobjekte')
+ ->relate($mietobjekt);
+ }
+
+ // 8. Get Vermieter (Kläger) from Mietverhältnis
+ $vermieterBeteiligte = $this->entityManager
+ ->getRepository('CVmhMietverhltnis')
+ ->getRelation($mietverhaeltnis, 'vmhbeteiligtevermieter')
+ ->find();
+
+ foreach ($vermieterBeteiligte as $vermieter) {
+ // Link as Kläger
+ $mietinkassoRepo
+ ->getRelation($mietinkasso, 'klaeger')
+ ->relate($vermieter);
+ }
+
+ // 9. Get Mieter (Beklagte) from Mietverhältnis
+ $mieterBeteiligte = $this->entityManager
+ ->getRepository('CVmhMietverhltnis')
+ ->getRelation($mietverhaeltnis, 'vmhbeteiligtemieter')
+ ->find();
+
+ foreach ($mieterBeteiligte as $mieter) {
+ // Link as Beklagte
+ $mietinkassoRepo
+ ->getRelation($mietinkasso, 'beklagte')
+ ->relate($mieter);
+ }
+
+ // 9b. Get Sonstige Bewohner (auch Beklagte) from Mietverhältnis
+ $sonstigeBewohner = $this->entityManager
+ ->getRepository('CVmhMietverhltnis')
+ ->getRelation($mietverhaeltnis, 'sonstigebesitzervmhmietverhltnis')
+ ->find();
+
+ foreach ($sonstigeBewohner as $bewohner) {
+ // Link as Beklagte
+ $mietinkassoRepo
+ ->getRelation($mietinkasso, 'beklagte')
+ ->relate($bewohner);
+ }
+
+ // 10. Copy all documents from Mietverhältnis, Mietobjekt and Beteiligte
+ // 10a. Dokumente vom Mietverhältnis
+ $dokumenteMV = $this->entityManager
+ ->getRepository('CVmhMietverhltnis')
+ ->getRelation($mietverhaeltnis, 'dokumentesvmhMietverhltnisse')
+ ->find();
+
+ foreach ($dokumenteMV as $dokument) {
+ $mietinkassoRepo
+ ->getRelation($mietinkasso, 'dokumentesmietinkasso')
+ ->relate($dokument);
+ }
+
+ // 10b. Dokumente vom Mietobjekt
+ if ($mietobjekt) {
+ $dokumenteMO = $this->entityManager
+ ->getRepository('CMietobjekt')
+ ->getRelation($mietobjekt, 'dokumentesMietobjekt')
+ ->find();
+
+ foreach ($dokumenteMO as $dokument) {
+ $mietinkassoRepo
+ ->getRelation($mietinkasso, 'dokumentesmietinkasso')
+ ->relate($dokument);
+ }
+ }
+
+ // 10c. Dokumente von allen Beteiligten (Vermieter + Mieter + Sonstige)
+ $alleBeteiligte = array_merge(
+ iterator_to_array($vermieterBeteiligte),
+ iterator_to_array($mieterBeteiligte),
+ iterator_to_array($sonstigeBewohner)
+ );
+
+ foreach ($alleBeteiligte as $beteiligter) {
+ $dokumenteBet = $this->entityManager
+ ->getRepository('CBeteiligte')
+ ->getRelation($beteiligter, 'dokumentesBeteiligte')
+ ->find();
+
+ foreach ($dokumenteBet as $dokument) {
+ $mietinkassoRepo
+ ->getRelation($mietinkasso, 'dokumentesmietinkasso')
+ ->relate($dokument);
+ }
+ }
+
+ // 11. Copy portal contacts from Mietverhältnis (nur vom Mietverhältnis!)
+ $portalContacts = $this->entityManager
+ ->getRepository('CVmhMietverhltnis')
+ ->getRelation($mietverhaeltnis, 'contactsMietverhltnis')
+ ->find();
+
+ foreach ($portalContacts as $contact) {
+ $mietinkassoRepo
+ ->getRelation($mietinkasso, 'contactsMietinkasso')
+ ->relate($contact);
+ }
+
+ // 12. Commit transaction
+ $this->entityManager->getTransactionManager()->commit();
+
+ return [
+ 'id' => $mietinkasso->getId(),
+ 'name' => $mietinkasso->get('name')
+ ];
+ } catch (\Exception $e) {
+ // Rollback on any error
+ $this->entityManager->getTransactionManager()->rollback();
+ throw $e;
+ }
+ }
+}
diff --git a/data/config.php b/data/config.php
index 7f8ddeca..990aa67d 100644
--- a/data/config.php
+++ b/data/config.php
@@ -359,8 +359,8 @@ return [
0 => 'youtube.com',
1 => 'google.com'
],
- 'cacheTimestamp' => 1769252191,
- 'microtime' => 1769252191.810945,
+ 'cacheTimestamp' => 1769253751,
+ 'microtime' => 1769253751.94842,
'siteUrl' => 'https://crm.bitbylaw.com',
'fullTextSearchMinLength' => 4,
'appTimestamp' => 1768843902,