Всем доброго времени суток!

Столкнулся с проблемой интеграции с Google Analytics. Написал сервис с с ипользованием следующих библиотек - https://prnt.sc/mmm8kd

Но при вызове сервиса вываливаются следующие ошибки -https://prnt.sc/mmm98e

Я понял что идет конфликт встроенных библиотек Google и тех что добавили в кастомном пакете.

Что можете посоветовать? 

Нравится

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

Добрый день, Алексей!



Приносим свои извинения за неудобства. Версии библиотек Google API планировали обновлять с версии 7.13.0, но в результате тестирования возникли технические проблемы. На данный момент ответственная команда занимается решением  этого вопроса. Обновления версии библиотек Google API запланировано на ближайшие релизы.



Спасибо за понимание!

Мотков Илья,

Благодарю!

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

Здравствуйте! Подскажите, а как добавить в фильтры серверного EntitySchemaQuery подзапрос или где в системе можно найти пример? На SQL запрос выглядит следующим образом:

SELECT
   * 
FROM
   [Contract] 
WHERE
   StartDate = (
        SELECT MAX(c.StartDate)
        FROM Premises p
        JOIN PremisesInContract pic ON p.Id = pic.PremisesId
        JOIN Contract c ON spic.ContractId = c.Id
        WHERE p.Id = --@someparameter
   )

 

Нравится

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

Всем доброго времени суток.

Было бы замечательным реализовать определение откуда "пришло" действие из СРМ или же осуществили добавление/изменение/удаление через API.

Например некий маркер IsFromAPI = true/false

Это касается интеграции по протоколу OData.

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

А зачем плодить сущности?

Можно добавить в объект обычное логическое поле IsFromAPI и при записи в объект по OData писать туда true.

Можно завести отдельного пользователя только для интеграций и работать по OData под ним. Тогда всё будет видно в полях «создал» и «изменил».

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

Всем доброго времени суток!

Коллеги нужна Ваша помощь. Необходимо в С# коде получить идентификатор SystemUser.

http://prntscr.com/lczocy

Хардкодом как то не очень хочется забивать.

Данный идентификатор необходим для работы в событийном слое Entity.

Заранее благодарен.

Нравится

3 комментария
Лучший ответ
//получить значение системной настройки
var mySystemUser=Terrasoft.Core.Configuration.SysSettings.GetValue(UserConnection, "SystemUser");
//получить значение системной настройки
var mySystemUser=Terrasoft.Core.Configuration.SysSettings.GetValue(UserConnection, "SystemUser");

Ну или, учитывая что система по-умолчанию работает от Supervisor-a:

UserConnection.AppConnection.SystemUserConnection.CurrentUser.Id

Не уверен, что сработает, но вроде должно)

Вставлю свои 5 копеек

в Terrasoft.Configuration определена константа id супервизора 

BaseConsts.SystemUserId 

 

 

 

 

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

Коллеги всем доброго времени суток. Подскажите, кто нибудь работал с событийным слоем Entity.

Написал класс обработчик в качестве примера для обучения скомпилировал приложение упал сайт.

Вот пример класса обработчика:

 

using System;
using System.Web;
using Terrasoft.Core;
using Terrasoft.Core.Entities;
using Terrasoft.Core.Entities.Events;
 
 
namespace Terrasoft.Configuration
{
    [EntityEventListener(SchemaName = "Order")]
    public class OrderEntityEventListener : BaseEntityEventListener
    {
 
        private UserConnection _userConnection;
 
        private LogService _logService;
        public UserConnection UserConnection
        {
            get
            {
                if (_userConnection != null)
                {
                    return _userConnection;
                }
                _userConnection = HttpContext.Current.Session["UserConnection"] as UserConnection;
                if (_userConnection != null)
                {
                    return _userConnection;
                }
                return _userConnection;
            }
 
            set { _userConnection = value; }
        }        
 
        public LogService LogService { get => _logService; set => _logService = value; }
 
        public OrderEntityEventListener()
        {
            LogService = new LogService(UserConnection);
        }
 
        public override void OnInserting(object sender, EntityBeforeEventArgs e)
        {
            try
            {
                base.OnInserting(sender, e);
 
                var systemUserName = UserConnection.CurrentUser.ContactName;
                var systemUserId = UserConnection.CurrentUser.ContactId;
                LogService.RecInfo($"LOG:[OnInserting]:systemUserId: {systemUserId}; systemUserName:{systemUserName}");
            }
            catch (Exception exception)
            {
                LogService.RecInfo($"LOG:[OnInserting]:Exception: {exception.Message}; {exception.InnerException}");
            }
 
        }
    }
}

Подскажите, что тут не так? Если кто работал, скинте пожалуйста примеры.

Заранее благодарен

Нравится

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

Добрый день, Алексей!

UserConnection необходимо получать из sender

Пример:

public override void OnSaved(object sender, EntityAfterEventArgs e) {

            base.OnSaved(sender, e);

            var entity = (Entity) sender;

            var userConnection = entity.UserConnection;

        }

Ошибка заключается в том, что слушатели изменений сущностей, работают не только при изменении значений через карточку (в данном случае есть HttpContext) но и в фоновом режиме (процессы, планировщик - без HttpContext).

Добрый день, Алексей!

UserConnection необходимо получать из sender

Пример:

public override void OnSaved(object sender, EntityAfterEventArgs e) {

            base.OnSaved(sender, e);

            var entity = (Entity) sender;

            var userConnection = entity.UserConnection;

        }

Ошибка заключается в том, что слушатели изменений сущностей, работают не только при изменении значений через карточку (в данном случае есть HttpContext) но и в фоновом режиме (процессы, планировщик - без HttpContext).

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

Добрый день!

 

Есть две таблицы:

1) ListObject с колонками ObjectName(строка), ObjectType(Lookup), ObjectRule(Lookup на RulesList )

2) RulesList с колонками NameRule(строка),  ObjectType(Lookup)

Нужно реализовать фильтр, для колонки ObjectRule, таблицы ListObject. 

Фильтр должен выводить доступными только те записи ObjectRule, ObjectType которых соответствует ObjectType таблицы ListObject.

 

rules: {
  "typeObjectFilter": {
	 lookupListConfig: {
	   filters: [
	     function() {
		   var ruleToObject = this.get("ObjectRule");
		   var filterGroup = Ext.create("Terrasoft.FilterGroup");
		   if (ruleToObject) {
			  var filterByTypeRule = Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL,
"[RulesList:ObjectType].value", ruleToObject.value);
			  filterGroup.add("filterByTypeRule", filterByTypeRule);
			  return filterGroup;
		   }
		 }
	   ]
    }
  }
}

В настоящий момент фильтр не работает, страница не загружается.

Нравится

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

engineer7,

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

https://academy.terrasoft.ru/documents/technic-sdk/7-13/postroenie-putey-k-kolonkam-otnositelno-kornevoy-shemy

Изначально Вы писали о том, что хотите фильтровать поле ObjectRule, а в коде реализуете фильтрацию для поля ObjectType.

Дальше Вы реализуете её в блоке rules, но, если делать через  lookupListConfig, то реализовывать нужно через блок attributes.

На мой взгляд, в данном случае Вы сильно усложнили себе задачу.

Ваш запрос на sql выглядит таким образом:

select Id

from RuleList

where RuleList.ObjectTypeId = @ObjectTypeId

где @ObjectTypeId - это значение ObjectType из таблицы ListObject

Реализовать такую фильтрацию можно легко с помощью мастера бизнес-правил https://academy.terrasoft.ru/documents/administration/7-13/nastroyka-biznes-pravil либо внести изменения в страницу редактирования на уровне конфигурации https://academy.terrasoft.ru/documents/technic-sdk/7-13/primer-primeneniya-pravila-filtration

1. Почему в выражении "[RulesList:ObjectType].value" Вы указали "value"?

Вместо value нужно указать поле, по которому будет фильтрация.

Например: "[RulesList:ObjectType].ObjectRule"

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

Алла,  добрый день! Спасибо за ответ!

1. Не знал этот момент.

2. Sql запрос выглядит таким образом:

SELECT ObjectRule

  FROM ListObject INNER JOIN RulesList ON

  ListObject.ObjectTypeId =  ListObject.ObjectRule.ObjectTypeId

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

Возможно мне нужно идти по другому пути и для начала получить значение из поля "Тип объекта"(ObjectRule таблицы ListObject), которое я добавляю первым? 

engineer7,

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

https://academy.terrasoft.ru/documents/technic-sdk/7-13/postroenie-putey-k-kolonkam-otnositelno-kornevoy-shemy

Изначально Вы писали о том, что хотите фильтровать поле ObjectRule, а в коде реализуете фильтрацию для поля ObjectType.

Дальше Вы реализуете её в блоке rules, но, если делать через  lookupListConfig, то реализовывать нужно через блок attributes.

На мой взгляд, в данном случае Вы сильно усложнили себе задачу.

Ваш запрос на sql выглядит таким образом:

select Id

from RuleList

where RuleList.ObjectTypeId = @ObjectTypeId

где @ObjectTypeId - это значение ObjectType из таблицы ListObject

Реализовать такую фильтрацию можно легко с помощью мастера бизнес-правил https://academy.terrasoft.ru/documents/administration/7-13/nastroyka-biznes-pravil либо внести изменения в страницу редактирования на уровне конфигурации https://academy.terrasoft.ru/documents/technic-sdk/7-13/primer-primeneniya-pravila-filtration

Алла, огромное спасибо!

Получилось реализовать, рабочий вариант: 

	// Набор правил для колонки [ObjectRule] модели представления.
			"ObjectRule": {
				// Правило фильтрации колонки [ObjectRule] по значению колонки [ObjectType].
				"FiltrationObjectRuleByObjectType": {
					// Тип правила FILTRATION.
					"ruleType": BusinessRuleModule.enums.RuleType.FILTRATION,
					// Будет выполняться обратная фильтрация.
					"autocomplete": true,
					// Будет выполняться очистка значения при изменении значения колонки [ObjectType].
					"autoClean": true,
					// Путь к колонке для фильтрации в справочной схеме [ObjectRule],
					// на которую ссылается колонка [ObjectRule] модели представления
					// страницы редактирования.
					"baseAttributePatch": "ObjectType",
					// Тип операции сравнения в фильтре.
					"comparisonType": Terrasoft.ComparisonType.EQUAL,
					// В качестве значения при сравнении выступает колонка (атрибут)
					// модели представления.
					"type": BusinessRuleModule.enums.ValueType.ATTRIBUTE,
					// Имя колонки модели представления страницы редактирования,
					// по значению которой будет выполняться фильтрация.
					"attribute": "ObjectType"
				}
			}

 

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

Коллеги всем доброго времени суток.

Нужна Ваша помощь.

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

[OperationContract]
        [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped,
        ResponseFormat = WebMessageFormat.Json)]
        public void StartProcessWithParameters(string processName, Dictionary<object, object> parameters)
        {
            try
            {
                _logService.RecInfo($"[AutobookingServicesPack]:[AbStartProcessManager]:[StartProcessWithParameters(string processName = '{processName}', Dictionary<string, string> parameters)]: ({parameters.Count()})");
                ProcessSchemaManager processSchemaManager = (ProcessSchemaManager)UserConnection.GetSchemaManager("ProcessSchemaManager");
                ProcessSchema processSchema = processSchemaManager.GetInstanceByName(processName);
                Process process = processSchema.CreateProcess(UserConnection);
                foreach (var parameter in parameters)
                {
                    process.SetPropertyValue(parameter.Key.ToString(), parameter.Value);
                }
                process.Execute(UserConnection);
            }
            catch (Exception e)
            {
                _logService.RecInfo($"[AutobookingServicesPack]:[AbStartProcessManager]:[StartProcess(string processName = '{processName}', Dictionary<string, string> parameters)]:Exception: {e.Message}; {e.InnerException}");
            }
 
        }

Через фидлер кидаю следующий json:

{

    "processName":"AbListenCallAfterCancelationOrderProcess",

    "parameters":{

        "OrderId":"e3f25831-2482-4804-9564-72d6670634a5",

        "CallsId":"e22be6b7-9cf7-ab57-3c0d-bdbfd6a0d168;3fcfab93-c395-7223-ee77-78a0a7efde9b;d2607c8b-7796-bb9b-f06f-aa5280240733;ea465baa-8205-914d-77e4-03a7a7249fda"

    }

}

В логах СРМ следующее:

[AutobookingServicesPack]:[AbStartProcessManager]:[StartProcessWithParameters(string processName = 'AbListenCallAfterCancelationOrderProcess', Dictionary parameters)]: (0)

Видно, что  processName десерелизовало, а вот parameters нет в Dictionary 0 элементов.

 

Подскажите в чем может быть проблема?

заранее благодарен.

Нравится

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

А еще лучше попробовать так

function toDictionary(obj) {
   var data = [];
   for (var key in obj) {
      data.push({ Key: key, Value: obj[key] });
    }
  return data;
}
 
var _parameters = {}
_parameters ["OrderId"] = "e3f25831-2482-4804-9564-72d6670634a5";
_parameters ["CallsId"] = "e22be6b7-9cf7-ab57-3c0d-bdbfd6a0d168;3fcfab93-c395-7223-ee77-78a0a7efde9b;d2607c8b-7796-bb9b-f06f-aa5280240733;ea465baa-8205-914d-77e4-03a7a7249fda";
 
 
 
var serviceData = {
   processName: "XrenyaNerabotaychayaProcess",
   parameters: toDictionary(_parameters)
};
//serviceData  это то что отправляется сервису

Чтобы получить в JSON.stringify(serviceData) чтото типа:

""processName":"XrenyaNerabotaychayaProcess",

"parameters":[

{"Key":"OrderId","Value":"e3f25831-2482-4804-9564-72d6670634a5"},

{"Key":"CallsId","Value":"e22be6b7-9cf7-ab57-3c0d-bdbfd6a0d168;3fcfab93-c395-7223-ee77-78a0a7efde9b;d2607c8b-7796-bb9b-f06f-aa5280240733;ea465baa-8205-914d-77e4-03a7a7249fda"}

]}"

 

 

 

Попробуйте может WebMessageBodyStyle.Bare вместо Wrapped

Литвинко Павел,

При 

Bare 500 ошибку вываливает

Попробуйте так

В описание метода:

 StartProcessWithParameters(string processName, Dictionary<string, string> parameters)

 

Параметры как то так попытайтесь подставить



"processName":"AbListenCallAfterCancelationOrderProcess",

"parameters":

[{"OrderId" :"e3f25831-2482-4804-9564-72d6670634a5"},

{"CallsId": "e22be6b7-9cf7-ab57-3c0d-bdbfd6a0d168;3fcfab93-c395-7223-ee77-78a0a7efde9b;d2607c8b-7796-bb9b-f06f-aa5280240733;ea465baa-8205-914d-77e4-03a7a7249fda"}]

}

 

В CallsId будет строка список Id через ; 

А еще лучше попробовать так

function toDictionary(obj) {
   var data = [];
   for (var key in obj) {
      data.push({ Key: key, Value: obj[key] });
    }
  return data;
}
 
var _parameters = {}
_parameters ["OrderId"] = "e3f25831-2482-4804-9564-72d6670634a5";
_parameters ["CallsId"] = "e22be6b7-9cf7-ab57-3c0d-bdbfd6a0d168;3fcfab93-c395-7223-ee77-78a0a7efde9b;d2607c8b-7796-bb9b-f06f-aa5280240733;ea465baa-8205-914d-77e4-03a7a7249fda";
 
 
 
var serviceData = {
   processName: "XrenyaNerabotaychayaProcess",
   parameters: toDictionary(_parameters)
};
//serviceData  это то что отправляется сервису

Чтобы получить в JSON.stringify(serviceData) чтото типа:

""processName":"XrenyaNerabotaychayaProcess",

"parameters":[

{"Key":"OrderId","Value":"e3f25831-2482-4804-9564-72d6670634a5"},

{"Key":"CallsId","Value":"e22be6b7-9cf7-ab57-3c0d-bdbfd6a0d168;3fcfab93-c395-7223-ee77-78a0a7efde9b;d2607c8b-7796-bb9b-f06f-aa5280240733;ea465baa-8205-914d-77e4-03a7a7249fda"}

]}"

 

 

 

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

Здравствуйте. Интересует способ использования "Group By"  в entitySchemaQuery (сервер). При поиске наткнулся на топик - https://community.terrasoft.ru/questions/gruppirovka-v-entityschemaquery , но увы, ссылка с ответом битая. 

Нравится

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

Пример использования на C#, документацию нашел только для клиентских запросов, но по аналогии все понятно

var query = new EntitySchemaQuery(_userConnection.EntitySchemaManager, schemaName);
var columnCount = query.AddColumn(query.CreateAggregationFunction(AggregationTypeStrict.Count, query.PrimaryQueryColumn.Name));
var countEntity = query.GetEntityCollection(UserConnection)[0];
var count = countEntity.GetTypedColumnValue&lt;int&gt;(comnCount.Name);
return count;

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

var esq = new Order(UserConnection);
esq.AddColumn(esq.CreateAggregationFunction(AggregationTypeStrict.Count, "Customer.Name").All());
	esq.AddColumn("Description");
	esq.AddColumn("Name");
	esq.AddColumn("Customer");

Добавит в запрос 

GROUP BY Order.Description, Document.Name, Order.CustomerId, Customer.Name;

 

 

 

 

 

Пример использования на C#, документацию нашел только для клиентских запросов, но по аналогии все понятно

var query = new EntitySchemaQuery(_userConnection.EntitySchemaManager, schemaName);
var columnCount = query.AddColumn(query.CreateAggregationFunction(AggregationTypeStrict.Count, query.PrimaryQueryColumn.Name));
var countEntity = query.GetEntityCollection(UserConnection)[0];
var count = countEntity.GetTypedColumnValue&lt;int&gt;(comnCount.Name);
return count;

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

var esq = new Order(UserConnection);
esq.AddColumn(esq.CreateAggregationFunction(AggregationTypeStrict.Count, "Customer.Name").All());
	esq.AddColumn("Description");
	esq.AddColumn("Name");
	esq.AddColumn("Customer");

Добавит в запрос 

GROUP BY Order.Description, Document.Name, Order.CustomerId, Customer.Name;

 

 

 

 

 

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

Спасибо большое!

Вот, на всякий случай, и пример серверного кода:

EntitySchema entitySchema = UserConnection.EntitySchemaManager.GetInstanceByName("SysProcessElement");
var query = new EntitySchemaQuery(entitySchema);
EntitySchemaQueryColumn elementUIdColumn = query.AddColumn("SchemaElementUId");
EntitySchemaQueryColumn executeCountColumn = query.AddColumn(query.CreateAggregationFunction(AggregationTypeStrict.Count,
	entitySchema.PrimaryColumn.Name));
 
EntityCollection entities = query.GetEntityCollection(UserConnection);

 

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

Я для серверного кода пример и приводил :) От клиентского только документация :)

Вот документация по серверной. Ещё в этом документе есть информация.

Показать все комментарии
Идея
Обсуждение

Сделать быструю проверку кода на ошибки перед тем как отправлять его компилировать.



Мне надоедает каждый раз написав скрипт с 1-2 сотнями строчек ждать по пол часа пока всё скомпилируется, и выдаст ошибку что я не поставил где то ";" ... 

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

Идея классная давно витает в воздухе и переодически выскакивает, вот только наврядли будет реализована. А почему просто не создать пустую сборку в VS и не подключить туда Terrasoft.Configuration и другие используеміе библиотеки. При работе с ФС проверка синтаксиса и автозавершение будет работать!

Как работать с серверным кодом в Visual Studio, описано тут. Если реально компилирует полчаса, то это что-то не то с производительностью веб-сервера.

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

Почему вэб сервера а не БД скажем? Вообщето компиляция при генерации включенной герерации исходных кодом и отладочной информации на мощной тачке идет под 20 минут. Если у тебя быстрее поделись секретиком как ты этого добился

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

Я конечно утрировал, не пол часа но всё равно долго. Если он компилирует 3-5мин и выдаёт постоянно мелкие ошибки, по 5-6 штук то это уже почти пол часа....

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

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

Коллеги всем доброго времени суток!

Столкнулся со следующей проблемой, обработки исключений возвращаемых при запросе к внешнему API.

Делаю запрос через WebClient

 

public void Patch(string method, Dictionary<string, string> parameters)
        {
            try
            {
                NameValueCollection parametersValues = new NameValueCollection();
 
                foreach (var parameter in parameters)
                {
                    if (String.IsNullOrEmpty(parameter.Value))
                    {
                        parametersValues.Add(parameter.Key, null);
                        continue;
                    }
                    parametersValues.Add(parameter.Key, parameter.Value);
                }
 
                string uri = $"{abUrl}{method}";
                using (WebClient webClient = new WebClient())
                {
                    string credentials = Convert.ToBase64String(
                        Encoding.ASCII.GetBytes(LoginDev + ":" + PassDev));
                    webClient.Headers[HttpRequestHeader.Authorization] = $"Basic {credentials}";
                    webClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                    byte[] result = webClient.UploadValues(uri, "PATCH", parametersValues);
                    var response = Encoding.UTF8.GetString(result);
                }
 
            }
            catch (WebException e)
            {
                var exceptionMessage = e.Message;
                if (e.Status.Equals(WebExceptionStatus.ProtocolError))
                {
                    if (e.Response is HttpWebResponse)
                    {
                        var sr = new StreamReader(e.Response.GetResponseStream());
                        var response = sr.ReadToEnd();
                        sr.Close();
                        sr.Dispose();
                    }
                }
            }
        }

Когда пытаюсь обработать WebException в Bpm то мне записывает в логи - http://prntscr.com/kr7aja

 

При выполнении данного кода через консольное приложение обработка срабатывает корректно - http://prntscr.com/kr7baj

Подскажите в чем может быть проблема, как бороться?

Заранее благодарен.

Нравится

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

Попробуйте, вместо public void Patch сделать public string Patch, объявите переменную, например result и в catch, сделайте result += e.Message, ну и по итогу соответственно return result

Литвинко Павел,

я в самом методе Patch в блоке catch логирую 

e.Message - http://prntscr.com/kr838h

в Bpm у меня метод Patch возвращает string. Суть в другом почему в catch обрабатывается Too many redirects, а при выполнении данного метода из консольного приложения обрабатывается корректно, то что возвращает сервер к которому запрос был.

Нигрескул Алексей,

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

Литвинко Павел,

В консоли - при выполнении кода вылетает в catch и потом на выход метода 

При выполнении метода из консоли запустил fiddler запрос ушел один раз - http://prntscr.com/kr8oum

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

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