Refactor AdvowareAkte ↔ CDokumente relationship from junction table to direct n:1 relationship
- Removed CAdvowareAktenCDokumente junction table and associated service. - Updated CDokumente entity to include foreign key cAdvowareAktenId and related fields. - Changed relationship in CDokumente from hasMany to belongsTo. - Updated CAdvowareAkten to reflect new direct relationship. - Implemented CDokumente service with duplicateDocument method for document duplication. - Refactored hooks to support new relationship and document propagation. - Removed obsolete API routes related to the junction table. - Added i18n translations for new fields and updated tooltips. - Document flow and auto-linking logic enhanced for better integration with Advoware. - Validation checks passed, and no data migration needed.
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
namespace Espo\Custom\Services;
|
||||
|
||||
use Espo\Services\Record;
|
||||
|
||||
/**
|
||||
* Junction Service: CAdvowareAkten ↔ CDokumente
|
||||
*
|
||||
* Handles business logic for the junction table.
|
||||
*/
|
||||
class CAdvowareAktenCDokumente extends Record
|
||||
{
|
||||
// Standard CRUD logic inherited from Record service
|
||||
}
|
||||
140
custom/Espo/Custom/Services/CDokumente.php
Normal file
140
custom/Espo/Custom/Services/CDokumente.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
namespace Espo\Custom\Services;
|
||||
|
||||
use Espo\Services\Record;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\Exceptions\{Forbidden, NotFound, BadRequest};
|
||||
use Espo\Core\FileStorage\Manager as FileStorageManager;
|
||||
use Espo\Core\Utils\File\Manager as FileManager;
|
||||
|
||||
/**
|
||||
* Service: CDokumente
|
||||
*/
|
||||
class CDokumente extends Record
|
||||
{
|
||||
public function __construct(
|
||||
private FileStorageManager $fileStorageManager,
|
||||
private FileManager $fileManager
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a document entity including its attachment file
|
||||
*
|
||||
* This creates a complete copy of a document with:
|
||||
* - All entity fields (name, description, etc.)
|
||||
* - Physical attachment file copied to new location
|
||||
* - Recalculated blake3hash
|
||||
* - Reset fileStatus to 'new'
|
||||
*
|
||||
* @param string $documentId Source document ID to duplicate
|
||||
* @return Entity New CDokumente entity
|
||||
* @throws NotFound If document doesn't exist
|
||||
* @throws Forbidden If no read access
|
||||
* @throws BadRequest If document has no attachment
|
||||
*/
|
||||
public function duplicateDocument(string $documentId): Entity
|
||||
{
|
||||
// 1. Load source document
|
||||
$sourceDoc = $this->entityManager->getEntity('CDokumente', $documentId);
|
||||
if (!$sourceDoc) {
|
||||
throw new NotFound('Document not found');
|
||||
}
|
||||
|
||||
// 2. ACL Check
|
||||
if (!$this->acl->check($sourceDoc, 'read')) {
|
||||
throw new Forbidden('No read access to document');
|
||||
}
|
||||
|
||||
// 3. Get source attachment
|
||||
$sourceAttachmentId = $sourceDoc->get('dokumentId');
|
||||
if (!$sourceAttachmentId) {
|
||||
throw new BadRequest('Document has no attachment');
|
||||
}
|
||||
|
||||
$sourceAttachment = $this->entityManager->getEntity('Attachment', $sourceAttachmentId);
|
||||
if (!$sourceAttachment) {
|
||||
throw new BadRequest('Source attachment not found');
|
||||
}
|
||||
|
||||
try {
|
||||
// 4. Copy attachment file physically
|
||||
$newAttachment = $this->duplicateAttachment($sourceAttachment);
|
||||
|
||||
// 5. Create new document entity
|
||||
$newDoc = $this->entityManager->getEntity('CDokumente');
|
||||
|
||||
// Copy all relevant fields
|
||||
$newDoc->set([
|
||||
'name' => $sourceDoc->get('name'),
|
||||
'description' => $sourceDoc->get('description'),
|
||||
'dokumentId' => $newAttachment->getId(),
|
||||
'assignedUserId' => $sourceDoc->get('assignedUserId'),
|
||||
'fileStatus' => 'new' // Reset to 'new'
|
||||
]);
|
||||
|
||||
// Copy teams
|
||||
$teamsIds = $sourceDoc->getLinkMultipleIdList('teams');
|
||||
if (!empty($teamsIds)) {
|
||||
$newDoc->setLinkMultipleIdList('teams', $teamsIds);
|
||||
}
|
||||
|
||||
// Copy preview if exists
|
||||
if ($sourceDoc->get('previewId')) {
|
||||
$sourcePreview = $this->entityManager->getEntity('Attachment', $sourceDoc->get('previewId'));
|
||||
if ($sourcePreview) {
|
||||
$newPreview = $this->duplicateAttachment($sourcePreview);
|
||||
$newDoc->set('previewId', $newPreview->getId());
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Save new document (this will trigger blake3hash calculation via Hook)
|
||||
$this->entityManager->saveEntity($newDoc);
|
||||
|
||||
// 7. Return new document
|
||||
return $newDoc;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('CDokumente duplicateDocument Error: ' . $e->getMessage());
|
||||
throw new \RuntimeException('Failed to duplicate document: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate an attachment entity including physical file
|
||||
*
|
||||
* @param Entity $sourceAttachment Source attachment to duplicate
|
||||
* @return Entity New attachment entity
|
||||
*/
|
||||
private function duplicateAttachment(Entity $sourceAttachment): Entity
|
||||
{
|
||||
// 1. Get source file path
|
||||
$sourceFilePath = $this->fileStorageManager->getLocalFilePath($sourceAttachment);
|
||||
|
||||
if (!file_exists($sourceFilePath)) {
|
||||
throw new \RuntimeException('Source file not found: ' . $sourceFilePath);
|
||||
}
|
||||
|
||||
// 2. Read source file content
|
||||
$fileContent = $this->fileManager->getContents($sourceFilePath);
|
||||
|
||||
// 3. Create new attachment entity
|
||||
$newAttachment = $this->entityManager->getEntity('Attachment');
|
||||
$newAttachment->set([
|
||||
'name' => $sourceAttachment->get('name'),
|
||||
'type' => $sourceAttachment->get('type'),
|
||||
'size' => $sourceAttachment->get('size'),
|
||||
'role' => $sourceAttachment->get('role') ?? 'Attachment',
|
||||
'storageFilePath' => null // Will be set by putContents
|
||||
]);
|
||||
|
||||
$this->entityManager->saveEntity($newAttachment);
|
||||
|
||||
// 4. Write file content to new location
|
||||
$this->fileStorageManager->putContents($newAttachment, $fileContent);
|
||||
|
||||
// 5. Return new attachment
|
||||
return $newAttachment;
|
||||
}
|
||||
}
|
||||
@@ -130,19 +130,27 @@ class CVmhMietverhltnis extends \Espo\Services\Record
|
||||
}
|
||||
|
||||
// 10. Copy all documents from Mietverhältnis, Mietobjekt and Beteiligte
|
||||
// 10a. Dokumente vom Mietverhältnis
|
||||
// Get CDokumente service for duplication
|
||||
$dokumenteService = $this->injectableFactory->create(\Espo\Custom\Services\CDokumente::class);
|
||||
|
||||
// 10a. Dokumente vom Mietverhältnis - DUPLICATE instead of relate
|
||||
$dokumenteMV = $this->entityManager
|
||||
->getRepository('CVmhMietverhltnis')
|
||||
->getRelation($mietverhaeltnis, 'dokumentesvmhMietverhltnisse')
|
||||
->find();
|
||||
|
||||
foreach ($dokumenteMV as $dokument) {
|
||||
$mietinkassoRepo
|
||||
->getRelation($mietinkasso, 'dokumentesmietinkasso')
|
||||
->relate($dokument);
|
||||
try {
|
||||
$duplicatedDoc = $dokumenteService->duplicateDocument($dokument->getId());
|
||||
$mietinkassoRepo
|
||||
->getRelation($mietinkasso, 'dokumentesmietinkasso')
|
||||
->relate($duplicatedDoc);
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('Failed to duplicate document from Mietverhältnis: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 10b. Dokumente vom Mietobjekt
|
||||
// 10b. Dokumente vom Mietobjekt - DUPLICATE instead of relate
|
||||
if ($mietobjekt) {
|
||||
$dokumenteMO = $this->entityManager
|
||||
->getRepository('CMietobjekt')
|
||||
@@ -150,13 +158,18 @@ class CVmhMietverhltnis extends \Espo\Services\Record
|
||||
->find();
|
||||
|
||||
foreach ($dokumenteMO as $dokument) {
|
||||
$mietinkassoRepo
|
||||
->getRelation($mietinkasso, 'dokumentesmietinkasso')
|
||||
->relate($dokument);
|
||||
try {
|
||||
$duplicatedDoc = $dokumenteService->duplicateDocument($dokument->getId());
|
||||
$mietinkassoRepo
|
||||
->getRelation($mietinkasso, 'dokumentesmietinkasso')
|
||||
->relate($duplicatedDoc);
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('Failed to duplicate document from Mietobjekt: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 10c. Dokumente von allen Beteiligten (Vermieter + Mieter + Sonstige)
|
||||
// 10c. Dokumente von allen Beteiligten (Vermieter + Mieter + Sonstige) - DUPLICATE instead of relate
|
||||
$alleBeteiligte = array_merge(
|
||||
iterator_to_array($vermieterBeteiligte),
|
||||
iterator_to_array($mieterBeteiligte),
|
||||
@@ -170,9 +183,14 @@ class CVmhMietverhltnis extends \Espo\Services\Record
|
||||
->find();
|
||||
|
||||
foreach ($dokumenteBet as $dokument) {
|
||||
$mietinkassoRepo
|
||||
->getRelation($mietinkasso, 'dokumentesmietinkasso')
|
||||
->relate($dokument);
|
||||
try {
|
||||
$duplicatedDoc = $dokumenteService->duplicateDocument($dokument->getId());
|
||||
$mietinkassoRepo
|
||||
->getRelation($mietinkasso, 'dokumentesmietinkasso')
|
||||
->relate($duplicatedDoc);
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('Failed to duplicate document from Beteiligter: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -267,7 +267,10 @@ class CVmhRumungsklage extends \Espo\Services\Record
|
||||
// 7. Collect all documents from Mietverhältnisse, Kündigungen, Mietobjekte and Beteiligte
|
||||
$alleLinkedDokumente = [];
|
||||
|
||||
// 7a. Dokumente from all Mietverhältnisse
|
||||
// Get CDokumente service for duplication
|
||||
$dokumenteService = $this->injectableFactory->create(\Espo\Custom\Services\CDokumente::class);
|
||||
|
||||
// 7a. Dokumente from all Mietverhältnisse - DUPLICATE instead of relate
|
||||
foreach ($alleMietverhaeltnisse as $mv) {
|
||||
$dokumenteMV = $this->entityManager
|
||||
->getRepository('CVmhMietverhltnis')
|
||||
@@ -277,14 +280,21 @@ class CVmhRumungsklage extends \Espo\Services\Record
|
||||
foreach ($dokumenteMV as $dokument) {
|
||||
if (!in_array($dokument->getId(), $alleLinkedDokumente)) {
|
||||
$alleLinkedDokumente[] = $dokument->getId();
|
||||
$raeumungsklagenRepo
|
||||
->getRelation($raeumungsklage, 'dokumentesvmhraumungsklage')
|
||||
->relate($dokument);
|
||||
|
||||
// Duplicate document instead of relate
|
||||
try {
|
||||
$duplicatedDoc = $dokumenteService->duplicateDocument($dokument->getId());
|
||||
$raeumungsklagenRepo
|
||||
->getRelation($raeumungsklage, 'dokumentesvmhraumungsklage')
|
||||
->relate($duplicatedDoc);
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('Failed to duplicate document from Mietverhältnis: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 7b. Dokumente from all Kündigungen
|
||||
// 7b. Dokumente from all Kündigungen - DUPLICATE instead of relate
|
||||
foreach ($alleKuendigungen as $kuendigung) {
|
||||
$dokumenteKuendigung = $this->entityManager
|
||||
->getRepository('CKuendigung')
|
||||
@@ -294,14 +304,21 @@ class CVmhRumungsklage extends \Espo\Services\Record
|
||||
foreach ($dokumenteKuendigung as $dokument) {
|
||||
if (!in_array($dokument->getId(), $alleLinkedDokumente)) {
|
||||
$alleLinkedDokumente[] = $dokument->getId();
|
||||
$raeumungsklagenRepo
|
||||
->getRelation($raeumungsklage, 'dokumentesvmhraumungsklage')
|
||||
->relate($dokument);
|
||||
|
||||
// Duplicate document instead of relate
|
||||
try {
|
||||
$duplicatedDoc = $dokumenteService->duplicateDocument($dokument->getId());
|
||||
$raeumungsklagenRepo
|
||||
->getRelation($raeumungsklage, 'dokumentesvmhraumungsklage')
|
||||
->relate($duplicatedDoc);
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('Failed to duplicate document from Kündigung: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 7c. Dokumente from all Mietobjekte
|
||||
// 7c. Dokumente from all Mietobjekte - DUPLICATE instead of relate
|
||||
foreach ($alleMietobjekte as $mietobjekt) {
|
||||
$dokumenteMO = $this->entityManager
|
||||
->getRepository('CMietobjekt')
|
||||
@@ -311,14 +328,21 @@ class CVmhRumungsklage extends \Espo\Services\Record
|
||||
foreach ($dokumenteMO as $dokument) {
|
||||
if (!in_array($dokument->getId(), $alleLinkedDokumente)) {
|
||||
$alleLinkedDokumente[] = $dokument->getId();
|
||||
$raeumungsklagenRepo
|
||||
->getRelation($raeumungsklage, 'dokumentesvmhraumungsklage')
|
||||
->relate($dokument);
|
||||
|
||||
// Duplicate document instead of relate
|
||||
try {
|
||||
$duplicatedDoc = $dokumenteService->duplicateDocument($dokument->getId());
|
||||
$raeumungsklagenRepo
|
||||
->getRelation($raeumungsklage, 'dokumentesvmhraumungsklage')
|
||||
->relate($duplicatedDoc);
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('Failed to duplicate document from Mietobjekt: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 7d. Dokumente from all Beteiligte
|
||||
// 7d. Dokumente from all Beteiligte - DUPLICATE instead of relate
|
||||
$alleBeteiligte = array_merge($alleVermieter, $alleMieter, $alleSonstigeBewohner);
|
||||
foreach ($alleBeteiligte as $beteiligter) {
|
||||
$dokumenteBet = $this->entityManager
|
||||
@@ -329,9 +353,16 @@ class CVmhRumungsklage extends \Espo\Services\Record
|
||||
foreach ($dokumenteBet as $dokument) {
|
||||
if (!in_array($dokument->getId(), $alleLinkedDokumente)) {
|
||||
$alleLinkedDokumente[] = $dokument->getId();
|
||||
$raeumungsklagenRepo
|
||||
->getRelation($raeumungsklage, 'dokumentesvmhraumungsklage')
|
||||
->relate($dokument);
|
||||
|
||||
// Duplicate document instead of relate
|
||||
try {
|
||||
$duplicatedDoc = $dokumenteService->duplicateDocument($dokument->getId());
|
||||
$raeumungsklagenRepo
|
||||
->getRelation($raeumungsklage, 'dokumentesvmhraumungsklage')
|
||||
->relate($duplicatedDoc);
|
||||
} catch (\Exception $e) {
|
||||
$GLOBALS['log']->error('Failed to duplicate document from Beteiligter: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user