diff --git a/application/Espo/Core/Action/Actions/Merge/Merger.php b/application/Espo/Core/Action/Actions/Merge/Merger.php index a9f5fb71bb..f779920cac 100644 --- a/application/Espo/Core/Action/Actions/Merge/Merger.php +++ b/application/Espo/Core/Action/Actions/Merge/Merger.php @@ -46,6 +46,7 @@ use Espo\ORM\Entity; use Espo\Entities\EmailAddress; use Espo\Entities\PhoneNumber; +use Espo\ORM\Type\RelationType; use stdClass; class Merger @@ -255,14 +256,24 @@ class Merger private function updateRelations(Entity $sourceEntity, Entity $targetEntity, string $link): void { + $entityType = $sourceEntity->getEntityType(); + + $columnAttributeMap = $this->getLinkColumnAttributeMap($entityType, $link); + $collection = $this->entityManager ->getRelation($sourceEntity, $link) ->find(); foreach ($collection as $relatedEntity) { + $map = null; + + if ($columnAttributeMap) { + $map = array_map(fn ($attribute) => $relatedEntity->get($attribute), $columnAttributeMap); + } + $this->entityManager ->getRelation($targetEntity, $link) - ->relate($relatedEntity); + ->relate($relatedEntity, $map); } } @@ -385,4 +396,35 @@ class Merger } } } + + /** + * @return ?array + */ + private function getLinkColumnAttributeMap(string $entityType, string $link): ?array + { + $entityDefs = $this->entityManager + ->getDefs() + ->getEntity($entityType); + + $columnAttributeMap = null; + + $relationDefs = $entityDefs->tryGetRelation($link); + + if ( + $relationDefs && + $relationDefs->getType() === RelationType::MANY_MANY && + $relationDefs->hasForeignEntityType() && + $relationDefs->hasForeignRelationName() + ) { + $foreignRelationDefs = $this->entityManager + ->getDefs() + ->getEntity($relationDefs->getForeignEntityType()) + ->getRelation($relationDefs->getForeignRelationName()); + + /** ?@var array $columnAttributeMap */ + $columnAttributeMap = $foreignRelationDefs->getParam('columnAttributeMap'); + } + + return $columnAttributeMap; + } } diff --git a/tests/integration/Espo/Actions/ActionTest.php b/tests/integration/Espo/Actions/ActionTest.php index da96aca66a..e7e8f0012e 100644 --- a/tests/integration/Espo/Actions/ActionTest.php +++ b/tests/integration/Espo/Actions/ActionTest.php @@ -38,9 +38,11 @@ use Espo\Core\Field\EmailAddress; use Espo\Core\Field\PhoneNumber; use Espo\Core\ORM\EntityManager; +use Espo\Modules\Crm\Entities\Account; use Espo\Modules\Crm\Entities\Contact; +use tests\integration\Core\BaseTestCase; -class ActionTest extends \tests\integration\Core\BaseTestCase +class ActionTest extends BaseTestCase { /** @var Application */ private $app; @@ -273,6 +275,47 @@ class ActionTest extends \tests\integration\Core\BaseTestCase ); } + public function testMergeRelationshipColumns(): void + { + $em = $this->getEntityManager(); + + $account = $em->getRDBRepositoryByClass(Account::class)->getNew(); + $accountSource = $em->getRDBRepositoryByClass(Account::class)->getNew(); + $contact = $em->getRDBRepositoryByClass(Contact::class)->getNew(); + + $em->saveEntity($account); + $em->saveEntity($accountSource); + $em->saveEntity($contact); + + $em->getRelation($accountSource, 'contacts') + ->relate($contact, [ + 'role' => 'Tester', + 'isInactive' => true, + ]); + + $merger = $this->getInjectableFactory()->create(Merger::class); + + /** @noinspection PhpUnhandledExceptionInspection */ + $merger->process( + new Params(Account::ENTITY_TYPE, $account->getId()), + [$accountSource->getId()], + (object) [] + ); + + $em->refreshEntity($account); + + $relation = $em->getRelation($account, 'contacts'); + + $this->assertEquals( + 'Tester', + $relation->getColumn($contact, 'role') + ); + + $this->assertTrue( + $relation->getColumn($contact, 'isInactive') + ); + } + public function testMergeNoEditAccess(): void { $this->createUser('tester', [