Здравствуйте, подскажите как можно заблокировать от изменений деталь "Средства связи" у контакта и контрагента. Добавоение "+" убирается без проблем, про вот изменение типа (выпадающий список) и значения как можно заблокировать?

Нравится

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

Александр, тут возможны два подхода: либо модифицировать схему детали ContactCommunicationDetailV2, добавив элементам свойство только для чтения, или AccountCommunicationDetailV2, либо настроить нужным пользователям права доступа только для чтения на объект этой детали. Но в случае последнего подхода нужно помнить, что в объекты разделов Contact и Account во встроенные БП добавлена логика синхронизации с деталью средств связи. Чтобы отсутствие прав не приводило к ошибкам, нужно в своём пакете переопределить функцию SynchronizeCommunication в процессах этих объектов.

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

первый вариант только на чтение - не выходит заблокировать выпадающий список

Александр, сложно сказать, не зная, что Вы меняли.

Это поле и выпадающий список формируются в ContactCommunicationDetailV2 здесь:

var typeMenuItems = [];
var communicationTypes = this.get("CommunicationTypes");
communicationTypes.each(function(item) {
	var name = item.get("Name");
	var value = item.get("Id");
	typeMenuItems.push({
		id: value,
		caption: name,
		tag: value,
		click: { bindTo: "typeChanged" }
	});
}, this);
var typeButtonConfig = {
	id: "type",
	className: "Terrasoft.Button",
	classes: {
		wrapperClass: ["label-wrap", "detail-type-btn-user-class"],
		textClass: ["detail-type-btn-inner-user-class"]
	},
	style: Terrasoft.controls.ButtonEnums.style.TRANSPARENT,
	selectors: {wrapEl: "#type"},
	caption: {
		bindTo: "CommunicationType",
		bindConfig: { converter: "typedStringValueConverter" }
	},
	menu: { items: typeMenuItems }
};

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

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

Имеете ввиду сделать override модуля, и в функции удалить 

var communicationTypes = this.get("CommunicationTypes");
communicationTypes.each(function(item) {
	var name = item.get("Name");
	var value = item.get("Id");
	typeMenuItems.push({
		id: value,
		caption: name,
		tag: value,
		click: { bindTo: "typeChanged" }
	});
}, this);

 

 

?

Прежде, чем менять, надо сначала ещё проверить в отладке, используется ли в этой версии логика из ContactCommunicationDetailV2 или из ContactCommunicationDetail и BaseCommunicationDetail, созданная для более старого интерфейса.

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

Подскажите как сделать валидацию номера для "Средства связи"? Читал статью https://community.terrasoft.ru/questions/validacia-zapolnenia-telefona-v-detali-sredstva-svazi но this.addColumnValidator("Number", newItem.validateField, newItem); что бы в детали сделать замену валидации нужно полностью переписать то что в "BaseCommunicationViewModel" но это модуль и его нельзя замещать.

Нравится

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

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

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

 

Вот потому то и спрашиваю так как эти способы не работают, как можно сделать валидацию другим способом, пока в голову не пришло идеи не какой, может подскажите?

Не работает именно валидация или замещение? Если второе — то по ссылке выше приводил, как делать обходным способом. По сути, переопределять все стандартные схемы, которые используются для работы со средствами связи на свои аналоги.

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

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

Там не переписывание всего текста модулей, а дополнение своим при помощи override. Но потом и замена всех упоминаний стандартного модуля на свой аналог.

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

Спасибо, получилось как описано в статье.

Сделал замещение BaseCommunicationDetail

 

define("BaseCommunicationDetail", ["KtValidBaseCommunicationViewModel"],
	function() {
		return {
			attributes: {},
			messages: {},
			methods: {},
			diff: /**SCHEMA_DIFF*/[]/**SCHEMA_DIFF*/
		};
	});

и BaseCommunicationViewModel

define("KtValidBaseCommunicationViewModel", ["terrasoft", "BaseCommunicationViewModel"],
	function(Terrasoft) {
		Ext.define("Terrasoft.KtValidBaseCommunicationViewModelOverrided", {
			override: "Terrasoft.BaseCommunicationViewModel",
			/**
			 * @override
			 * ######### ######### ###### ########.
			 * @param {String} value ########### ########.
			 * @return {Boolean} true #### ###### ######## ##### ########.
			 */
 
			isPhoneNumber: function(value) {
				//Terrasoft.SysSettings.querySysSettingsItem("CommunicationPhoneRegExpValid", function(phoneRegExp) {
				//	var phonePattern = new RegExp(this.get("PhoneRegExp"));
				//	return phonePattern.test(value);
				//}, this);
				var phonePattern = /^\98(\(\d{3}\)\d{3}\-\d{2}\-\d{2})$/;
				return phonePattern.test(value);
			},
		});
	}
);

Все отлично работает если вшить регулярное выражение.

А как можно получить из системной настройки его? Получение значение в ней асинхронное и нужно сделать через callback, но не как не выходит. Помогите в реализации такого callback, пожалуйста.

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

Доброго всем дня.

 

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

Функционал отрабатывает следующим образом, на примере:

1. У контакта есть 2 телефона: 1 и 2. В профиле, в качестве основного, стоит номер 1. В средствах связи два номера телефона: 1 и 2.

2. При попытке сделать основным телефоном номер 2 при помощи его перезаписи поверх на номер 1 в профиле записи, в детали "Средства связи" номер 1 перезапишется номером 2, на что система ругнётся по факту дубля (номер 1 заменяется на 2, и в детали номера: 2 и 2).

Есть ли возможность корректно без "левых движений" определять номер телефона из детали "Средства связи" как основной, если этот номер таковым не является?

Нравится

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

Если стандартного способа не предусмотрено, можно сделать БП, запускающийся из меню кнопки «Запустить процесс»: сначала на преднастроенной странице выбирать запись из справочника средств связи, отфильтрованных по контакту, потом её писать в соответствующее её типу поле выбранной записи в таблице контактов.

Тоже столкнулись с такой необходимостью и сделали дополнение



https://marketplace.terrasoft.ru/template/upravlenie-osnovnymi-sredstva…

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

Доброго времени суток! Подскажите, пожалуйста, как добавить CheckBox в средства связи. Для средства связи добавил в объекте логическое значение, например с названием "UsrIsMain".

Нравится

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

Мягко говоря не тривиальная задача, посмотрите как в BaseCommunication формируются конфиги для элементов управления для существующих в короюке типов комуникации и по аналогии для своего (добавленного) логического типа создайте конфиг для контрола. 

Мягко говоря не тривиальная задача, посмотрите как в BaseCommunication формируются конфиги для элементов управления для существующих в короюке типов комуникации и по аналогии для своего (добавленного) логического типа создайте конфиг для контрола. 

Григорий Чех,

В принципе мне понятно, как конфиг строится. Добавлял TextEdit поле, Lookup поле. А вот CheckBox не получается, выбрасывает ошибку следующую:

 

message: Cannot read property 'changeEvent' of undefined 

 date: Thu Jun 13 2019 11:01:45 GMT+0300 (Москва, стандартное время)

moduleId: CardModuleV2_########GUID###########_ContactPageV2_detail_ContactCommunication

moduleName: DetailModuleV2



Строил сам CheckBox вот так:

{

                        className: "Terrasoft.CheckBoxEdit",

                        id: "CheckBoxEditId",

                        classes: {

                            wrapClass: ["communication-detail-edit-user-class", "communication-additional-edit-label-wrap"]

                        },

                        value: {

                            bindTo: "UsrIsMain"

                        },

                        change: {

                            bindTo: "changeIsMainFlag"

                        }

  };



Сама функция changeIsMainFlag объявлена в ViewConfig'е.

 

Григорий Чех,

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

В детали средств связи контакта в пакете MarketingCampaign есть такое поле «Не актуальный» (NonActual) у каждого средства связи. Посмотреть на реализацию можно в схеме ContactCommunicationDetail.

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

Добрый день!

Подскажите, как корректно добавить лукап-поле в деталь средств связи. С текстовым проблем не возникает, а вот  лукап поле с типом Enum не получается сделать (чтобы при нажатии в поле появлялся выпадающий список со значениями справочника указанного в bindTo).



Вот мой код добавления конфига данного поля:

    getCountryConfig: function() {

                    var rowConfig = ViewUtilities.getContainerConfig("grid-cols-9",

                            ["grid-cols-3"]);

                    var result =

                    {

                        className: "Terrasoft.ComboBoxEdit",

                        value: {

                            bindTo: "Country"

                        },

                        visible: {bindTo: "MobilePhoneFieldsVisibility"}

                    };

                    var label = this.getLabel("");

                    rowConfig.items.push(label, result);

                    return rowConfig;

                },

Нравится

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

Добавил биндинг из листа, но не заходит в метод указанный в prepareList



getCountryConfig: function() {

                    var rowConfig = ViewUtilities.getContainerConfig("grid-cols-9",

                            ["grid-cols-3"]);

                    

                    var result =

                    {

                        className: "Terrasoft.ComboBoxEdit",

                        value: {bindTo: "Country"},

                        list: {bindTo: "CountryList"},

                        prepareList: {bindTo: "getCountryList"},

                        visible: {bindTo: "MobilePhoneFieldsVisibility"}

                    };

                    

                    var label = this.getLabel("");

                    rowConfig.items.push(label, result);

                    return rowConfig;

                },

Добрый день.

Для добавления справочного поля в детали средств связи, для начала, нужно заместить метод _getCommunicationEditItemViewConfig из схемы BaseCommunicationDetail. В методе нужно добавть блок if для типа Lookup, подписаться на prepareList и забиндится на справочное поле, которое ранее добавили в деталь ContactCommunication.

Так же, добавить логику выгрузки коллекции значений для справочного поля - в методе, который указали в prepareList, например getCommunicationList.

На скриншоте набросала приблизительный план действий - http://prntscr.com/ivb7ox

Demchenko Olha,

Добрый день, Ольга!

метод 

getCommunicationItemViewConfig переопределен, в нем вызываю метод получения конфига моего элемента. 

В BaseCommunicationViewModel метод выгрузки коллекции для prepareList, коллекция грузится, все ок.

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

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

getCommunicationItemViewConfig: function() {
	var itemViewConfig = ViewUtilities.getContainerConfig("item-view",
			//["detail-edit-container-user-class", "control-width-15"]);
			["grid-row grid-module"]);
	var typeMenuItems = this.getTypeMenuItems();
	var typeButtonConfig = this.getTypeButtonConfig(typeMenuItems);
	var iconTypeButtonConfig = this.getIconTypeButtonConfig();
	var textEditConfig = this.getTextEditConfig();
	var isValidConfig = this.getIsValidConfig();
	var operatorCodeConfig = this.getOperatorCodeConfig();
	var validNameConfig = this.getNameLabel();
	var operatorCodeNameConfig = this.getOperatorCodeLabel();
	var countryConfig = this.getCountryConfig();
	var countryLabelConfig = this.getCoutryLabel();
	itemViewConfig.items.push(validNameConfig, isValidConfig, countryLabelConfig, countryConfig,
	operatorCodeNameConfig, operatorCodeConfig, textEditConfig, typeButtonConfig);
	return itemViewConfig;
},
 
getCountryConfig: function() {
	var rowConfig = ViewUtilities.getContainerConfig("grid-cols-9",
			["grid-cols-3"]);
 
	var result =
	{
		className: "Terrasoft.ComboBoxEdit",
		value: {bindTo: "Country"},
		list: {bindTo: "CountryList"},
		prepareList: {bindTo: "getCountryList"},
		visible: {bindTo: "getMobilePhoneFieldsVisibility"}
	};
	var label = this.getLabel("");
	rowConfig.items.push(label, result);
	return rowConfig;
},
 
getCountryList: function() {
	var esq = Ext.create("Terrasoft.EntitySchemaQuery", {
		rootSchemaName: "Country"
	});
	esq.addColumn("Id");
	esq.addColumn("ISOAlpha2");
	esq.getEntityCollection(function(result) {
		var collection = Ext.create("Terrasoft.Collection");
		if (result.success) {
			var selectedTags = result.collection;
			selectedTags.each(function(item) {
				var countryId = item.get("Id");
				var countryName = item.get("ISOAlpha2");
				var menuItemConfig = {
					value: countryId,
					displayValue: countryName
				};
				collection.add(countryId, menuItemConfig);
			}, this);
		}
		this.set("CountryList", collection);
 
	}, this);
},

 

Тетиков Дмитрий Владимирович,

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

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

Не совсем понятно, не подгружается список средств связи или список значений справочника?

Какую информацию дала отладка?

Demchenko Olha,

Ольга, добрый день!

Не прогружается выпадающий список, см. скриншот.

Если запись есть в БД, то по клику в поле список появляется.

Если запись только что создана, нажатием на кнопку добавления и в БД еще нет записи, то при клике в комбобокс, ничего не происходит.

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

 

Тетиков Дмитрий Владимирович,

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

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

Добрый день!

Часто для контакта есть 2 обязательных к заполнению средства связи (e-mail и мобильный телефон).

Посоветуйте, как в карточке контакта (bpm 7.8) реализовать автоматическое добавление этих пустых средств связи (только с выбранным типом, но не заполнеными номерами)?

Вряд ли тут подойдет БП по сохранению контакта, так как контакт еще не создан.

contact

Нравится

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

Смотрите базовую карточку контакта... как там организована деталь "Средства связи". Ну и обязательность поля и его отображение в true. Только вы имейте в виду, как бы это с миникарточкой не начало конфликтовать (поля делайте обязательными только в карточке, не на уровне БД)

Можно попробовать вынести поля из объекта контакт и показывать их только при добавлении записи. После сохранения они синхронизируются в деталь.

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

Всем привет!

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

Сегодня хочу поговорить о средствах связи контрагента.
Итак, рассмотрим коробочную версию данного функционала (хотя как правило, оно достается уже с высокой степенью кастомизации) для Terrasoft XRM 3.2 например (более низкие версии тоже подойдут).
Имеем: средства связи контрагента у нас хранятся в 2-х местах:
1) В таблице контрагента tbl_Account (поля Communication1, Communication2,...,Communication5 и Communication1TypeID, Communication2TypeID,...Communication5TypeID)
2) В Таблице средств связи контрагента tbl_AccountCommunication

Плюсов такого подхода, если честно, не вижу (кроме может быть случая, когда мы данные импортируем, например, из файла Excel и в "грязном" виде заливаем в таблицу tbl_Account, что так же считаю не правильным: их можно залить в какую-то таблицу, специально созданную для импорта, а потом после обработки уже переносить в целевую таблицу).

Минусы ниже:
1) Главный: нам нужно поддерживать целостность данных в 2-х местах: операция Insert/Update/Delete в средствах связи ведет к необходимости выполнить операцию Update в контрагенте и наоборот.
2) Очень часто изменения в таблицах логируются (например, в обеих таблицах). Изменяем 1 телефон в таблице контрагента, вопрос: Сколько записей будет вставлено в таблицы логов? Зависит от реализации механизма поддержки целостности: от 2-х до 6.
3) Бизнес-пользователям захотелось видеть на форме компаний 6 средств связи... или даже 10. Придется добавлять новые поля в таблицу, изменять механизм поддержки целостности, апдейтить таблицу контрагентов (не забыв/забыв о том, что изменения логируются)
4) Часто CRM интегрирована с другими существующими системами. Сколько пакетов интеграции "полетит" при изменении 1 телефона в таблице контрагентов? Аналогично: от 2 до 6.
Возможно, забыл что-то еще. Выяснить все детали того, что происходит можно профайлером на тестовой среде.

Как избавиться от данных проблем: провести нормализацию. Не буду вдаваться в подробности 1,2,3 нормальных форм, нормализации/денормализации, но, очевидно, что средства связи должны храниться в таблице средств связи :smile: (Вопрос о целесообразности tbl_AccountCommunication и tbl_ContactCommunication оставим в стороне).

Что тогда с выводом информации об N средствах связи на форме компании? Есть 2 опции:
1) Заменить вывод информации в соответствующие контролы из таблицы tbl_Account на tbl_AccountCommunication
2) Вывести на форме компании весь грид средств связи и убрать его из деталей.

Я рассмотрю 1 подход.
Нам потребуется внести незначительные изменения в 2 сервиса: wnd_AccountEdit и scr_AccountEdit.

1) Добавляем 5 невизуальных компонентов DatasetLink на форму Контрагента (dlCommunication1, dlCommunication2, ... dlCommunication5) и для каждого из них выбираем датасет ds_AccountCommunication.
Невизуальные компоненты

2) На самой форме меняем в соответствующих контролах DatasetLink на те, что мы создали в первом шаге и выбираем соответствующие поля DatafieldName (тип средства связи CommunicationTypeID и номер Number).
Изменения в контролах
Сохраняем изменения и переходим к обработчику.

3) Теперь нам нужно подтянуть данные для новых контролов при открытии формы.
Напишем функцию, которая в качестве параметра будет принимать порядковый номер средства связи (Position) и будет заполнять данными наши новые датасеты.
Если какого-то средства связи нет, то нужно добавить запись в датасет, обозначив только связь с контрагентом и номер позиции.

//Функция подготовки данных для отображения средств связи из таблицы tbl_AccountCommunication
function InitCommunication(position){  
        eval('var CommunicationDataset = dlCommunication'+ position + '.Dataset');//Определяем целевой датасет
        ApplyDatasetFilter(CommunicationDataset, 'AccountID', dlData.Dataset('ID'), true);//Фильтр по контрагенту
        ApplyDatasetFilter(CommunicationDataset, 'Position', position, true);//Фильтр по номеру позиции   
        RefreshDataset(CommunicationDataset);
        if(CommunicationDataset.IsEmpty){//Если средства связи не существует, нужно его добавить и связать с контрагентом и присвоить номер позиции
                CommunicationDataset.Append();
                CommunicationDataset('Position') = position;
                CommunicationDataset('AccountID') = dlData.Dataset('ID');
        }                      
}

В обработчик события OnPrepare добавляем вызов функции.

function wnd_AccountEditOnPrepare(Window) {
        Initialize();
        wnd_BaseDBEditOnPrepare(Window);
/* PRODUCT XRM */
        UpdateAccountBasicCurrencyCaptions();
        InitializePostponementPayment(Self);
/* ENDPRODUCT XRM */
        InitAccountInfo();
//Заполняем датасеты средств связи
        for(var i = 1; i 6; i++)
           InitCommunication(i);
}

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

4) Займемся сохранением данных в tbl_AccountCommunication при сохранении формы контрагента. Нужно добавить обработчик события OnDatasetAfterPost к невизуальному компоненту основного датасета dlData.
Это самый простой вариант: если запись контрагента новая, то при срабатывании этого события она гарантированно сохранена в базе данных, и у нас не получится так, что средства связи окажутся привязаны к несуществующей компании (или если у нас есть внешний ключ из tbl_AccountCommunication на tbl_Account мы получим ошибку от SQL сервера).
Если нам вдруг требуется валидация средств связи перед их сохранением (например у нас нет обработки правильности ввода информации "на лету" еще при заполнении данных на форме), то можем соответствующий функционал добавить в обработчик OnDatasetBeforePost к невизуальному компоненту основного датасета, с возможностью отменить сохранение основной записи контрагента.

function dlDataOnDatasetAfterPost(Dataset) {
        for(var i = 1; i 6; i++)
           CommitCommunication(i);
}

function CommitCommunication(position){
        eval('var CommunicationDataset = dlCommunication'+ position + '.Dataset');
        if(GetFieldsValuesAreChanged(CommunicationDataset, 'Number', 'CommunicationTypeID'))   
                CommunicationDataset.Post();
}

Основная идея здесь - нам нужно сохранять записи только в том случае, если они действительно менялись, причем менялись или тип или номер. Если например мы добавили пустые датасеты (при подготовке данных для формы) и ничего в эти поля не ввели, то у нас не будет никакого "мусора" из пустых записей в таблице tbl_AccountCommunication.

Можно сохранять изменения и проверять работу.

Замечания.
Поскольку это "учебный" пример, для его доработки до уровня продакшена нужно будет самостоятельно посмотреть следующие моменты:
1) Стоит сохранить число средств связи на форме в глобальной переменной AccountEdit в одном месте (чтобы не приходилось искать каждый раз где нужно изменить его если число контролов на форме потом увеличится/уменьшится), и соответственно исправить код вызова функций.
2) Нужно будет найти и отключить функционал поддержки целостности при изменении записи в детали Средства связи (чтобы запись контрагента не обновлялась) и, на всякий случай, такой же функционал у контрагента.
3) Возможно у кого-то возникнет вопрос, что если раньше при открытии формы контрагента шел 1 запрос на выборку из tbl_Account а теперь 1 + 5 запросов из tbl_AccountCommunication и в чем тогда преимущество? Преимущество в том, что мы избавляемся от 4-х серьезных минусов, которые я описал в самом начале, но да, получаем увеличение числа запросов на выборку данных при открытии формы контрагента, но эти запросы очень "легкие" и вряд ли скажутся на производительности (+ мы всегда можем ее увеличить "допиливанием" индексов).
4) Потребуется доработка функционала для отображения телефонов в гриде контрагентов.

Вроде все. Надеюсь, получилось не слишком сложно о простом :smile:
Будут какие-то мысли, комментарии, вопросы, предложения - welcome!

Нравится

Поделиться

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

"komgbu" написал:3) Возможно у кого-то возникнет вопрос, что если раньше при открытии формы контрагента шел 1 запрос на выборку из tbl_Account а теперь 1 + 5 запросов из tbl_AccountCommunication и в чем тогда преимущество?

А если вытягивать эти все 4 средства связи всё в том же sq_Account, используя LEFT JOIN или в колонках подзапросов?
Хранить в обычных текстовых и лукапных полях без привязки к БД, на AfterPost делать проверку на изменение значения, а затем INSERT или UPDATE в таблицу средств связи контрагента программно.
Будет ли при этом некоторый выигрыш в производительности?

"Зверев Александр" написал:А если вытягивать эти все 4 средства связи всё в том же sq_Account, используя LEFT JOIN или в колонках подзапросов?

Спасибо, Александр, я совсем забыл про этот вариант...
Это хороший вариант - причем у него есть плюсы по сравнению с тем, который я привел в своем примере:
- не нужно трогать стандартные обработчики событий и форму контрагента
"Зверев Александр" написал:Хранить в обычных текстовых и лукапных полях без привязки к БД, на AfterPost делать проверку на изменение значения, а затем INSERT или UPDATE в таблицу средств связи контрагента программно.

Вот этого делать не придется - контролы на форме остаются абсолютно те же самые, никаких дополнительных изменений не нужно. При сохранении датасета ядро (или не ядро - не важно :smile:) само определяет что поля не относятся к таблице tbl_Account и все сохраняется корректно.
- не нужно беспокоиться об отображении средств связи в гриде контрагентов

Я попробовал сделать через соединение:
1) Добавляем 5 раз таблицы tbl_AccountCommunication (с алиасами tbl_AccountCommunication1, tbl_AccountCommunication2,...) связываем их с tbl_Account по AccountID используя LEFT JOIN
Соединение таблиц
2) Удаляем существующие 5 соединений с tbl_CommunicationType и создаем новые с аналогичными именами, но связанные с соответствующими таблицами средств связи
Соединение таблиц
3) Создаем 5 параметров целого типа с цифрами 1...5 (Int1, Int2,...Int5)
Параметры
4) Добавляем условия в WHERE (причем все условия оставляем включенными) в них пишем что tbl_AccountCommunication1.Position = Int1
tbl_AccountCommunication2.Position = Int2
...
Условия
5) В полях запроса Communication1, Communication1TypeID ...Communication5, Communication5TypeID меняем таблицы с tbl_Account на tbl_AccountCommunication1,... tbl_AccountCommunication5 и поля соответственно на Number
Поля
6) Проверяем что все хорошо с полями CommunicationType1Name,...CommunicationType5Name (там не придется ничего менять, если сохранили оригинальное наименование алиасов таблиц)
7) Из таблицы tbl_Account удаляем сначала индексы, затем внешние ключи а затем и поля, относящиеся к средствам связи (ведь главная цель - хранить информацию в одном месте). (На продакшене это делать не обязательно, особенно если нет уверенности в том, что данные в tbl_Account соответствуют данным в tbl_AccountCommunication :smile:)
Таблица контрагентов
Все готово, можно запускать клиент и проверять, что все работает.

По поводу того, какой вариант будет работать быстрее - нужно смотреть (к сожалению под рукой сейчас нет профайлера чтобы собрать запросы которые генерируются в первом и втором вариантах а потом проверить планы выполнения). И это нужно делать на копии продакшена с актуальным объемом данных (этого тоже нет :smile:). Плюс как-то нужно учесть логирование изменений, отправку пакетов интеграции...

Мое мнение - и тот и другой вариант не будут сильно "перегружать" SQL сервер запросами.
Поверьте, не это "убивает" производительность системы, а те причины, по которым стоит переделать данный функционал :smile:

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

Долго не мог понять, почему если я вношу телефон в "Средство связи 1", а такой же указан в "Средство связи 2" другого контакта, то CRM меня не предупреждает?
Вижу, не только меня этот вопрос интересует...
http://community.terrasoft.ua/forum/topic/4121
Как можно решить?

Мой вариант (предлагаю использовать процедуру, для быстроты поиска)

CREATE PROCEDURE [dbo].[tsp_CheckForDoublet] (
        @ID AS uniqueidentifier,
        @Communication1 AS nvarchar(250),
        @Communication2 AS nvarchar(250),
        @Communication3 AS nvarchar(250),
        @Communication4 AS nvarchar(250),
        @NumOfDigits AS int,
        @ReturnResult AS int = 0 OUTPUT
        )
AS

SET @ReturnResult = 0

IF EXISTS (SELECT [ID] FROM [dbo].[tbl_Contact] WHERE
[ID] > @ID AND (  
RIGHT(REPLACE(REPLACE([Communication1], '-', ''), ' ', ''), @NumOfDigits) IN (@Communication1, @Communication2, @Communication3, @Communication4)
OR
RIGHT(REPLACE(REPLACE([Communication2], '-', ''), ' ', ''), @NumOfDigits) IN (@Communication1, @Communication2, @Communication3, @Communication4)
OR
RIGHT(REPLACE(REPLACE([Communication3], '-', ''), ' ', ''), @NumOfDigits) IN (@Communication1, @Communication2, @Communication3, @Communication4)
OR
RIGHT(REPLACE(REPLACE([Communication4], '-', ''), ' ', ''), @NumOfDigits) IN (@Communication1, @Communication2, @Communication3, @Communication4)
) ) SET @ReturnResult = 1

RETURN

Процедура вылавливает дубли даже с тире и пробелами :lol:
8-032-229-99-99 и 2299999 - в данном случае считаются дублями. Если Вас это не устраивает, просто в параметр NumOfDigits большое количество символов для обработки

CreateSPParameter(Parameters, 'NumOfDigits', pdtInteger, 12);

Добавим права доступа на запуск данной процедуры группой "Все пользователи"

Правим событие в файле scr_Contact

function SelfOnDatasetBeforePost(Dataset, DoPost) {

..........
/* START */
        var Parameters = CreateSPParameters();
        CreateSPParameter(Parameters, 'ID', pdtGUID, Dataset('ID'));
        CreateSPParameter(Parameters, 'Communication1', pdtString, Dataset('Communication1'));
        CreateSPParameter(Parameters, 'Communication2', pdtString, Dataset('Communication2'));
        CreateSPParameter(Parameters, 'Communication3', pdtString, Dataset('Communication3'));
        CreateSPParameter(Parameters, 'Communication4', pdtString, Dataset('Communication4'));
        CreateSPParameter(Parameters, 'NumOfDigits', pdtInteger, 7); //количество символов
        CreateSPParameter(Parameters, 'isDoublet', pdtInteger, 0);
        Parameters.ItemsByName('isDoublet').ParamType = 1;
        var SQLText = 'exec dbo.tsp_CheckForDoublet :ID, :Communication1, :Communication2, :Communication3, :Communication4, :NumOfDigits, :isDoublet OUTPUT';
        Connector.DBEngine.ExecuteCustomSQL(SQLText, Parameters);
        var isDoublet = Parameters.ItemsByName('isDoublet').ValAsInt;  
        if (isDoublet == 1) {
                var Message = 'Дублируются средства связи!';
                ShowWarningDialog(Message);
                DoPost.Value = false; // не сохранять в базу
        }
/* END */
}

Доработки - возвращать массив ID и заполнять реестр нового окна ссылками на контактов, с возможностью "перейти к" (В данном варианте мы не знаем где именно дубль, но зато проверяем по всей базе, даже если отсутствуют права на чтение)

Развиваем идею здесь!

Нравится

Поделиться

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

"Виталий Ковалишин aka samael" написал:но зато проверяем по всей базе, даже если отсутствуют права на чтение

:twisted:

"Underscore a.k.a. _" написал::twisted:

Спасибо! А "плюс Адин" где? :wink:

--
www.it-sfera.com.ua

Сорри, забыл. Плюсанул :)

Оооо, другое дело!

Спасибо! :biggrin:

--
www.it-sfera.com.ua

Что-то меня терзают смутные сомнения, что если вы редактируете существующую запись, то вы её тоже выловите своей хранимой процедурой как дублирующуюся. Т.е. как минимум надо добавить проверку по ID записи.

SelfOnDatasetBeforePost

BeforePost может же быть и при редактировании существующей записи.

Согласен!
Нужно добавить проверку на ID :wink:
подправим...

--
www.it-sfera.com.ua

"Раловец Ольга" написал:BeforePost может же быть и при редактировании существующей записи.

Да что Вы говорите /*шепотом так и с большими круглыми глазами*/ :) Но проверка дублей делается только при вставке, или я ошибаюсь?

"Underscore a.k.a. _" написал:Но проверка дублей делается только при вставке, или я ошибаюсь?

"Да что Вы говорите" (с) я вижу вызов процедуры прямо в обработчике события DatasetBeforePost, или я ошибаюсь?

Там жирных 10 точек стоит перед вызовом, или я ошибаюсь?

Не ошибаетесь, ровно 10.

"Underscore a.k.a. _" написал:Там жирных 10 точек стоит

Надо же посчитали :)
Ну и что что точки?
Нигде не написано, что проверку делать надо только при вставке записи. Кроме того это логически не правильно. Проверку делать надо и при обновлении записи.

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

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

"Раловец Ольга" написал:Не ошибаетесь, ровно 10.

Учите матчасть, мадам. Ну и не ленитесь смотреть в исходники.

Поулыбался над комментами :smile:

Виталий, огромное спасибо.
Но нубский вопрос - Куда вставлять данную процедуру?
И можно полностью разжевать, а?
______________
www.abs.com.kz
Агентство недвижимости АБС Royal Estate

Как малые дети! :lol:

1. Проверка и так на стороне сервера (процедуры), не должно быть напрягов :)
2. Можно не на датасет а на карточку повесить, тогда она не будет закрываться (как это сейчас происходит и мы заново должны все набирать из-за одного телефона)
3. Да, точек 10 - но это случайно получилось
4. Сегодня постараюсь подправить процедуру на проверку по ID (вместо баловства - могли бы и помочь) -- уже подправил
5. После - полностью разъясню куда и что вставить (я о процедуре) :)

Спасибо, не ожидал такого бурного обсуждения! :biggrin:

--
www.it-sfera.com.ua

"Евгений Лемеш" написал:Поулыбался над комментами

Видать вчера магнитные бури были. Сам себе удивляюсь :)

"Биккинин Т.Р." написал:Куда вставлять данную процедуру?

Вам необходимо открыть SQL Server Management Studio на сервере и выполнить данный скрип - процедура будет создана. После, добавить права доступа (иначе, только Supervisor сможет нею воспользоваться):

GRANT EXECUTE ON PROCEDURE [dbo].[tsp_CheckForDoublet] TO public

Дальше код в админке :)

Это быстрое решение, для большего удобства пользования, оно еще нуждается в доработке... :lol:

--
www.it-sfera.com.ua

Виталий, добрый день.
Добрался до Вашего ответа.
Скрипт на сервере прошел, но на

 
GRANT EXECUTE ON PROCEDURE [dbo].[tsp_CheckForDoublet] TO public 

выдает следующее:
Server: Msg 156, Level 15, State 1, Line 1
Incorrect syntax near the keyword 'PROCEDURE'.

Что я делаю не так?
______________
www.abs.com.kz
Агентство недвижимости АБС Royal Estate

GRANT EXECUTE ON [dbo].[tsp_CheckForDoublet] TO public 

А так?

Копи-пейст он до добра не доводит :)

Ольга,

Server: Msg 208, Level 16, State 11, Line 1
Invalid object name 'dbo.tsp_CheckForDoublet'.

______________
www.abs.com.kz
Агентство недвижимости АБС Royal Estate

С помощью скрипта Виталия процедура создалась успешно, правильно я понимаю? Убедитесь, что она действительно существует, и что grant execute... Вы выполняете для той же базы данных.

Ольга,

в том то и дело, что процедура создалась успешно, только я не понял для какой именно базы. на сервере у меня их 3 лежит. Таким же образом добавляю grant execute, но она не проходит. ______________
www.abs.com.kz
Агентство недвижимости АБС Royal Estate

Для начала нужно определиться с именем нужной базы данных :) Приблизительная последовательность действий: открываем SQL Server Management Studio, подключаемся к серверу, далее слева в инспекторе объектов разворачиваем ветку "Databases" и останавливаемся на той, в которой хотим создать процедуру. Далее нажимаем кнопочку "New Query", откроется окно редактирования и появится еще одна строчка в меню, там будет выпадающий список, в котором выбрана та база данных, на которой мы стояли в инспекторе объектов. Если сразу нажать "New Query", то будет выбрана бд "master".

Ольга, спасибо. Пошел проверять. Отпишусь позднее.
______________
www.abs.com.kz
Агентство недвижимости АБС Royal Estate

Вот, что называется отсутствие знаний!
Ольга, все сделал, как Вы сказали, обе процедуры прошли успешно. Добавил функцию в скрипт scr_Contact, но новая функция не работает.
Вот так выглядит теперь функция SelfOnDatasetBeforePost:

function SelfOnDatasetBeforePost(Dataset, DoPost) {
	var ContactsDataset = Dataset;
	Contact.NeedActualizeCommunication = GetFieldsValuesAreChanged(Dataset,
		'Communication1', 'Communication2', 'Communication3',
		'Communication4', 'Communication1TypeID', 'Communication2TypeID',
		'Communication3TypeID', 'Communication4TypeID');
	Contact.NeedActualizeAddress = GetFieldsValuesAreChanged(Dataset, 'Address',
		'CountryID', 'AddressTypeID', 'StateID', 'CityID', 'TerritoryID');
	Contact.NeedActualizeCareer = GetFieldsValuesAreChanged(Dataset, 'AccountID',
		'JobID','JobTitle','DepartmentID');
	/* MODULE CAMPAIGNS */
	Contact.NeedActualizeCampaign = GetFieldsValuesAreChanged(Dataset,
		'CampaignID');
	/* ENDMODULE CAMPAIGNS */
	if(!IsEmptyValue(Dataset.Values('AccountID')))
	{
	Dataset.Values('WorkName') = Dataset.Values('Name')+' , '+GetDatasetFieldValueByID('ds_Account',Dataset.Values('AccountID'),'Name');
	}
	else
	{
	Dataset.Values('WorkName') = Dataset.Values('Name');
}
 
        Contact.IsAppend = (Dataset.State == dstInsert);  
        //SetItemSystemNumber('Contact', Dataset, 'ClientNumber');
 
    var Parameters = CreateSPParameters();
        CreateSPParameter(Parameters, 'ID', pdtGUID, Dataset('ID'));
        CreateSPParameter(Parameters, 'Communication1', pdtString, Dataset('Communication1'));
        CreateSPParameter(Parameters, 'Communication2', pdtString, Dataset('Communication2'));
        CreateSPParameter(Parameters, 'Communication3', pdtString, Dataset('Communication3'));
        CreateSPParameter(Parameters, 'Communication4', pdtString, Dataset('Communication4'));
        CreateSPParameter(Parameters, 'NumOfDigits', pdtInteger, 7); //количество символов
        CreateSPParameter(Parameters, 'isDoublet', pdtInteger, 0);
        Parameters.ItemsByName('isDoublet').ParamType = 1;
        var SQLText = 'exec dbo.tsp_CheckForDoublet :ID, :Communication1, :Communication2, :Communication3, :Communication4, :NumOfDigits, :isDoublet OUTPUT';
        Connector.DBEngine.ExecuteCustomSQL(SQLText, Parameters);
        var isDoublet = Parameters.ItemsByName('isDoublet').ValAsInt;  
        if (isDoublet == 1) {
                var Message = 'Дублируются средства связи!';
                ShowWarningDialog(Message);
                DoPost.Value = false; // не сохранять в базу
        }    
}

Что неправильно?
______________
www.abs.com.kz
Агентство недвижимости АБС Royal Estate

Я вот подумала, что не очень-то хорошо прописывать строки, среди которых есть вывод сообщения пользователю в скрипте датасета. Лучше сделать это в скрипте карточки (scr_ContactEdit). Открываете окно wnd_ContactEdit, переходите на закладку не визуальные(в левой верхней стороне), становитесь на dlData, ниже среди событий находите OnDatasetBeforePost, нажимаете 2 раза и пишете

if (Dataset.State == dstInsert) {
var Parameters = CreateSPParameters();
        CreateSPParameter(Parameters, 'ID', pdtGUID, Dataset('ID'));
        CreateSPParameter(Parameters, 'Communication1', pdtString, Dataset('Communication1'));
        CreateSPParameter(Parameters, 'Communication2', pdtString, Dataset('Communication2'));
        CreateSPParameter(Parameters, 'Communication3', pdtString, Dataset('Communication3'));
        CreateSPParameter(Parameters, 'Communication4', pdtString, Dataset('Communication4'));
        CreateSPParameter(Parameters, 'NumOfDigits', pdtInteger, 7); //количество символов
        CreateSPParameter(Parameters, 'isDoublet', pdtInteger, 0);
        Parameters.ItemsByName('isDoublet').ParamType = 1;
        var SQLText = 'exec dbo.tsp_CheckForDoublet :ID, :Communication1, :Communication2, :Communication3, :Communication4, :NumOfDigits, :isDoublet OUTPUT';
        Connector.DBEngine.ExecuteCustomSQL(SQLText, Parameters);
        var isDoublet = Parameters.ItemsByName('isDoublet').ValAsInt;  
        IF (isDoublet == 1) {
                var Message = 'Дублируются средства связи!';
                ShowWarningDialog(Message);
                DoPost.Value = false; // не сохранять в базу
        }    
}
}

Ольга, спасибо.
Заработало. Результатом является появление диалогового сообщения, причем повторного и карточка не сохраняется. И при этом система не различает телефон 123456, 123-456, 12-34-56 и 123 456. По идее должно же?

______________
www.abs.com.kz
Агентство недвижимости АБС Royal Estate

"Биккинин Т.Р." написал:Результатом является появление диалогового сообщения, причем повторного и карточка не сохраняется.

Что значит повторного? Вы, наверное, не убрали из скрипта датасета то, что внесли до того, как я посоветовала редактировать scr_ContactEdit.
"Биккинин Т.Р." написал:И при этом система не различает телефон 123456, 123-456, 12-34-56 и 123 456. По идее должно же?

Судя по тому, что написал Виталий, должно считать их одним и тем же.
"Виталий Ковалишин aka samael" написал:Процедура вылавливает дубли даже с тире и пробелами :lol:
8-032-229-99-99 и 2299999 - в данном случае считаются дублями. Если Вас это не устраивает, просто в параметр NumOfDigits большое количество символов для обработки

Еще обратите внимание, что проверка выполняется только при попытке сохранить новую запись.

Ольга,

"Раловец Ольга" написал:Что значит повторного? Вы, наверное, не убрали из скрипта датасета то, что внесли до того, как я посоветовала редактировать scr_ContactEdit.
Да, действительно не удалил.
[br]
"Раловец Ольга" написал:Судя по тому, что написал Виталий, должно считать их одним и тем же.
Походу не считает, даже если дубль записывается в то же поле "средство связи".
[br]
"Раловец Ольга" написал:Еще обратите внимание, что проверка выполняется только при попытке сохранить новую запись.
Так оно и есть, тем не менее, что-то здесь с тире и пробелами не так.

______________
www.abs.com.kz
Агентство недвижимости АБС Royal Estate

В качестве примера:
Есть карточка, с телефонов 379007 в поле "Средство связи 1".
Если я создаю другую карточку с телефоном 379-007 или 379 007 в этом же поле, то система не воспринимает это как дубль и карточку сохраняет.
______________
www.abs.com.kz
Агентство недвижимости АБС Royal Estate

Добрый день!

Проверил - считает дублями записи 2233445 и 22-33-44-5!
У Вас 6-ти значные номера телефонов? Попробуйте заменить

CreateSPParameter(Parameters, 'NumOfDigits', pdtInteger, 7); //количество символов

На:

CreateSPParameter(Parameters, 'NumOfDigits', pdtInteger, 6); //количество символов

--
www.it-sfera.com.ua

Как-то странно отрабатывает:
1) Не различает 123456, 1-23-456, 12 34 56. При этом абсолютно одинаковые номера отлавливает.
2) Если одно или более средств связи остаются пустыми, срабатывает как на дубль;
3) Не проверяет средства связи не отображенные в карточке.

"Виталий Ковалишин aka samael" написал:У Вас 6-ти значные номера телефонов? Попробуйте заменить

В системе хранятся номера с различным кол-вом знаков, каким тогда задавать параметр NumOfDigits?

"Виталий Ковалишин aka samael" написал:Это быстрое решение, для большего удобства пользования, оно еще нуждается в доработке...

Я написал как пример реализации. Вы можете внести свой вклад в развитие идеи и ее совершенствования (Доработки - читали? А никто так ничего и не предложил...)

"Виталий Ковалишин aka samael" написал:вместо баловства - могли бы и помочь

Welcome to OpenSource :) Развиваем!

--
www.it-sfera.com.ua

Подтверждаю слова Олега.
Все аналогично
______________
www.abs.com.kz
Агентство недвижимости АБС Royal Estate

"Виталий Ковалишин aka samael" написал:Я написал как пример реализации. Вы можете внести свой вклад в развитие идеи и ее совершенствования

Сорь, упустил этот момент.:biggrin:

У меня предварительно получилось что-то такое:
1) Хранимка

CREATE PROCEDURE [dbo].[tsp_CheckForDoublet] (
        @ID AS uniqueidentifier,
        @Communication1 AS nvarchar(250),
        @Communication2 AS nvarchar(250),
        @Communication3 AS nvarchar(250),
        @Communication4 AS nvarchar(250),
        @ReturnResult AS int = 0 OUTPUT
        )
AS
 
SET @ReturnResult = 0
SET @Communication1 = REPLACE(REPLACE(@Communication1, '-', ''), ' ', '')
SET @Communication2 = REPLACE(REPLACE(@Communication2, '-', ''), ' ', '')
SET @Communication3 = REPLACE(REPLACE(@Communication3, '-', ''), ' ', '')
SET @Communication4 = REPLACE(REPLACE(@Communication4, '-', ''), ' ', '')
 
IF EXISTS (SELECT [ID] FROM [dbo].[tbl_ContactCommunication] WHERE 
[ContactID] <> @ID AND (  
REPLACE(REPLACE([Number], '-', ''), ' ', '') IN (@Communication1, @Communication2, @Communication3, @Communication4)
) ) SET @ReturnResult = 1
 
RETURN
GO

2) Функция:

function CheckContactCommunication(Dataset) {
	var Parameters = CreateSPParameters();
	CreateSPParameter(Parameters, 'ID', pdtGUID, Dataset.ValAsGUID('ID'));
	CreateSPParameter(Parameters, 'Communication1', pdtString, 
		Dataset.ValAsStr('Communication1'));
	CreateSPParameter(Parameters, 'Communication2', pdtString, 
		Dataset.ValAsStr('Communication2'));
	CreateSPParameter(Parameters, 'Communication3', pdtString,
		Dataset.ValAsStr('Communication3'));
	CreateSPParameter(Parameters, 'Communication4', pdtString,
		Dataset.ValAsStr('Communication4'));
	CreateSPParameter(Parameters, 'isDoublet', pdtInteger, 0);
    Parameters.ItemsByName('isDoublet').ParamType = 1;
    var SQLText = 'exec dbo.tsp_CheckForDoublet :ID,:Communication1,:Communication2,:Communication3,:Communication4,:isDoublet OUTPUT';
    Connector.DBEngine.ExecuteCustomSQL(SQLText, Parameters);
    var isDoublet = Parameters.ItemsByName('isDoublet').ValAsInt;   
    if (isDoublet == 1) {
        ShowWarningDialog('Дублируются средства связи!');
        return false;
    }
    return true;
}

3)Вызов функции:

function dlDataOnDatasetBeforePost(Dataset, DoPost) {
	if (!CheckContactCommunication(Dataset)) {
		DoPost.Value = false;
	}
}

Возник вопрос:
Если в процедуру добавляю еще один параметр, то выпадает ошибка:
Formal parameter 'такой-то' was defined as OUTPUT but the actual parameter not declared OUTPUT.
На сколько я смог понять, это связанно с типом параметра, но не могу понять как. Может кто-то сталкивался?

Если Вы добавили исходящий параметр в процедуру, то и при вызове нужно указать тип исходящий. Как с этим:

Parameters.ItemsByName('isDoublet').ParamType = 1;

Так в том то и вопрос что параметр входящий, а для них(входящих), я так понял, указывать тип не надо.

По умолчанию тип входящий. Может, каким-то образом порядок повлиял?

Действительно. Поставил параметр в описании первым и все заработало.:confused:

Я думаю, было бы удобно повесить этот алгоритм поиска на сервис проверки дублей, для поиска в уже введенных контактах.
Может кто подскажет, как это сделать.

Руслан, думаю, адаптировать проверку дублей в базовой версии для реализации данного функционала будет не совсем верно, так как он является универсальным для поиска по одному полю. Данный пример реализации таковым не является, так как с физической точки зрения поля Communication1 и Communication4 отличаются между собой так же, например, как Communication1 и Name.

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

CREATE PROCEDURE [dbo].[tsp_CheckForDoublet]
			@ContactID uniqueidentifier,
			@NumOfDigits int
AS
begin
 
	declare @SecondID uniqueidentifier
	declare @ContactName nvarchar(250)
	declare @SecondName nvarchar(250)
	declare @Communication1 nvarchar(250)
        	declare @Communication2 nvarchar(250)
        	declare @Communication3 nvarchar(250)
        	declare @Communication4 nvarchar(250)
	create table #tmp (
		ID uniqueidentifier,
		ParentRecordID uniqueidentifier,
		ChildRecordID uniqueidentifier,
		ParentRecordValue nvarchar(250),
		ChildRecordValue nvarchar(250))
 
 
	SELECT @ContactName = [Name], @Communication1 = RIGHT(REPLACE(REPLACE([Communication1], '-', ''), ' ', ''), @NumOfDigits),
		@Communication2 = RIGHT(REPLACE(REPLACE([Communication2], '-', ''), ' ', ''), @NumOfDigits),
		@Communication3 = RIGHT(REPLACE(REPLACE([Communication3], '-', ''), ' ', ''), @NumOfDigits),
		@Communication4 = RIGHT(REPLACE(REPLACE([Communication1], '-', ''), ' ', ''), @NumOfDigits) FROM tbl_Contact
	WHERE [ID] = @ContactID
 
 
	declare c_Contact cursor for
		SELECT [ID], Name FROM [dbo].[tbl_Contact] WHERE
		[ID] <> @ContactID AND (  
		(RIGHT(REPLACE(REPLACE([Communication1], '-', ''), ' ', ''), @NumOfDigits) IN (@Communication1, @Communication2, @Communication3, @Communication4) and RIGHT(REPLACE(REPLACE([Communication1], '-', ''), ' ', ''), @NumOfDigits) <> '')
		OR
		(RIGHT(REPLACE(REPLACE([Communication2], '-', ''), ' ', ''), @NumOfDigits) IN (@Communication1, @Communication2, @Communication3, @Communication4) and RIGHT(REPLACE(REPLACE([Communication2], '-', ''), ' ', ''), @NumOfDigits) <> '')
		OR
		(RIGHT(REPLACE(REPLACE([Communication3], '-', ''), ' ', ''), @NumOfDigits) IN (@Communication1, @Communication2, @Communication3, @Communication4) and RIGHT(REPLACE(REPLACE([Communication3], '-', ''), ' ', ''), @NumOfDigits) <> '')
		OR
		(RIGHT(REPLACE(REPLACE([Communication4], '-', ''), ' ', ''), @NumOfDigits) IN (@Communication1, @Communication2, @Communication3, @Communication4) and RIGHT(REPLACE(REPLACE([Communication4], '-', ''), ' ', ''), @NumOfDigits) <> '')
		)
 
	open c_Contact
	while 1 = 1
	begin
		fetch next from c_Contact into @SecondID, @SecondName
		if @@fetch_status = -1 break
		if @@fetch_status = -2 continue
 
		insert into #tmp (ID, ParentRecordID, ChildRecordID, ParentRecordValue, ChildRecordValue)
		values (NewID(), @ContactID, @SecondID, @ContactName, @SecondName)
	end
 
	close c_Contact
	deallocate c_Contact
 
	select distinct ParentRecordID, ChildRecordID, ChildRecordValue from #tmp
	--where ParentRecordID = @ContactID
 
	drop table #tmp
 
end
GO

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

Олег Лабьяк,
разработчик,
3-я линия Службы поддержки Terrasoft.

Доброго времени суток!
Возникла необходимость реализовать поиск дублей в уже введенных контактах.
Подскажите пожалуйста, т.к. я новичок еще в доработках CRM, какие изменения возможно внести в уже готовый сервис поиска дублей для решения поставленной задачи или есть какое-то альтернативное решение?

Здравствуйте,
Подскажите какую версию Terrasoft вы используете ?
Ознакомьтесь, пожалуйста, с вложением "Настройка поиска дублей", такова реализация в 3.4.0

Здравствуйте.
Используется Terrasoft Press версии 3.3.0.45.
С вложением ознакомился, но видимо данный функционал в имеющейся версии не представлен.

Хатемкин В.А.
ОАО "Пилот-Медиа"

Функционал поиска дублей в существующих записях полноценно реализован начиная с версии 3.3.2

Для 3.3.0 его можно только дорабатывать вручную, а это довольно серьезный объем работы.

Алгоритм таков:

1. Создать зранимую процедуру поиска дублей в БД;
2. Создать интерфейсную часть в Terrasoft, которая позволила бы пользователям задавать настройки поиска и слияния дублей;
3. Интегрировать поиск дублей в систему.

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

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

Добрый день.
Иногда перед нами стоит задача заполнить карточки контактов информацией о контрагентах (адрес, средства связи), которые указаны в их карточках в поле Контрагент.  Для того, чтобы Выполнить этой действие Вам необходимо выполнить скрипт на базе данных:

--обновляет средсва связей и адреса контактов по их контрагентам.
--Детали адреса и средства связей контактов наполняются по их контрагентам, при этом старые записи остаются
-- если старые записи не нужны убрать/закомментировать строчку помеченную /*!!!*/
declare @NeedOldDetailRecords int
set @NeedOldDetailRecords = 0
set @NeedOldDetailRecords = 1   /*!!!*/


print 'Update tbl_Contact'
update t1
set     t1.Communication1=t2.Communication1,
    t1.Communication2=t2.Communication2,
    t1.Communication3=t2.Communication3,
    t1.Communication4=t2.Communication4,
    t1.Communication1TypeID=t2.Communication1TypeID,
    t1.Communication2TypeID=t2.Communication2TypeID,
    t1.Communication3TypeID=t2.Communication3TypeID,
    t1.Communication4TypeID=t2.Communication4TypeID,
    t1.TerritoryID    =t2.TerritoryID,
    t1.AddressTypeID =t2.AddressTypeID,
    t1.CountryID =t2.CountryID,
    t1.StateID =t2.StateID,
    t1.CityID =t2.CityID,
    t1.ZIP =t2.ZIP,
    t1.Address =t2.Address
from tbl_Contact t1, tbl_Account t2
where t1.AccountID is not null
and t2.id=t1.AccountID

if @NeedOldDetailRecords = 1
begin
    print 'update tbl_ContactCommunication'
    update tbl_ContactCommunication
    set position = null
    where exists(select * from tbl_Contact t2 where tbl_ContactCommunication.ContactID=t2.ID and t2.AccountID is not null)
   
    print 'update tbl_ContactAddress'
    update tbl_ContactAddress
    set IsPrimary = null
    where exists(select * from tbl_Contact t2 where tbl_ContactAddress.ContactID=t2.ID and t2.AccountID is not null)
end
if @NeedOldDetailRecords = 0
begin
    print 'delete from tbl_ContactAddress'
    delete tbl_ContactAddress
    from tbl_ContactAddress t1
    where exists(select * from tbl_Contact t2 where t1.ContactID=t2.ID and t2.AccountID is not null)
   
    print 'delete from tbl_ContactCommunication'
    delete tbl_ContactCommunication
    from tbl_ContactCommunication t1
    where exists(select * from tbl_Contact t2 where t1.ContactID=t2.ID and t2.AccountID is not null)
end

print 'Insert into tbl_ContactAddress'
insert into tbl_ContactAddress(id,ContactID,AddressTypeid,Address,CityID,StateID, Zip, CountryID, IsPrimary, TerritoryID)
select newid(), t1.id, t3.AddressTypeid, t3.Address, t3.CityID, t3.StateID, t3.Zip, t3.CountryID, t3.IsPrimary, t3.TerritoryID
from tbl_Contact t1, tbl_Account t2, tbl_AccountAddress t3
where t1.accountid is not null
and t1.accountid=t2.id
and t2.id=t3.accountid

print 'Insert into tbl_ContactCommunication'
insert into tbl_ContactCommunication(id,ContactID,Number,CommunicationTypeID,Position,Digits)
select newid(), t1.id, t3.Number, t3.CommunicationTypeID, t3.Position, t3.Digits
from tbl_Contact t1, tbl_Account t2, tbl_AccountCommunication t3
where t1.accountid is not null
and t1.accountid=t2.id
and t2.id=t3.accountid

С уважением,
Коваленко Ирина

Нравится

Поделиться

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