Кастомизация
22
Ноя
37

Интеграция картографических веб-сервисов с MS CRM

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

Яндекс.Карты

В этом примере, как и в последующем, отобразим на графической карте адрес, записанный на какой-либо из карточек (сейчас это будет карточка Бизнес-партнера).

  • Первым делом зарегистрируйтесь в системе Яндекс (если Вы уже зарегистрированы, то залогинтесь) – это необходимо для того чтобы получать API-ключи доступа к системе;
  • Далее регистрируем сам ключ http://api.yandex.ru/maps/form.xml. Регистрация происходит путем ввода названия домена, на котором будет запускаться карта. Этот ключ будет задействован в скриптах, которые подключают карту и без него ничего работать не будет! Сохраните куда-нибудь этот ключ – он нам потом понадобится.
    Но, тут нужно сделать одно замечание: Яндекс не позволяет зарегистрировать ключ для домена первого уровня! Т.е. у Вас не получится зарегить домен http://crmserver/. Воркэраунд в отношении CRM системы состоит в регистрации домена второго уровня, например, http://crmserver.loc/ или http://mycrm.ru/, а затем в DNS, используемым в Вашей сети, сопоставить этот домен с IP-адресом CRM сервера (я не стал для примера мудрить с DNS, а записал сопоставление в файл C:/WINDOWS/system32/drivers/etc/hosts). Т.е. в итоге в CRM Вы будете заходить по http://crmserver.loc/<org>/. Вторым вариантом является внутреннее IFD развертывание;
  • Добавьте api-maps.yandex.ru в доверенные (возможно Вам также придется поиграться с настройками доверенной группы, чтобы отключить всякие всплывающие окна с предупреждениями и т.д.);



  • Затем создайте в папке <сайт CRM>/ISV/ файл с именем ymap.html и со следующем содержимым (обязательно поменяйте в нам ключ на свой):
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <title>Яндекс.Карты</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <script src="http://api-maps.yandex.ru/1.1/index.xml?key=ANmAukoBAAAAVNkIbgIAHwqu8oFhn1pocIiy-4rmgDeGjXcAAAAAAAAAAAC1YveXxxErIBc_6SCQTaWfAGnqgg==" type="text/javascript"></script>
    <script type="text/javascript">
    window.onload = function() {
    	// объявляем карту, центрируем ее и подключем основные элементы управления
    	var map = new YMaps.Map(document.getElementById("YMapsID"));
    	map.setCenter(new YMaps.GeoPoint(37.64, 55.76), 10);
    	var typeControl = new YMaps.TypeControl();
    	map.addControl(typeControl);
    	var toolbarControl = new YMaps.ToolBar();
    	map.addControl(toolbarControl);
    
    	// разбираем переданные в URL переменные
    	var _GET_Keys; // в этом масиве будут хранится названия переменных
    	var _GET_Values; // а в этом значения этих переменных
    	var _GET_Count = 0;
    
    	get = new String(window.location); // присваеваем переменной значение адресной строки
    	x = get.indexOf('?');
    	if (x != -1) {
    
    		l = get.length;
    		get = get.substr(x + 1, l - x); // вырезаем подстроку с переменными
    
    		l = get.split('&');
    		x = 0;
    
    		_GET_Count = l.length;
    		_GET_Values = new Array(_GET_Count);
    
    		// отделяем название переменных от их значений и заносим в соответствующие массивы
    		for (i in l) {
    			get = l[i].split('=');
    			_GET_Values[x] = get[1];
    
    			x++;
    		}
    		
    		// инициализируем поиск координат
    		var geocoder = new YMaps.Geocoder(_GET_Values[0]);
    
    		// ищем значение первой переданной переменной и первый найденный результат отображаем на карте
    		YMaps.Events.observe(geocoder, geocoder.Events.Load, function() {
    			if (this.length()) {
    				// первым параметром для балуна указываем координаты, а вторым отображаемый текст (вторая переменная из URL0
    				map.openBalloon(this.get(0).getGeoPoint(), _GET_Values[1])
    			} else {
    				alert("Ничего не найдено")
    			}
    		});
    
    		// в случаи ошибки выводим ее
    		YMaps.Events.observe(geocoder, geocoder.Events.Fault, function(geocoder, error) {
    			alert("Произошла ошибка: " + error)
    		});
                    
    	}
    
    }
    </script>
    </head>
    <style type="text/css">
    <!--
    body {
    	margin: 0;
    	padding: 0;
    }
    -->
    </style>
    <body>
    <script language="JavaScript">
        document.write("<div id=\"YMapsID\" style=\"width:" + document.documentElement.clientWidth + "px;height:" + document.documentElement.clientHeight + "px\"></div>")
    </script>    
    </body>
    </html>
    

    Код на этой странице делает следующее: первым делом подключает саму карту через скрипт, затем выцепляет из строки URL переменные. Одна из которых пердставляет собой адрес и координаты этого адрема ищутся в сервисе геокодирования. Затем первый найденный результат отображается на карте вместе с подписью переданной во втором папрметре в строке URL.
    Готовая HTML-страница;


  • Откройте настройку формы Бизнес-партнеров и добавьте на нее новую вкладу и новую секцию. На этой вкладке разместите iFrame с такими параметрами:
    • Имя: IFRAME_ya;
    • URL: about:blank;
    • Снемите галку «Ограничить ользование сценариев между кадрами».
    • Установите автоматическо развертование на всю доступную облась и отключите прокрутку;
  • На онлоад формы повесьте такой скрипт:
    var tabVar= crmForm.all.tab4Tab;
    
    if(typeof(tabVar) != "undefined" && tabVar != null) {
    
    	crmForm.all.tab4Tab.attachEvent('onclick',loadframe, false); 
    
    }
    
    function loadframe() {
    
    	crmForm.all.IFRAME_ya.src = "/isv/ymap.html?s=" + crmForm.all.address1_name.DataValue + "&m=" + crmForm.all.name.DataValue;
    
    }
    

    Этот скрипт составляет строку URL для айфрейма (при щелчке по пятой вкладке), путем объединения значений адреса и названия организации;



  • Открываем какую-либо запись Бизнес-партнера с обязательно заполненными полями Название организации и Название в секции Адрес. Переходим на вкладку с iFrame’ом. Вуа-ля 🙂


Яндекс.Карты обладают гораздо более широкими возможностями, чем описаны здесь. Для более подробной информации обратитесь на сайт разработки http://api.yandex.ru/maps/

Google Maps

Теперь рассмотрим, что нам может предложить «конкурент» 🙂

  • Как и в случаи с Яндексом сначала необходимо получить «Золотой ключик». Но для этого также нужно зарегистрироваться на https://www.google.com/accounts/ManageAccount (щелкните Создайте аккаунт прямо сейчас);
  • После этого на странице http://code.google.com/intl/ru/apis/maps/signup.html получите ключ доступа. Для этого введите названия домена, на котором будет использоваться карта. Сохраните сгенерированный ключ. И кстати в отличие от Яндекса (и это является большим плюсом) в Google Maps можно зарегистрировать домен первого уровня;
  • Добавьте сайт https://maps.google.com в доверенные;



  • Создайте web-страницу <сайт CRM>/ISV/gmap.html со следующем содержимым (обязательно поменяйте в нам ключ на свой):
    <!DOCTYPE html "-//W3C//DTD XHTML 1.0 Strict//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">  
    <head>    
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>    
    <title>Google Maps JavaScript API Example</title>    
    <script src="http://maps.google.com/maps?file=api&v=2&key=ABQIAAAALUFMmadV4mENaPGgfSUy1BRu7NqVTvYkJ9cvgasUxySwflY0VxQ7YdPc8sZV6DwUWf9XvVqEIYM2ZQ&sensor=false" type="text/javascript"></script>
    <script type="text/javascript">
    function initialize() {
    	if (GBrowserIsCompatible()) {
    
    		// объявляем карту и подключем основные элементы управления
    		var map = new GMap2(document.getElementById("map_canvas"));
    		map.setUIToDefault();
    
    		// разбираем переданные в URL переменные
    		var _GET_Keys; // в этом масиве будут хранится названия переменных
    		var _GET_Values; // а в этом значения этих переменных
    		var _GET_Count = 0;
    
    		get = new String(window.location); // присваеваем переменной значение адресной строки
    		x = get.indexOf('?');
    
    		if (x != -1) {
    
    			l = get.length;
    			get = get.substr(x + 1, l - x); // вырезаем подстроку с переменными
    
    			l = get.split('&');
    			x = 0;
    
    			_GET_Count = l.length;
    			_GET_Values = new Array(_GET_Count);
    
    			// отделяем название переменных от их значений и заносим в соответствующие массивы
    			for (i in l) {
    				get = l[i].split('=');
    				_GET_Values[x] = get[1];
    
    				x++;
    			}
    
    			// задействуем поиск
    			var geocoder = new GClientGeocoder();
    			geocoder.getLatLng(_GET_Values[0],
    				function(point) {
    					// если ничего не найдено выводим сообщение
    					if (!point) {
    						alert(_GET_Values[0] + " не найден");
    					} else {
    						// если поиск успешен, то выводим маркер и информационную область на карту
    						map.setCenter(point, 13);
    						var marker = new GMarker(point);
    						map.addOverlay(marker);
    						marker.openInfoWindowHtml(_GET_Values[1]);
    					}
    				}
    			);
    		}
    	}
    }
           
    </script>  
    </head>
    <style type="text/css">
    <!--
    body {
    	margin: 0;
    	padding: 0;
    }
    -->
    </style>
    <body onload="initialize()" >    
    <script language="JavaScript">
    	document.write("<div id=\"map_canvas\" style=\"width:" + document.documentElement.clientWidth + "px;height:" + document.documentElement.clientHeight + "px\"></div>")
    </script> 
    </body>
    </html>
    

    Этот код почти идентичен приведенному в предыдущем примере, а именно: создает карту, ищет адрес по переданной в URL строке и отображает его на карте.
    Готовая HTML-страница;


  • Далее откройте настройку формы Контакт и создайте на ней новую вкладку и секцию. Далее добавьте iFrame’е с такими параметрами:
    • Имя: IFRAME_gm;
    • URL: about:blank;
    • Снемите галку «Ограничить ользование сценариев между кадрами»;
    • Установите автоматическо развертование на всю доступную облась и отключите прокрутку.
  • На онлоад добавьте такой код:
    var tabVar= crmForm.all.tab4Tab;
    
    if(typeof(tabVar) != "undefined" && tabVar != null) {
    
    	crmForm.all.tab4Tab.attachEvent('onclick',loadframe, false); 
    
    }
    
    function loadframe() {
    
    	crmForm.all.IFRAME_gm.src = "http://crm2008/isv/gmap.html?s=" + crmForm.all.address1_name.DataValue + "&m=" + crmForm.all.lastname.DataValue;
    
    }
    

    Этот код просто составляет (при щелчке на пятой вкладке) строчку URL для карты: подставляет в передаваемые параметры фамилию и название адреса;



  • Открываем запись Контакта, заполняем поля Фамилия и Название в секции Адрес. Ну, и переходим на вкладку с картой 🙂 все 🙂


Сайт разработки Google Maps: http://code.google.com/intl/ru/apis/maps/

Комментарии (37)
  • Олег 22.11.2009

    Спасибо за полезную статью. А как на одной карте можно отобразить всех клиентов, которые есть в CRM. Например карту я отображу через SiteMap в рабочей области. Как туда передать адреса? Какие классы API надо использовать? Спасибо

  • slivka_83 22.11.2009

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

    // задействуем поиск
    var geocoder = new GClientGeocoder();
    geocoder.getLatLng(_GET_Values[0],

    function(point) {
    // если ничего не найдено выводим сообщение
    if (!point) {
    alert(_GET_Values[0] + » не найден»);
    } else {
    // если поиск успешен, то выводим маркер и информационную область на карту
    map.setCenter(point, 13);
    var marker = new GMarker(point);
    map.addOverlay(marker);
    marker.openInfoWindowHtml(_GET_Values[1]);
    }
    }
    );

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

  • Олег 22.11.2009

    Большое спасибо за овтвет. Я хочу попробовать написать SOAP-запрос с помощью расш. поиска. В ответ я получу xml-поток. Верно? Как мне этот результат расш. поиска подружить с входящими параметрами функции установки меток, т.е. преобразовать xml в массив координат [x;y]. Спасибо

  • slivka_83 22.11.2009

    Вот способ получить XML поток http://msdn.microsoft.com/en-us/library/cc677073.aspx (для состовления fetch-запроса можете воспользоваться этим инструментом http://mmcrm.ru/?p=494). В этом же примере происходит его перебор и вывод на экран. Вам же нужно на каждом шаге перебора отправлять адрес в geocoder (функцию которую я привел выше) и таким образом на каждом шаге перебора получать геокоординаты и ставить один маркер.

  • Oleg 22.11.2009

    OK, спасибо большое. Буду пробовать

  • Андрей 22.11.2009

    По поводу Google Maps. Проблема в том, что если адрес гугл найти не может, к примеру стоят символы в поле адреса или еще что-то, то он выдает сообщение «Такой-то адрес найти не могу». При нажатии ОК, он перегружает карточку…но скрипт-то на onload() и он опять выдает, через 10 повторений это прекращается. Это как-то можно пофиксить?

  • slivka_83 22.11.2009

    Возможно это можно обработать как-нибудь с помощью API. Попробуйте обратится на форум разработчиков Google Maps: http://www.google.com/support/forum/p/maps?hl=ru

  • Андрей 22.11.2009

    А нельзя как-то предотвратить зацикливание? Запретить фрейму перегружать карточку или ловить этот alert и его блокировать…

  • slivka_83 22.11.2009

    Боюсь мне это неизвестно… хотя это странно что страница перезагружается… если не ошибаюсь выполнение скриптов запрещено из одного домена в другой 🙂

  • Анатолий 22.11.2009

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

  • Анатолий 22.11.2009

    Извините не совсем правильно выразился, т.е. я имел ввиду привязать к onload некий запрос к геокодеру с сохранением ответа в каком то поле.

  • slivka_83 22.11.2009

    сейчас не могу ничего проверить, но чисто тоеретически надо подапрвить функцию geocoder:

    var geocoder = new GClientGeocoder();
    geocoder.getLatLng(_GET_Values[0],
    	function(point) {
    		// если что-нибудь найдено
    		if (point) {
    			
    			// Передайм point в поле в CRM
    
    		}
    	}
    );
    

    А чтобы достучаться до поля из айфрейма, можете воспользоваться разделом «Доступ к форме CRM из iFrame’а» статьи http://mmcrm.ru/?p=1009

  • Анатолий 22.11.2009

    Добрый день!
    Врде бы как разобрался с пременной ответственной за координаты — дело в том, что у меня карта yandex, а пременная (this.get(0).getGeoPoint()) вроде как выдает координаты.
    Но проблема с присвоением значения этой переменной полю, не совсем понятно (для меня :))) ) что означает [parent.document] во фразе:
    [//var myAttributeValue = parent.document.crmForm.all.new_attribute.DataValue;] из статьи http://mmcrm.ru/?p=1009
    Спасибо.

  • Анатолий 22.11.2009

    Извините! 🙂 не совсем правильно изьяснился :), т.е. я имел ввиду — будет ли все это работать в обратном порядке т.е. //parent.document.crmForm.all.new_attribute.DataValue = this.get(0).getGeoPoint()) и как это првильно написать?
    Спасибо.

  • slivka_83 22.11.2009

    Да, так и нужно писать (если с получением координат все верно):

    parent.document.crmForm.all.new_attribute.DataValue = this.get(0).getGeoPoint();
    
  • Анатолий 22.11.2009

    Доброго времени суток.
    Тут такое дело: CRM выдает такое сообщение ошибки — «Этот элемент управления принимает только строковое значение или Null.» Судя по всему либо я неправильно обознаяил поле (я пробовал nvachar и ntext), либо что то надо сделать с //this.get(0).getGeoPoint(), что бы изменить непосредственно формат этой переменной, дело в том, что я пробовал все это конструировать на тестовом сайте и выводить //this.get(0).getGeoPoint(), просто алертом и все при этом нормально работает, алерт выдает координаты, значит видимо это формат записи, или я что то не так понимаю. Помогите если можно. Кстати это может быть проблема IFD?

  • slivka_83 22.11.2009

    с помощью

    alert(typeof this.get(0).getGeoPoint());

    узнайте тип переменной 🙂

  • Анатолий 22.11.2009

    Тип переменной object, и что теперь делать? Может быть это масив и следует выводить как то так: \\parent.document.crmForm.all.new_attribute.DataValue = this.get(0).getGeoPoint()[0]’,’this.get(0).getGeoPoint()[1], как вы думаете?

  • slivka_83 22.11.2009

    Для начала давайте узнаем из чего состоит объект 🙂

    for( var ArrVal in this.get(0).getGeoPoint() ) {
    alert( ArrVal );
    }
    
  • Анатолий 22.11.2009

    Здравствуйте.
    Спасибо еще и еще раз, что отзываетесь, в принципе проблему разрешил так:
    //var koordinat = this.get(0).getGeoPoint();
    var koordinatstr = koordinat.toString();
    parent.document.crmForm.all.new_map_key.DataValue = koordinatstr;

    вроде все работает но что то как то криво.
    А на Ваше предложение алерт сообщает equals, а что это значит? 🙂

  • Анатолий 22.11.2009

    Здрасте еще раз, извините поспешил, дальше появляется второй алерт — distance, следующий — moveBy, дальше — diff, moveTo, getLat, setLat, getLng, setLng, isUnbounded, _unbounded, setX, _lng, setY, _lat, copy, getX, getY, короче похоже перебирает весь xml геокодера. А что таперь делать?

  • Анатолий 22.11.2009

    Но с другой стороны метод //toString() вроде решает все проблемы, что вы посоветуете стоит ли дальше анализировать?

  • slivka_83 22.11.2009

    Ну если задача решена, то можно с ней завязывать 🙂

  • Евгений 22.11.2009

    Добрый день. Спасибо за интересную и нужную статью.
    Возник такой вопрос: делаю все как Вы написали, за тем исключением что данные адреса берутся из строки new_address. Когда открываю вкладку Яндекс.Карты то пишет: Не удается найти требуемую страницу HTTP Error 404 — File or directory not found.
    Internet Information Services (IIS)
    IIS перезапускал, но результат тот же. Сама html страничка работает нормально. Где могла закрасться ошибка?

  • slivka_83 22.11.2009

    Здрасьте 🙂
    1. Какое у Вас имя сервера CRM (т.е. что Вы пишите в адресной строке) и на каое доменное имя Вы зарегили акаунт в яндекс.картах?
    2. В доверенный добавили сайт яндекс.карт?
    3. Прежде чем писать какой-нибудь код, лучше отобразить самую простую страничку с картой 🙂

  • Евгений 22.11.2009

    1. svr-data1/company, а имя на картах crm.kr.local
    2. да, добавил конечно.
    3. я собственно так и поступил: оставил в скрипте на загрузку только:
    function loadframe() {
    сrmForm.all.IFRAME_ya.src = «/isv/ymap.html?s=»

  • slivka_83 22.11.2009

    ну собственно у Вас акаунт зареген на домен третьего уровня crm.kr.local, а Вы пытаетесь подключитьтся из домена первого уровня svr-data1 🙂 вот и не проходит проверка на доступ 🙂

  • Евгений 22.11.2009

    Все заработало! Большое спасибо

  • Greg 22.11.2009

    Есть необходимость подключить карту прямо в форму CRM.

    Пытаюсь сделать это следующим образом:

    include = function (src) {
    var script = document.createElement(‘script’);
    script.type = ‘text/javascript’;
    script.src = src+»?»+new Date().toString();
    document.getElementsByTagName(‘head’)[0].appendChild(script);
    }

    include(«http://api-maps.yandex.ru/1.1/index.xml?key=APmBjU4BAAAAURWyDwMA2PPDXC72nqsBNf9V3yyCAgN_mGoAAAAAAAAAAAAwvusZUMAmg9j9jM2AZIsGDYjGlg==»);

    После выполнения этого кода на onLoad формы Выдаётся сообщение: «Неверный ключ».

    Но этот же ключ работает если взять страницу с Вашего сайта, и всё выполнить как написано здесь, то всё работает. То есть код верен. с чем это может быть связано? Заранее спасибо.

  • Greg 22.11.2009

    Ошибку нашёл, надо убрать добавление случайно числа. Теперь другая проблема Объект YMaps подгружается, но в нём почему то нет просто огромной кучи методов и прочего. есть только метод load и onload, а также location. Вот что сейчас делать пока не понимаю. Уже повесил код вызова этого объекта на onchange одного из полей и вызываю после того, как форма уже точно загрузилась. Статус формы в этот момент loaded. Заранее спасибо

  • Greg 22.11.2009

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

  • slivka_83 22.11.2009

    Ну, боюсь что проблема весьма специфична 🙂 Решения я ее не знаю. Попробуйте обратится на форум по Яндексюкартам 🙂

  • Алексей Шачнев 22.11.2009

    Прошу ткнуть меня носом в материал, как можно из последней MS CRM обращаться к внешнему (стороннему) SOAP-сервису? В частности, имеется сервер 1С:Предприятие 8, на котором опубликован Web-сервис (SOAP, wsdl). Мне нужно из формы контакта отправить этому веб-сервису строку, а он мне тоже вернет строку. Возможно ли это сделать на JS или нужно только подписанный плагин использовать?

  • Greg 22.11.2009

    function getXml (req) {
    var myReq = new XMLHttpRequest();
    if (myReq) {
    var url = «http://yourservice.asmx;
    myReq.open(«GET», url, false);
    myReq.send();
    // alert(myReq.responseText);
    return myReq.responseXML;
    }
    }

    Вот так можно.

  • Greg 22.11.2009

    Но лучше такой вопрос задавать на http://axforum.info

  • Greg 22.11.2009

    В предыдущем посте немного ошибся.

    Вы в req передаёте название веб-метода и параметры. Это для вызова веб-сервиса методом Get.

    function getXml (req) {
    var myReq = new XMLHttpRequest();
    if (myReq) {
    var url = «http://yourservice.asmx» + req;
    myReq.open(«GET», url, false);
    myReq.send();
    // alert(myReq.responseText);
    return myReq.responseXML;
    }
    }

  • slivka_83 22.11.2009

    2Алексей Шачнев
    Вот тут делал вызов своего веб-сервиса из JS http://mmcrm.ru/?p=1319. Причем это был предыдущий вид сервиса (не WCF) и примитивная реализация.

*

code