Все красиво, и работает корректно. Открывается окно выбора ().
Но предположим, что мне необходимо добавить дополнительную кнопку, либо изменить страницу LookupPage.
Что я делаю - наследую LookupPage.
В итоге не смог разобраться, а официальный саппорт отклоняет подобные запросы по SLA, хотя, здесь явно ошибка в стандартной конфигурации. Вобщем - ничего нового...
Предлагаю поступить немного иначе.
Не наследоваться через зависимости (в карточку), А унаследоваться от схемы справочника (LookupPageV2) (создать свою схему наследника, не замещающую),
там в схеме "играться" с "дифами" и замещенеим, так как мы это делаем при классическом замещении клиентских схем.
И уже своего наследника подключать в зависимости.
(Мне кажется это более каноничный подход)
PS:У меня в ближайшем будущем есть аналогичная задача - скастомить окно на базе типового окна справочника, со своими кнопками и т.д.
Я планировал действовать так.
Прошу Вас отписаться если предложенный мною вариант окажется рабочим, чтобы душа была спокойна :)
Вообщем Вы были изначально на правильном пути, но упустили несколько моментов, по сути Ваши начинания "в купе с моей идеей" и есть итоговое решение, т.к. наследование на уровне схем все равно оказалось необходимым условием (правда через зависимости, а не механизм наследования)
Разобраться помогла схема "MultiLookupModule" входящая в базовую поставку
1) Вы реализуете модуль, зависимости которого необходимо указать не только в исходном коде но и в меню структуры
2) Сам модуль не должен быть чьим либо наследником, всё необходимое идет в его зависимости, в т.ч. и LookupPage который мы идеологически наследуем, но архитектурно через наследование в схемах - это почему-то не работает (Если у казать "Выбор из справочника" как родителя - получаем очень много разного рода ошибок при вызове).
3) Далее вот вообщем-то пример самого кода
[javascript]
define("MyCustomLookupPage", ["LookupPage", "LookupPageViewGenerator", "LookupUtilities", "css!LookupPageCSS"],
function(LookupPage, LookupPageViewGenerator) {
return Ext.define("Terrasoft.configuration.KmGMSLookupPage", {
alternateClassName: "Terrasoft.KmGMSLookupPage",
extend: "Terrasoft.LookupPage",
gridWrapClasses: ["my-custom-lookup-control"]
});
});
[/javascript]
перчим солим, замещаем, дополняем по вкусу и
ну и вызываем его через LookupUtilites или же есть более простой способ через this.openLookup (что само собой является оберткой над вышеуказанным и "сахаром" по сути).
Уже в таком виде - Ваш "отнаследованный" lookupPage можно вызвать следующим образом
[javascript]
var config = {
entitySchemaName: "Lead",
columns: ["Id"],
multiSelect: false,
actionsButtonVisible: false,
lookupPageName: "MyCustomLookupPage"
};
this.openLookup(config, function(result){console.log(result)})
[/javascript]
Вот.
PS:
Были попытки отнаследовать через механизм наследования схем, насколько хватило фантазии и времени - но все безрезультатны.
Хотя очень хотелось бы вот так вот взять и в diff все свои вынести изменения.
Тем не менее описанный способ, предположительно - позволяет делать то что нужно...
Как оказалось - решение было близко)
Спасибо большое, что поделились окончательной реализацией! Я то уже переключился на более приоритетные задачи, так как посчитал эту затею бесперспективной.
На самом деле - решение не окончательное... бой продолжается :)
Есть прям несколько моментов - которые весьма и весьма удивляют... например что кнопки по факту "захардкожены" в LookupPageViewGenerator и собственно не зависят от каких либо конфигурационных параметров вызова этого самого генератора, что приводит к необходимости наследовать и его, с переопределением некоторых функций, в свою очередь "копия" генератора подобным образом не работает полностью или частично, т.к. используя наследника в качестве зависимости - получаем ряд ошибок про "sunbox of undefined" из core.js (что практически убивает надежду "по быстрому разобраться") при попытке выбора записей из такого справочника и т.д.
Кстати по какой-то причине не удается подключить к карточке страницы LookupUtilities
В конечном итоге в контексте карточки нет объекта "LookupUtilities"
[javascript]
define("MyCustomPage", ["LookupUtilities"],
function(LookupUtilities) {
...
[/javascript]
В конечном итоге в контексте карточки LookupUtilities - undefined
Не поделитесь, как вы подключали LookupUtilities к странице ?
(Я уже и в dependecies структуры добавил его, так же безрезультатно)
Кастом окна справочника через наследование его базового Ext-класса возможен, и даже, как оказалось не сложен.
В основном наверное в таком случае требуется юзкейз по управлению панелью с кнопками такого окна - переименование, сокрытие, добавление своих, стилизация и т.д. HowTo по порядку
1) Создаем пользовательский модуль например "MyCustomLookupWindow" [color=red]Внимание![/color]
Модуль не должен являться чьим либо наследником
В структуре метаданных модуля, дублируем зависимости, которые объявим в самом листинге
схемы при создании необходимо выбирать по заголовкам (давняя боль), облегчаю задачу: LookupPage - Выбор из справочника ( NUI ) LookupPageCSS - Стили модуля справочника ( NUI ) LookupPageViewGenerator, LookupUtilities, MaskHelper - имеют такие же заголовки как и их имена
2) Для вызова lookup-наследника используйте в вашей логике this.openLookup метод (присутствует в контексте любой карточки/схемы/детали), если же случилось так что его нет - то см.выше - надо будет подключить LookupUtilitesV2 в зависимости схемы, и использовать метод Open предоставляемый объектом зависимости. (Примеры использования см.в исходниках и в начале темы)
[javascript]
//Подготовим минимальный конфигурационный объект
var config = {
//Для примера справочник "нацелен" на раздел "Лиды"
entitySchemaName: "Lead",
columns: ["Id"],
multiSelect: true,
actionsButtonVisible: true,
//Вот сюда передаем имя модуля-наследника
lookupPageName: "MyCustomLookupWindow"
};
//пример вызова
this..openLookup(
//наш конфигурационный объект
config,
//callback принимающий результат/результаты выбора в виде коллекции
function(selected){console.log(selected)},
//контекст для коллбэка
this
)
[/javascript]
3) Для того чтобы закастомить кнопки необходимо в нашем наследнике переопределить метод renderLookupView
в который произвести инъекцию своей логики
[javascript]
define("MyCustomLookupWindow", ["LookupPage", "LookupPageViewGenerator", "LookupUtilities", "css!LookupPageCSS"],
function(LookupPage, LookupPageViewGenerator) {
return Ext.define("Terrasoft.configuration.MyCustomLookupWindow", {
alternateClassName: "Terrasoft.MyCustomLookupWindow",
extend: "Terrasoft.LookupPage",
gridWrapClasses: ["km-gms-lookup-control"],
//Переопределяем метод в котором мы можем управлять сформированной конфигурацией до рендеринга.
renderLookupView: function(schema, profile) {
var config = this.getLookupConfig(schema, profile);
var topPanelConfig = LookupPageViewGenerator.generateFixed(config);
//----------------------- инъекция логики (начало) ----------------------
//переменная для хранения ссылки на массив объектов-конфигурайий кнопок
var buttonsConfig;
//Получаем ссылку на аттрибут-массив конфигурационных объектов-кнопок
//Используем Underscore.some с возможностью прерывания переборы по возврату от предиката "true"
_.some(topPanelConfig.items, function(target) {
//выделяем объект группы кнопок (Wrapper) по id контейнера
if (target.id === "selectionControlsContainerLookupPage") {
//в нем ищем подчиненные объекты являющиеся массивом
_.some(target, function(target) {
//согласно структуры конфигурационного объекта панели
//"чистым" массивом является только объект с конфигами кнопок
if (Array.isArray(target)) {
//сохраняем ссылку на него в переменной для дальнейшего использования
buttonsConfig = target;
//Прерываем перебор
return true;
}
});
//Прерываем перебор
return true;
}
});
//Создаем новую кнопку, клонируя любую, первой как правило идет кнопка "Отмена"
var newButton = Terrasoft.deepClone(buttonsConfig[0]);
//Используем Underscore.extend для объединения склонированного объекта
//с анонимным объектом-разницы в котором опишем необходимые изменения
_.extend(
newButton,
{
caption: "Добавленная кнопка",
click: {
bindTo: "AddTender"
},
//Внимаение! свойство "tag" должено быть уникальным для каждой кнопки
tag: "AddTender",
//меняем стиль
style: "green"
}
);
//Добавляем конфигурационный объект кнрпки в массив конф.объектов
buttonsConfig.push(newButton);
//Поиск конфигурационного объекта кнопки "Выбрать" в искомом массиве по caption
_.some(buttonsConfig, function(target) {
if (target.caption === "Выбрать") {
//В найденном объекте меняем значение аттрибута caption на "Создать тендеры".
target.caption = "Переименованная кнопка" ;
return true;
}
});
//Поиск конфигурационного объекта кнопки "Добавить" в искомом массиве по caption
_.some(buttonsConfig, function(target) {
if (target.caption === "Действия") {
//В найденном объекте меняем значение аттрибута visible на "false"
//тем самым скрывая кнопку-меню
target.visible = false ;
return true;
}
});
//----------------------- инъекция логики (конец) ----------------------
this.renderLookupControls(config, topPanelConfig);
}
});
});
[/javascript]
Вот какое-то такое получится окно
Здравствуйте! Продукт sales enterprice 7.9.0. Необходимо принудительно сортировать записи на детали по колонке "Дата создания" по возрастанию.
При этом пользователь должен иметь возможность сортировать записи по своему усмотрению с помощью стандартных средств сортировки на детали. Но при добавлении новой записи на детали должна снова применяться принудительная сортировка скриптом записей по возрастанию даты создания. Как это реализовать?
Сомневаюсь, что в фильтре можно задавать сортировку. Я бы смотрел:
1) В сторону метода заполнения коллекции GridData в детали. Желательно докопаться до esq запроса в бд и вставить что-то вроде
[javascript]
var col = esq.addColumn("CreatedOn");
col.orderDirection = Terrasoft.OrderDirection.ASC;
col.orderPosition = 0;
[/javascript]
2) В описании Grid(опять же в детали) в diff вроде есть
[javascript]
"sortColumn": {"bindTo": "sortColumn"},
"sortColumnDirection": {"bindTo": "GridSortDirection"},
"sortColumnIndex": {"bindTo": "SortColumnIndex"}
[/javascript]
Скорее всего и тут можно задать
Здравствуйте, коллеги. Пытаюсь построить сервис, который закрывает обращение и дает возможность добавлять комментарии при закрытии.
Скелет класса CaseRatingService (клон CaseRatingManagementService):
namespace Terrasoft.Configuration.CaseManagementService
{
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.ServiceModel.Activation;
using System.ServiceModel.Channels;
using System.Security.Principal;
using System.Web;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Terrasoft.Common;
using Terrasoft.Common.Json;
using Terrasoft.Core;
using Terrasoft.Core.DB;
using Terrasoft.Core.Entities;
using Terrasoft.Core.Store;
using Terrasoft.Nui.ServiceModel;
using Terrasoft.Nui.ServiceModel.Extensions;
using Terrasoft.UI.WebControls;
#region Class: CaseManagementService
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class CaseManagementService {
[OperationContract]
[WebGet(UriTemplate = "Case/{id}/{rating}")]
public void GetCase(string id, string rating) {
var context = HttpContext.Current;
try{
HttpRequest request = context.Request;
if (id == null || rating == null) {
throw new ArgumentNullOrEmptyException("rating");
}
_appConnection = HttpContext.Current.Application["AppConnection"] as AppConnection;
_sysUserName = _appConnection.SystemUserConnection.CurrentUser.Name;
_sessionId = Guid.NewGuid().ToString("N");
Thread.CurrentPrincipal = new TerrasoftPrincipal(new GenericIdentity(_sysUserName), new string[0], _sessionId);
this._setResponseText(context.Response, "Ok");
} catch (Exception ex) {
this._setResponseText(context.Response, ex.Message);
}
}
Date: 08.12.2016 11:27:01
Date (UTC): 08.12.2016 9:27:01
Exception Message: Не удалось найти тип "Terrasoft.Configuration.CaseManagementService.CaseManagementService", заданный значением атрибута Service в директиве ServiceHost или указанный в элементе конфигурации system.serviceModel/serviceHostingEnvironment/serviceActivations.
Exception Type: System.InvalidOperationException
Exception Source: System.ServiceModel.Activation
Exception Stack Trace:
в System.ServiceModel.Activation.ServiceHostFactory.CreateServiceHost(String constructorString, Uri[] baseAddresses)
в System.ServiceModel.ServiceHostingEnvironment.HostingManager.CreateService(String normalizedVirtualPath, EventTraceActivity eventTraceActivity)
в System.ServiceModel.ServiceHostingEnvironment.HostingManager.ActivateService(ServiceActivationInfo serviceActivationInfo, EventTraceActivity eventTraceActivity)
в System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath, EventTraceActivity eventTraceActivity)
SessionID: 1r4srdi5za0yv132xnjywfzg
Request URL: /WebApp770/0/ServiceModel/CaseManagementService.svc
Request Path: /WebApp770/0/ServiceModel/CaseManagementService.svc
Request Type: GET
User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36
User Host Address: ::1
User: EFrolov
Is Authenticated: True
Authentication Type: Forms
Is Secure Connection: False
Application Version: 7.7.0.0
Application Path: G:\Projects\Core\TSBpm\Src\Lib\Terrasoft.WebApp.Loader\Terrasoft.WebApp\
Application Virtual Path: /WebApp770/0
Application Trust Level: Full
Machine Name: PC-23-N
Is Local: True
Process ID: 10396
Process Name: iisexpress.exe
Process Account Name: INDOORMEDIA\developer
Thread Account Name: INDOORMEDIA\developer
OS Version: Microsoft Windows NT 6.2.9200.0
Net Framework Version: 4.0.30319.42000
DBExecutor Type: MSSqlExecuto
1. в ServiceModel поцепил файлик CaseManagementService.svc:
Добрый день!
Имеется следующий пример реализации.
В конфигурации необходимо создать схему исходного кода c контрактом сервиса:
[ServiceContract]
public interface IService
{
[OperationContract]
SPMClientInfoResponse SPMClientInfo(string Login);
}
[DataContract]
public class SPMClientInfoResponse
{
bool success = true;
string errorText = "";
[DataMember]
public bool Success
{
get { return success; }
set { success = value; }
}
[DataMember]
public string ErrorText
{
get { return errorText; }
set { errorText = value; }
}
}
и схему исходного кода c реализацией сервиса:
public class SPMSUBPService : IService
{
public SPMClientInfoResponse SPMClientInfo(string Login)
{
return new SPMClientInfoResponse();
}
}
Методы конфигурационного веб-сервиса должны быть помечены атрибутами [OperationContract] и
[WebInvoke] с параметрами.
Например, вот так: [OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, BodyStyle
= WebMessageBodyStyle.Wrapped, ResponseFormat = WebMessageFormat.Json)]
Далее, в папке В папке Terrasoft.WebApp\ServiceModel создаем файл CaseManagementService.svc с примерно таким текстом:
<%@ ServiceHost Language="C#" Debug="true" Service="CaseManagementService.CaseManagementService" Factory="System.ServiceModel.Activation.ServiceHostFactory" %>
Добавить в файл Terrasoft.WebApp\ServiceModel\http\services.config описание сервиса:
В разделе активности, в списочном реестре, если вытянуть поле Контрагент, реестр не загружается (Заголовки колонок тоже пропадают). Если поле убрать все работает нормально. Данная проблема наблюдается под одним пользователем, на разных компьютерах и браузерах.
Подскажите на что стоит обратить внимание, в чем может быть проблема? Может кто то сталкивался?
В разделе Документы настроены итоги, записи раздела отфильтрованны по быстрому фильтру даты (например выбран Сегодня), при переходе в другой раздел и возврате в раздел Документы итоги отображаються как если бы фильтра по дате не было, если нажать Сегодня итоги обновляться и отображаться верно.
Подскажите пожалуйста, с чем это может быть связанно и что возможно сделать, что бы исправить проблему?
Подскажите, пожалуйста, каким образом можно добавить клиентским кодом на страницу элементы сторонних сайтов, напр. видео YouTube, либо контейнер iframe?
Здравствуйте,
В системе есть пример уже, это встраивание видео на главной странице.
Суть написания сводится к использованию контейнера, и написанию в нем произвольного html кода, вплоть до iframe.
Еще пример:
Только с iframe стоит учитывать, что встраивать можно только те страницы, которые себя в явном виде позволяют встраивать, к примеру, youtube видео, а также протокол передачи должен совпадать, если ваш сайт работает через https, то и страница фрейма должна работать через https.
А при компиляции он ругается на AppScheduler и SimpleTriggerImpl, типа, они не объявлены.
Пробовал перед ними добавлять альясы Usings, например "Terrasoft.Core.Scheduler.AppScheduler", но это тоже не помогло.
Связано ли это с тем, что БП, создающий Job находится в созданном мной отдельном пакете, в котором из зависимостей только базовые пакеты Base и UIv2?
Добрый день! Нужно создать справочник с страницей редактирования.
Был создан скрытый раздел, и добавлен справочник. Но возникает проблема, при попытке открыть справочник открывает:
как видите в адресной строке пишет: LookupSectionModule/ilayDocReportsSection
и не отображаются записи справочника.
Но после нажатия Вид -> Настроить колонки(или Настроить итоги) и возврата с страницы настройки назад:
и в адресной строке теперь пишет: SectionModuleV2/ilayDocReportsSection
Смотрел в базе как зарегистрирован аналогичный справочник "Библиотека блоков контента" не нашел отличий.
:
Если не ошибаюсь, для реализации подобной задачи справочник необходимо регистрировать как отдельный раздел. В Бд есть хранимые процедуры RegisterPage и RegisterSection.
Как альтернатива - можно создать отдельный раздел мастером, после чего добавить его на необходимую страницу, как справочное поле.
Если не ошибаюсь, для реализации подобной задачи справочник необходимо регистрировать как отдельный раздел. В Бд есть хранимые процедуры RegisterPage и RegisterSection.
Как альтернатива - можно создать отдельный раздел мастером, после чего добавить его на необходимую страницу, как справочное поле.
Добрый день, Илья!
Дело в том что как раз через мастер и был создан раздел, и "страницой реестра" была указана секция созданного мастером раздела.
И проблема в том, что при открытии наполнения справочника сначала открывается LookupSectionModule/ilayDocReportsSection, а после открытия страницы настройки колонок открывается уже SectionModuleV2/ilayDocReportsSection.
Здравствуйте,
Отличный вопрос. Виной всему вот этот метод:
Если посмотреть схему секции контент блока, Вы увидите, что он переопределен, это же необходимо сделать и Вам в Вашей странице секции, а так же добавить зависимость:
Почистить кеш, перезайти на сайт, зайти в наполнение справочника, заново настроить колонки, после чего они удачно сохранятся и будут корректно открываться без всяких манипуляций с «настроить колонки» каждый раз при заходе в наполнение.
На днях озаботились такой проблемой - клиенту надо посмотреть почтовые сообщения за последнюю неделю. И опа :) В стандартном почтовом клиенте средств фильтрации нет никаких, о папках вообще молчу. Решение - выводить в списке активностей еще и e-maily, благо активности и есть.
Реализация занимает 5 минут, поэтому, если кому надо, то вот последовательность действий:
1. Создаем замещающую схему секции активностей (ActivitySectionV2).
2. В ней определяем, что используем. Нам надо по большому счету BaseFiltersGenerateModule и все.
3. В ветке methods сносим фильтр NotEmailFilter, который собственно и убирает из списка активностей e-maily
getFilters:function(){ var filters =this.callParent(arguments); if(filters.contains("NotEmailFilter")){
filters.removeByKey("NotEmailFilter"); } return filters; }
сохряняем, сносим кэш браузера и готово. Можно наслаждаться отбром почты по любым параметрам.
Используется bpm'online sales 7.7 после перехода с Terrasoft, т.е. все данные были перенесены (порядка 6 млн. активностей). По результату Terrasoft отрабатывает быстрее чем bpm'online. При запуске сайта локально на сервере отрабатывает терпимо, при работе на пользовательских машинах иногда данные вообще не отображаются.
Может кто-то может поделится наработками в вопросе увеличения быстродействия bpm'online on-site: где/как искать проблему, как/что оптимизировать.
Здравствуйте.
Не совсем корректно сравнивать быстродействие BPM'online и Terrasoft 3.x. Эти систему имеют абсолютно разную архитектуру. Terrasoft 3.x - это классическая 2-звенная (клиент-сервер) система. BPM'online - 3-звенная (клиент-сервер базы данных-сервер приложения). Если время отклика системы значительно отличается на сервере и на клиентской машине, тогда стоит обратить внимание на сетевую составляющую (стабильность, ширина канала, особенно если это Wi-Fi), также нужно обеспечить достаточные ресурсы клиентской машины (часть кода выполняется, именно, на пользовательском ПК). Также рекомендую в connectionstrings.config в строке подключения к Redis-серверу не использовать в качестве адреса localhost или 127.0.0.1 (указать сетевое имя или реальный IP-адрес). В противном случае игнорируются параметры, отвечающие за многопоточность подключения к Redis. Это параметры maxReadPoolSize=; maxWritePoolSize= (находятся в том же файле в той же строке). По умолчанию равны – 25. Желательно устанавливать по количеству пользователей *10.
Олег, а вы в браузере посмотрите сколько запросов идет и как долго они выполняются. Таким образом сможете выявить где тормозит и падает.
Например, в версии 7.2 по умолчанию стоит таймаут в 30 секунд на запрос. В результате любые запросы более менее тяжелые могут отвалиться, и при этом останется висеть лоадер, либо просто ничего не произойдет. Самый простой вариант, руками поменять таймаут на больший. И как показывает практика, больше времени уходит на обработку уже на серверной стороне.