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



Добавить новую страницу продаж с помощью мастера раздела "Продажи" не имеем возможности по историческим причинам (он тупо не сохраняет результаты).



Создали новую страницу в Конфигурации, унаследовав ее от существующей страницы продажи.



Однако, в списке доступных для открытия страниц в блоке "Открыть страницу редактирования" бизнес-процесса она отсутствует.



Какие есть обходные варианты добавления в этот список новой страницы без использования мастера?

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

Нравится

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

Виктор, если открыть в браузере «инструменты разработчика» и посмотреть в уходящие на сервер запросы при выпадении этого списка, видно, что он получает данные из SysModule, таблицы, где хранятся данные о заведенных разделах.

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

 

Также страницу раздела невозможно выбрать в элементе Открыть страницу редактирования, если в SysModule запись есть, но по какой-то причине проставилось значение true колонки IsSystem. 

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

Подскажите, пожалуйста, как можно запустить процесс на объекте ПОСЛЕ исполнения процессов, определенных в родительском объекте?

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

Нравится

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

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

Вы можете использовать событийный слой и там разрабатывать обработку данных объекта, естественно после отработки базовой логики + событийный слой более гибкий том можно работать с данными как перед сохранением/изменением в бд так и после. Более детально в описано в данной статье.

Нигрескул Алексей пишет:

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

В статье написано, что Механизм событийного слоя Entity срабатывает после выполнения событийных подпроцессов объекта

Однако, в данном случае поле Code (заполняется в процессе объекта на OnSaved) оказывается пустым.



 

[EntityEventListener(SchemaName = "Account")] 
public class UsrAccountEntityEventListener : BaseEntityEventListener
 {
  public override void OnSaved(object sender, EntityAfterEventArgs e) {
    base.OnSaved(sender, e);
 
    var entity = (Entity) sender;
    var userConnection = entity.UserConnection;
 
    string accountCode = entity.GetTypedColumnValue<string>("Code");
 
    throw new Exception(entity.Schema.Name + " " + accountCode);

 

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

Зверев Александр пишет:

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

выяснили, что процесс выполняется первым, но в событийном слое у  entity нет тех значений, которые установились в процессе: https://community.terrasoft.ua/questions/poluchenie-znacheniy-v-sobytiy…

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

При попытке создать раздел на основании существующего объекта (Платежные реквизиты, например), получаю информацию, что тэгов и групп в нём не будет.



А как сделать полноценный раздел в этом случае?

Или необходимо дублировать уже имеющийся объект?Изображение удалено.

Нравится

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

Удалось разобраться, опишу шаги:

 

1. Создать отдельный пакет (с нужной зависимостью), в котором будет создан раздел 

(если в объекте не установлена колонка для отображения, то придётся создавать ещё один пакет, в котором указать колонку для нужного объекта)



2. Установить системную настройку CurrentPackageId на созданный в п.1



3. Установить пустое значение в системной настройке SchemaNamePrefix



4. Создать раздел на основе существующего объекта, используя Мастер разделов



5. Вернуть обратно системные настройки CurrentPackageId и SchemaNamePrefix



6. Если для объекта уже существовала страница редактирования, то теперь их будет две. Можно найти соответствующие записи

select * from SysModuleEdit 
where  SysModuleEntityId IN 
    (Select Id from SysModuleEntity 
                where SysEntitySchemaUId = (SELECT Uid FROM SysSchema WHERE Name = 'AccountBillingInfo' AND ExtendParent = 0))



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

update SysModuleEdit 
set CardSchemaUId = '8790CD96-F7D5-4ADC-931A-8248CEF25EC0'
where CardSchemaUId = '625E1D4C-BC26-4872-B76E-267C473ECDCC'

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

Зверев Александр пишет:

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

В презентации прямо приводили пример банковских реквизитов как идеальный вариант для перевода из детали в раздел. Странно, что на практике всё было не так (и не могло быть так), как презентовали.



Но на самом деле, это нормальный путь, если представить, что пакет в пункте 1 - это стандартный пакет Creatio



Ну, и надеюсь, что R&D читают community и учтут этот кейс

Да, если нужно визирование в разделе, то его тоже надо делать в пункте 4

Спасибо за обратную связь. Команда разработки ответила, что учтёт этот кейс в будущих релизах.

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

Добрый день!



Планирую интеграцию одного приложения с Creatio CRM через внешний API на OData4.

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

 

для MySQL

SELECT * FROM INFORMATION_SCHEMA.TABLES

для Oracle

SELECT * FROM ALL_TABLES

для PostgreSQL

SELECT table_name FROM information_schema.tables

 

У меня есть только данные для авторизации юзера через OData4, и этот запрос мне никак не выполнить.



Есть ли какой-то способ через внешний API получить список всех таблиц?

Нравится

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

Павел, можно ещё

https://{server}/0/odata/

Так будет результат в  JSON.

 

Либо воспользоваться информацией из таблицы SysSchema, которая тоже доступна по OData. За объекты отвечает EntitySchemaManager, получить список можно с таким фильтром:

https://{server}/0/odata/SysSchema?$filter=ManagerName%20eq%20%27EntitySchemaManager%27

А вариант с выбором из базы не очень подходит, поскольку зависит от платформы, плюс не все таблицы в базе связаны с объектами конфигурации и могут быть доступны по OData. И наоборот, часть объектов сделаны не на основе table, а по view.

Нашел сам



Запрос на 

https://{server}/0/odata/$metadata

возвращает внутри <Schema Namespace="Terrasoft.Configuration.OData" xmlns="http://docs.oasis-open.org/odata/ns/edm"> все сущности

Павел, можно ещё

https://{server}/0/odata/

Так будет результат в  JSON.

 

Либо воспользоваться информацией из таблицы SysSchema, которая тоже доступна по OData. За объекты отвечает EntitySchemaManager, получить список можно с таким фильтром:

https://{server}/0/odata/SysSchema?$filter=ManagerName%20eq%20%27EntitySchemaManager%27

А вариант с выбором из базы не очень подходит, поскольку зависит от платформы, плюс не все таблицы в базе связаны с объектами конфигурации и могут быть доступны по OData. И наоборот, часть объектов сделаны не на основе table, а по view.

Зверев Александр, Спасибо! А метадату конкретной сущности как получить? 

Требуется узнать какие поля у модели.

В https://{server}/0/odata/$metadata всё есть.

Pavel Buev,

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

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

Добрый день!Ф

такая задача:

есть фильтр по дате в формате с-по. Как сделать быстрый фильтр где из календаря можно выбрать 1 конкретную дату в 1 поле, не выбирая с и по?

Нравится

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

По идее, нужно примерно так, как с добавлением целочисленного или логического фильтра по полю, но там, где делают  filterCollection.add, вставлять два условия фильтрации, связанные and, по дате больше или равно начала тех суток, которые выбраны в календаре, и дате меньше начала следующих суток.

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

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

Функция getFilters не подходит?

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

Стоит задача использовать название продажи в элементах БП. Название может и будет меняться постоянно. Можно ли сделать параметр-метод которым можно было бы получать актуальное название продажи? Сейчас делается через чтение данных, но элементов слишком много и выходит, что везде нужно использовать дополнительный элемент чтения

Нравится

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

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

 

  • Сделать в БП функцию на C#, вычисляющую значение при помощи классов EntitySchemaQuery или Select. В нескольких блоках-скриптах её вызывать, получая и передавая кодом значения в параметры.
  • Сделать отдельную схему — пользовательское действие, в которой и реализовать на C# логику вычисления. У действия есть входящие и исходящие параметры. В БП самодельное действие добавляется блоком «Выполнить действие процесса», как и стандартные.
  • Если хочется без кода, можно создать подпроцесс, где и добавить элементы чтения данных. Вставлять в основной процесс блоком «Подпроцесс (Действие вызов)». Данные аналогично передавать в параметрах.

Да, бывает такое желание один раз объявить элемент чтения, и при обращении к нему каждый раз получать свежие данные

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

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

Приветствую! 



Недавно занялся изучением и работой с платформой Creatio. По тому по коду прошу не ругать ;) 

 

Задача популярная: выбрать из таблицы записи по фильтру, посчитать кол-во совпадений и сравнить с системной настройкой. 

Код реализации с getEntity:

var message;
 
var recordId = this.get("Id");
 
if(recordId) {
		var esq1 = this.Ext.create("Terrasoft.EntitySchemaQuery", {
						rootSchemaName: "UsrSwimmProgramm"
		});
 
		var filterA = esq1.createColumnFilterWithParameter(
					Terrasoft.ComparisonType.EQUAL, "UsrSPBooleanActive" , 1);
		var filterP = esq1.createColumnFilterWithParameter(
					Terrasoft.ComparisonType.EQUAL, "UsrSPLookupPeriod.Name" , "Ежедневно");
		esq1.filters.logicalOperation = Terrasoft.LogicalOperatorType.AND;
		esq1.filters.add("filterA",filterA);
		esq1.filters.add("filterP",filterP);
 
		esq1.addAggregationSchemaColumn("Id", Terrasoft.AggregationType.COUNT, 
						"NumberDayLesson", Terrasoft.AggregationEvalType.ALL);
 
 
				esq1.getEntity(recordId, function(result) {
					if (!result.success) {
						this.showInformationDialog("Ошибка запроса");
						return;
					}
					message = result.entity.get("NumberDayLesson") + "\n";
				this.showInformationDialog(message);
				}, this);
 
				}

Код реализации с getEntityCollection:

var message;
var recordId = this.get("Id");
 
if(recordId) {
			var esq1 = this.Ext.create("Terrasoft.EntitySchemaQuery", {
						rootSchemaName: "UsrSwimmProgramm"
				});
 
				var filterA = esq1.createColumnFilterWithParameter(
					Terrasoft.ComparisonType.EQUAL, "UsrSPBooleanActive" , 1);
				var filterP = esq1.createColumnFilterWithParameter(
					Terrasoft.ComparisonType.EQUAL, "UsrSPLookupPeriod.Name" , "Ежедневно");
				esq1.filters.logicalOperation = Terrasoft.LogicalOperatorType.AND;
				esq1.filters.add("filterA",filterA);
				esq1.filters.add("filterP",filterP);
 
				esq1.addAggregationSchemaColumn("Id", Terrasoft.AggregationType.COUNT, 
						"NumberDayLesson", Terrasoft.AggregationEvalType.ALL);
 
				esq1.getEntityCollection( function(result){
					message = result.collection.getByIndex(0).get("NumberDayLesson");
					this.showInformationDialog(message);
				},this);
 
	}



 

Так вот, почему при всех равных метод getEntity выдает не определенной значение, а вот с getEntityCollection все работает норм ? 

 

 

Нравится

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

Добрый вечер.

 

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

И в Вашем случае этот метод не срабатывает, потому что в заданном наборе данных не находит запись по этому recordId. Фактически, при использовании этого метода Вы добавляете ещё один фильтр по полю Id записи.

 

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

Добрый вечер.

 

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

И в Вашем случае этот метод не срабатывает, потому что в заданном наборе данных не находит запись по этому recordId. Фактически, при использовании этого метода Вы добавляете ещё один фильтр по полю Id записи.

 

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

Спасибо большое, Алла. Уже смотрю.

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

Как в SelectQuery добавить фильтр Greater на колонку CreatedOn

Нравится

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

Добрый вечер.

А где нужно на сервере или на клиенте?

c# esq.Filters.Add(esq.CreateFilterWithParameters(FilterComparisonType.Greater, "CreatedOn", ТвояДата));

js filterGroup.add("Sort", Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.GREATER, "CreatedOn", "ТвояДата"));

На всякий случай еще есть GREATER_OR_EQUAL

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

Добрый день! Подскажите пожалуйста, как построить график плановой кривой в разделе "Итоги"? Какие необходимы данные для этого и можно ли вообще реализовать данный функционал?

Пример прикреплен в картинке:Изображение удалено.

Нравится

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

Андрей, а где у вас хранится информация о плане? В разделе Планирование?

В общем, при первом приближении я бы попробовал взять данные из объекта Планирование (надо смотреть как там это хранится), либо сделал бы View на основе опять же Планирования и выводил это на график как отдельную серия с типом линия.

Сидоров Александр Валерьевич,

Вся информация будет храниться там, да

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

 

По поводу данных, в новых версиях уже используется не общий объект «Планирование», а отдельные: «Планирование по контрагенту», «Планирование по ответственному» и др. Смотрите объекты, входящие в пакет CoreForecast.

 

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

 

А вообще, в разделе планирования уже настроены итоги:

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

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

Добрый день!

 

А можно ли как-то сделать, чтобы при добавлении записи из раздела не использовалась мини-карточка (а открывалась сразу полноценная), а из других объектов (например, создание контакта из лида) добавлялось с помощью мини-карточки (как и сейчас происходит)?

 

Спасибо!

 

Нравится

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

Здравствуйте! В нужном Вам разделе перейдите в мастер разделов(https://prnt.sc/tr326c), затем уберите галочки(https://prnt.sc/tr32qw) - это что касается первой части вопроса.

Касательно второго, прочтите данное обсуждение

Я бы наоборот. Галочки оставил, но в разделе что-то поменял бы. Так как раздел - это как раз исключение из правила (в остальных многочисленных местах мини-карточка должна работать).

Владимир, в таком случае нужно смотреть, где в коде отрисовки разделов идёт считывание поля MiniPageModes из SysModuleEdit (точнее, из представления VwSysModuleSchemaEdit) и вносить там правку, чтобы для конкретного раздела читало одно, а действовало, как будто бы там другое.

 

Пока нашёл схему ConfigurationSectionHelper, которая генерирует  Terrasoft.configuration.ModuleStructure с информацией о всех разделах, доступной из JS. Возможно, поможет правка в этой схеме в функции GetModuleStructure, где читают свойства каждого зарегистрированного раздела, в том числе и MiniPageModes, и другой функции GetQuickAddConfig в этой же схеме. Вот она:

  /// &lt;summary&gt;
  /// Gets quick add menu items configuration.
  /// &lt;/summary&gt;
  /// &lt;param name="userConnection"&gt;User connection.&lt;/param&gt;
  /// &lt;returns&gt;Quick add menu items configuration.&lt;/returns&gt;
  public virtual string GetQuickAddConfig(UserConnection userConnection) {
   Dictionary&lt;Guid, string&gt; quickItems = new Dictionary&lt;Guid, string&gt; ();
   string tpl = @"{{QuickAddMenu: [{0}]}}";
   string itemTpl = @"{{itemId:'{0}',SysEditId:'{1}',name:{2},EditPageName:'{3}',TypeColumnValue:'{4}',TypeColumnName:{5},ModuleName:'{6}',EntitySchemaName:'{7}',miniPageSchema:'{8}',hasAddMiniPage:{9}}}";
   bool useMultilanguageData = !userConnection.GetIsFeatureEnabled("DoNotUseMultilanguageData");
   Select quickAddMenuItemsSelect = GetQuickAddMenuItemsSelect(userConnection);
   using (DBExecutor dbExecutor = userConnection.EnsureDBConnection()) {
    using (IDataReader dataReader = quickAddMenuItemsSelect.ExecuteReader(dbExecutor)) {
     while (dataReader.Read()) {
      Guid itemId = dataReader.GetColumnValue&lt;Guid&gt;("Id");
      Guid sysModuleEditId = dataReader.GetColumnValue&lt;Guid&gt;("SysModuleEditId");
      Guid typeColumnValue = dataReader.GetColumnValue&lt;Guid&gt;("TypeColumnValue");
      string editPageName = dataReader.GetColumnValue&lt;string&gt;("EditPageName");
      string name = dataReader.GetColumnValue&lt;string&gt;("Name");
      string caption = useMultilanguageData ? dataReader.GetColumnValue&lt;string&gt;("Caption") : string.Empty;
      name = Json.Serialize(caption.IsNullOrEmpty() ? name : caption);
      string typeColumnName = Json.Serialize(String.Empty);
      Guid sysEntitySchemaUId = dataReader.GetColumnValue&lt;Guid&gt;("SysEntitySchemaUId");
      string entitySchemaName = dataReader.GetColumnValue&lt;string&gt;("EntityName");
      string moduleName = dataReader.GetColumnValue&lt;string&gt;("ModuleName");
      // TODO CRM-53025
      bool rightLevel = string.IsNullOrEmpty(entitySchemaName) ? true :
       userConnection.DBSecurityEngine.GetIsEntitySchemaAppendingAllowed(entitySchemaName);
      Guid columnUId = dataReader.GetColumnValue&lt;Guid&gt;("TypeColumnUId");
      Guid miniPageSchemaUId = dataReader.GetColumnValue&lt;Guid&gt;("MiniPageSchemaUId");
      string miniPageModes = dataReader.GetColumnValue&lt;string&gt;("MiniPageModes");
      bool hasAddMiniPage = HasSchemaEditAddMiniPage(userConnection, entitySchemaName, miniPageModes);
      if (columnUId != null &amp;&amp; columnUId != Guid.Empty) {
       typeColumnName = Json.Serialize(userConnection.EntitySchemaManager.GetInstanceByUId(sysEntitySchemaUId).Columns.GetByUId((Guid)columnUId).Name);
      }
      if (!quickItems.ContainsKey(itemId) &amp;&amp; rightLevel) {
       quickItems.Add(itemId, string.Format(itemTpl, itemId, sysModuleEditId, name, editPageName, typeColumnValue, typeColumnName, moduleName, entitySchemaName, miniPageSchemaUId, hasAddMiniPage.ToString().ToLower()));
      }
     }
    }
   }
   var items = string.Join(",", quickItems.Select(x =&gt; x.Value));
   return string.Format(tpl, items);
  }

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

 

Главное, не забыть потом.

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