Есть раздел UsrTenant, в нем есть поле Room, принимающее значения из справочника UsrRoom. В свою очередь, в UsrRoom есть поле-справочник Shop, принимающее значение из справочника UsrShop. В UsrShop есть текстовое поле Name. Задача - показать в карточке UsrTenant значение UsrTenant.Room.Shop.Name (сделать привязку элемента управления на это значение), лучше в режиме "только чтение". Как это сделать?



Теоретически, можно прикрутить к UsrTenant атрибут, привязать к нему элемент управления в diff, в UsrTenant.onEntityInitialized() определять значение Room.Shop.Name через EntitySchemaQuery и присваивать его атрибуту. Но нет ли способа, позволяющего напрямую привязать элемент управления по такой иерархии? Что-то, может, вроде:

 

// Конечно, прямо в таком виде код не работает, выдается ошибка, 
// что невозможно найти колонку по конфигурации Room.Shop.Name
{
  "operation": "insert",
  "name": "ShopNameView",
  "values": {
    "layout": {
      "colSpan": 12,
      "rowSpan": 1,
      "column": 12,
      "row": 1,
      "layoutName": "Header"
    },
    "bindTo": "Room.Shop.Name",
    "enabled": false,
    "contentType": 5
  },
  "parentName": "Header",
  "propertyName": "items",
  "index": 3
}

 

Нравится

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

Добрый день.

 

В атрибутах добавляете виртуальное поле, которое будет отображаться в карточке:

			"DBMReady": {
				"dataValueType": Terrasoft.DataValueType.BOOLEAN,
				"type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
				"value": false
			}

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

			"BTContactPatient": {
				"lookupListConfig": {
					"columns": ["BTDBMReady"]
				}
			},

Переопределяете метод onEntityInitialized - добавляете вызов метода, в котором будут считываться нужные значения из справочника и устанавливаться в виртуальные поля:

			onEntityInitialized: function() {
				this.callParent(arguments);
				if (!this.isNew) {
					this.setPatientFieldsValue();
					}
			},
			setPatientFieldsValue: function() {
				var patient = this.get("BTContactPatient");
				if (!patient) {
					return;
				}
				this.set("DBMReady", patient.BTDBMReady);
			},

 

Алла Савельева,

Добрый день. Может, я не совсем точно донес свою мысль...



В общем, моя ситуация такова, что BTDBMReady - не скалярное значение, а ссылка на справочник. И атрибуту на странице мне нужно присвоить, например, BTContactPatient.ВTDBMReady.CustomParameter (т.е. простым

ВTDBMReady.displayValue тут не обойтись). Можно ли это сделать без EntitySchemaQuery? 

Юрий, а почему не использовать EntitySchemaQuery?

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

/**
 * Last activity in opportunity.
 * @type {Object}
 */
"LastActivity": {
	"dataValueType": Terrasoft.DataValueType.LOOKUP,
	"type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
	"referenceSchemaName": "Activity"
},
 
/**
 * Last activity result caption in opportunity.
 * @type {String}
 */
"LastActivityCaption": {
	"dataValueType": Terrasoft.DataValueType.TEXT,
	"type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
	"value": ""
},
...
onEntityInitialized: function() {
...
	this.getLastActivity();
...
	this.callParent(arguments);
},
...
/**
 * Gets last activity from opportunity.
 * @protected
 */
getLastActivity: function() {
	var esq = this.getLastActivitySelect();
	esq.getEntityCollection(function(response) {
		if (response && response.success) {
			var collection = response.collection;
			if (collection.getCount() > 0) {
var firstItem = collection.getByIndex(0);
this.setLastActivityAttributes(firstItem);
			}
		}
	}, this);
},
 
/**
 * Sets last activity attributes.
 * @protected
 * @param {Object} activity Activity object from response.
 */
setLastActivityAttributes: function(activity) {
	var date = this.getShortDate(activity.get("DueDate"));
	this.set("LastActivityCaption", Ext.String.format(
			this.get("Resources.Strings.LastActivityCaption"), date));
	this.set("LastActivity", {
		value: activity.get("Id"),
		displayValue: Ext.String.format("{0} / {1}", activity.get("CategoryName"),
				activity.get("StatusName"))
	});
},
 
/**
 * Gets last activity select.
 * @protected
 * @return {Terrasoft.EntitySchemaQuery} Last activity select query.
 */
getLastActivitySelect: function() {
	var esq = Ext.create("Terrasoft.EntitySchemaQuery", {
		rootSchemaName: "Activity"
	});
	esq.rowCount = 1;
	esq.addColumn("Status.Name", "StatusName");
	esq.addColumn("ActivityCategory.Name", "CategoryName");
	var dueDate = esq.addColumn("DueDate");
	dueDate.orderPosition = 0;
	dueDate.orderDirection = Terrasoft.OrderDirection.DESC;
	this.setLastActivityRequestFilters(esq);
	return esq;
},
 
/**
 * Sets last activity filters.
 * @protected
 * @param {Terrasoft.EntitySchemaQuery} esq Activity select query.
 * @return {Terrasoft.EntitySchemaQuery} Activity select query with filter.
 */
setLastActivityRequestFilters: function(esq) {
	var id = this.get("Id");
	if (!Ext.isEmpty(id)) {
		esq.filters.add("opportunityFilter", Terrasoft.createColumnFilterWithParameter(
			Terrasoft.ComparisonType.EQUAL, "Opportunity", id));
	}
	return esq;
},

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

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

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



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



Вроде бы все должно работать, и значение поля CalculatedValue должно вычисляться после загрузки страницы и после изменения значений в полях TurnoverF и TurnoverP. Но реально изменение случается только при загрузке страницы - т.е. метод, прописанный в dependencies, не срабатывает. Почему?

entitySchemaName: "UsrTenant",
attributes: {
 "CalculatedValue": {
     dataValueType: Terrasoft.DataValueType.FLOAT,
     dependencies: {
       columns: ["TurnoverF", "TurnoverP"], 
       methodName: "calculateValue"
     }
  }
},
methods: {
  onEntityInitialized: function() {
    this.callParent(arguments);				
    this.calculateValue();
  },
  calculateValue: function() {
    var turnoverF = this.get("TurnoverF");
    if (!turnoverF) {
      turnoverF = 0;
    }
    var turnoverP = this.get("TurnoverP");
     if (!turnoverP) {
       turnoverP = 0;
     }				
    var result = (turnoverP === 0) ? 0 : (turnoverF / turnoverP - 1);
    this.set("CalculatedValue", result);
  }
},
diff : {
//.....................
{
  "operation": "insert",
  "name": "INT_TurnoverF",
  "values": {
    "layout": {
      "colSpan": 12,
      "rowSpan": 1,
      "column": 0,
      "row": 0,
      "layoutName": "Tab7ec5c1bfTabLabelGridLayout4f9aa333"
    },
    "bindTo": "TurnoverF",
    "enabled": true
  },
  "parentName": "Tab7ec5c1bfTabLabelGridLayout4f9aa333",
  "propertyName": "items",
  "index": 0
},
{
  "operation": "insert",
  "name": "INT_TurnoverP",
  "values": {
    "layout": {
      "colSpan": 12,
      "rowSpan": 1,
      "column": 12,
      "row": 0,
      "layoutName": "Tab7ec5c1bfTabLabelGridLayout4f9aa333"
    },
    "bindTo": "TurnoverP",
    "enabled": true
  },
  "parentName": "Tab7ec5c1bfTabLabelGridLayout4f9aa333",
  "propertyName": "items",
  "index": 1
},
{
  "operation": "insert",
  "name": "INTCalculatedValue",
  "values": {
    "caption": "Вычисляемое поле",
    "bindTo": "CalculatedValue",
    "layout": {
      "column": 12, 
      "rowSpan": 1,
      "row": 4, 
      "layoutName": "Header",
      "colSpan": 9
    }
  },
  "parentName": "Header",
  "propertyName": "items",
  "enabled": true,
  "index": 9
},
//.....................
}

 

Нравится

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

Добрый день.

Попробуйте добавить квадратные скобки после dependencies, как на примере ниже:

	"dependencies": [
		{
			"columns": ["Patient"],
			"methodName": "setPatientFieldsValue"
		}
	]

 

Добрый день.

Попробуйте добавить квадратные скобки после dependencies, как на примере ниже:

	"dependencies": [
		{
			"columns": ["Patient"],
			"methodName": "setPatientFieldsValue"
		}
	]

 

Алла Савельева,

как и предполагалось, ошибка была абсолютно дурацкой...

Спасибо!

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

Доброе время суток!

А могу я уточнить в чем разница между установкой пакетов с изменениями через Вас (@Terrasoft) и из приложения как показано на скрине: 

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

Буду благодарен за более глубокий ответ.

Нравится

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

Andrew Prymenko,

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

И как могут загружаться проектные доработки из Маркетплейса? 

 

Принципиально - ничем. Через Установить из файла вы загружаете свои проектные доработки. Ну а выбрать из Маркетплейс - очевидно их названия. 

Вы говорите принципиально - ничем, но все же есть отличия. Хочу узнать о них. Это может даже целый ряд моментов которые на первый взгляд не принципиальные, но все же имеют место быть. И в первом и во втором случае однозначно загружаються проектные доработки, а что под капотом? Может какие-то backup'ы........ 

Andrew Prymenko,

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

И как могут загружаться проектные доработки из Маркетплейса? 

 

Андрей, в случае выбора «установить из файла» делается бекап только тех пакетов, которые устанавливаются и если установка пройдет с ошибками, то можно сразу же откатить изменения на предыдущие версии. Бекап БД не делается.

 

А при автоматической установке пакетов обновлений вызывается операция WorkspaceConsole InstallFromRepository. Если в действии установлено соответствующее значение, делается бекап БД с последующим восстановлением в случае возникновения ошибки в ходе выполнения.

 

Также интересующий Вас вопрос описан в статье академии «Установка пакетов из приложения» и более новой «Раздел [Установка и удаление приложений]».

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

Александр, добрый вечер!

посмотрите внимательно на фото, которое я прикрепил. Я там обвел синим цветом "Установить из файла". Этим самым говорю, что я проектные доработки из Маркетплейса не загружаю))) А делаю это через пункт меню который обвел синим цветом "Установить из файла". Спасибо, что берете участие в обсуждении поднятых вопросов.

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

Добрый день!

При выборе шаблона email сообщения, в Обращении, заполняется тема из указанного шаблона, но при отправке подставляется  номер и тема из Обращения 

После выбора шаблона тема: "SR00540466 Проверка технической возможности, Київська обл,Київ,вул"

При отправке тема: "Re: SR00540466 Новое включение Включение"



Просьба подсказать как исправить

Нравится

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

Юрий, это не ошибка, это так специально. Ещё с 7.10:

Уведомления по обращению содержат тему из исходного письма, по которому зарегистрировано обращение. Это позволит заявителю отследить историю писем по обращению. Если обращение регистрируется по другим каналам (например, по звонку), то в поле [Тема] уведомлений указывается тема из шаблона письма.

Чтобы поменять, смотрите логику в схеме EmailWithMacrosManager, там в SendEmail и её аналогах вызывают функции, заполняющие заголовок:

/// <summary>
/// Builds an e-mail activity then sends it.
/// The sender and recipients will be obtained automatically.
/// Use this method to reply to all the participants of the parent (root) e-mail activity.
/// </summary>
/// <param name="caseId">Case record identifier.</param>
/// <param name="tplId">Template record identifier.</param>
public virtual void SendEmail(Guid caseId, Guid tplId) {
	Activity activity = CreateActivity();
	PreProcess(activity, caseId, tplId);
	CaseData data = GetCaseData(caseId);
	activity.Sender = GetSender(data);
	FillActivityWithCaseData(activity, data);
	FillTitle(activity, caseId, tplId);
	activity.Save();
	SendActivity(activity.Id);
}
...
/// <summary>
/// Fills an <paramref name="activity"/> with given <paramref name="data"/>.
/// </summary>
/// <param name="activity">Activity entity.</param>
/// <param name="data">Case data.</param>
protected virtual void FillActivityWithCaseData(Activity activity, CaseData data) {
	activity.Title = data.Title;
	activity.Recepient = data.Recipient;
	activity.CopyRecepient = data.CC;
	activity.BlindCopyRecepient = data.BCC;
	if (data.ParentActivityInReplyTo.IsNotNullOrEmpty()) {
		activity.HeaderProperties = AddInReplyToHeaderProperty(data.ParentActivityInReplyTo, activity.HeaderProperties);
	}
}
...
/// <summary>
/// Sets activity title as a subject from e-mail template if it is not specified yet.
/// Otherwise, adds a replying prefix (<see cref="ReplyingPrefix" />)
/// to an <paramref name="activity"/>'s title.
/// </summary>
/// <param name="activity">Activity entity.</param>
/// <param name="caseId">Case record identifier.</param>
/// <param name="tplId">Template record identifier.</param>
protected void FillTitle(Activity activity, Guid caseId, Guid tplId) {
	var title = activity.Title;
	activity.Title = title.IsNullOrEmpty()
		? GetTemplateSubject(SchemaName, caseId, tplId)
		: ApplyPrefix(title);
}

Видимо, Вам нужно переделать, чтобы всегда брался только GetTemplateSubject, а не тема из обращения с добавлением в начале префикса «Re:».

Александр, спасибо, пошли править, отпишусь

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

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

 

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

 

Читал про Terrasoft.chain , но не очень понятно, как именно передавать результат без return

Нравится

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

Всем доброго времени! Есть вопрос на примере простенького процесса:

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

 

Если пользователь выбирает вариант:

"Отлично" - шаг должен завершится без обязательного заполнения поля "комментарий".

"Плохо" - шаг должен завершится с обязательным заполнением поля "комментарий".

 

Как проще реализовать проверку факта заполнения поля в зависимости от нажатой кнопки?

 

Нравится

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

Сделайте поле "Комментарий" обязательным

И при кнопке "Хорошо" уберите проверку обязательности

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Сделайте поле "Комментарий" обязательным

И при кнопке "Хорошо" уберите проверку обязательности

t.ponomarov,

спасибо, этот вариант не хотел использовать чтобы не "отлучать" пользователя от информации на главной странице.

Владимир Соколов,

спасибо, как то не сообразил за эту функцию. 

 

Если в один элемент, то только через преднастроенную страницу.

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

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

Меня интересуют способы отправки имейлов с бэкенда.

Попробовал сделать БП по такой ссылке, но наткнулся на такую проблему, что адрес из таблицы берётся по колонке smtp адреса, а не exchange.

 

Что делать, как обойти эту проблему?

Нравится

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

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

 

Вот пример БП, который корректно работает и  отправляет email.



var activityId = Get<Guid>("ActivityID");
var emailClientFactory = ClassFactory.Get<EmailClientFactory>(new ConstructorArgument("userConnection", UserConnection));
var activityEmailSender = new ActivityEmailSender(emailClientFactory, UserConnection);
activityEmailSender.Send(activityId);
return true;

 

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

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

Добрый день, товарищи разработчики!

Возникла проблема с коннектором IIT E-Signature for Creatio.

Точнее с системой, при попытке обновления 7.15.3 -> 7.15.4 Service enterprise возникает ошибка:

Autogenerated\Src\DigitalSignatureFileUploadInfo.DigitalSignature.cs(19,47) ошибка CS0535: 'DigitalSignatureFileUploadInfo' does not implement interface member 'IFileUploadInfo.AdditionalParams'

Помогите забороть эту проблему.

Нравится

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

Добрый день, Петр!

 

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

Добрый день, Петр!

 

Опубликовала обновленный пакет на маркетплейсе с исправлением ошибки. Установите дополнение повторно из маркетплейс и проверьте обновления.

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

Есть деталь в разделе, на которой есть действие "Удалить всех согласующих"

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

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

Код ниже на скринах

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

и 

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

Нравится

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

Добрый день.

Реализуйте доступ на эту функциональность через доступ на операции.

Пример реализации можно посмотреть, как реализован доступ на действие 'Экспорт в Excel'.

Добрый день.

Реализуйте доступ на эту функциональность через доступ на операции.

Пример реализации можно посмотреть, как реализован доступ на действие 'Экспорт в Excel'.

Я бы сделал проверку на админа через добавления доступа по операциям

init: function() {

    this.callParent(arguments);

    RightUtilities.checkCanExecuteOperation({

        operation: "IsSysAdminUser"

    }, function(result) {

        this.set("IsSysAdminUser", result);

    }, this);

}

После этого можно попробовать указать привязку visible к атрибуту IsSysAdminUser. 

Если это не будет работать, тогда

init: function() {

    this.callParent(arguments);

    

    RightUtilities.checkCanExecuteOperation({

        operation: "IsSysAdminUser"

    }, function(result) {

        this.set("IsSysAdminUser", result);

        this.removeMenuItem();

    }, this);

},

removeMenuItem: function() {

    var toolsButtonMenu = this.get("ToolsButtonMenu");

    if (toolsButtonMenu !== undefined && !this.get("IsSysAdminUser")) {

        for (var key in toolsButtonMenu.collection.map) {

            if (toolsButtonMenu.collection.map[key].values.Caption.bindTo === "Resources.Strings.DeleteMenuCaption") {//Пример проверки

                toolsButtonMenu.removeByKey(key);

            }

        }

    }

},

addRecordOperationsMenuItems: function(toolsButtonMenu) {

    this.callParent(arguments);

    

    //добавление элементов

    

    //после добавления

    this.removeMenuItem();

}

Вместо доступа по оцерациям, можно проверить через esq сделав выборку по таблице SysAdminUnitInRole, в SysAdminUnit = id текущего пользователя, в SysAdminUnitRole = 83A43EBC-F36B-1410-298D-001E8C82BCAD (это id из таблицы SysAdminUnit). В функции получения результата esq также this.set("IsSysAdminUser", ); this.removeMenuItem();

Завязываться на роль админа прямо в коде — будет работать, но идеологически некрасиво. Рано или поздно захочется дать кому-то ещё и придётся переписывать код. Куда лучше создать отдельную операцию с «говорящим» названием, проверять наличие прав на неё, а в разделе настройки прав дать право на неё группе админов.

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

В мобильном приложении на странице Обращения есть справочная колонка Группа (FK на SysUserInRole(SysRoleId) и через FK на SysAdminUnit(Id) -> ContactId).

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

Если жмакнуть по пункту - откроется отфильтрованный список сотрудников в группе:

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

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

https://community.terrasoft.ru/questions/dobavlenie-aktivnostei

https://community.terrasoft.ru/questions/deistvia-nad-zapisami-detali-v…

https://community.terrasoft.ru/questions/nastroika-polzovatelskogo-rabo…



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



Как быть?

Нравится

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

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

 

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

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