From 82efcefac74da43040112432533902b350ffb142 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Mon, 10 Jul 2023 11:19:58 +0300 Subject: [PATCH] duplicate check inprovements --- .../DuplicateWhereBuilders/Company.php | 84 +----- .../DuplicateWhereBuilders/General.php | 262 ++++++++++++++++++ .../Classes/DuplicateWhereBuilders/Person.php | 91 +----- .../Espo/Core/Templates/Entities/Base.php | 5 +- .../Espo/Core/Templates/Entities/BasePlus.php | 5 +- .../Templates/Metadata/Base/recordDefs.json | 3 + .../Metadata/BasePlus/recordDefs.json | 3 + .../Metadata/Company/recordDefs.json | 4 +- .../Templates/Metadata/Company/scopes.json | 5 +- .../Templates/Metadata/Person/recordDefs.json | 4 +- .../Templates/Metadata/Person/scopes.json | 5 +- .../Resources/i18n/en_US/EntityManager.json | 4 +- .../metadata/app/entityManagerParams.json | 24 ++ .../metadata/recordDefs/Account.json | 2 +- .../metadata/recordDefs/Contact.json | 2 +- .../Resources/metadata/recordDefs/Lead.json | 2 +- .../Resources/metadata/scopes/Account.json | 3 +- .../Resources/metadata/scopes/Contact.json | 3 +- .../Crm/Resources/metadata/scopes/Lead.json | 3 +- .../Resources/i18n/en_US/EntityManager.json | 6 +- .../metadata/app/entityManagerParams.json | 50 ++++ .../fields/duplicate-check-field-list.js | 68 +++++ upgrades/7.6/scripts/AfterUpgrade.php | 48 ++++ 23 files changed, 495 insertions(+), 191 deletions(-) create mode 100644 application/Espo/Classes/DuplicateWhereBuilders/General.php create mode 100644 application/Espo/Core/Templates/Metadata/Base/recordDefs.json create mode 100644 application/Espo/Core/Templates/Metadata/BasePlus/recordDefs.json create mode 100644 client/src/views/admin/entity-manager/fields/duplicate-check-field-list.js diff --git a/application/Espo/Classes/DuplicateWhereBuilders/Company.php b/application/Espo/Classes/DuplicateWhereBuilders/Company.php index 4f0aa751d9..608a9e4440 100644 --- a/application/Espo/Classes/DuplicateWhereBuilders/Company.php +++ b/application/Espo/Classes/DuplicateWhereBuilders/Company.php @@ -29,85 +29,5 @@ namespace Espo\Classes\DuplicateWhereBuilders; -use Espo\Core\Duplicate\WhereBuilder; -use Espo\Core\Field\EmailAddressGroup; -use Espo\Core\ORM\Entity as CoreEntity; - -use Espo\ORM\Entity; -use Espo\ORM\Query\Part\Condition as Cond; -use Espo\ORM\Query\Part\Where\OrGroup; -use Espo\ORM\Query\Part\WhereItem; - -/** - * @implements WhereBuilder - */ -class Company implements WhereBuilder -{ - /** - * @param CoreEntity $entity - */ - public function build(Entity $entity): ?WhereItem - { - $orBuilder = OrGroup::createBuilder(); - - $toCheck = false; - - if ($entity->get('name')) { - $orBuilder->add( - Cond::equal( - Cond::column('name'), - $entity->get('name') - ), - ); - - $toCheck = true; - } - - if ( - ($entity->get('emailAddress') || $entity->get('emailAddressData')) && - ( - $entity->isNew() || - $entity->isAttributeChanged('emailAddress') || - $entity->isAttributeChanged('emailAddressData') - ) - ) { - foreach ($this->getEmailAddressList($entity) as $emailAddress) { - $orBuilder->add( - Cond::equal( - Cond::column('emailAddress'), - $emailAddress - ) - ); - - $toCheck = true; - } - } - - if (!$toCheck) { - return null; - } - - return $orBuilder->build(); - } - - /** - * @return string[] - */ - private function getEmailAddressList(CoreEntity $entity): array - { - if ($entity->get('emailAddressData')) { - /** @var EmailAddressGroup $eaGroup */ - $eaGroup = $entity->getValueObject('emailAddress'); - - return $eaGroup->getAddressList(); - } - - if ($entity->get('emailAddress')) { - return [ - $entity->get('emailAddress') - ]; - } - - return []; - } -} +class Company extends General +{} diff --git a/application/Espo/Classes/DuplicateWhereBuilders/General.php b/application/Espo/Classes/DuplicateWhereBuilders/General.php new file mode 100644 index 0000000000..3af1f5f7f0 --- /dev/null +++ b/application/Espo/Classes/DuplicateWhereBuilders/General.php @@ -0,0 +1,262 @@ + + */ +class General implements WhereBuilder +{ + public function __construct( + private Metadata $metadata, + private Defs $ormDefs + ) {} + + /** + * @param CoreEntity $entity + */ + public function build(Entity $entity): ?WhereItem + { + /** @var string[] $fieldList */ + $fieldList = $this->metadata->get(['scopes', $entity->getEntityType(), 'duplicateCheckFieldList']) ?? []; + + $orBuilder = OrGroup::createBuilder(); + + $toCheck = false; + + foreach ($fieldList as $field) { + $toCheckItem = $this->applyField($field, $entity, $orBuilder); + + if ($toCheckItem) { + $toCheck = true; + } + } + + if (!$toCheck) { + return null; + } + + return $orBuilder->build(); + } + + private function applyField( + string $field, + CoreEntity $entity, + OrGroupBuilder $orBuilder + ): bool { + + $type = $this->ormDefs + ->getEntity($entity->getEntityType()) + ->tryGetField($field) + ?->getType(); + + if ($type === 'personName') { + return $this->applyFieldPersonName($field, $entity, $orBuilder); + } + + if ($type === 'email') { + return $this->applyFieldEmail($field, $entity, $orBuilder); + } + + if ($type === 'phone') { + return $this->applyFieldPhone($field, $entity, $orBuilder); + } + + if ($entity->getAttributeType($field) === AttributeType::VARCHAR) { + return $this->applyFieldVarchar($field, $entity, $orBuilder); + } + + return false; + } + + private function applyFieldPersonName( + string $field, + CoreEntity $entity, + OrGroupBuilder $orBuilder + ): bool { + + $first = 'first' . ucfirst($field); + $last = 'last' . ucfirst($field); + + if (!$entity->get($first) && !$entity->get($last)) { + return false; + } + + $orBuilder->add( + Cond::and( + Cond::equal( + Cond::column($first), + $entity->get($first) + ), + Cond::equal( + Cond::column($last), + $entity->get($last) + ) + ) + ); + + return true; + } + + private function applyFieldEmail( + string $field, + CoreEntity $entity, + OrGroupBuilder $orBuilder + ): bool { + + $toCheck = false; + + if ( + ($entity->get($field) || $entity->get($field . 'Data')) && + ( + $entity->isNew() || + $entity->isAttributeChanged($field) || + $entity->isAttributeChanged($field . 'Data') + ) + ) { + foreach ($this->getEmailAddressList($entity) as $emailAddress) { + $orBuilder->add( + Cond::equal( + Cond::column($field), + $emailAddress + ) + ); + + $toCheck = true; + } + } + + return $toCheck; + } + + private function applyFieldPhone( + string $field, + CoreEntity $entity, + OrGroupBuilder $orBuilder + ): bool { + + $toCheck = false; + + if ( + ($entity->get($field) || $entity->get($field . 'Data')) && + ( + $entity->isNew() || + $entity->isAttributeChanged($field) || + $entity->isAttributeChanged($field . 'Data') + ) + ) { + foreach ($this->getPhoneNumberList($entity) as $phoneNumber) { + $orBuilder->add( + Cond::equal( + Cond::column($field), + $phoneNumber + ) + ); + + $toCheck = true; + } + } + + return $toCheck; + } + + private function applyFieldVarchar( + string $field, + CoreEntity $entity, + OrGroupBuilder $orBuilder + ): bool { + + if (!$entity->get($field)) { + return false; + } + + $orBuilder->add( + Cond::equal( + Cond::column($field), + $entity->get($field) + ), + ); + + return true; + } + + /** + * @return string[] + */ + private function getEmailAddressList(CoreEntity $entity): array + { + if ($entity->get('emailAddressData')) { + /** @var EmailAddressGroup $eaGroup */ + $eaGroup = $entity->getValueObject('emailAddress'); + + return $eaGroup->getAddressList(); + } + + if ($entity->get('emailAddress')) { + return [ + $entity->get('emailAddress') + ]; + } + + return []; + } + + /** + * @return string[] + */ + private function getPhoneNumberList(CoreEntity $entity): array + { + if ($entity->get('phoneNumberData')) { + /** @var PhoneNumberGroup $eaGroup */ + $eaGroup = $entity->getValueObject('phoneNumber'); + + return $eaGroup->getNumberList(); + } + + if ($entity->get('phoneNumber')) { + return [$entity->get('phoneNumber')]; + } + + return []; + } +} diff --git a/application/Espo/Classes/DuplicateWhereBuilders/Person.php b/application/Espo/Classes/DuplicateWhereBuilders/Person.php index 297624faaf..9e1e6f62ae 100644 --- a/application/Espo/Classes/DuplicateWhereBuilders/Person.php +++ b/application/Espo/Classes/DuplicateWhereBuilders/Person.php @@ -29,92 +29,5 @@ namespace Espo\Classes\DuplicateWhereBuilders; -use Espo\Core\ORM\Entity as CoreEntity; - -use Espo\Core\Duplicate\WhereBuilder; -use Espo\Core\Field\EmailAddressGroup; - -use Espo\ORM\Entity; -use Espo\ORM\Query\Part\Condition as Cond; -use Espo\ORM\Query\Part\Where\OrGroup; -use Espo\ORM\Query\Part\WhereItem; - -/** - * @implements WhereBuilder - */ -class Person implements WhereBuilder -{ - /** - * @param CoreEntity $entity - */ - public function build(Entity $entity): ?WhereItem - { - $orBuilder = OrGroup::createBuilder(); - - $toCheck = false; - - if ($entity->get('firstName') || $entity->get('lastName')) { - $orBuilder->add( - Cond::and( - Cond::equal( - Cond::column('firstName'), - $entity->get('firstName') - ), - Cond::equal( - Cond::column('lastName'), - $entity->get('lastName') - ) - ) - ); - - $toCheck = true; - } - - if ( - ($entity->get('emailAddress') || $entity->get('emailAddressData')) && - ( - $entity->isNew() || - $entity->isAttributeChanged('emailAddress') || - $entity->isAttributeChanged('emailAddressData') - ) - ) { - foreach ($this->getEmailAddressList($entity) as $emailAddress) { - $orBuilder->add( - Cond::equal( - Cond::column('emailAddress'), - $emailAddress - ) - ); - - $toCheck = true; - } - } - - if (!$toCheck) { - return null; - } - - return $orBuilder->build(); - } - - /** - * @return string[] - */ - private function getEmailAddressList(CoreEntity $entity): array - { - if ($entity->get('emailAddressData')) { - /** @var EmailAddressGroup $eaGroup */ - $eaGroup = $entity->getValueObject('emailAddress'); - - return $eaGroup->getAddressList(); - } - - if ($entity->get('emailAddress')) { - return [ - $entity->get('emailAddress') - ]; - } - - return []; - } -} +class Person extends General +{} diff --git a/application/Espo/Core/Templates/Entities/Base.php b/application/Espo/Core/Templates/Entities/Base.php index ef83fe94be..ea0c00a1ff 100644 --- a/application/Espo/Core/Templates/Entities/Base.php +++ b/application/Espo/Core/Templates/Entities/Base.php @@ -29,6 +29,9 @@ namespace Espo\Core\Templates\Entities; -class Base extends \Espo\Core\ORM\Entity +use Espo\Core\ORM\Entity; + +class Base extends Entity { + public const TEMPLATE_TYPE = 'Base'; } diff --git a/application/Espo/Core/Templates/Entities/BasePlus.php b/application/Espo/Core/Templates/Entities/BasePlus.php index b3c506c85f..6869ad5ff1 100644 --- a/application/Espo/Core/Templates/Entities/BasePlus.php +++ b/application/Espo/Core/Templates/Entities/BasePlus.php @@ -29,6 +29,9 @@ namespace Espo\Core\Templates\Entities; -class BasePlus extends \Espo\Core\ORM\Entity +use Espo\Core\ORM\Entity; + +class BasePlus extends Entity { + public const TEMPLATE_TYPE = 'BasePlus'; } diff --git a/application/Espo/Core/Templates/Metadata/Base/recordDefs.json b/application/Espo/Core/Templates/Metadata/Base/recordDefs.json new file mode 100644 index 0000000000..cfd6715672 --- /dev/null +++ b/application/Espo/Core/Templates/Metadata/Base/recordDefs.json @@ -0,0 +1,3 @@ +{ + "duplicateWhereBuilderClassName": "Espo\\Classes\\DuplicateWhereBuilders\\General" +} diff --git a/application/Espo/Core/Templates/Metadata/BasePlus/recordDefs.json b/application/Espo/Core/Templates/Metadata/BasePlus/recordDefs.json new file mode 100644 index 0000000000..cfd6715672 --- /dev/null +++ b/application/Espo/Core/Templates/Metadata/BasePlus/recordDefs.json @@ -0,0 +1,3 @@ +{ + "duplicateWhereBuilderClassName": "Espo\\Classes\\DuplicateWhereBuilders\\General" +} diff --git a/application/Espo/Core/Templates/Metadata/Company/recordDefs.json b/application/Espo/Core/Templates/Metadata/Company/recordDefs.json index 806dc8449e..cfd6715672 100644 --- a/application/Espo/Core/Templates/Metadata/Company/recordDefs.json +++ b/application/Espo/Core/Templates/Metadata/Company/recordDefs.json @@ -1,3 +1,3 @@ { - "duplicateWhereBuilderClassName": "Espo\\Classes\\DuplicateWhereBuilders\\Company" -} \ No newline at end of file + "duplicateWhereBuilderClassName": "Espo\\Classes\\DuplicateWhereBuilders\\General" +} diff --git a/application/Espo/Core/Templates/Metadata/Company/scopes.json b/application/Espo/Core/Templates/Metadata/Company/scopes.json index 62badf46e1..80235c7e4c 100644 --- a/application/Espo/Core/Templates/Metadata/Company/scopes.json +++ b/application/Espo/Core/Templates/Metadata/Company/scopes.json @@ -7,5 +7,6 @@ "aclPortalLevelList": ["all", "account", "contact", "own", "no"], "customizable": true, "importable": true, - "notifications": true -} \ No newline at end of file + "notifications": true, + "duplicateCheckFieldList": ["name", "emailAddress"] +} diff --git a/application/Espo/Core/Templates/Metadata/Person/recordDefs.json b/application/Espo/Core/Templates/Metadata/Person/recordDefs.json index 2bdd0bd295..cfd6715672 100644 --- a/application/Espo/Core/Templates/Metadata/Person/recordDefs.json +++ b/application/Espo/Core/Templates/Metadata/Person/recordDefs.json @@ -1,3 +1,3 @@ { - "duplicateWhereBuilderClassName": "Espo\\Classes\\DuplicateWhereBuilders\\Person" -} \ No newline at end of file + "duplicateWhereBuilderClassName": "Espo\\Classes\\DuplicateWhereBuilders\\General" +} diff --git a/application/Espo/Core/Templates/Metadata/Person/scopes.json b/application/Espo/Core/Templates/Metadata/Person/scopes.json index a7cafed656..63990678b2 100644 --- a/application/Espo/Core/Templates/Metadata/Person/scopes.json +++ b/application/Espo/Core/Templates/Metadata/Person/scopes.json @@ -8,5 +8,6 @@ "customizable": true, "importable": true, "notifications": true, - "hasPersonalData": true -} \ No newline at end of file + "hasPersonalData": true, + "duplicateCheckFieldList": ["name", "emailAddress"] +} diff --git a/application/Espo/Modules/Crm/Resources/i18n/en_US/EntityManager.json b/application/Espo/Modules/Crm/Resources/i18n/en_US/EntityManager.json index 97232c259c..0bd15f40bc 100644 --- a/application/Espo/Modules/Crm/Resources/i18n/en_US/EntityManager.json +++ b/application/Espo/Modules/Crm/Resources/i18n/en_US/EntityManager.json @@ -3,11 +3,9 @@ "activityStatusList": "Activity Statuses", "historyStatusList": "History Statuses", "completedStatusList": "Completed Statuses", - "canceledStatusList": "Canceled Statuses", - "updateDuplicateCheck": "Duplicate check on update" + "canceledStatusList": "Canceled Statuses" }, "tooltips": { - "updateDuplicateCheck": "Perform checking for duplicates when updating a record.", "activityStatusList": "Status values determining that an activity record should be displayed in the Activity panel and considered as actual.", "historyStatusList": "Status values determining that an activity record should be displayed in the History panel.", "completedStatusList": "Status values determining that an activity is completed.", diff --git a/application/Espo/Modules/Crm/Resources/metadata/app/entityManagerParams.json b/application/Espo/Modules/Crm/Resources/metadata/app/entityManagerParams.json index c59c1d2756..c17cac31d6 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/app/entityManagerParams.json +++ b/application/Espo/Modules/Crm/Resources/metadata/app/entityManagerParams.json @@ -6,6 +6,14 @@ "type": "bool", "tooltip": true } + }, + "duplicateCheckFieldList": { + "location": "scopes", + "fieldDefs": { + "type": "bool", + "tooltip": true, + "view": "views/admin/entity-manager/fields/duplicate-check-field-list" + } } }, "Contact": { @@ -15,6 +23,14 @@ "type": "bool", "tooltip": true } + }, + "duplicateCheckFieldList": { + "location": "scopes", + "fieldDefs": { + "type": "bool", + "tooltip": true, + "view": "views/admin/entity-manager/fields/duplicate-check-field-list" + } } }, "Lead": { @@ -24,6 +40,14 @@ "type": "bool", "tooltip": true } + }, + "duplicateCheckFieldList": { + "location": "scopes", + "fieldDefs": { + "type": "bool", + "tooltip": true, + "view": "views/admin/entity-manager/fields/duplicate-check-field-list" + } } }, "Meeting": { diff --git a/application/Espo/Modules/Crm/Resources/metadata/recordDefs/Account.json b/application/Espo/Modules/Crm/Resources/metadata/recordDefs/Account.json index 7cb561389c..cfd6715672 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/recordDefs/Account.json +++ b/application/Espo/Modules/Crm/Resources/metadata/recordDefs/Account.json @@ -1,3 +1,3 @@ { - "duplicateWhereBuilderClassName": "Espo\\Classes\\DuplicateWhereBuilders\\Company" + "duplicateWhereBuilderClassName": "Espo\\Classes\\DuplicateWhereBuilders\\General" } diff --git a/application/Espo/Modules/Crm/Resources/metadata/recordDefs/Contact.json b/application/Espo/Modules/Crm/Resources/metadata/recordDefs/Contact.json index 13d7b7df23..cfd6715672 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/recordDefs/Contact.json +++ b/application/Espo/Modules/Crm/Resources/metadata/recordDefs/Contact.json @@ -1,3 +1,3 @@ { - "duplicateWhereBuilderClassName": "Espo\\Classes\\DuplicateWhereBuilders\\Person" + "duplicateWhereBuilderClassName": "Espo\\Classes\\DuplicateWhereBuilders\\General" } diff --git a/application/Espo/Modules/Crm/Resources/metadata/recordDefs/Lead.json b/application/Espo/Modules/Crm/Resources/metadata/recordDefs/Lead.json index 13d7b7df23..cfd6715672 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/recordDefs/Lead.json +++ b/application/Espo/Modules/Crm/Resources/metadata/recordDefs/Lead.json @@ -1,3 +1,3 @@ { - "duplicateWhereBuilderClassName": "Espo\\Classes\\DuplicateWhereBuilders\\Person" + "duplicateWhereBuilderClassName": "Espo\\Classes\\DuplicateWhereBuilders\\General" } diff --git a/application/Espo/Modules/Crm/Resources/metadata/scopes/Account.json b/application/Espo/Modules/Crm/Resources/metadata/scopes/Account.json index 59321cbbe9..784a33b4a0 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/scopes/Account.json +++ b/application/Espo/Modules/Crm/Resources/metadata/scopes/Account.json @@ -10,5 +10,6 @@ "importable": true, "notifications": true, "object": true, - "hasPersonalData": true + "hasPersonalData": true, + "duplicateCheckFieldList": ["name", "emailAddress"] } diff --git a/application/Espo/Modules/Crm/Resources/metadata/scopes/Contact.json b/application/Espo/Modules/Crm/Resources/metadata/scopes/Contact.json index e513e12463..e484a3afd2 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/scopes/Contact.json +++ b/application/Espo/Modules/Crm/Resources/metadata/scopes/Contact.json @@ -10,5 +10,6 @@ "importable": true, "notifications": true, "object": true, - "hasPersonalData": true + "hasPersonalData": true, + "duplicateCheckFieldList": ["name", "emailAddress"] } diff --git a/application/Espo/Modules/Crm/Resources/metadata/scopes/Lead.json b/application/Espo/Modules/Crm/Resources/metadata/scopes/Lead.json index fafe1c7c36..c5502dbfbf 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/scopes/Lead.json +++ b/application/Espo/Modules/Crm/Resources/metadata/scopes/Lead.json @@ -11,5 +11,6 @@ "notifications": true, "object": true, "statusField": "status", - "hasPersonalData": true + "hasPersonalData": true, + "duplicateCheckFieldList": ["name", "emailAddress"] } diff --git a/application/Espo/Resources/i18n/en_US/EntityManager.json b/application/Espo/Resources/i18n/en_US/EntityManager.json index 1d8907f118..92aec2b21a 100644 --- a/application/Espo/Resources/i18n/en_US/EntityManager.json +++ b/application/Espo/Resources/i18n/en_US/EntityManager.json @@ -40,7 +40,9 @@ "fullTextSearch": "Full-Text Search", "parentEntityTypeList": "Parent Entity Types", "foreignLinkEntityTypeList": "Foreign Links", - "optimisticConcurrencyControl": "Optimistic concurrency control" + "optimisticConcurrencyControl": "Optimistic concurrency control", + "updateDuplicateCheck": "Duplicate check on update", + "duplicateCheckFieldList": "Duplicate check fields" }, "options": { "type": { @@ -75,6 +77,8 @@ "beforeSaveApiScript": "A script called on create and update API requests before an entity is saved. Use for custom validation and duplicate checking." }, "tooltips": { + "duplicateCheckFieldList": "Which fields to check when performing checking for duplicates.", + "updateDuplicateCheck": "Perform checking for duplicates when updating a record.", "optimisticConcurrencyControl": "Prevents writing conflicts.", "statusField": "Updates of this field are logged in stream.", "textFilterFields": "Fields used by text search.", diff --git a/application/Espo/Resources/metadata/app/entityManagerParams.json b/application/Espo/Resources/metadata/app/entityManagerParams.json index 8b62c9cabd..c7cb087487 100644 --- a/application/Espo/Resources/metadata/app/entityManagerParams.json +++ b/application/Espo/Resources/metadata/app/entityManagerParams.json @@ -15,6 +15,14 @@ "type": "bool", "tooltip": true } + }, + "duplicateCheckFieldList": { + "location": "scopes", + "fieldDefs": { + "type": "bool", + "tooltip": true, + "view": "views/admin/entity-manager/fields/duplicate-check-field-list" + } } }, "@Person": { @@ -24,6 +32,48 @@ "type": "bool", "tooltip": true } + }, + "duplicateCheckFieldList": { + "location": "scopes", + "fieldDefs": { + "type": "bool", + "tooltip": true, + "view": "views/admin/entity-manager/fields/duplicate-check-field-list" + } + } + }, + "@Base": { + "updateDuplicateCheck": { + "location": "recordDefs", + "fieldDefs": { + "type": "bool", + "tooltip": true + } + }, + "duplicateCheckFieldList": { + "location": "scopes", + "fieldDefs": { + "type": "bool", + "tooltip": true, + "view": "views/admin/entity-manager/fields/duplicate-check-field-list" + } + } + }, + "@BasePlus": { + "updateDuplicateCheck": { + "location": "recordDefs", + "fieldDefs": { + "type": "bool", + "tooltip": true + } + }, + "duplicateCheckFieldList": { + "location": "scopes", + "fieldDefs": { + "type": "bool", + "tooltip": true, + "view": "views/admin/entity-manager/fields/duplicate-check-field-list" + } } } } diff --git a/client/src/views/admin/entity-manager/fields/duplicate-check-field-list.js b/client/src/views/admin/entity-manager/fields/duplicate-check-field-list.js new file mode 100644 index 0000000000..33874366ff --- /dev/null +++ b/client/src/views/admin/entity-manager/fields/duplicate-check-field-list.js @@ -0,0 +1,68 @@ +/************************************************************************ + * This file is part of EspoCRM. + * + * EspoCRM - Open Source CRM application. + * Copyright (C) 2014-2023 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko + * Website: https://www.espocrm.com + * + * EspoCRM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EspoCRM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EspoCRM. If not, see http://www.gnu.org/licenses/. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU General Public License version 3. + * + * In accordance with Section 7(b) of the GNU General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +import MultiEnumFieldView from 'views/fields/multi-enum'; + +class DuplicateFieldListCheckEntityManagerFieldView extends MultiEnumFieldView { + + fieldTypeList = [ + 'varchar', + 'personName', + 'email', + 'phone', + 'url', + 'barcode', + ] + + setupOptions() { + let entityType = this.model.get('name'); + + let options = + this.getFieldManager() + .getEntityTypeFieldList(entityType, { + typeList: this.fieldTypeList, + onlyAvailable: true, + }) + .sort((a, b) => { + return this.getLanguage().translate(a, 'fields', this.entityType) + .localeCompare( + this.getLanguage().translate(b, 'fields', this.entityType) + ); + }); + + this.translatedOptions = {}; + + options.forEach(item => { + this.translatedOptions[item] = this.translate(item, 'fields', entityType); + }) + + this.params.options = options; + } +} + +export default DuplicateFieldListCheckEntityManagerFieldView; diff --git a/upgrades/7.6/scripts/AfterUpgrade.php b/upgrades/7.6/scripts/AfterUpgrade.php index acdf5015b2..427a214cec 100644 --- a/upgrades/7.6/scripts/AfterUpgrade.php +++ b/upgrades/7.6/scripts/AfterUpgrade.php @@ -32,6 +32,11 @@ use Espo\Entities\Role; use Espo\ORM\EntityManager; use Espo\ORM\Query\Part\Expression; use Espo\ORM\Query\UpdateBuilder; +use Espo\Core\Templates\Entities\Company; +use Espo\Core\Templates\Entities\Person; +use Espo\Core\Templates\Entities\Base; +use Espo\Core\Templates\Entities\BasePlus; +use Espo\Core\Utils\Metadata; class AfterUpgrade { @@ -40,6 +45,10 @@ class AfterUpgrade $this->updateRoles( $container->getByClass(EntityManager::class) ); + + $this->updateMetadata( + $container->getByClass(Metadata::class) + ); } private function updateRoles(EntityManager $entityManager): void @@ -51,4 +60,43 @@ class AfterUpgrade $entityManager->getQueryExecutor()->execute($query); } + + private function updateMetadata(Metadata $metadata): void + { + $defs = $metadata->get(['scopes']); + + foreach ($defs as $entityType => $item) { + $isCustom = $item['isCustom'] ?? false; + $type = $item['type'] ?? false; + + if (!$isCustom) { + continue; + } + + if ( + !in_array($type, [ + BasePlus::TEMPLATE_TYPE, + Base::TEMPLATE_TYPE, + Company::TEMPLATE_TYPE, + Person::TEMPLATE_TYPE + ]) + ) { + continue; + } + + $recordDefs = $metadata->getCustom('recordDefs', $entityType) ?? (object) []; + $scopes = $metadata->getCustom('scopes', $entityType) ?? (object) []; + + $recordDefs->duplicateWhereBuilderClassName = "Espo\\Classes\\DuplicateWhereBuilders\\General"; + + $scopes->duplicateCheckFieldList = []; + + if ($type === Company::TEMPLATE_TYPE || $type === Person::TEMPLATE_TYPE) { + $scopes->duplicateCheckFieldList = ['name', 'emailAddress']; + } + + $metadata->saveCustom('recordDefs', $entityType, $recordDefs); + $metadata->saveCustom('scopes', $entityType, $scopes); + } + } }