Есть БП, внутри которого в Сценарии на C# вычисляется большая таблица данных. Её нужно один-в-один положить в БД. Все поля известны.

Как с наименьшими затратами это лучше сделать? БП будет вызываться часто, поэтому сохранять надо быстро с минимальным временем блокировки таблицы.

Нравится

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

Добрый день

Конечно не очень хорошо в памяти держать большую таблицу :), но раз так, то подумаем логически что нам нужно сделать:

  1. Можно попробовать передать по одной записи, но тогда будут потери времени на уровне сетевых устройств + постоянное создание транзакций 
  2. Можно одним запросом реализовав хранимую процедуру

Лучше всего использовать хранимую процедуру, которая может иметь также 2 варианта:

  1. Передаются данные параметром (все записи вместе)
  2. Хранимая процедура берет данные из файла (который уже положен на диск, если есть такая возможность)

Оптимальный вариант №1. Но какой параметр. Варианта опять 2:

  1. Зарегистрировать свой тип на уровне БД 
  2. Передать все в виде xml

Оба варианта не понятны будут для приложения bpmonline, но, тип xml хорошо преобразовуется в nvarchar(max) и обратно.

Поэтому финальный вариант я вижу такой:

  1. На сервере в С# коде готовим XML с данными
  2. На уровне БД пишем хранимую процедуру с входящим параметром nvarchar(max)
  3. Внутри процедуры преобразовуем в xml и начинаем работать с xml на уровне SQL (https://docs.microsoft.com/ru-ru/sql/t-sql/xml/nodes-method-xml-data-type

 

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

товарищи подскажите как реализовать запрос вида:

WHERE Название LIKE '%компьютер%'

 

 

 

 

Нравится

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

Роман,

esq.Filters.Add(esq.CreateFilterWithParameters(FilterComparisonType.Contain, "Название", "компьютер"));

 

Толмачев Дмитрий Юрьевич,от души! Спасибо

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

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

Нравится

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

Да, например в бизнес процессе можно указать контрагента

Евгений.,

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

Станислав,

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

Соротюк Татьяна,

Понял, спасибо

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

В продолжение данного топика 

Правило дедубликации только по телефону не работает. Более того, методом научного тыка было обнаружено следующее:

1) Мы создали правило поиска по телефону и ещё одному полю UsrDedub (по аналогии правило поиска по названию и телефону).

2) Сделали значение UsrDedub = 1 в нескольких контрагентах (около 20). В лиде UsrDedub также = 1. Правило отработало корректно.

3) Сделали значение UsrDedub = 1 для всех контрагентов (около 7 500). Правило работать перестало.

4) Изменили параметр @offsetLimit в SQL запросе tsp_FindAccountSimilarRecords с 200 на 7500. Правило вновь стало работать.

Отсюда мы сделали вывод - правило поиска по телефону отрабатывает только по коллекции контрагентов, которые уже отобраны каким либо правилом поиска дублей.

Копаем дальше, но может кто-то может подсказать:

1) Насколько изменение @offsetLimit может быть критичным для быстродействия системы. Изменение до 10 000 визуально никаки систему не загрузили.

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

3) Либо если он работает в паре с другим правилом - в первую очередь проводить поиск именно по телефону?

Нравится

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

Оказалось, что именно низкий @offsetLimit не позволяет правилу поиска похожих контрагентов по телефону отрабатывать корректно индивидуально. Чем чревато увеличение этого значения - пока не знаю.

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

1. Если будет слишком большое количество записей, то вполне возможен отвал запроса по timeout.

2, 3.  Для того, чтобы правило поиска похожих контрагентов в лидах работало отдельно, необходимо в хранимой процедуре tsp_FindAccountSimilarRecords изменить блок "IF @execStmt IS NOT NULL..."  на:

               SET @detailsSelectQuery = N'SELECT TOP '+ CAST(@offsetLimit as NVARCHAR) +

                ' ' + @queryColumnName + CHAR(10) + 

                'FROM ' + CHAR(10) + @schemaName + CHAR(10) +

                'WITH (NOLOCK) ' + CHAR(10) +

                'WHERE 1=1 ';

                IF @execStmt IS NOT NULL

                BEGIN

                    SET @detailsSelectQuery = 

                    @detailsSelectQuery + CHAR(10) + 'AND' + CHAR(10) + @queryColumnName + CHAR(10) + ' IN ('    + @execStmt + ')';

                END

                IF @equalQueryConditions IS NOT NULL

                BEGIN

                    SET @detailsSelectQuery = @detailsSelectQuery + CHAR(10) + 

                    @equalQueryConditions;

                END

                IF @inQueryConditions IS NOT NULL

                BEGIN

                    SET @detailsSelectQuery = @detailsSelectQuery + CHAR(10) + @inQueryConditions;

                END

                IF @detailsSelectQuery IS NOT NULL

                BEGIN

                    INSERT INTO @resultIdsTable

                    EXEC sp_executesql @detailsSelectQuery;

                END

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

Изображение удалено.

 

Я создал деталь с редактируемым реестром и страницу для нее.

 

Мне нужно либо скрыть в активной записи на конкретном справочнике, либо повесить обработчик на событие нажатия на эту кнопку.  И получить в обработчике  информацию на какой записи и справочнике был клик.

https://academy.terrasoft.ru/jscoresdk/source/clearicon.html

По ссылке нашел некоторые методы, но не знаю как их использовать.

Нравится

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

Вооружаемся напильником и в схеме детали прописываем что-то вроде

getDefaultCellControlsConfig: function(columnName, params) {
	var config = {
		itemType: Terrasoft.ViewItemType.MODEL_ITEM,
		name: columnName,
		labelConfig: {visible: false}
	};
	//columnName - название колонки из объекта
	if (columnName === 'ContainerType') {
		config.hasClearIcon=false;
	}
	return this.Ext.apply(config, params);
},

 

 Добрый день.

Если вы хотите поставить свой обработчик, то в таком случае нужно заместить модуль ConfigurationGrid, в нем переопределить метод renderRowControls. В этом методе происходит генерация моделей полей. Вам нужно в модели нужного поля подписаться на событие “cleariconclick “. Например viewModel.on(“cleariconclick “, myHandler).

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

Добрый день!

Столкнулись с проблемой с манифестами мобильного приложения

На дев среде был указан префикс, издатель = ХХХ.

создали рабочее место в мобильном приложении. Манифест содержит в себе префикс ХХХ.

Перенесли на тест среду пакет с изменениями. На тест среде префикс пустой. Наше рабочее место не открывается. Прописали на тест среде наш префикс XXX. Все заработало.

Кроме нас на проекте есть еще другая компания, которая поставляет свои пакеты с префиксом YYY. Но у них пока нет доработок мобильного приложения

Вопросы:

1) Как повлияет указание префикса XXX на промышленной среде на работу в системе? все ли будет доступно из нашего пакета(префикс XXX) и из пакеты другой организации(префикс YYY)? Где еще может всплыть проблема с префиксом? мне казалось, что он отвечает только за сам префикс создаваемых объектов и схем.

2) Как создавать рабочие места в мобильном приложении и вообще настраивать его без привязки к префиксам?

Нравится

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

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

bpm'online по особому обрабатывает схемы манифестов мобильного приложения.

В ядре идет завязка на имя схемы.

Имя должно быть либо MobileApplicationManifestXXX либо PrefixMobileApplicationManifestXXX , где Prefix берется из текущего значения системной настройки «SchemaNamePrefix».

 

Рекомендуем следующее:

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

(по окончании работы с мастером не забудьте ее снова вернуть)

  1. Во время разработки дополнительной бизнес логики (когда разработчик вручную создает новые схемы) все схемы необходимо создавать обязательно с префиксом!

При этом префикс может быть любой (главное чтоб не пустой).

 

Вильшанский Дмитрий,

если создадим с пустым префиксом, потом на тестовой и промышленной средне префикс должно быть пусто?

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

 

Zaitova Liubov,

1) без разницы какой будет

2) каждый раз когда создаете новое рабочее место через дизайнер нужен пустой префикс

Вильшанский Дмитрий,

2) каждый раз когда создаете новое рабочее место через дизайнер нужен пустой префикс



а редактирование существующего рабочего места тоже с пустым префиксом должно быть или можно ставить свой ХХХ?

Zaitova Liubov,

Желательно пустым

Спасибо

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

Добрый день, Коллеги!

1. На community видел упоминание что есть инструкция как влючить логирование синхронизации с LDAP кто может, прошу поделится.

2. При добавлении пользователя через синхронизацию с LDAP пользователь помешается в группу "Все сотрудники компании", как коректно отключить этот механизм мне надо чтоб пользователь был только в тех группах с которыми он синхронизирован через LDAP?

3. Есть учетка в LDAP числится в нужной группе которая синхронизирована с организационной ролью BPM другие учетки есть а этой нет. Куда копать?

 

установка он сайт.

Нравится

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

Здравствуйте, Алексей

По Вашим вопросам:

1. Настройка логирования LDAP

  1. Открываем файл log4net.config в папке <site>/Terrasoft.WebApp
  2. Находим фильтр:

                                <filter type="log4net.Filter.LoggerMatchFilter">

                                                <loggerToMatch value="Terrasoft.Core.LDAP" />

                                                <acceptOnMatch value="false" />

                                </filter>

Если данный фильтр отсутствует, то добавляем. Пример:

<?xml version="1.0" encoding="utf-8"?>

<log4net>

                <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">

                                <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

                                <connectionString value="Data Source=#SQLServer#; Initial Catalog=syslog; Persist Security Info=True; MultipleActiveResultSets=True; User ID=svc-log4net; Password=FoaFkplBSl0Iqqdbo2a7fj6JfdyE1jmu; Pooling = true; Max Pool Size = 100" />

                                <commandText value="INSERT INTO [dbo].[log4net] ([Date],[Host],[Site],[Thread],[Level],[Logger],[User],[Message],[Exception]) VALUES (@log_date, @log_host, @log_site, @log_thread, @log_level, @log_logger, @log_user, @log_message, @log_exception)" />

                                <bufferSize value="1" />

                                <filter type="log4net.Filter.LoggerMatchFilter">

                                                <loggerToMatch value="Terrasoft.Core.LDAP" />

                                                <acceptOnMatch value="false" />

                                </filter>

                                <parameter>

                                                <parameterName value="@log_date" />

                                                <dbType value="DateTime" />

                                                <layout type="log4net.Layout.PatternLayout" value="%date{yyyy'-'MM'-'dd HH':'mm':'ss'.'fff}" />

                                </parameter>

  1. Находим атрибут appender

                <appender name="ldapAppender" type="log4net.Appender.RollingFileAppender">

                                <file type="log4net.Util.PatternString">

                                                <converter>

                                                                <name value="AspNet" />

                                                                <type value="log4net.Util.PatternStringConverters.AspNetPatternConverter,log4net.Util.PatternStringConverters" />

                                                </converter>

                                                <conversionPattern value="${TEMP}\BPMonline\Site_%AspNet{SiteId}\%AspNet{ApplicationPath}\Log\" />

                                </file>

                                <appendToFile value="true" />

                                <rollingStyle value="Date" />

                                <datePattern value="yyyy_MM_dd'\\LDAP.log'" />

                                <staticLogFileName value="false" />

                                <layout type="log4net.Layout.PatternLayout">

                                                <conversionPattern value="%date [%thread] %-5level %username %logger %method - %message%newline" />

                                </layout>

                </appender>

Если не находим, то добавляем. Пример:

                </appender>

                <appender name="ldapAppender" type="log4net.Appender.RollingFileAppender">

                                <file type="log4net.Util.PatternString">

                                                <converter>

                                                                <name value="AspNet" />

                                                                <type value="log4net.Util.PatternStringConverters.AspNetPatternConverter,log4net.Util.PatternStringConverters" />

                                                </converter>

                                                <conversionPattern value="${TEMP}\BPMonline\Site_%AspNet{SiteId}\%AspNet{ApplicationPath}\Log\" />

                                </file>

                                <appendToFile value="true" />

                                <rollingStyle value="Date" />

                                <datePattern value="yyyy_MM_dd'\\LDAP.log'" />

                                <staticLogFileName value="false" />

                                <layout type="log4net.Layout.PatternLayout">

                                                <conversionPattern value="%date [%thread] %-5level %username %logger %method - %message%newline" />

                                </layout>

                </appender>

                <root>

                                <level value="WARN" />

                                <appender-ref ref="AdoNetAppender" />

                </root>

  1. Находим атрибут logger

                <logger name="Terrasoft.Core.LDAP" >

                                <level value="ALL" />

                                <appender-ref ref="ldapAppender" />

                </logger>

и устанавливаем  уровень логирования в ALL, вместо INFO или DEBUG

Если не находим, то добавляем. Пример:

                <root>

                                <level value="WARN" />

                                <appender-ref ref="AdoNetAppender" />

                </root>

                <logger name="Terrasoft.Core.LDAP" >

                                <level value="ALL" />

                                <appender-ref ref="ldapAppender" />

                </logger>

</log4net>

 

2. Если имеется ввиду то что, к примеру, создается пользователь (Администратор) который должен получить роль "Системные администраторы" а получает дополнительно роль "Все сотрудники компании" - то это корректное поведение приложения так как по логиге пользователь получает все родительские роли полученной роли, а так как роль системных администраторов является дочерней ролью Всех сотрудников компании то это правильно.

 

3. Необходимо сравнить атрибуты пользователелей в AD который попал в приложение при синхронизации и который не попал и уже после отталкиваться от результатов.

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

Привет, подскажите, пожалуйста, в 7.11.3 в свойствах объекта появился флаг «Наследовать права доступа от объекта», как он должен работать? В документации не нашел.

Например, если я открываю контрагент для которого у меня права только на чтение и пытаюсь добавить запись на детали «Адреса», то адрес сохраняется без проблем. Но при попытке отредактировать этот же адрес, выдается ошибка, что у меня не хватает прав. Правильно я понимаю, что флаг «Наследовать права доступа от объекта» просто копирует правила из родительского объекта, но не проверяет права на родительскую запись?

Нравится

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

Здравствуйте, Сергей!

Суть изменений в том, что есть детали которые не администрируются по записям вообще и настраивать правила достаточно трудозатратно и не оправданно сложно. На текущий момент в версиях 7.11.3 данная фича по умолчанию выключена. Т.е. вне зависимости от заполненности поля поведение по проверке прав не изменится. 

Общая логика работы фичи такая: если по объекту выключено администрирование по записям и указан родительский объект, то при работе с записями вычитываются накладываются права на из родительского объекта. Никакого копирования прав не происходит, проверка на лету.



По  кейсу с ошибкой изменения адреса. Скорее всего дело вот в чем: при изменении записи адреса выполняется его синхронизация с основной записью контрагента (обновление в Контрагент основного адреса из детали Адреса для возможности удобной работы с этими данными в реестре Контрагенты). Т.к прав на изменение не хватает, то и изменение завершается с ошибкой.

Коллеги, подскажите, пожалуйста, где можно прочитать о данной функциональности? Не могу найти в Системе данный чекбокс.

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

Спасибо!!!!!

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

Запрос в браузере возвращает результат нормально https://goldcoachcrm.bpmonline.com/0/ServiceModel/EntityDataService.svc…)

а вот если делать его на php curl  используя функцию http://prntscr.com/i5xuim тогда получаем вместо ответа ОШИБКУ

https://goldcoachcrm.bpmonline.com/0/ServiceModel/EntityDataService.svc…)



Bad Request


Bad Request


HTTP Error 400. The request is badly formed.


 

С чем это может быть связано и как это обойти?

Нравится

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

Кейс следующий: сделать правило дедубликации в разделе лиды по похожим контрагентам с проверкой ТОЛЬКО по телефону. Базовое правило не подходит, поскольку проверяет по названию И телефону.

Добавил правило в DuplicatesRule, добавил процедуру поиска с помощью SQL запроса (см. AccountPhoneRuleUsr). Ничего не выходит. Что интересно, если убрать из базового правила (phoneAndAccountNameRule) упоминание поиска по названию, то он тоже перестаёт отрабатывать в принципе. Такое ощущение, что где-то прописано, что поиск по телефону работает только в паре с каким-то совпадением.

Кто-нибудь сталкивался с данной задачкой?

IF(OBJECT_ID('dbo.tsp_FindAccountSimilarRecords','P')) IS NOT NULL
	DROP PROCEDURE dbo.tsp_FindAccountSimilarRecords
GO
 
CREATE PROCEDURE tsp_FindAccountSimilarRecords (@primaryColumnValue uniqueidentifier, @rawXMLConfig nvarchar(MAX), @offsetLimit int = 200)
AS 
 
 
DECLARE @parsedConfig TABLE (
	SchemaName nvarchar(128),
	ParentShemaName nvarchar(128) NULL,
	ColumnName nvarchar(128),
	ColumnValue nvarchar(4000),
	ColumnValueTypeId nvarchar(128)
 );
 
DECLARE @accountRulesConfig TABLE (
	RuleId uniqueidentifier, 
	SchemaName sysname,
	ParentSchemaName sysname NULL,
	ColumnName sysname, 
	NormalizeFnPattern nvarchar(4000),
	CleanDataSchemaName sysname NULL);
 
DECLARE @xmlBody xml;
 
DECLARE @webAndAccountNameRule uniqueidentifier = 'd3607fd4-8303-40ae-b66e-000000000013'	,
		@phoneAndAccountNameRule uniqueidentifier = 'd3607fd4-8303-40ae-b66e-000000000014'	,
		@cityAndAccountNameRule uniqueidentifier = 'd3607fd4-8303-40ae-b66e-000000000015'	,
		@countryAndAccountNameRule uniqueidentifier = 'd3607fd4-8303-40ae-b66e-000000000016'	,
		@AccountNameRule uniqueidentifier = 'd3607fd4-8303-40ae-b66e-000000000017'	,
		@AccountPhoneRuleUsr uniqueidentifier = '778e768d-3941-4461-8954-59702c1f1fab'	,
		@AccountInnRule uniqueidentifier = '1470da34-31ae-4da5-b6c8-9410751133a4'	;
 
DECLARE @leadSchemaUId uniqueidentifier = '41AF89E9-750B-4EBB-8CAC-FF39B64841EC' ;
 
DECLARE @accountSchemaName sysname = 'Account',
		@accountCleanDataSchemaName sysname = 'VwAccountCleanDataValues',
		@accountCommunicationSchemaName sysname = 'AccountCommunication',
		@accountCommunicationCleanDataSchemaName sysname = 'VwAccountCommunicationCleanDataValues',
		@accountAddressSchemaName sysname = 'AccountAddress';
 
DECLARE @accountNameColumn sysname = 'Name',
		@accountInnColumn sysname = 'UsrINN',
		@accountCommunicationColumn sysname = 'Number',
		@accountCommunicationPhoneColumn sysname = 'SearchNumber',
		@accountCountryColumn sysname = 'CountryId',
		@accountCityColumn sysname = 'CityId',
		@communicationTypeIdColumn sysname = 'CommunicationTypeId';
 
DECLARE @resultIdsTable TABLE (Id uniqueidentifier);
 
DECLARE @phoneCommunicationTypeTable TABLE (Id uniqueidentifier);
 
DECLARE @preStmtValues TABLE (
	ColumnName sysname NOT NULL,
	ColumnValue nvarchar (4000) NULL,
	SchemaName sysname NOT NULL,
	ColumnValueTypeId NVARCHAR(128)
);
 
 
DECLARE @emailNormalizePattern nvarchar(50) = N'0-9a-zа-я@_.',
		@nameNormalizePattern nvarchar(50) = N'0-9a-zа-я',
		@normalizedFnSelectStmt nvarchar(250),
		@innNormalizePattern nvarchar(12) = N'0-9',
		@normalizedFnValueStmt nvarchar(250);
 
DECLARE @execStmt nvarchar(MAX),
		@baseNormalizeFn sysname = 'dbo.fn_NormalizeString',
		@phoneNormalizeFn sysname = 'dbo.fn_ExtractDigitLimitFromNumber',
		@webNormalizeFn sysname = 'dbo.fn_ExtractDomainFromUrl';
 
DECLARE @ruleId uniqueidentifier,
		@countDetailValues int = 0;
 
DECLARE @inQueryConditions nvarchar(MAX),
		@equalQueryConditions nvarchar(MAX),
		@schemaName sysname,
		@queryColumnName nvarchar(128);
 
BEGIN
 
 
	SET @xmlBody  = CAST(@rawXMLConfig AS XML);
 
	INSERT INTO @phoneCommunicationTypeTable
	VALUES('0DA6A26B-D7BC-DF11-B00F-001D60E938C6') ,
		('3DDDB3CC-53EE-49C4-A71F-E9E257F59E49') ,
		('D4A2DC80-30CA-DF11-9B2A-001D60E938C6') ,
		('2B387201-67CC-DF11-9B2A-001D60E938C6') ,
		('6A3FB10C-67CC-DF11-9B2A-001D60E938C6') ,
		('E9D91E45-8D92-4E38-95A0-EF8AA28C9E7A') 
 
 
	INSERT INTO @accountRulesConfig (RuleId, SchemaName, ParentSchemaName, ColumnName, NormalizeFnPattern, CleanDataSchemaName)
	VALUES 	(@webAndAccountNameRule, @accountCommunicationSchemaName, @accountSchemaName, @accountCommunicationColumn,  @webNormalizeFn + '(N##VALUE##)', @accountCommunicationCleanDataSchemaName),
			(@webAndAccountNameRule, @accountSchemaName, NULL, @accountNameColumn,  @baseNormalizeFn + '(N##VALUE##,N''' + @nameNormalizePattern + ''')',  @accountCleanDataSchemaName),
			(@phoneAndAccountNameRule, @accountCommunicationSchemaName, @accountSchemaName, @accountCommunicationPhoneColumn,  @phoneNormalizeFn + '(##VALUE##)', @accountCommunicationCleanDataSchemaName),
			(@phoneAndAccountNameRule, @accountSchemaName, NULL, @accountNameColumn,  @baseNormalizeFn + '(N##VALUE##,N''' + @nameNormalizePattern + ''')', @accountCleanDataSchemaName),
			(@cityAndAccountNameRule, @accountAddressSchemaName, @accountSchemaName, @accountCityColumn, NULL, NULL),
			(@cityAndAccountNameRule, @accountSchemaName, NULL, @accountNameColumn, @baseNormalizeFn + '(N##VALUE##,N''' + @nameNormalizePattern + ''')', @accountCleanDataSchemaName),
			(@countryAndAccountNameRule, @accountAddressSchemaName, @accountSchemaName, @accountCountryColumn, NULL,NULL),
			(@countryAndAccountNameRule, @accountSchemaName, NULL, @accountNameColumn, @baseNormalizeFn + '(N##VALUE##,N''' + @nameNormalizePattern + ''')', @accountCleanDataSchemaName),
			(@AccountInnRule, @accountSchemaName, NULL, @accountInnColumn, @baseNormalizeFn + '(N##VALUE##,N''' + @innNormalizePattern + ''')', NULL),
			(@AccountPhoneRuleUsr, @accountCommunicationSchemaName, @accountSchemaName, @accountCommunicationPhoneColumn,  @phoneNormalizeFn + '(##VALUE##)', NULL),
			(@AccountNameRule, @accountSchemaName, NULL, @accountNameColumn, @baseNormalizeFn + '(N##VALUE##,N''' + @nameNormalizePattern + ''')', @accountCleanDataSchemaName);
 
	INSERT INTO @parsedConfig
	SELECT NULLIF(p.value('(./schemaName)[1]', 'VARCHAR(128)'),'') AS SchemaName,
		NULLIF(p.value('(./parentSchemaName)[1]', 'VARCHAR(128)'),'') AS ParentSchemaName,
		NULLIF(p.value('(./columnName)[1]', 'NVARCHAR(128)'),'') AS ColumnName,
		NULLIF(p.value('(./columnValue)[1]', 'NVARCHAR(4000)'),'') AS ColumnValue,
		NULLIF(p.value('(./typeId)[1]', 'NVARCHAR(128)'),'') AS ColumnValueTypeId
		FROM @xmlBody.nodes('/columns/item') t(p)
 
	UPDATE	@parsedConfig 
	SET ColumnValue = dbo.fn_GetPhoneNumberSearchForm(ColumnValue)
	WHERE ColumnName = @accountCommunicationPhoneColumn
 
	DECLARE activeRuleList CURSOR
	FOR SELECT Id from DuplicatesRule
		WHERE ObjectId = @leadSchemaUId
		AND Id IN (SELECT RuleId FROM @accountRulesConfig)
		AND isActive = 1;
 
	OPEN activeRuleList
	FETCH NEXT
		FROM activeRuleList INTO @ruleId 
 
	WHILE @@FETCH_STATUS = 0
	BEGIN
 
		SET @execStmt = NULL;
		SET @countDetailValues = 0;
		SET @equalQueryConditions = NULL;
		SET @inQueryConditions = NULL;
 
 
		IF EXISTS (SELECT NULL
			FROM @accountRulesConfig config
			LEFT JOIN @parsedConfig parsed
			ON config.ColumnName = parsed.ColumnName
			AND config.SchemaName = parsed.SchemaName
			WHERE parsed.ColumnName IS NULL 
			AND config.RuleId = @ruleId)
		BEGIN 
			FETCH NEXT
			FROM activeRuleList INTO @ruleId
			CONTINUE
		END
		ELSE
		BEGIN	
			IF EXISTS (
				SELECT NULL
					FROM @parsedConfig parsed, @accountRulesConfig config
					WHERE parsed.ColumnName =config.ColumnName
					AND parsed.SchemaName = config.SchemaName
					AND config.ParentSchemaName IS NULL
					AND parsed.ColumnValue IS NULL
					AND config.RuleId = @ruleId)
			BEGIN
				FETCH NEXT
					FROM activeRuleList INTO @ruleId
				CONTINUE
			END
			ELSE
			BEGIN
				DELETE FROM @preStmtValues
				INSERT INTO @preStmtValues (ColumnName, ColumnValue, SchemaName)
				SELECT ISNULL(rulesCfg.CleanDataSchemaName, rulesCfg.SchemaName)+'.'+rulesCfg.ColumnName,
						CASE WHEN NormalizeFnPattern IS NOT NULL THEN 
							REPLACE(NormalizeFnPattern, '##VALUE##', QUOTENAME(parsedCfg.ColumnValue, ''''))
						ELSE QUOTENAME(parsedCfg.ColumnValue, '''') END ColumnValue, 
						ISNULL(rulesCfg.CleanDataSchemaName, rulesCfg.SchemaName)
					FROM @accountRulesConfig rulesCfg, 
						 @parsedConfig parsedCfg
					WHERE rulesCfg.ColumnName = parsedCfg.ColumnName
					AND rulesCfg.SchemaName = parsedCfg.SchemaName
					AND rulesCfg.ParentSchemaName IS NULL
					AND RuleId = @ruleId
 
				SET @queryColumnName = 'Id';
 
				SELECT TOP 1 @schemaName = SchemaName FROM @preStmtValues 
 
				SELECT DISTINCT @inQueryConditions = 
					' AND ' + ColumnName + ' IN (' + STUFF((SELECT ',' + ColumnValue FROM @preStmtValues f 
					WHERE f.ColumnName = t.ColumnName AND f.ColumnName IN (
						SELECT t.ColumnName 
							FROM @preStmtValues t 
						GROUP BY ColumnName HAVING COUNT(*) > 1)
					FOR XML PATH(''), TYPE).value('(./text())[1]','nvarchar(max)'), 1,1, '') +')' FROM @preStmtValues t
 
				SELECT DISTINCT @equalQueryConditions = 
					STUFF((SELECT ' AND ' 
							+ ColumnName + ' LIKE ' 
							+ ColumnValue FROM @preStmtValues f
					WHERE f.ColumnName IN (
						SELECT t.ColumnName 
							FROM @preStmtValues t 
						GROUP BY ColumnName HAVING COUNT(*) = 1) 
					FOR XML PATH(''), TYPE).value('(./text())[1]','nvarchar(max)'), 1,1, '')
 
				SET @execStmt = 
					N'SELECT TOP '+CAST(@offsetLimit as NVARCHAR)+' '+@queryColumnName + CHAR(10) + 
					'FROM '+ CHAR(10) +
					@schemaName + CHAR(10) +
					'WITH (NOLOCK) ' + CHAR(10) +
					'WHERE 1=1'+ CHAR(10);
 
				IF @equalQueryConditions IS NOT NULL
				BEGIN
					SET @execStmt = @execStmt + CHAR(10) + 
					@equalQueryConditions
				END
 
				IF @inQueryConditions IS NOT NULL
				BEGIN
					SET @execStmt = @execStmt + CHAR(10) + 
					@inQueryConditions
				END
 
 
				IF EXISTS (SELECT NULL FROM @accountRulesConfig 
								WHERE RuleId = @ruleId
								AND ParentSchemaName IS NOT NULL)
				BEGIN
					IF EXISTS (
						SELECT NULL
						 FROM @parsedConfig parsed, @accountRulesConfig config
							WHERE parsed.ColumnName =config.ColumnName
								AND parsed.SchemaName = config.SchemaName
								AND parsed.ParentShemaName = config.ParentSchemaName
								AND parsed.ColumnValue IS NOT NULL
								AND config.RuleId = @ruleId)
					BEGIN
 
						DELETE FROM @preStmtValues
						INSERT INTO @preStmtValues (ColumnName, ColumnValue, SchemaName, ColumnValueTypeId)
						SELECT ISNULL(rulesCfg.CleanDataSchemaName, rulesCfg.SchemaName)+'.'+rulesCfg.ColumnName, 
							CASE WHEN NormalizeFnPattern IS NOT NULL THEN 
							REPLACE(NormalizeFnPattern, '##VALUE##', QUOTENAME(parsedCfg.ColumnValue, ''''))
							ELSE QUOTENAME(parsedCfg.ColumnValue, '''') END ColumnValue, 
							ISNULL(rulesCfg.CleanDataSchemaName, rulesCfg.SchemaName),
							ColumnValueTypeId
							FROM @accountRulesConfig rulesCfg, 
								 @parsedConfig parsedCfg
							WHERE rulesCfg.ColumnName = parsedCfg.ColumnName
							AND rulesCfg.ParentSchemaName = parsedCfg.ParentShemaName
							AND rulesCfg.SchemaName = parsedCfg.SchemaName
							AND RuleId = @ruleId
 
 
						SET @queryColumnName = 'AccountId';
 
						IF EXISTS (
							SELECT NULL 
								FROM @phoneCommunicationTypeTable 
								WHERE Id in (SELECT ColumnValueTypeId FROM @preStmtValues f))
						BEGIN 
							INSERT INTO @preStmtValues (ColumnName, ColumnValue, SchemaName)
							SELECT @communicationTypeIdColumn, 
							'''' + CAST(Id AS NVARCHAR(50)) + '''', 
							(SELECT TOP 1 SchemaName FROM @preStmtValues)
							FROM @phoneCommunicationTypeTable
						END
						ELSE 
						BEGIN
							INSERT INTO @preStmtValues (ColumnName, ColumnValue, SchemaName)
							SELECT @communicationTypeIdColumn, 
							'''' + ColumnValueTypeId + '''',
							SchemaName FROM @preStmtValues
							WHERE ColumnValueTypeId IS NOT NULL
						END	
 
						SELECT TOP 1 @schemaName = SchemaName FROM @preStmtValues 
 
						SELECT DISTINCT @inQueryConditions = 
							' AND ' + ColumnName + ' IN (' + STUFF((SELECT ',' + ColumnValue FROM @preStmtValues f 
							WHERE f.ColumnName = t.ColumnName AND f.ColumnName IN (
								SELECT t.ColumnName 
									FROM @preStmtValues t 
								GROUP BY ColumnName HAVING COUNT(*) > 1)
							FOR XML PATH(''), TYPE).value('(./text())[1]','nvarchar(max)'), 1,1, '') +')' FROM @preStmtValues t
 
						SELECT DISTINCT @equalQueryConditions = 
							STUFF((SELECT ' AND ' 
									+ ColumnName + ' LIKE ' 
									+ ColumnValue FROM @preStmtValues f
							WHERE f.ColumnName IN (
								SELECT t.ColumnName 
									FROM @preStmtValues t 
								GROUP BY ColumnName HAVING COUNT(*) = 1) 
							FOR XML PATH(''), TYPE).value('(./text())[1]','nvarchar(max)'), 1,1, '')
 
						IF @execStmt IS NOT NULL
						BEGIN
							SET @execStmt = 
							N'SELECT TOP '+CAST(@offsetLimit as NVARCHAR)+' '+@queryColumnName + CHAR(10) + 
							'FROM ' + CHAR(10) + 
							@schemaName + CHAR(10) +
							'WITH (NOLOCK) ' + CHAR(10) +
							'WHERE '+ @queryColumnName + CHAR(10) + 
							' IN ('	+ @execStmt + ')';
 
							IF @equalQueryConditions IS NOT NULL
							BEGIN
								SET @execStmt = @execStmt + CHAR(10) + 
								@equalQueryConditions;
							END	
 
							IF @inQueryConditions IS NOT NULL
							BEGIN
								SET @execStmt = @execStmt + CHAR(10) + 
								@inQueryConditions;
							END	
 
							INSERT INTO @resultIdsTable
							EXEC sp_executesql @execStmt;
						END
					END
				END
				ELSE
				BEGIN
					INSERT INTO @resultIdsTable
					EXEC sp_executesql @execStmt;
				END
			END
		END
 
	FETCH NEXT
		FROM activeRuleList INTO @ruleId
	END
	CLOSE activeRuleList
	DEALLOCATE activeRuleList
 
	DELETE
		FROM @resultIdsTable
		WHERE Id = @primaryColumnValue
 
	SELECT DISTINCT TOP (@offsetLimit) Id 
		FROM @resultIdsTable
 
END

 

Нравится

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

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

Кастомизация правил поиска похожих лидов осуществляется по инструкции ниже.

1. Необходимо добавить скриптом новое правило в таблицу DuplicatesRule.

Пример скрипта:

insert into DuplicatesRule

(Name, IsActive, ObjectId)

values

('Имя правила', 1, '41AF89E9-750B-4EBB-8CAC-FF39B64841EC')

 

2. Заместить хранимую процедуру tsp_FindAccountSimilarRecordsMSSQL (для примера поиска по контрагентам, по контактам соответственно tsp_FindContactSimilarRecordsMSSQL). Для этого необходимо скопировать весь код из tsp_FindAccountSimilarRecordsMSSQL и дублировать его в новый SQL Script.

3. Объявить своё правилj в блоке с остальными правилами, где указать id добавленного правила с пункта 1 (из таблицы DuplicatesRule) http://prntscr.com/e2m5wz

4. Аналогично существующим правилам, добавить и своё в @accountRulesConfig http://prntscr.com/e2m72y

Например, если правило только для Телефона, копируем выделенную строчку на скриншоте и заменяем название правила на кастомное: http://prntscr.com/i5yogj&nbsp;

5. Сохранить хранимую процедуру и выполнить установку в БД.

Если привязаться к Вашему коду, то ошибка вероятнее всего в п. 4. , т.к. правило определено неправильно: http://prntscr.com/i5yppg

 

Приятной работы

 

Пробовал и полностью скопировав это правило (если вы про отсутствие @accountCleanDataSchemaName в конце) - не работает.

Что интересно, если я прописываю правило с поиском по ИНН+Телефон, то поиск отрабатывает корректно - проверяет совпадение в обоих полях. Но как только пробую оставить ТОЛЬКО по телефону, то такая дедубликация не работает.

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

Правила поиска дублей для похожих контрагентов/контактов в лиде сделаны таким образом, что нет возможности обращаться отдельно к таблице средств связи, по которой и происходит поиск только по телефону. Логика хранимых процедур была написана с учётом базовых правил, которые предполагают поиск похожих контактов/контрагентов только по правилам, которые включают поиск в таблице Account/Contact плюс таблица средств связи. А обращение только к таблице средств связи в текущей реализации хранимой процедуры не предусмотрено.

Для работы своего правила только по телефону или любого другого средства связи, необходимо внести изменения в хранимую процедуру tsp_FindAccountSimilarRecords, а именно вместо блока "IF @execStmt IS NOT NULL..."  (http://prntscr.com/ibe672) добавить код ниже:



               SET @detailsSelectQuery = N'SELECT TOP '+ CAST(@offsetLimit as NVARCHAR) +

                ' ' + @queryColumnName + CHAR(10) + 

                'FROM ' + CHAR(10) + @schemaName + CHAR(10) +

                'WITH (NOLOCK) ' + CHAR(10) +

                'WHERE 1=1 ';



                IF @execStmt IS NOT NULL

                BEGIN

                    SET @detailsSelectQuery = 

                    @detailsSelectQuery + CHAR(10) + 'AND' + CHAR(10) + @queryColumnName + CHAR(10) + ' IN ('    + @execStmt + ')';

                END



                IF @equalQueryConditions IS NOT NULL

                BEGIN

                    SET @detailsSelectQuery = @detailsSelectQuery + CHAR(10) + 

                    @equalQueryConditions;

                END



                IF @inQueryConditions IS NOT NULL

                BEGIN

                    SET @detailsSelectQuery = @detailsSelectQuery + CHAR(10) + @inQueryConditions;

                END



                IF @detailsSelectQuery IS NOT NULL

                BEGIN

                    INSERT INTO @resultIdsTable

                    EXEC sp_executesql @detailsSelectQuery;

                END

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