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

Есть класс SideBar и я создал модуль NrbSideBarWithCountersModule который наследуется от него. 

define("NrbSideBarWithCountersModule", ["terrasoft", "ext-base"], function() {
	Ext.define("Terrasoft.controls.NrbSideBarWithCountersModule", {
		extend: "Terrasoft.SideBar",
		alternateClassName: "Terrasoft.NrbSideBarWithCountersModule",
		Ext: null,
		sandbox: null,
		Terrasoft: null,
		itemTplMap: [
			"caption",
			"imageUrl",
			"href",
			"domAttributes",
			"availableEntities"
		],
 
		itemTpl: [
			"<tpl if=\"visible != false\">",
			"<li data-item-index=\"{itemIndex}\"",
			"<tpl if=\"isSelected == true\">",
			"class=\"ts-sidebar-selected-item\"",
			"</tpl>",
			">",
			"<span class=\"vertical-strip\"></span>",
			"<tpl if=\"href\"><a target=\"_self\" class=\"sidebar-item-link\" href=\"{href}\"></tpl>",
			"<div id=\"sidebar-item-wrapper-{itemIndex}\" class=\"ts-sidebar-item-wrapper\">",
			"<div id=\"sidebar-item-image-{itemIndex}\" class=\"ts-sidebar-item-image\" data-item-marker=\"{caption}\" " +
			"style=\"background-image:url({imageUrl})\" <tpl foreach=\"domAttributes\">{$}=\"{.}\"</tpl>></div>",
			"<div id=\"sidebar-item-text-{itemIndex}\" class=\"ts-sidebar-item-text\"> {caption} ",
			"<div id=\"sidebar-item-counter-{itemIndex}\" class=\"ts-sidebar-item-counter\"> {availableEntities} ",
			"</div>",
			"</div>",
			"<tpl if=\"href\"></a></tpl>",
			"</li>",
			"</tpl>"
		],
 
	});
	return Terrasoft.NrbSideBarWithCountersModule;
});

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

define("NrbSectionMenuModule", ["ServiceHelper", "NrbSectionMenuModuleResources", "ModuleUtils", "MaskHelper",
		"LeftPanelUtilitiesV2", "CheckModuleDestroyMixin"],
	function (resources, ModuleUtils, ServiceHelper, MaskHelper, LeftPanelUtilities) {
		Ext.define("Terrasoft.configuration.NrbSectionMenuModule", {
			alternateClassName: "Terrasoft.NrbSectionMenuModule",
			override: "Terrasoft.SectionMenuModule",
			render: function (renderTo) {
				if (!Ext.isEmpty(viewModel) && !Ext.isEmpty(sideBar)) {
					sideBar.destroy();
				}
				sideBar = Ext.create("Terrasoft.NrbSideBarWithCountersModule", Terrasoft.deepClone(sideBarConfig));
				sideBar.bind(viewModel);
				sideBar.render(renderTo);
				MaskHelper.HideBodyMask();
			},
			getSideBarItems: function () {
				var config = [];
				var moduleStructure = Terrasoft.configuration.ModuleStructure;
				var modules = info && info.modules ? info.modules : Object.keys(moduleStructure);
				if (!modules) {
					return;
				}
				var availableSections = info.AvailableSections;
				var defaultIconUrl = Terrasoft.ImageUrlBuilder.getUrl(resources.localizableImages.DefaultIconSvg);
				modules.forEach(function (module) {
					if (module.moduleId !== Terrasoft.GUID_EMPTY) {
						var moduleName = module.moduleName ? module.moduleName : module;
						var moduleConfig = moduleStructure[moduleName];
						if (moduleConfig.hide !== "true") {
							if (!Ext.isArray(availableSections) || availableSections.indexOf(moduleName) >= 0) {
								var caption = moduleStructure[moduleName].moduleCaption;
								var tag = ModuleUtils.getModuleTag(moduleName);
								var imageId = moduleStructure[moduleName].imageId;
								var imageUrl = imageId ? getImageUrl(imageId) : defaultIconUrl;
								var itemUrl = Terrasoft.workspaceBaseUrl + "/Nui/ViewModule.aspx#" + tag;
								var count = this.getCountOfAvailableProjects();
								var itemConfig = {
									caption: caption,
									tag: tag,
									imageUrl: imageUrl,
									href: itemUrl,
									domAttributes: { "module-name": module.moduleName },
									availableEntities: count
								};
								if (!Terrasoft.Features.getIsEnabled("SectionMenuLink")) {
									itemConfig.href = null;
								}
								config.push(itemConfig);
							}
						}
					}
				});
				return config;
			},
 
			getCountOfAvailableProjects: function () {
				var requestConfig = {
					serviceName: "NrbProjectCounterService",
					methodName: "GetNumberOfAvailableProjects",
					callback: this.showResult
				};
				this.callService(requestConfig, this);
			},
 
			showResult: function (result) {
				console.log(result);
			},
		});
 
		return Terrasoft.NrbSectionMenuModule;
	});

Переопределение не даёт нужного результата и возник такой вопрос. Правильно ли тут использовать переопределение или лучше использовать наследование? 

И как я понял мне надо будет переопределить все модули которые взаимодействовали с SectionMenuModule и поменять на NrbSectionMenuModule?

Нравится

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

Модули не замещаются как карточки. То что создали свой модуль NrbSideBarWithCountersModule это ваш модуль. А левую панель так и продолжает выводить SectionMenuModule.

Динар, для того, чтобы Ваш класс заменил стандартный, нужно в нём указать override и название того, что он замещает. А затем упомянуть название Вашего модуля в замещённом BootstrapModulesV2, который загружается при старте. Более подробно о таком подходе см. пример в этой теме.

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

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

Нравится

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

Как вариант, сменить пользователю PasswordExpireDate.

Например: update t set t.PasswordExpireDate='2040-05-20 00:00:00.000'

В теории обнулить ForceChangePassword и PasswordExpireDate в таблице SysAdminUnit у конкретного пользователя.

Логика авторизации находится в методе Terrasoft.WebApp.Loader.ServiceModel.AuthService.Login это в библиотеке \bin\Terrasoft.WebApp.Loader.dll.

Как вариант, сменить пользователю PasswordExpireDate.

Например: update t set t.PasswordExpireDate='2040-05-20 00:00:00.000'

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

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

Нравится

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

Найти разработчика который умеет решать проблемы.

Нагашыбай, проверьте, есть ли у старых сервисных договоров на детали «Объекты обслуживания» нужный контакт или контрагент. Возможно, их кто-то вручную или в БП очищает. Или же у Вашего пользователя просто нет прав на чтение этих записей.

Больше о логике работы выбора сервисных договоров есть в статье, возможно, из неё станет ясно, на что ещё нужно обратить внимание.

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

на старых сервисных договорах добавлены нужные контрагенты и контакты но все равно проблема повторяется.

Проверьте  ещё права доступа на их чтение.

И опишите подробнее, со скриншотами, может, были какие-то доработки раздела?

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

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

Под доступными подразумеваются те проекты, которые удовлетворяют следующим условиям (фиксированным фильтрам раздела + права + статусы):

Ответственный = текущий пользователь

И

текущий пользователь имеет права на чтение

И

Состояние проекта не равно (Завершён, Отменён)

 

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

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

Я начинающий разработчик и не очень понимаю как именно это реализовать. Была идея создать визуальны модуль с счётчиком, но пока не понимаю как это сделать. Уже написан вэб-сервис, который возвращает доступное количество проектов, но пока нет понимая откуда его вызвать и как установить эти счётчики.

 

Нравится

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

Не факт что туда можно влезть. Левую панель с разделами отрисовывает модуль SideBarModule.

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

Уже есть настроенный показатель «Активных проектов», можно изменить его условие фильтрации:

Или ещё проще, настроить динамическую группу с нужным фильтром, включить в разделе итог «Отображать количество записей» и при нахождении внутри группы над реестром будет видно интересующее число.

Зверев Александр, Спасибо за предложение, но всё-таки мне требуется выводить это число в панели разделов

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

Динар, кажется, придумал ещё один способ.

Из JS доступна структура Terrasoft.configuration.ModuleStructure, где хранится информация по разделам, в том числе и названия. Если там вручную из консоли браузера сменить название, дописав цифру, то при обновлении перечня разделов, например, при смене рабочего места, она появится. Можно менять её программно, а потом вызывать перестройку списка разделов, как при смене рабочего места. Эта структура генерируется на серверной стороне в схеме ConfigurationSectionHelper.

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

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

Ошибка при выполнении запроса из пункта 3 в "Подключить сервис глобального поиска в Creatio"(https://academy.terrasoft.ru/docs/user/ustanovka_i_administrirovanie/ra…). Так и не смог понять в чем проблема, до этого все пункты были сделаны без ошибок.

Нравится

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

Проблема решилась после полной переустановки всех компонентов и настройки серверов с 0.

Вероятно ошибка при создании в пункте 2. Проверьте индекс и сам созданный сайт.

http://[gs-webapi]:81/sites/my-test-site

http://[gs-webapi]::81/sites/my-test-site/search

http://external.elasticsearch:9200/indexname

 

Попробуйте создать новый сайт или пересоздать старый.

Команда на удаление curl -v -X DELETE http://[gs-webapi]:81/sites/my-test-site/search

Полозюков Евгений Петрович,

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

Полозюков Евгений Петрович,

Может ли проблема быть в неправильном написании строки подключения к БД при регистрации сайта в системе?

Эта команда должна дать информацию curl -X GET "http://[gs-webapi]:81/sites/my-test-site/search/state?includeInfo=true" -H "accept: application/json"

Также можно перейти по ссылке и попасть в gui админку

http://[gs-webapi]:81/api

http://[gs-webapi]:81/sites/my-test-site

Было что-то похожее, когда пытался подставить сюда [SERVER2_IP_ADDRESS] доменное имя, с доменным именем что-то не работало, а вот с ip адресом заработало. Ip адрес так же прописывал в /opt/compose/services/.env

Трефилов Павел Сергеевич,

В данный момент и так использую IP-адресс

Полозюков Евгений Петрович,

Выводит ту же ошибку, и еще несколько, последние 4 как я понял не важны, так как я еще не дошел до этого пункта

Пытался добавить индекс в ручную, при помощи "curl -v -X PUT http://[IP-elasticа]:9200/wgjii24eiweauyoaxikjqzgcomlijl8vt83g1dyhy3xaave1ggthkmsli8i8p2pe". Результат тот-же

Мишустин Василий,

У вас явно не получается создать индекс, это все что можно сказать.

Полозюков Евгений Петрович,

Но ведь я даже в ручную создал индекс в ES, так почему же он пишет что его нету?

Мишустин Василий,

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

Проблема решилась после полной переустановки всех компонентов и настройки серверов с 0.

Александр, 



Проверьте доступность сервисов с IIS-сервера по портам 81, 82 и 9200 (telnet хоста, где расположен Linux-сервер с сервисами Глобального Поиска). Запрос на регистрацию может вернуть ошибку в случае, если с сервера 2 не открыт доступ на IIS сервер, где развернута on-site среда.

Попробуйте указать IP адрес сервера на котором развернут эластик вместо dns имени, бывают проблемы с тем, что не определяется dns-имя в докере.

Также убедитесь, что строка подключения к еластику корректная.

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

Кейс:

Есть объект контрагенты (стандартный)

Есть объект Чаты. Одно из полей объекта - контрагент. С одним контрагентом может быть заведено несколько чатов (из разных источников)

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

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

 

Задача:

вывести на карточку недвижимости список чатов со всеми контрагентами, которые хотят посмотреть данную недвижимость.



Можете подсказать, как построить такую деталь?

Нравится

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

На карточку недвижимости вывести деталь по объекту чаты. В описании детали указать свою функцию в значении filterMethod(пример можно найти в AccountPageV2).

В фильтре указать равенство

[Просмотры недвижимости:Контрагент:Контрагент].Недвижимость = 

Id текущей записи недвижимости (this.get('Id"))

 

https://academy.terrasoft.ru/docs/7-17/developer/front-end_development/…

На карточку недвижимости вывести деталь по объекту чаты. В описании детали указать свою функцию в значении filterMethod(пример можно найти в AccountPageV2).

В фильтре указать равенство

[Просмотры недвижимости:Контрагент:Контрагент].Недвижимость = 

Id текущей записи недвижимости (this.get('Id"))

 

https://academy.terrasoft.ru/docs/7-17/developer/front-end_development/…

Спасибо! Все получилось.

Полозюков Евгений Петрович,

Здравствуйте! а доступ к этой статье только у сертифицированных разработчиков?

Лидия, структура и содержимое справки недавно менялись, теперь см. эту статью.

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

Добрый день, коллеги!



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



Возможно ли это реализовать и каким образом?



Где именно происходит добавление этой иконки к базовым полям, когда включена телефония? Можно ли каким-либо образом добавить и свое кастомоное поле в "список", чтобы базовая логика также добавляла эту иконку при включении интеграции с телефонией?



Скриншот с примером во вложении.



Заранее спасибо за любую помощь в решении данного кейса!

Прикрепленные файлы

Нравится

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

В карточке контакте есть поля, проанализируйте схему ContactPageV2. Также есть код в детали Средства связи схема ContactCommunicationDetail.



Поля выводятся с помощью элемента PhoneEdit который отнаследован от BaseEdit. Коды элементов лежат в папке \Terrasoft.WebApp\Resources\ui\Terrasoft\controls\

Если включить debugmode можно увидеть коды этих элементов в консоли разработчика в Sources.

В карточке контакте есть поля, проанализируйте схему ContactPageV2. Также есть код в детали Средства связи схема ContactCommunicationDetail.



Поля выводятся с помощью элемента PhoneEdit который отнаследован от BaseEdit. Коды элементов лежат в папке \Terrasoft.WebApp\Resources\ui\Terrasoft\controls\

Если включить debugmode можно увидеть коды этих элементов в консоли разработчика в Sources.

Максим, на скриншоте видно миникарточку, но там кнопки звонка нет. Это нарисованный пример?

Евгений, тут, скорее, надо смотреть в строну реализации ContactCommunicationDetail и её родительскую схему BaseCommunicationDetail. Там задают iconTypeButtonConfig, указывая функцию getTypeImageConfig. Она задана с кнопками для сайта и почты в BaseCommunicationViewModel и дополнена кнопкой звонка в CTIBaseCommunicationViewModel. Там же  в onLinkClick заданы обработчики нажатия для разных типов: открытие веб-страницы, запуск почтовой программы или звонок.

Поскольку тип у Вас будет всегда равен «телефон», а не храниться в отдельном поле, можно выкинуть эту и ей подобную логику проверок, оставив только содержимое ветви кода для типа-телефона:

var type = this.get("CommunicationType");
if (this.isPhoneType(type.value)) {
...

Вопрос доработки поля (только для почты) обсуждался ещё в этой теме.

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

Добрый день, можно ли как-то сделать поле с типом Enum, ссылочным?

Нравится

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

Александр, что Вы имеете в виду под  полем с типом Enum и ссылочным? В 7.Х нет Enum, они были только в 3.Х, вместо них для перечислений тоже используются справочники.

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

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

Я имел ввиду отображение значения справочного поля.

Справочное поле (с модальным окном) - значение отображается как ссылка.

Справочное поле (выпадающий список) - значение отображается  как простой текст.

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

Если в карточке, то похожее для текстового поля решают присвоением свойства showValueAsLink равного true, а также свойства href, где будет URL ссылки, и обработчика linkclick, где указать запускаемую при нажатии функцию. См. тут и тут. Но это для текстового поля (например, поля «сайт»), нужно попробовать, будет ли работать для поля выпадающего списка.

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

Нет, пробовал такие варианты для выпадающего списка, не работают. 

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

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

Добрый день! Перерабатываю синхронизацию с LDAP. Заказчик требует выполнять раз в сутки полную синхронизацию и чуть ли не каждые 15 мин частичную. То есть при частичной только добавление новых пользователей и обновление измененных в течении последних 15 мин. После синхронизации в штатной реализации выполняется процедура актуализации ролей. Эта процедура вызывает хранимую процедуру tsp_ActualizeAdminUnitInRole, которая в свою очередь вызывает хранимую процедуру tsp_GetAdminUnitList. На 50000 пользователях эта хранимая процедура выполняется слишком долго (перекрывает 15 минут). Хотелось бы понять, что делает tsp_ActualizeAdminUnitInRole, в чем ее назначение? Если пользователи просто добавляются в группу "All Employees" и еще в одну, вложенную в "All Employees", возможно ли отключить вызов ХП tsp_ActualizeAdminUnitInRole при частичной синхронизации и вызывать только обновление кэша SysAdminUtilities.ReloadSysAdminUnitsCache?

Нравится

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

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

По словам разработчиков, актуализацию переделали на версии 7.17.1, там  она работает на порядок быстрее.

Они рекомендуют обновиться до версии 7.17.1+.

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

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

Добрый день! Появилась необходимость создать бизнес процессы через POST-запросы. 

Используя мануал для DataServise , написал кейс такого вида: 

 

 data = {
    "columnValues": {
        "items": {
            "Account": {
                "expressionType": 2,
                "parameter": {
                    "dataValueType": 10,
                    "value": "f3f8f781-3c5b-4ecb-89ef-5c1ed4da12ga"
                },
            },
 
            "Category": {
                "expressionType": 2,
                "parameter": {
                    "dataValueType": 10,
                    "value": "1c0bc159-150a-e111-a31b-f2g4hd04c01d"
                },
            },
....
            "operationType": 1,
            "rootSchemaName": "Case"
     },
    },
}

Но при post запросе вида: 

 

h = requests.post("
https://bpm-test.ru/0/dataservice/json/Reply/InsertQuery
", headers=headers, cookies=p.cookies, data=json.dumps(data))

Появляется ошибка :



 

{'responseStatus': {'ErrorCode': 'ArgumentNullOrEmptyException', 'Message': 'Значение аргумента "name" не может быть пустым', 'Errors': []}, 'rowsAffected': -1, 'nextPrcElReady': False, 'success': False}

С чем это может быть связано? если поле name даже не присутствует 

Нравится

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

Дмитрий, уточните, Вы спрашиваете о запуске процесса или о создании записи в каком-то разделе (в Вашем примере — Case)?

Если интересует именно первое, нужно использовать не DataService, а ProcessEngineService. Как его запускать в POST, см. тему.

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