Симптомы

После обновления на версию 7.7.0 возникает ошибка при получении количества уведомлений с помощью выполнения метода GetUserNotificationCount сервиса RemindingsDataService. Текст ошибки следующий: Cannot create an instance of Terrasoft.Configuration.BaseNotificationProvider because it is an abstract class.

Причина

В версии 7.7.0 в таблице NotificationProvider изменили название класса Terrasoft.Configuration.BaseNotificationProvider на Terrasoft.Configuration.SystemNotificationProvider. При обновлении изменение должно применяться с помощью установки данных, но в конфигурации клиента эти данные по какой-то причине не установились.

Решение

Необходимо на базе клиента выполнить следующий скрипт:

UPDATE [NotificationProvider]
SET
[ClassName] = 'Terrasoft.Configuration.SystemNotificationProvider',
[Type] = 2
WHERE [ClassName] = 'Terrasoft.Configuration.BaseNotificationProvider'

Необходимые условия и возможные ограничения

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

Нравится

Поделиться

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

Вопрос

Нет кнопки "учетные записи почты"

 

Ответ

В демосайте данные настройки не отображаются. Для того, чтобы они отобразились необходимо в системной настройке «Отображать Демо cсылки» («ShowDemoLinks») убрать признак.

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

В результате данные настройки отображаются.

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

 

Также, как вариант, ящик можно добавлять через коммуникационную панель.

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

 

Нравится

Поделиться

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

Симптомы

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

Причина

Данная ситуация проявляется, когда при создании активности (по процессу или вручную) ответственный автоматически не попадает на деталь "Участники". В разделе "Активности" фильтрация происходит по данной детали: отображаются все записи, у которых на детали "Участники" присутствует пользователь, указанный в фильтре "Ответственный" раздела.

Решение

Необходимо исправить пользовательский процесс или логику создания активности, чтобы при создании ответственный добавлялся на деталь "Участники". После этого скриптом добавить отсутствующих участников. Пример скрипта:

INSERT INTO [ActivityParticipant]
([Id], [ActivityId], [ParticipantId], [RoleId])
SELECT NewId(), a.[Id], a.[OwnerId], '53fc4a92-b0ea-e111-96c4-00165d094c12'
FROM [Activity] a
WHERE NOT EXISTS (
SELECT ap.[Id] FROM [ActivityParticipant] ap
WHERE ap.[ActivityId] = a.[Id]
AND ap.[ParticipantId] = a.[OwnerId]
)

 

Нравится

Поделиться

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

Вопрос

Как удалить/скрыть элемент в "Действие"

Ответ

Для скрытия действия можно использовать параметр removeByIndex.

Пример:

removeSubscribeButton: function(actionMenuItems) {
    var subscribeButtonIndex;
    actionMenuItems.each(function(item, index) {
        if (item.values.Tag === "subscribeUser") {
            subscribeButtonIndex = index;
        }
    });
    if (subscribeButtonIndex) {
        actionMenuItems.removeByIndex(subscribeButtonIndex);
    }
},
getActions: function() {
    var actionMenuItems = this.callParent(arguments);
    this.removeSubscribeButton(actionMenuItems);
    actionMenuItems.addItem(this.getButtonMenuItem({
        "Caption": {"bindTo": "Resources.Strings.RemoveButtonCaption"},
        "Enabled": { "bindTo": "DeleteButtonEnable" },
        "Tag": "canRemoveRecord"
    }));
    return actionMenuItems;
}

ИЛИ

Пример как скрыть в разделе: 

getSectionActions: function() {
    var actionMenuItems = this.callParent(arguments);
    actionMenuItems.each(function(item, index) {
        if (item.values.Type === "Terrasoft.MenuSeparator") {
            actionMenuItems.removeByIndex(index);
        }
        if (item.values.Tag === "sendToVisa") {
            actionMenuItems.removeByIndex(index);
        }
    }, this);
    return actionMenuItems;
},

Пример как скрыть в карточке: 

getActions: function() {
    var actionMenuItems = this.callParent(arguments);
    actionMenuItems.each(function(item, index) {
        if (item.values.Type === "Terrasoft.MenuSeparator") {
            actionMenuItems.removeByIndex(index);
        }
        if (item.values.Tag === "sendToVisa") {
            actionMenuItems.removeByIndex(index);
        }
    }, this);
    return actionMenuItems;
},

 

Нравится

Поделиться

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

Вопрос

Как добавить/изменить цвет в разделе "Настройка цвета панели разделов"

Ответ

Дополнить или изменить существующий цвет в разделе «Настройка цвета панели разделов» Вы можете в исходнике all-combined.js (…\Terrasoft.WebApp\Resources\ui\combined\all-combined.js). Именно свойство «colors» в «ColorMenuItem». 

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

Хотелось бы отметить, что в данном случае можно использовать только код цвета, так как в приложении прописано, что для этого свойства использовать «background-color». Ссылки на изображения не получиться вставить.

Нравится

Поделиться

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

Симптомы

Попытался повторно синхронизироваться. Вижу ошибку:

Тип: Terrasoft.SyncException%0D%0AСообщение: В процессе синхронизации произошла ошибка%0D%0A%0D%0AТип: Terrasoft.ServerException%0D%0AСообщение: Запрос на сервер вернул ошибку%0D%0AДополнительная информация: %0D%0A%09{"request":{"id":6,"headers":{"X-Terrasoft-Mobile":"true","Accept":"application/json","Content-Type":"application/json","Authorization":"Cookie","X-Requested-With":"XMLHttpRequest"},"options":{"url":"http://xxx.xxx.xxx.xxx/0/Services/ProfileService.asmx/Logout","method":"POST","jsonData":{"customData":"","doLogout":"true"},"headers":{"X-Terrasoft-Mobile":"true","Accept":"application/json","Content-Type":"application/json","Authorization":"Cookie"},"disableCaching":false,"scope":{"initialConfig":{"url":"http://xxx.xxx.xxx.xxx/0/Services/ProfileService.asmx/Logout","method":"POST","jsonData":{"customData":"","doLogout":"true"},"headers":{"X-Terrasoft-Mobile":"true","Accept":"application/json","Content-Type":"application/json","Authorization":"Cookie"},"disableCaching":false},"performanceCounter":{"startDate":"2015-06-08T12:11:51.319Z"}}},"async":true},"requestId":6,"status":500,"statusText":"Internal Server Error","responseText":"System.InvalidOperationException: Request format is invalid: application/json.\r\n   at System.Web.Services.Protocols.HttpServerProtocol.ReadParameters()\r\n   at System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()\r\n","responseXML":null,"responseBytes":null}%0D%0A%0D%0A

Причина

Проблема в настройках IIS

Решение

Заходим в настройки IIS - модуль Handler Mappings

Ищем правила для файлов с расширением *.asmx с такими названиями:

"WebServiceHandlerFactory-Integrated"

"WebServiceHandlerFactory-ISAPI-2.0_32bit"

"WebServiceHandlerFactory-ISAPI-2.0_64bit"

(это всё устаревшие версии)

и удаляем их

Нравится

Поделиться

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

Вопрос

Как убрать действие в мобильном приложении и как можно их отсортировать

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

Ответ

Примерный алгоритм.

1. Необходимо создать свой модуль (например «TestOrderRemoveMobileAccountModuleConfig»)

2. Для удаления действия необходимо использовать следующий метод - Terrasoft.sdk.Actions.remove: function(model, actionName)

 

Пример:

Terrasoft.sdk.Actions.remove("Account", "addAccountAnniversary");



3. Для сортировки нужно использовать:

Terrasoft.sdk.Actions.setOrder("model ", {
    "actionName1": 0,
    "actionName2": 1,
    …,
    "actionName5": 4
});

Пример:

Terrasoft.sdk.Actions.setOrder("Account", {
    "Phone": 0,
    "Email": 1,
    "Meeting": 2,
    "Terrasoft.ActionCopy": 3,
    "Terrasoft.ActionDelete": 4
});
 

4. Потом подключить в пользовательском манифесте мобильного приложения (можно ознакомиться на нашем SDK)  в секции «Models» для объекта «Account» (если это контрагенты) в разделе «PagesExtensions». Например:

"Models": {
    "Account": {
        "RequiredModels": [],
        "ModelExtensions": [],
        "PagesExtensions": [
            "TestOrderRemoveMobileAccountModuleConfig"
        ]
    },
 



где TestOrderRemoveMobileAccountModuleConfig – произвольное название схемы.

 

Реализацию логики добавления адреса можете посмотреть в схеме «MobileAccountModuleConfig» (действие называется «addAccountAddress»).

Нравится

Поделиться

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

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

Нет, при помощи Terrasoft SDK такой возможности нет.

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

Вопрос

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

Ответ

При сохранении объекта с портала, срабатывает процесс определенный в объекте Case пакета Портал,событие CaseInserting, в нем очищается Ответственный, если UserType = SSP.

Нравится

Поделиться

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

Вопрос

После обновления не применяются стили для иконок твиттера и фейсбука на детали "Средства связи"

Ответ

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

Для устранения проблемы необходимо перенести стили из базового модуля CommonCSSV2 в замещающий и в конце добавить обратно пользовательские стили. Также можно убрать текст из вкладки "Исходный код" модуля - стили, которые содержатся на этой вкладке, применяться не будут.

Нравится

Поделиться

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

Вопрос

Скажите, как правильно вызвать веб-сервис созданный в одной bpm из сторонней bpm?

Получаю ошибку Cross-origin resource sharing.

Ответ

Все что будет сказано далее, тестировалось на своих тестовых площадках. Единственное что колонку в лиде, по которой будет производиться поиск, назвали с префиксом: UsrTest. И сами классы утилс, и сервиса, так же назвали с префиксами: UsrMyTestService, UsrLeadSearchUtils.

В итоге, на старте, у нас имеется:

Код схемы сервиса

namespace Terrasoft.Configuration.CustomConfigurationService
{
	using System;
	using System.Collections.Generic;
	using System.Runtime.Serialization;
	using System.Linq;
	using System.ServiceModel;
	using System.ServiceModel.Web;
	using System.ServiceModel.Activation;
	using System.Web;
	using System.IO;
	using Terrasoft.Core;
	using Terrasoft.Core.Configuration;
	using Terrasoft.Core.DB;
	using Terrasoft.Core.Entities;
 
	[ServiceContract]
	[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
	public class UsrMyTestService
	{
		private UserConnection _userConnection;
 
		private UserConnection UserConnection {
			get {
				return _userConnection ?? (_userConnection = HttpContext.Current.Session["UserConnection"] as UserConnection);
			}
		}
 
		[OperationContract]
		[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped,
			ResponseFormat = WebMessageFormat.Json)]
		public testclass myTestServiceFunction(string test)
		{
			//////
			var currentWebOperationContext = WebOperationContext.Current;
			var outgoingResponseHeaders = currentWebOperationContext.OutgoingResponse.Headers;
			var incomingRequestOrigin = currentWebOperationContext.IncomingRequest.Headers["Origin"];
			outgoingResponseHeaders.Add("Access-Control-Allow-Origin", incomingRequestOrigin);
			///////
			testclass result = new testclass();
			UsrLeadSearchUtils leadSearchUtil = new UsrLeadSearchUtils(this.UserConnection);
			try {
				var leadCollection = leadSearchUtil.SearchLeadByCustomerID(test);
				foreach (var item in leadCollection) {
					result.Response.Add(new SearchItem() {
						Id = item.GetTypedColumnValue<Guid>("LeadId"),
						Name = item.GetTypedColumnValue<string>("LeadName"),
					});
				}
			} catch (Exception ex) {
				result.Success = false;
				result.ErrorMessage = ex.Message;
			};
			return result;
		}
 
		////////
		[OperationContract]
		[WebInvoke(Method = "OPTIONS", UriTemplate = "SaveWebFormLeadData")]
		public void GetWebFormLeadDataRequestOptions() {
			var outgoingResponseHeaders = WebOperationContext.Current.OutgoingResponse.Headers;
			outgoingResponseHeaders.Add("Access-Control-Allow-Origin", "*");
			outgoingResponseHeaders.Add("Access-Control-Allow-Methods", "POST");
			outgoingResponseHeaders.Add("Access-Control-Allow-Headers", "Origin, Content-Type, Accept");
		}
		//////////
 
		[DataContract]
		public class testclass {
			public testclass() {
				this.Success = true;
				this.Response = new List<SearchItem>();
			}
			[DataMember]
			public List<SearchItem> Response {
				get;
				set;
			}
			[DataMember]
			public bool Success {
				get;
				set;
			}
			[DataMember]
			public string ErrorMessage {
				get;
				set;
			}
		}
 
		[DataContract]
		public class SearchItem {
			[DataMember]
			public Guid Id {
				get;
				set;
			}
			[DataMember]
			public string Name {
				get;
				set;
			}
		}
	}
}

Код схемы утилит

namespace Terrasoft.Configuration
{
	using System;
	using System.Collections.Generic;
	using System.IO;
	using Terrasoft.Core;
	using Terrasoft.Core.Configuration;
	using Terrasoft.Core.DB;
	using Terrasoft.Core.Entities;
 
	public class UsrLeadSearchUtils {
		public UsrLeadSearchUtils (UserConnection userConnection) {
			this.UserConnection = userConnection;
		}
 
		private UserConnection UserConnection {
			get;
			set;
		}
 
		public EntityCollection SearchLeadByCustomerID(string email) {
			if (string.IsNullOrEmpty(email)) {
				throw new ArgumentException("email is undefined.");
			}
 
			var entitySchemaManager = this.UserConnection.GetSchemaManager("EntitySchemaManager") as EntitySchemaManager;
			var esq = new EntitySchemaQuery(entitySchemaManager, "Lead");
			esq.RowCount = 20;
			esq.AddColumn("Id").Name = "LeadId";
			esq.AddColumn("LeadName").Name = "LeadName";
			esq.AddColumn("UsrTest").Name = "UsrTest";
			esq.Filters.Add(esq.CreateFilterWithParameters(FilterComparisonType.Equal, "UsrTest", 
				email));
 
			return esq.GetEntityCollection(this.UserConnection);
		}
	}
}

Сервис пробуем вызвать из той же bpm’online где сервис и создан, следующим образом:

onGetServiceInfoClick: function() {
    var usrTest = "test1";
    var serviceData = {
        test: usrTest
    };
    ServiceHelper.callService("UsrMyTestService", "myTestServiceFunction",
        function(response) {
            var result = response.myTestServiceFunctionResult;
            alert(result.Response[0].Name);
        }, serviceData, this
    );
}

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

Теперь давайте попробуем сделать запрос к этому сервису из другой bpm’online.

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

Правки в конфигах детальнее.

1) Вносим правки в наш «UsrMyTestService», помещаем его в немспейс «Terrasoft.Configuration», дописываем «UriTemplate» к методу, и опциям, наследуемся от «BaseService» а так же используем «SystemUserConnection» вместо обычного, так как авторизации не будет.

Результат:

namespace Terrasoft.Configuration
{
	using System;
	using System.Collections.Generic;
	using System.Runtime.Serialization;
	using System.Linq;
	using System.ServiceModel;
	using System.ServiceModel.Web;
	using System.ServiceModel.Activation;
	using System.Web;
	using System.IO;
	using Terrasoft.Core;
	using Terrasoft.Core.Configuration;
	using Terrasoft.Core.DB;
	using Terrasoft.Core.Entities;
	using Terrasoft.Web.Common;
 
	[ServiceContract]
	[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
	public class UsrMyTestService : BaseService
	{
		private SystemUserConnection _systemUserConnection;
		private SystemUserConnection SystemUserConnection {
			get {
				return _systemUserConnection ?? (_systemUserConnection = (SystemUserConnection)AppConnection.SystemUserConnection);
			}
		}
 
		[OperationContract]
		[WebInvoke(Method = "GET", RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped,
		ResponseFormat = WebMessageFormat.Json, UriTemplate = "myTestServiceFunction/{test}/")]
		public testclass myTestServiceFunction(string test)
		{
			///////
			var currentWebOperationContext = WebOperationContext.Current;
			var outgoingResponseHeaders = currentWebOperationContext.OutgoingResponse.Headers;
			var incomingRequestOrigin = currentWebOperationContext.IncomingRequest.Headers["Origin"];
			outgoingResponseHeaders.Add("Access-Control-Allow-Origin", incomingRequestOrigin);
			///////
			testclass result = new testclass();
			try {
				UsrLeadSearchUtils leadSearchUtil = new UsrLeadSearchUtils(this.SystemUserConnection);
				var leadCollection = leadSearchUtil.SearchLeadByCustomerID(test);
				foreach (var item in leadCollection) {
					result.Response.Add(new SearchItem() {
						Id = item.GetTypedColumnValue<Guid>("LeadId"),
						Name = item.GetTypedColumnValue<string>("LeadName"),
					});
				}
			} catch (Exception ex) {
				result.Success = false;
				result.ErrorMessage = ex.Message;
			};
			return result;
		}
 
		[OperationContract]
		[WebInvoke(Method = "OPTIONS", UriTemplate = "*")]
		public void GetWebFormLeadDataRequestOptions() {
			var outgoingResponseHeaders = WebOperationContext.Current.OutgoingResponse.Headers;
			outgoingResponseHeaders.Add("Access-Control-Allow-Origin", "*");
			outgoingResponseHeaders.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
			outgoingResponseHeaders.Add("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, X-Requested-With, X-Requested-With, x-request-source");
			outgoingResponseHeaders.Add("Access-Control-Request-Headers", "X-Requested-With, x-request-source, accept, content-type");
		}
 
		[DataContract]
		public class testclass {
			public testclass() {
				this.Success = true;
				this.Response = new List<SearchItem>();
			}
			[DataMember]
			public List<SearchItem> Response {
				get;
				set;
			}
			[DataMember]
			public bool Success {
				get;
				set;
			}
			[DataMember]
			public string ErrorMessage {
				get;
				set;
			}
		}
 
		[DataContract]
		public class SearchItem {
			[DataMember]
			public Guid Id {
				get;
				set;
			}
			[DataMember]
			public string Name {
				get;
				set;
			}
		}
	}
}

Класс утилит остается таким, как и был.

2) В папке сайта в котором написан наш сервис, *сайт*\Terrasoft.WebApp\ServiceModel

Создаем файл «UsrMyTestService.svc»

В содержимое файла пишем:

<%@ ServiceHost Language="C#" Debug="true" Service="Terrasoft.Configuration.UsrMyTestService" CodeBehind="UsrMyTestService.svc.cs" %>

3) В папке *сайт* \Terrasoft.WebApp в файле Web.config  рядом с другими локациями, добавляем:



   

     

         

     

   

4) В папке *сайт*\Terrasoft.WebApp\ в файле Web.config в секции appSettings меняем  значение ключа AllowedLocations. В значение добавляем:

ServiceModel/UsrMyTestService.svc;

5) В папке *сайт*\Terrasoft.WebApp\ServiceModel\http в файле services.config в блоке сервисов добавляем:



   

      address=""

      binding="webHttpBinding"

      behaviorConfiguration="RestServiceBehavior"

      bindingNamespace="http://Terrasoft.WebApp.ServiceModel"

      contract="Terrasoft.Configuration.UsrMyTestService" />

6) В папке *сайт*\Terrasoft.WebApp\ServiceModel\https в файле services.config в блоке сервисов делаем то же самое, что и в пункте 5.

Сервис будет доступен по адресу:

http(или https)://адрес-сайта/0/ServiceModel/UsrMyTestService.svc

В рамках теста, все выше сказанное было сделано на нашем локальном сайте:

http://localhost:8012/



Теперь для того что бы обратится к этому сервису с другого сайта, который на наших тестовых площадках находится по адресу:

http://localhost:8006/

Доработаем к примеру схему контакта следующим образом:

define("ContactPageV2", ["ContactPageV2Resources", "GeneralDetails", "JQuery"],
function(resources, GeneralDetails) {
	return {
		entitySchemaName: "Contact",
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[
		]/**SCHEMA_DIFF*/,
		attributes: {},
		methods: {
 
			onEntityInitialized: function() {
				this.callParent(arguments);
				document.scp = this;
			},
 
			test: function() {
				var params = {
					test: "test1"
				};
				require(["jQuery"], function() {
					$.ajax({
						url: "http://localhost:8012/0/ServiceModel/UsrMyTestService.svc/myTestServiceFunction/" +
							params.test + "/",
						success: function(data) {
							alert(JSON.stringify(data));
						},
						error: function() {
							alert("Error occured!");
						}
					});
				});
			}
		},
		rules: {},
		userCode: {}
	};
});

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

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

 

Нравится

Поделиться

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