Добрый день, уважаемые коллеги!

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

Рассматриваю применение 2-х вариантов:

Бизнес-правило 1: Фильтр записей Подразделений по признаку UsrProhibitSelectionAccount (см. ниже 1-й метод). Метод отрабатывает и при выборе отображает только то, что можно

Бизнес-правило 2: Выдавать сообщение пользователю, если выбранная ранее запись некорректна (см. ниже 2-й метод).  И вот тут почему-то в консоли видно, что не считывается корректно значение поля UsrProhibitSelectionAccount. Оно всегда false. Правда не во всех записях оно заполнено (см. скрин).

Помогите, пожалуйста, определить в чем некорректность 2-го метода. Благодарю за помощь

// Правило 1
/*Terrasoft.sdk.Model.addBusinessRule("Account", {
	ruleType: Terrasoft.RuleTypes.Filtration,
	position: 1,
	triggeredByColumns: ["UsrDepartment"],
	events: [Terrasoft.BusinessRuleEvents.ValueChanged,Terrasoft.BusinessRuleEvents.Load],
	 message: Terrasoft.LocalizableStrings.DepartmentMustBeAllowed,
	 filters: Ext.create("Terrasoft.Filter", {
        modelName: "UsrAccountDepartments",
        property: "UsrProhibitSelectionAccount",
        value: false
    })
});*/
 
// Правило 2
Terrasoft.sdk.Model.addBusinessRule("Account", {
	name: "AccountDepartmentMustBeAllowedRule",
	ruleType: Terrasoft.RuleTypes.Custom,
	triggeredByColumns: ["UsrDepartment"],
	events: [Terrasoft.BusinessRuleEvents.Load,
		Terrasoft.BusinessRuleEvents.ValueChanged, Terrasoft.BusinessRuleEvents.Save],
	executeFn: function(record, rule, column, customData, callbackConfig) {
		var AccountDepartment = record.get("UsrDepartment");
	var isValid = AccountDepartment.get("UsrProhibitSelectionAccount");
 
		record.changeProperty("UsrDepartment", {
			isValid: {
				value: isValid,
				message: Terrasoft.LocalizableStrings.DepartmentMustBeAllowed
			}
		});
		Ext.callback(callbackConfig.success, callbackConfig.scope, [isValid]);
	}
});

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

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

Нравится

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

Марина, а оно точно так должно синхронно читать из объекта справочника так, как у Вас?

В схеме MobileActivityOpportunityModelConfig значение Opportunity.Account читают с асинхронным вызовом так:

Terrasoft.sdk.Model.addBusinessRule("Activity", {
	name: "ActivityAccountOpportunityFiltrationRule",
	ruleType: Terrasoft.RuleTypes.Filtration,
	events: [Terrasoft.BusinessRuleEvents.Load, Terrasoft.BusinessRuleEvents.ValueChanged],
	triggeredByColumns: ["Account"],
	filteredColumn: "Opportunity",
	filters: Ext.create("Terrasoft.Filter", {
		property: "Account"
	})
});
 
Terrasoft.sdk.Model.addBusinessRule("Activity", {
	name: "ActivityAccountByOpportunityRule",
	ruleType: Terrasoft.RuleTypes.Custom,
	triggeredByColumns: ["Opportunity"],
	events: [Terrasoft.BusinessRuleEvents.ValueChanged, Terrasoft.BusinessRuleEvents.Save,
		Terrasoft.BusinessRuleEvents.Load],
	executeFn: function(record, rule, column, customData, callbackConfig) {
		var opportunityRecord = record.get("Opportunity");
		if (opportunityRecord) {
			var accountRecord = record.get("Account");
			if (!accountRecord) {
				var opportunityModel = Ext.ModelManager.getModel("Opportunity");
				opportunityModel.load(opportunityRecord.getId(), {
					isCancelable: false,
					queryConfig: Ext.create("Terrasoft.QueryConfig", {
						columns: ["Account"],
						modelName: "Opportunity"
					}),
					success: function(loadedRecord) {
						if (loadedRecord) {
							record.set("Account", loadedRecord.get("Account"), true);
						}
						Ext.callback(callbackConfig.success, callbackConfig.scope);
					},
					failure: function(record, operation) {
						var exception = operation.getError();
						Ext.callback(callbackConfig.failure, callbackConfig.scope, [exception]);
					},
					scope: this
				});
				return;
			}
		}
		Ext.callback(callbackConfig.success, callbackConfig.scope);
	},
	position: 1
});

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

Марина, а оно точно так должно синхронно читать из объекта справочника так, как у Вас?

В схеме MobileActivityOpportunityModelConfig значение Opportunity.Account читают с асинхронным вызовом так:

Terrasoft.sdk.Model.addBusinessRule("Activity", {
	name: "ActivityAccountOpportunityFiltrationRule",
	ruleType: Terrasoft.RuleTypes.Filtration,
	events: [Terrasoft.BusinessRuleEvents.Load, Terrasoft.BusinessRuleEvents.ValueChanged],
	triggeredByColumns: ["Account"],
	filteredColumn: "Opportunity",
	filters: Ext.create("Terrasoft.Filter", {
		property: "Account"
	})
});
 
Terrasoft.sdk.Model.addBusinessRule("Activity", {
	name: "ActivityAccountByOpportunityRule",
	ruleType: Terrasoft.RuleTypes.Custom,
	triggeredByColumns: ["Opportunity"],
	events: [Terrasoft.BusinessRuleEvents.ValueChanged, Terrasoft.BusinessRuleEvents.Save,
		Terrasoft.BusinessRuleEvents.Load],
	executeFn: function(record, rule, column, customData, callbackConfig) {
		var opportunityRecord = record.get("Opportunity");
		if (opportunityRecord) {
			var accountRecord = record.get("Account");
			if (!accountRecord) {
				var opportunityModel = Ext.ModelManager.getModel("Opportunity");
				opportunityModel.load(opportunityRecord.getId(), {
					isCancelable: false,
					queryConfig: Ext.create("Terrasoft.QueryConfig", {
						columns: ["Account"],
						modelName: "Opportunity"
					}),
					success: function(loadedRecord) {
						if (loadedRecord) {
							record.set("Account", loadedRecord.get("Account"), true);
						}
						Ext.callback(callbackConfig.success, callbackConfig.scope);
					},
					failure: function(record, operation) {
						var exception = operation.getError();
						Ext.callback(callbackConfig.failure, callbackConfig.scope, [exception]);
					},
					scope: this
				});
				return;
			}
		}
		Ext.callback(callbackConfig.success, callbackConfig.scope);
	},
	position: 1
});

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

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

 

Александр, большое спасибо за рекомендации. Да, видимо, я неверно читаю объект справочника. Надо попробовать разобраться с асинхронным вызовом. Спасибо за приведенный пример.

Заполнение колонки UsrProhibitSelectionAccount в локальной базе пока не помогло.

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

Добрый день, уважаемые коллеги!

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

2. В записях справочника Подразделений есть признак (булево) можно выбирать это подразделение для указания в Контрагентах или нет - UsrProhibitSelectionAccount

3. Нужно запретить выбор записи Подразделения, если признак UsrProhibitSelectionAccount = true

4. Настраиваю правило валидации, как описано https://academy.terrasoft.ru/documents/technic-sdk/7-16/dobavlenie-vali…

5. При выборе значения из справочника правило отрабатывает. Но если вводить запрещенное подразделение вручную, то оно спокойно остается в поле и позволяет сохранить значение.

Подскажите, пожалуйста, что нужно сделать в данном случае? Благодарю за помощьИзображение удалено.

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

Добавленные методы в карточку Контрагента

methods: {
			// Метод-валидатор значения колонки [UsrDepartment] // semmrn
            DepartmentValidator: function() {
                // Переменная для хранения сообщения об ошибке валидации.
                var invalidMessage = "";
                // Проверка значения колонки [UsrDepartment]
                var AccountDepartments = this.get("UsrDepartment");
                if (AccountDepartments.UsrProhibitSelectionAccount) {
                    // Если значение колонки [UsrAccountDepartments.UsrProhibitSelectionAccount] 
                    // запрещено к выбору = да, то
                    // в переменную invalidMessage помещается значение локализуемой строки с сообщением
                    // об ошибке валидации.
                    invalidMessage = this.get("Resources.Strings.DepartmentMustBeAllowed");
                }
                // Объект, свойство которого содержит сообщение об ошибке валидации.
                // Если валидация прошла успешна, в объекте возвращается пустая строка.
                return {
                    // Сообщение об ошибке валидации.
                    invalidMessage: invalidMessage
                };
            },
            // Переопределение базового метода, инициализирующего пользовательские валидаторы.
            setValidationConfig: function() {
                // Вызывает инициализацию валидаторов родительской модели представления.
                this.callParent(arguments);
                // Для колонки [UsrDepartment] добавляется метод-валидатор DepartmentValidator().
                this.addColumnValidator("UsrDepartment", this.DepartmentValidator);
                }
		},

 

Нравится

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

Для того, что Вы хотите есть как раз валидация, которую Вы пробовали в первом сообщении. Она должна предотвращать и от ручного ввода. Не смотрели в отладке, функция-валидатор выполняется, что в поле AccountDepartments и как срабатывает условие?

Доброго дня, Марина. Почему Вы не хотите сделать настройку посредством бизнес-правила (фильтрацией)? 

Марина, ещё можно деактивировать при помощи стандартного механизма:

Если одно или несколько значений справочника устарели и больше не используются, то такие значения можно деактивировать (Рис. 2). Деактивированное значение не будет отображаться при выборе значений в справочных полях. При этом пользователи продолжат видеть это значение в тех записях, где оно было указано ранее, и смогут использовать его для фильтрации. По умолчанию возможность деактивировать значения справочника выключена. Разрешить деактивацию записей для нужного справочника можно в разделе [Конфигурация]. Подробнее о настройке читайте в статье “Деактивация записей объектов”.

Рис. 2 — Деактивированное значение справочника [Типы статей базы знаний]

section_lookups_deactivated_record_example.png 

Но так будет запрещён выбор не в конкретной карточке, а везде. 

Уважаемые коллеги, спасибо за Ваши предложения.

Стандартные фильтры и деактивация не подходят, так как в справочнике настроена иерархия, которая не совмещается с данными инструментами.

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

Для того, что Вы хотите есть как раз валидация, которую Вы пробовали в первом сообщении. Она должна предотвращать и от ручного ввода. Не смотрели в отладке, функция-валидатор выполняется, что в поле AccountDepartments и как срабатывает условие?

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

 

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

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

Добрый день.

 

Есть раздел "Контакты", который может содержит записи с типом "Сотрудник".

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

 

Вопрос: В каких случаях необходимо/эффективнее задействовать еще и раздел "Сотрудники"?

//Чтобы его заполнить все равно приходится обращаться к записям раздела "Контакты", а для создания записи в разделе "Пользователи" достаточно предварительно создать запись в "Контакты" .

Нравится

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

Это два разных объекта, Contact и Employee. Во втором есть справочное поле — ссылка на контакт и дополнительная информация, связанная с должностью и руководителем, началом, концом карьеры, испытательным сроком и увольнением.

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

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

Добрый день.

 

Есть 3 потребности в работе с аналитикой:

  1. возможность делать скриншот отдельных виджетов
  2. при нажатии на действия "скриншот" Система должна делать скриншот виджета, сохранять изображение, на котором отсутствуют вспомогательные иконки (шестеренка и т.д.")
  3. расширить контейнер, чтобы влезал текст на диаграмме целиком?



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

1) Подскажите возможна ли такая реализация? Куда необходимо смотреть?

2) Планируется ли реализация этих требований в  базовой функциональности платформы в последующих версиях?

Нравится

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

Добрый день.

С версии  7.15.2 изменились схемы отрисовки графиков, был выполнен переход к Chart.js. На текущей версии, к сожалению, ещё нет возможности настроить полное отображение текстов подписей графиков через пользовательский интерфейс. 

По информации от авторов функциональности:

1. Такой возможности не планировали давать. В Windows и macOs есть штатные средства для создания скриншотов, а также существуют утилиты, которые помогают редактировать скриншот по усмотрению пользователя, например, ShareX.

2. Аналогично.

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

 

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

 

Согласен, что по п.1 и п.2 можно использовать штатные/сторонние средства.

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

 

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

 

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

 

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

Если необходимо расширить контейнер с текстом в рамках текущей версии (а не спустя 8 месяцев), то каким образом Вы рекомендовали бы действовать?

По Вашим вопросам и пожеланиям зарегистрированы идеи: «Итоги. Надо сохранять скриншот только одного графика (виджета), а не только всей вкладки итогов.» и «Итоги. Работа с графиками. Текст (подпись/заголовок) в графике не отображается полностью, обрезаны подписи».

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

Спасибо за помощь.

Остался один только вопрос: Если необходимо расширить контейнер с текстом в рамках текущей версии (а не спустя 8 месяцев), то каким образом Вы рекомендовали бы действовать?

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

Кстати, на демо-сайте не вижу таких примеров как у Вас, там легенда слева написана вертикально (но если делать длинной, на другую строку не перенеслась):

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

Скажите пожалуйста, будет ли приложение отображать push активностей/email активностей, созданные из бизнес процессов?

Нравится

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

К сожалению push уведомление не работает, в системе от  другого пользователя добавил активность, указав себя ответственным по задаче, но в телефоне pusha нет. система ios

Олег, добрый день!

 

push-уведомление создается ответственному сотруднику, если установлено напоминание ответственному (признак Ответственному в группе полей Напоминания на странице активности).

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

На сборку Sales enterprise (on-site) установлено приложение "Расчет рабочих дней в бизнес-процессах" https://marketplace.terrasoft.ru/template/raschet-rabochih-dney-v-biznes-processah

Пакет отображается в конфигурации, но действия процесса не попадают в список действий процесса при добавлении в пользовательский бизнес-процесс.

Каким образом можно это исправить?

 

Нравится

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

Скорее всего проблема в иерархии пакетов. Сделайте зависимость своего пакета от их пакета

Скорее всего проблема в иерархии пакетов. Сделайте зависимость своего пакета от их пакета

Добрый день, Мария!

 

Алексей прав, по умолчанию действия процесса будут доступны только для бизнес-процессов пакета Custom. Если вы настраиваете бизнес-процессы в своем пользовательском пакете, добавьте зависимость в свой пакет от пакета приложения GlbCalendarService.

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

Всем добрый день!

Столкнулся с следующей проблемой перестали выгружаться пакеты в ФС, пишет. что изменений не обнаружено. Редис чистил, пересоздавал приложение в IIS, удалял пакеты из папки Pkg в конфигурации - не помогает.

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

Нравится

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

Скорее всего включена разработка в файловой системе, когда она включена то при разратобке используется локальный статический контент (к примеру C# файлы)

Скорее всего включена разработка в файловой системе, когда она включена то при разратобке используется локальный статический контент (к примеру C# файлы)

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

Коллеги, добрый день.

Заказчик разработал рекламное письмо с pdf-ной подложкой и с элементами ссылок в виде кружочков с ссылками на соцсети: VK, Одноклассники, facebook и т.п.

Позволяет  ли fastreport в отчете формата pdf, получить ссылки на соцсети, после вывода отчета в файл?

т.е. можно ли кружочек сделать в виде ссылки и в сформированном отчете, перейти по этой ссылки

Спасибо.

Нравится

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

Марина, в дизайнере отчётов у текста или картинки есть свойство «Hyperlink», там можно настроить ссылку.

 

Но эта функциональность ещё не тестировалась, не во всех случаях может работать. Обычная ссылка-константа у меня добавилась:

Александр, спасибо

 

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

Исходные данные JS-кода.

Атрибут.

"ArticleName": {
  "dataValueType": this.Terrasoft.DataValueType.TEXT,
  "type": this.Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
  "value": ""
},

Метод получения значения атрибута

getArticleName: function() {
  var articleName = this.get("ArticleName");
  return articleName ? articleName : "";
},

Метод кнопки

onGetArticleNameClick: function () {
// Первый запуск - <пусто>
// Последующие запуски - <наименование>
this.showInformationDialog(this.getArticleName());
this.set("ArticleName", "");
// Данные реестра.
var gridData = this.getGridData();
if (gridData) {
  var activeRow = this.getActiveRow();
  if (activeRow) {
    // Поле Id.
    var activeRowId = activeRow.get("Id");
    if (activeRowId)
    {
      var serviceData = { id: activeRowId };
      ServiceHelper.callService("VSKBSourceCodeServices", "GetArticleName", 
        function (response) {
          var result = response.GetArticleNameResult;
          this.set("ArticleName", result);
        }, serviceData, this);
    }
  }
}
// Всегда <пусто>
this.showInformationDialog(this.getArticleName());
},

Не могу понять, почему ServiceHelper.callService не заполняет значение атрибута ArticleName сразу? Ощущение, как будто вызов клиентского веб-сервиса GetArticleName выполняется асинхронно.

Нравится

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

Здравствуйте! Данная проблема связана из-за асинхронности js ф-ция обратного вызова запускается в другом потоке и контекст this - уже не виден. Вам нужно до вызова ServiceHelper объявить переменную, например scope и записать в нее this:

 var scope = this; 

а в ф-ции обратного вызова уже писать:

scope .set("ArticleName", result);

Как то так, должно стработать.

Здравствуйте! Данная проблема связана из-за асинхронности js ф-ция обратного вызова запускается в другом потоке и контекст this - уже не виден. Вам нужно до вызова ServiceHelper объявить переменную, например scope и записать в нее this:

 var scope = this; 

а в ф-ции обратного вызова уже писать:

scope .set("ArticleName", result);

Как то так, должно стработать.

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

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

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

ServiceHelper.callService, и затем выполнить обращение к этому атрибуту.

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

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

Коллеги, подскажите в чем причина.

Добавляю на закладки быстрый фильтр по ответственному. 

На в разделе Контакты все отлично работает,

но в разделах Сервис и Контрагенты работает только если ремарить фильтр

//filter: BaseFiltersGenerateModule.OwnerFilter, Но работает без этого фильтра.

В чем может быть причина? Спасибо.

/**
 * @class AccountSectionV2.ELBase
 * @extends AccountSectionV2.NUI
 */
define(
	"AccountSectionV2",
	[
		"ProcessModuleUtilities",
		"ELJSConst",
		"ELAccountActionMenuMixin", 
		"BaseFiltersGenerateModule"
	],
	function(
		ProcessModuleUtilities,
		ELJSConst, 
		BaseFiltersGenerateModule
	) {
		return {
			entitySchemaName: "Account",
 
			mixins: {
				ELAccountActionMenuMixin: "Terrasoft.ELAccountActionMenuMixin"
			},
 
        methods: {
 
				initFixedFiltersConfig: function() {
				var fixedFilterConfig = {
					entitySchema: this.entitySchema,
					filters: [
						{name: "Owner",
							caption: this.get("Resources.Strings.OwnerFilterCaption"),
							dataValueType: Terrasoft.DataValueType.LOOKUP,
							filter: BaseFiltersGenerateModule.OwnerFilter,
							columnName: "Owner",
							defValue: this.Terrasoft.SysValue.CURRENT_USER_CONTACT
						}
					]
				};
				this.set("FixedFilterConfig", fixedFilterConfig);
			},
define("BaseFiltersGenerateModule", ["BaseFiltersGenerateModuleResources", "ConfigurationConstants"], 
	function(resources, ConfigurationConstants) {
		function getIsNotNullFilterGroup(refSchema) {
			const userFilter = Terrasoft.createColumnIsNotNullFilter(refSchema + ".Id");
			const filters = Ext.create("Terrasoft.FilterGroup");
			filters.addItem(userFilter);
			return filters;
		}
 
		function employeesFilter() {
			const sysAdminUnitRef = "[SysAdminUnit:Contact]";
			const employeesFilter = Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL,
					sysAdminUnitRef + ".ConnectionType",
					ConfigurationConstants.SysAdminUnit.ConnectionType.AllEmployees);
			const filters = getIsNotNullFilterGroup(sysAdminUnitRef);
			filters.addItem(employeesFilter);
			return filters;
		}
 
		function allUsersFilter() {
			return getIsNotNullFilterGroup("[VwSystemUsers:Contact]");
		}
 
		function selfFilter() {
			let primaryColumnName = "Id";
			if (this.entitySchema && this.entitySchema.primaryColumnName) {
				primaryColumnName = this.entitySchema.primaryColumnName;
			}
			const primaryColumnValue = this.get(primaryColumnName);
			return Terrasoft.createColumnFilterWithParameter(
				Terrasoft.ComparisonType.NOT_EQUAL, primaryColumnName, primaryColumnValue);
		}
 
		return {
			OwnerFilter: employeesFilter,
			SelfFilter: selfFilter,
			AllUsersFilter: allUsersFilter
		};
	});

 

Нравится

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

Уточните, когда и где работает или нет? И как не работает, как будто фильтра не существует или пустая выборка?

 

Все наложенные фильтры можно увидеть в запросе, уходящем к /0/DataService/json/SyncReply/SelectQuery, а если система развёрнута  локально, итоговый запрос в базу видно в SQL-профайлере. Возможно, условия стандартного и добавленного фильтров противоречат?

 

Также проверьте, всё ли правильно сделано в соответствии со статьёй о создании быстрого фильтра.

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