Здравствуйте! На 7.16.1 необходимо настроить поиск дублей в Контрагенте по кастомному полю (ИНН). Добавил хранимую процедуру и зарегистрировал её, как описано в данной статье: https://academy.terrasoft.ru/documents/technic-sdk/7-11/dobavlenie-pravila-poiska-dubley?_ga=2.85605657.1051876412.1592819009-1256785345.1592483512. Установил признак "Использовать при сохранении", но при сохранении не отрабатывает, хотя при массовом поиске дубли находит. При этом стандартные правила отрабатывают при сохранении, если включить этот признак. 
Название кастомного поля: InfTIN
Ссылка на код процедуры: https://pastebin.com/Gwir4eDg
Ссылка на код регистрации правила: https://pastebin.com/L9uVR8kR

Нравится

9 комментариев

Константин, на первый взгляд, Ваши хранимки отличаются от примера только названиями полей. Но это инструкция к 7.11, это очень старая версия. Сейчас функциональность поиска дублей завязана на на работу сервиса глобального поиска.

 

Что интересно, если в Вашей ссылке поменять версию на 7.16, заголовок сменится на Добавление правила массового поиска дублей | Creatio Academy и содержит примечание:

Описанный процесс добавления правила массового поиска дублей актуален для Creatio версий 7.13.2 и ниже. Для версий системы 7.13.4 и выше функциональность поиска дублей описана в статье “Поиск и объединение дублей”, а работа с правилами поиска дублей при сохранении записи — в статье "Добавление правила поиска дублей при сохранении записи".

Поскольку у Вас 7.16.1, эта статья не подходит, выберите подходящую статью.

 

Зверев Александр,

В самом начале данной статьи есть предупреждение:

Описанный процесс добавления правила поиска дублей актуален для Creatio версий 7.13.2 и ниже. Для версий системы 7.13.4 и выше функциональность поиска дублей описана в статье “Поиск и объединение дублей”, а работа с правилами массового поиска дублей — в статье "Добавление правила массового поиска дублей".

При этом процедура работает, похожа на стандартные процедуры, которые работают при сохранении, при установке признака "Использовать при сохранении".
Не хотелось бы завязывать поиск дублей на функциональности для версий 7.13.2 и ниже, ведь если в последующих обновлениях её поправят, то отвалится поиск дублей. 

Для версии 7.16 поиск дублей описан в статьях «Как выполнить поиск дублей», «Как работает поиск дублей» и «Правила поиска дублей», там хранимки уже не упоминаются. Уверены, что те стандартные способы, с которыми сравниваете, используют такие хранимки?

Зверев Александр,

В статье "Правила поиска дублей" описывается достаточно странный процесс, потому что в "Правила поиска дублей" нет кнопки добавить. 

При этом, стандартные правила используют такие же хранимые процедуры. Например, правило поиска дублей контрагента по имени: https://pastebin.com/iKnzi8Nm

Кроме того, все правила описаны в таблице DuplicatesRule, которая явно указывает на хранимые процедуры.

Судя по результатам выборки, хранимки есть не у всех правил, даже активных.

Зверев Александр,

Но по статье для 7.16 необходимо замещение модулей, а с версии 7.13 оно запрещено

Где именно? Статья «Добавление правила поиска дублей при сохранении записи», несмотря на адрес с цифрой 16, тоже, похоже, относится к версии до 7.13.2.

 

По изначальному вопросу: если у Вас 7.16.1, те правила поиска, которые с хранимкой и наподобие которых Вы хотите сделать своё, точно ли отрабатывают или остались от старых версий?

 

Зверев Александр,

Да, я именно про эту статью. 

Они, скорее всего, остались от старых версий, но они отрабатывают.

В DuplicatesRuleManager в функции проверяются условия для правил:

/// <inheritdoc cref="IDuplicatesRuleManager.GetDuplicatesRules(string)"/>
public IEnumerable<DuplicatesRuleDTO> GetDuplicatesRules(string schemaName) {
	return GetAllDuplicatesRules()
		.Where(rule => rule.SchemaName == schemaName &&
			(schemaName == LeadSchemaName ? rule.IsActive : rule.UseAtSave) &&
			(string.IsNullOrEmpty(rule.SearchSchemaName) || rule.SearchSchemaName == schemaName));
}

Проверьте, все ли они выполняются для Вашего.

Показать все комментарии

Здравствуйте, уважаемое сообщество!

В базе имеется много Контрагентов внесённых вручную, сделали интеграцию Контрагентов из 1С при помощи 1С Коннектора, естественно появились дубли

Но заметил, что при объединении дублей остаётся идентификатор старого объекта, а не нового который я выбираю... всё бы ничего но мне нужен новый идентификатор, который был присвоен после интеграции так как по нему идёт связь с Коннектором в таблице SmrSyncHistory для дальнешей синхронизации

Вопрос, как повлиять на то какой объект оставить в системе во время объединения дублей, а какой удалить?

Проверяю следующим запросом к базе:

select * from Account where name = 'Высший Вкус ООО (ВВ)';
select * from SmrSyncHistory where localid in (select id from Account where name = 'Высший Вкус ООО (ВВ)');

 

До объединения:

 

После объединения:

 

Нравится

5 комментариев
Лучший ответ

Gorbunov Alexander, Если акция разовая, то можно сделать апдейт даты в таблице на какую-то более старую.

Пока в стандартном механизме платформы нет выбора золотой записи.
В случае использования 1C Connector вы можете сразу настроить признак [Использовать для дедупликации] по полю Название, чтобы дубли вообще не создавались. Подробнее -https://samarasoft.com/docs/1c-connector/integration-setting-bpmonline/fields-mapping/

В стандартном механизме в качестве золотой берётся первое значение в коллекции. В схеме DeduplicationMergeHandler функция MergeEntityDublicates начинается так:

public void MergeEntityDublicates(string schemaName, int groupId, List<Guid> duplicateRecordIds, Dictionary<string, string> resolvedConflicts) {
	EntitySchema entitySchema = _userConnection.EntitySchemaManager.GetInstanceByName(schemaName);
	EntityCollection duplicates = GetEntityDublicates(schemaName, duplicateRecordIds);
	Entity goldenEntity = duplicates.FirstOrDefault();
	if (goldenEntity == null) {
		return;
	}
	duplicates.RemoveFirst();
	duplicateRecordIds.Remove(goldenEntity.PrimaryColumnValue);
	if (duplicates.Count == 0) {
		return;
	}

А коллекция отсортирована по дате создания, старые вначале:

public EntityCollection GetEntityDublicates(string schemaName, List<Guid> ids, List<string> columns = null) {
	EntitySchema schema = _userConnection.EntitySchemaManager
		.GetInstanceByName(schemaName);
	var esq = new EntitySchemaQuery(schema);
	if (columns == null) {
		esq.AddAllSchemaColumns();
	} else {
		esq.PrimaryQueryColumn.IsAlwaysSelect = true;
		if (!columns.Contains("CreatedOn")) {
			esq.AddColumn("CreatedOn");
		}
		foreach (string columnName in columns) {
			esq.AddColumn(columnName);
		}
	}
	esq.Filters.Add(esq.CreateFilterWithParameters(FilterComparisonType.Equal,
		esq.RootSchema.PrimaryColumn.Name, ids.Cast<object>()));
	EntityCollection entityCollection = esq.GetEntityCollection(_userConnection);
	entityCollection.Order("CreatedOn", OrderDirection.Ascending);
	return entityCollection;
}

Вы можете либо изменить эту логику, но учитывая возможные побочные эффекты, либо же последовать совету Александра, чтобы избежать создания дублей.

Здравствуйте!

Спасибо за советы, только сейчас пролучилось вернуться к этому кейсу

Я так понимаю что включать признак дедупликации в Коннекторе уже поздно, ну или чистить записи и проводить интеграцию заново

Хочу попробовать сделать по совету Зверева Александра, но не могу найти статью или ответы на комьюнити как правильно замещать "Исходный код", я так понял надо просто поменять Ascending на Descending.

Gorbunov Alexander, Если акция разовая, то можно сделать апдейт даты в таблице на какую-то более старую.

Трефилов Павел Сергеевич,

ОМАЙГАДБЛ... всё гениальное просто, спасибо, попробую ))))))))

Показать все комментарии

Sales Enterprise

Версия 7.13.3.481

Настраиваю новое правило поиска дублей по полю  "Основной телефон" согласно инструкции:
https://academy.terrasoft.ru/documents/technic-sdk/7-13/dobavlenie-pravila-poiska-dubley
Признак "Использовать правило при сохранении" установлен: https://yadi.sk/i/546AVOF8xgA95A

Но при создании очевидного дубля по полю основной телефон новая запись благополучно сохраняется.

При запуске глобального поиска дублей поиск по новому правилу отрабатывает корректно.

 

Нравится

7 комментариев

Вероятно, новое правило было создано неверно. Если самостоятельно разобраться в инструкции не получилось, попробуйте обратиться в техническую поддержку, описав свои шаги подробнее и предоствив доступ к серверу.

Правило отрабатывает корректно при глобальном поиске.
Дополнительных настроек кроме признака "Использовать при сохранении" в академии не нашёл.

Если бы правило было настроено верно, оно бы отрабатывало везде. Кроме простановки галочки ещё нужно написать хранимую процедуру, о чём написано в инструкции.

Так же выявлено, что и базовые правила не отрабатывают при сохранении

Базовые правила должны отрабатывать при сохранении контакта и контрагента.

О сохранении Контрагента и идёт речь

Попробуйте развернуть демо-версию того же продукта и проверить на ней. Если там дубли проверяются, то дело в доработках. Если тоже нет, то сообщите в поддержку.

Показать все комментарии

Перестал работать поиск дублей при сохранении записи "Контакт", "Контрагент", хотя поиск дублей через действия раздела работает корректно.

Признак правила поиска "Использовать правило при сохранении" установлен.

Провел отладку: метод findDuplicates() модуля DuplicatesSearchUtilitiesV2 корректно передает данные в DeduplicationService.FindDuplicatesOnSave()

В свою очередь метод сервиса FindDuplicatesOnSave вызывает метод DeduplicationProcessing.FindDuplicates, из которого идет обращение к хранимой процедуре tsp_FindDuplicates.

Код хранимой процедуры не отличается от кода этой же процедуры на чистой базе.

В результате в callback метод падает пустой массив дублей, хотя в разделе есть полностью аналогичные записи.

В чем может быть проблема? Есть подозрение, что проблема появилась после перехода на версию 7.12

Нравится

1 комментарий

Для включения поиска дублей при сохранении необходимо открыть Дизайнер системы -> Правила поиска дублей, выбрать необходимое правило и установить признак Проверять при сохранении.
Рекомендую использовать правила поиска по нескольким полям, это позволит избежать проблем с производительностью и выбором записей при сохранении(меньше записей будет в выборке).

Если это сделано, дело ещё может быть в том, что в разделе Контакты есть несколько карточек редактирования. Работа в таком режиме была исправлена в 7.12.2.

Рекомендуем обновить до версии 7.12.2, после обновления проблема будет решена. А на версии 7.12.3 также работает поиск дублей при сохранении при создании контакта через мини-карточку.

Показать все комментарии

Коллеги, выручайте. У меня были записи в разделе, которые создали разные контакты, но данные контакты по факту являются дубликатами.
Сделал поиск дубликатов для контактов, объединил найденные дубликаты. 

И получилось так, что у части записей затерлось поле "Создал", select возвращает данное поле со старым id создавшего, которого по факту уже нет так-как он объединился.

Как вернуть все обратно? Если возможность посмотреть логи дубликатов?

Я ожидал, что после объединения дубликатов, система сама перезапишет во всех записях поле создал на новый id.

Нравится

1 комментарий

Добрый день!
 

Дедупликация записи - это не создание новой.
Фактически, Вы удаляете одну из записей, и обновляете значения оставшейся.

Вернуть результат дедупликации пользовательскими средствами нельзя.
Вы можете поднять бэкап БД до обновления и перенести значения CreatedBy при помощи запроса в БД.

Показать все комментарии

Здравствуйте, хотел написать свое правило поиска дублей для контактов пример того как сделать взял с академии но во время выполнения скрипта появляется ошибка что переменная @parsedConfig имеет недопустимый тип данных. и еще в таблице ContactDuplicateSearchResult нету поля SysAdminUnitId вместо него идет поле GroupHash. Как изменить скрипт чтобы он заработал? bpm 7.8.0

-- Проверка наличия хранимой процедуры с именем tsp_FindContactDuplicateByNameAndContragent.
IF NOT OBJECT_ID('[dbo].[tsp_FindContactDuplicateByNameAndContragent]') IS NULL
BEGIN
    -- Удаление хранимой процедуры.
    DROP PROCEDURE [dbo].[tsp_FindContactDuplicateByNameAndContragent];
END;
GO
-- Создание хранимой процедуры.
CREATE PROCEDURE [dbo].[tsp_FindContactDuplicateByNameAndContragent] (
    -- Этот табличный параметр передается только в случае сохранения нового контакта.
    -- Содержит данные нового контакта.
    -- В случае запуска процесса глобального поиска дублей переданный параметр не содержит данных.
    @parsedConfig CreatingObjectInfo READONLY,
    -- Уникальный идентификатор пользователя, который запустил поиск дублей.
    @sysAdminUnit UNIQUEIDENTIFIER,
    -- Идентификатор текущего правила из таблицы [ContactDuplicateSearchResult].
    -- Этот идентификатор создается после регистрации правила в системе.
    @ruleId UNIQUEIDENTIFIER
)
AS
BEGIN
    -- Получение количества записей из принимаемой таблицы для определения запуска глобального поиска дублей.
    DECLARE @parsedConfigRowsCount INT = (SELECT COUNT(*) FROM @parsedConfig);
    -- Создание временной таблицы с данными контактов для поиска.
    CREATE TABLE #searchContact (
        [Name] nvarchar(250),
                [AccountId] uniqueidentifier,
        [SortDate] DATETIME
    );
    -- В случае глобального поиска выполняется заполнение временной таблицы данными.
    IF @parsedConfigRowsCount = 0
    BEGIN
        -- Добавление во временную таблицу данных для поиска дублей.
        INSERT INTO #searchContact ([Name], [AccountId],[SortDate])
        -- Запрос на выборку данных контактов.
        SELECT
            -- Выбираются колонки ИНН даты модификации контакта.
            [Name],[AccountId],
            MAX([ModifiedOn])
        FROM [Contact]
        -- Добавляется группировка по полям для возможности использовать проверку на количество.
        GROUP BY [Name],[AccountId]
        -- Таблица заполняется только в случае наличия более одного контакта.
        HAVING COUNT(*) > 1;
    END;
   
    -- Заполнение таблицы результатов.
    INSERT INTO [ContactDuplicateSearchResult] ([ContactId], [GroupId], [RuleId], [SysAdminUnitId])
    SELECT
        -- Идентификатор дубля контакта.
        [vr].[Id],
        -- Формирование номера группы.
        DENSE_RANK() OVER (ORDER BY [vr].[SortDate] DESC, [vr].[Name],[vr].[AccountId]),
        -- Идентификатор правила.
        @ruleId RuleId,
        -- Идентификатор пользователя, под которым запущен процесс поиска дублей.
        @sysAdminUnit
    FROM (
        -- Подзапрос, из которого заполняется таблица дублей.
        SELECT
            -- Идентификатор контакта.
            [v].[Id],
            --ФИО контакта.
            [v].[Name],
           --ИД контрагента.
            [v].[AccountId],
            -- Дата сортировки.
            [r].[SortDate]
        -- Таблицы, из которых берутся данные.
        FROM [Contact] [v], #searchContact r
        -- Правило, по которому определяется, что контакты — дубли.
        WHERE [v].[Name] = [r].[Name] AND [v].[AccountId] = [r].[AccountId]
        -- Группировка результата поиска.
        GROUP BY [v].[Name],[v].[AccountId], [r].[SortDate], [v].[Id]
    ) [vr];
END;
GO

Нравится

3 комментария

Здравствуйте, я новичок в системе. Но насколько, я понял, статья https://academy.terrasoft.ru/documents/technic-sdk/7-8/dobavlenie-pravi… с примером справедлива для bpm 7.8.3 и выше... В версии 7.8.0 в таблице DuplicatesRule нет поля "Название процедуры"... Поэтому непонятно, как будет происходить вызов вновь созданной процедуры в 7.8.0, если только изменить DeduplicationSearch...

Здравствуйте,
Правила дедупликации расположены в таблице [DuplicatesRule]
Там же в колонке [ProcedureName] указаны имена хранимых процедур, ответственных за обработку.
Почитайте код любой из данных процедур и напишите свой по аналогии.
К примеру, во вложении, код процедуры [tsp_FindAccountDuplicateByWeb]

new_text_document.txt

Здраствуйте
1. Касательно этого блока:
IF @parsedConfigRowsCount = 0
BEGIN
-- Добавление во временную таблицу данных для поиска дублей.
INSERT INTO #searchContact ([Name], [AccountId],[SortDate])
-- Запрос на выборку данных контактов.
SELECT
-- Выбираются колонки ИНН даты модификации контакта.
[Name],[AccountId],
MAX([ModifiedOn])
FROM [Contact]
-- Добавляется группировка по полям для возможности использовать проверку на количество.
GROUP BY [Name],[AccountId]
-- Таблица заполняется только в случае наличия более одного контакта.
HAVING COUNT(*) > 1;
END;

Я бы сделал как в базовой хранимке:
IF @parsedConfigRowsCount = 0
BEGIN
INSERT INTO #searchAccount ([Name], [SortDate])
SELECT
[dedup].[Name],
MAX([dedup].[SortDate]) [SortDate]
FROM (
SELECT [Id],
[dbo].[fn_NormalizeString]([Name], @validChar) AS [Name],
MAX([ModifiedOn]) [SortDate]
FROM [Account]
GROUP BY [Id], [Name]
) AS [dedup]
GROUP BY [dedup].[Name]
HAVING COUNT(*) > 1;
END;
ELSE
BEGIN
INSERT INTO #searchAccount ([Name], [SortDate])
SELECT
[Name],
GETDATE() AS [SortDate]
FROM @parsedConfig
END;
2. В версии 7.8 в таблице ContactDuplicateSearchResult были поля ContactId, GroupId, RuleId, GroupHash
А где-то с 7.8.3 добавилось ещё поле SysAdminUnitId
Но GroupHash не добавлялось и раньше, было так:
INSERT INTO [ContactDuplicateSearchResult] ([ContactId], [GroupId], [RuleId])
А теперь так:
INSERT INTO [ContactDuplicateSearchResult] ([ContactId], [GroupId], [RuleId], [SysAdminUnitId])

3. Что касается "Названия процедуры", то это поле действительно появилось с 7.8.3. Так что инструкция с академии подходит именно для версий 7.8.3+, т.к.

Показать все комментарии

Доброго дня!
Хочу делегировать право поиска и слияния дублей нескольким пользователям, т.к. сейчас это доступно только администратору.
Подскажите, как правильно это сделать?
Какие объекты выбрать и какое включить администрирование для них?

Нравится

1 комментарий

Здравствуйте!

Права на поиск и объединение дублей ограничены системной операцией "Поиск дублей"
Для решения:
1) Перейдите в "Дизайнер системы"
2) Перейдите в "Права доступа на операции"
3) Найдите операцию "Поиск дублей"
4) Выдайте права нужным пользователям.

Показать все комментарии

подскажите, как повторить функционал поиска дублей, но для Лидов. Версия 7.5

Нравится

13 комментариев

Добрый день!
Лид в bpm'online - потребность клиента. Для одного и того же контакта/контрагента может быть несколько одинаковых лидов(потребностей) в разные промежутки времени. В случае поиска дублей лидов все эти потребности объединятся, что является логически некорректным.

В связи с этим не совсем понятна бизнес-задача, которую необходимо решить поиском дублей лидов.

предусмотрено ведение различных задач по различным продуктам для одного лида. поиск дублей должен производится по детали «Средства связи» (по полям, содержащим телефон, а также по полю «E-mail»).

Валентин, добрый день!

Функциональность поиска дублей по лидам уже присутствует в версии 7.7
В данном случае оптимальным решением является обновление до последней версии.

Дополнительно хочу отметить, что самостоятельная реализация подобной задачи в 7.5 будет достаточно трудоемка.

Наталья, спасибо за информацию. Но пока обновления до 7.7 не планируется. Хотелось бы все таки получить рекомендации для решения задачи

Здравствуйте, Валентин!

Для того, чтобы объединить дубли лидов необходимо выполнить следующие действия:
1) Создать процесс для поиска лидов (аналогично процессу StartGlobalContactDuplicatesSearch). Для поиска необходимо создать процедуру в базе данных, аналогичную tsp_GloballySearchForDuplicates. Код процедуры tsp_GloballySearchForDuplicates можно найти в конфигурации, перейдя на вкладку "SQL сценарии". Процедура будет записывать данные в созданный Вами объект LeadDuplicates (создайте объект по аналогии с объектом ContactDuplicate)

2) Необходимо создать новую страницу редактирования, которая будет отображать данные из созданного объекта LeadDuplicates по аналогии со страницей DuplicatesPageV2 (либо заместите DuplicatesPageV2, добавив в нее нужную логику).

А теперь самое интересное - слияние лидов и удаление лишних. Так как удалить лид средствами системы не получится (связи, запущенный процесс...), то придется придумывать что-то новое.
Я бы в этот момент создавал бы новую запись лида и изменял данной записи значение в поле "Стадия" на нужное. Остальным дублям изменял бы стадию на "Дисквалифицирован" и указывал причину дисквалификации "Дубль".

Но лучше, наверное, обновиться до 7.7.0.:smile:

Спасибо.
Процесс StartGlobalContactDuplicatesSearch запускается в действии процесса StartGlobalDuplicatesSearch. Как теперь правильно заместить StartGlobalDuplicatesSearch, чтобы добавить в него запуск процесса StartGlobalLeadDuplicatesSearch? или если создать подобное действие процесса со своей логикой, где оно должно запускаться?

"Медведев Валентин Николаевич" написал:

Как теперь правильно заместить StartGlobalDuplicatesSearch, чтобы добавить в него запуск процесса StartGlobalLeadDuplicatesSearch?

Замещение процессов в системе не предусмотрено. Вам необходимо создать новый процесс и запускать его.

"Медведев Валентин Николаевич" написал:

или если создать подобное действие процесса со своей логикой, где оно должно запускаться?

Запускать новый процесс Вы можете, например, из библиотеки процессов (самая простая реализация). Также Вы можете добавить соответствующее действие в раздел "Лиды". Более подробно о создании действий, Вы можете прочитать в Академии.

имелось в виду
что собой и представляет StartGlobalDuplicatesSearch.

Добрый день!

Добавить соответствующее действие в раздел "Лиды" – подразумевается в замещающей странице редактирования лидов добавить новую кнопку в меню “Дейсвтия”. Также добавить обработчик, который будет запускать процесс по нажатию на эту кнопку.

StartGlobalDuplicatesSearch(Действие процесса) содержит сценарий:

string jobProcessName = (SchemaName == "Account") ? "StartGlobalAccountDuplicatesSearch" : "StartGlobalContactDuplicatesSearch";
AppScheduler.ScheduleImmediateProcessJob("DuplicatesSearchJob", "DuplicatesSearchGroup",
jobProcessName, UserConnection.Workspace.Name, UserConnection.CurrentUser.Name);
return true;

если я создам подобное, в котором переменная jobProcessName будет получать "StartGlobalLeadDuplicatesSearch", то где оно должно вызываться?

Здравствуйте!

Этот код добавляет в планировщик Quartz процесс StartGlobalAccountDuplicatesSearch, если поиск осуществляется со схемы Account, StartGlobalContactDuplicatesSearch - при поиске с другой схемы.

Для Вашей задачи, Вы можете действие процесса заменить на следующее:

string jobProcessName = "StartGlobalLeadDuplicatesSearch";
AppScheduler.ScheduleImmediateProcessJob("DuplicatesSearchJob", "DuplicatesSearchGroup",
jobProcessName, UserConnection.Workspace.Name, UserConnection.CurrentUser.Name);
return true;

спасибо всем за помощь! разобрался.
теперь такой вопрос... можно ли изменить логику поиска дублей Лидов при импорте данных из Exel? например, чтобы телефоны "+7 (978) 123 12 31" и "+79781231231" считались совпадением. в каких модулях реализована стандартная логика?

Здравствуйте!

При импорте из Excel записи +7 (978) 123 12 31 и +79781231231 действительно будут считаться разными записями. Проверка на тождественность этих записей не осуществляется.

При импорте из Excel Вы можете проверять уникальность по другим полям/признакам. Также непосредственно перед импортом Вы можете в файле Excel автозаменой символов "(", ")", " " на "".

Показать все комментарии