Добрый день!
При удалении инцидента появляется ошибка "ParentDataset - есть null или не является объектом"
Отладчик ссылается на строчку var Dataset = DataFields.ParentDataset; скрипта ds_IncidentScript

function DataChange(DataField){
        if (DataField == null) {
                return;
        }      
        var DataFields = DataField.ParentDataFields;
        var Dataset = DataFields.ParentDataset;
        if (Dataset.Attributes('IsUpdating')) {
                return;
        }
        var Name = DataField.Name;
                            ...

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

Нравится

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

Добрый день, Дмитрий!
Проверил указанное Вами поведение в базовой версии продукта, данная проблема не воспроизводится.
Прошу Вас, укажите детальный кейс воспроизведения, версию продукта, на котором воспроизводилась проблема и необходимо выполнить анализ произведенных доработок в системе.
Спасибо!

Если это какая-то доработка, можно добавить в скрипт дополнительные проверки на null или обернуть в try.

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

Добрый день, Владимир!
Мы зарегистрировали Ваше пожелание для доработки базовой логики продукта.
В данный момент, если такова необходимость есть, Вы можете реализовать данную логику на странице обращения.
Реализовать необходимо проверку заполнения поля "Решение", которая будет осуществляться в момент перевода инцидента в соответствующий статус.
Но, в таком случае, необходимо учесть еще фактор "переоткрытия" инцидента, данное поле у Вас останется заполненым по умолчанию, и пользователь сможет закрыть инцидент не меняя значения данного поля. Это не всегда корректно, и очищать данное поле не корректно также.
Потому данный кейст и требует доработки и он ранее не был включен в коробку.

Спасибо за понимание и удачного Вам дня!

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

"Михайленко Михаил" написал:Это не всегда корректно, и очищать данное поле не корректно также

Переносить решение в Историю, и очищать - как вариант. Или требовать подтверждения решения - при переоткрытии. Да, необходимо логику проработать

"Михайленко Михаил" написал:Это не всегда корректно, и очищать данное поле не корректно также

Переносить решение в Историю, и очищать - как вариант. Или требовать подтверждения решения - при переоткрытии. Да, необходимо логику проработать

"Владимир Соколов" написал:Переносить решение в Историю, и очищать - как вариант. Или требовать подтверждения решения - при переоткрытии.

Я думаю Владимир идея у вас очень правильная, но я бы к примеру оставил только Вариант помещения в Историю и очищение поля, без всяких подтверждений. Если пользователь идет на шаг "Переоткрытие" инцидента, то он идет на данный шаг осознанно. И переспрашивать о каждом действие, я думаю это нелогично. Переспрашивать нужно только если действие пользователя может привести к примеру к патовой ситуации, или отмене всех его введенных данных.

"Власов Михаил Викторович" написал:то он идет на данный шаг осознанно

Особенно, когда клиент высылает "Спасибо" в ответ на письмо о решении инцидента :D :D :D

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

Добрый день!

Очень беспокоит вопрос появления напоминаний о создании новых E-mail сообщений по инциденту. Не знаю в какой момент, но у ответственного пользователя по инциденту появляются напоминания о том, что у инцидента появилось новое E-mail сообщение. Посмотрев деталь E-mail в инцидентах я действительно нашел там новые сообщения. Никто их туда специально не привязывал. Значит, получается, что при разборе почты каким-то образом это сообщение автоматически привязалось к инциденту. Я догадываюсь, что автоматическая привязка происходит по теме сообщения и номеру инцидента. Но это не совсем корректно, т.к. к инциденту номер 200 привязываются сообщения с темой "АКЦИЯ! Рекламный авиатур в Черногорию 200 евро!".

Как настроить корректную работу данного функционала или отключить его вообще?

Нравится

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

"Ozzy" написал:Значит, получается, что при разборе почты каким-то образом это сообщение автоматически привязалось к инциденту. Я догадываюсь, что автоматическая привязка происходит по теме сообщения и номеру инцидента.

Да, так и есть. В скрипте scr_MailLinks это делают функции FindIncidentNumber и GetIncidentDataBySubject:

function FindIncidentNumber(Str) {
	var Mask = GetSystemParameterValueEx('IncidentMask', false);
	var NumList = Str.match(/(\d+)/g);
	if (!NumList) {
		return;
	}
	for (var i = 0; i < NumList.length; i++) {
		var Num = Mask.replace(/\%1/g, NumList[i]);
		var SafeNum = '(^|:|#|№|-|\\s+)' + NormalizeForRegex(Num) + '(\\s|\\.|$)';
		if (Str.match(new RegExp(SafeNum, 'ig'))) {
			return Num;
		}
	}
}
 
function GetIncidentDataBySubject(Subject) {
	if (!System.GetHasLicense('Incidents')) {
		return false;
	}
 
	var IncidentNumber = FindIncidentNumber(Subject);
	if (!IncidentNumber) {
	    return null;
	}
    var SelectQuery = GetSingleItemByCode('sq_GetIncidentDataByNumber',
		'MailLinks');
    SelectQuery.Parameters('IncidentNumber').Value = IncidentNumber;
    var Dataset = SelectQuery.Open();
    try {
	    if (IsDatasetEmpty(Dataset)) {
	        return null;
	    }
	    return LoadRecord(Dataset);
	} finally {
		Dataset.Close();
	}
}

Если эта логика вообще не нужна, то можно во второй функции в начале вписать return null; .

Если же хочется привязывать к инцидентам только нужные письма, можно договориться о формате номера (например, [200] или #200) и соответствующим образом модифицировать первую функцию.

Спасибо за подсказку. Для начала просто отключу, так как с работой регулярных выражений еще поразбираться надо.

Александр, воспользовался Вашим советом и отключил функционал полностью в функции GetIncidentDataBySubject. Но в результате по-прежнему появляются напоминания о привязанных письмах к инцидентам ((( Может еще где-то отключить надо?

Странно. То есть напоминания по новым письмам, пришедшим после изменения кода и перезапуска Аутлука?

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

Ozzy, закомментируйте в function SaveMailItem(MailItem, Options) сервиса scr_MSOutlookLibrary строку:

Incident = GetIncidentDataBySubject(MailItem.Subject);

Спасибо, Наталья! Попробую закрыть таким образом. На самом деле такая функция реализуется в пяти разных скриптах. Не хотелось бы закрыть что-нибудь лишнее.

Если функция GetIncidentDataBySubject уже отключена изнутри, какой смысл отключать все её вызовы? Надо смотреть, может, похожая логика сделана ещё где-то отдельно.

Я же говорю, похожая функция реализована в 5 разных скриптах. Её реализация есть в scr_MailLinks, который Вы указали, а есть и в scr_MSOutlookLibrary, который указала Наталья. Необходимо найти правильный и заблокировать вызов.

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

Пытаюсь действовать по этому блогу
https://community.terrasoft.ru/blogs/9079
Но почти на первом же шаге ошибка. Когда вставляем первый скрипт

var defValuesId = Guid.NewGuid();
var defValues = new Dictionary string, object>();
defValues.Add("Contact", Page.ContactEdit.Value.ToString());
defValues.Add("Account", Page.AccountEdit.Value.ToString());
defValues.Add("Title", Page.TitleEdit.Value.ToString());
var IncidentId = Guid.NewGuid();

UserConnection.UserContext.Add(defValuesId.ToString(), defValues);
var parameters =  
      new Dictionarystring, string> {
            {"createWithUId", IncidentId.ToString()},
                        {"entitySchemaUId", "C449D832-A4CC-4B01-B9D5-8A12C42A9F89"},
                        {"defValuesId", defValuesId.ToString()}
      };
OpenTaskEditPage.OpenerInstanceId = InstanceUId;
OpenTaskEditPage.UseCurrentActivePage = true;
OpenTaskEditPage.PageUId = new Guid("F2BF397B-8FA3-48BA-B691-57360871967A");
OpenTaskEditPage.PageParameters = parameters ;
OpenTaskEditPage.CloseMessage = "IncidentEditPageClosed";

Выдает ошибки:

И во-вторых, где взять ид, которые есть в скрипте
"entitySchemaUId", "C449D832-A4CC-4B01-B9D5-8A12C42A9F89"}
OpenTaskEditPage.PageUId = new Guid("F2BF397B-8FA3-48BA-B691-57360871967A");

Нравится

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

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

По поводу ошибки "Имя OpenTaskEditPage отсутсвует в текущем контексте", в блоге указано (пункт 3), что после создания действия процесса "Открыть страницу", ему следует присвоить наименование. Вы либо не добавили UserTask, либо не так назвали.
А по поводу "UserContext" - какая у Вас версия, не 5.2 случаем?

PS:
ID в принципе вы можете не менять, т.к. это стандартные схемы(окна) и ID У Вас совпадут. А вообще, посмотреть их можно либо запросом в БД, либо в адресной строке, при открытии того или иного окна, указывается также и его идентификатор.

Да, действительно, я слишком поспешил, но по-моему там немного порядок нарушен.
Что касается версии, то 5.4.0.591
После всего остались две ошибки: "UserContext" и еще во втором скрипте (после сигнала IncidentEditPageClosed)

TargetScemaQuery.Filters.Add(TargetScemaQuery.CreateFilterWithParameters(
        FilterComparisonType.Equal, 
        "Id", 
        IncidentId));

используется IncidentId, но в скрипте он до этого не объявлен.

Александр, есть там объявление (в первом листинге):

var IncidentId = Guid.NewGuid();

По поводу UserContext - он был в 5.2. В 5.4 теперь это SessionData. Т.е. по сути Вам нужно заменить

UserConnection.UserContext.Add(defValuesId.ToString(), defValues);

на

UserConnection.SessionData.[defValuesId.ToString()] = defValues;

Если там потом этот ключ где считывается, то код следующий:

var test = Page.UserConnection.SessionData[defValuesId.ToString()];

C "UserContext", т.е. с SessionData получилось.
На счет IncidentId. Она объявлена в первом скрипте, но второй скрипт идет как отдельная функция, т.е. она не видит что происходит в первом скрипте, соответственно не видит объявление IncidentId.
Или я что-то не понимаю? Как можно передать переменную из одного скрипта в другой?

Добрый день.

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

Логика следующая:

(сигнал нажатия на кнопку) -> (скрипт подготовки действия процесса "открыть карточку") -> (действие процесса открыть карточку) -> (сигнал закрытия карточки инцидента) -> (скрипт генерации номера инцидента).

И действительно - в последнем элементе набор данных "Инцидент" фильтруется по идентификатору только что созданной новой записи, и её присваивается определенный номер.
Т.к. для фильтрации используется переменная, скорее всего, нужно просто создать параметр страницы, и в первом скрипте, после генерации нового идентификатора, инициализировать этот параметр сгенерированным значением:

myParam =  IncidentId;

А в последнем скрипте, вместо IncidentId использовать параметр.

Параметр нужно создать с типом Guid.

Спасибо. Теперь подберусь немного к сути задачи. Мне надо создать именно инцидент (поставил id инцидента и его карточки, все нормально открывается). Что надо сделать, чтобы после нажатия на кнопку "Ок" у инцидента, т.е. инцидент создан в карточке e-mail он добавился в поле инцидент?

Александр, необходимо добавить сроку кода в элементе (скрипт генерации номера инцидента), т.е. в последний скриптовый элемент в рамках добавленной вами логики:

Page.DataSource.ActiveRow.SetColumnValue("IncidentId", myParam);

где myParam - параметр, в котором хранится Id созданного инцидента.

По сути эта строчка должна добавлять инцидент к письму

Page.IncidentEdit.SetValueAndText(incidentCollection[0].GetTypedColumnValue<Guid>(primaryColumn.Name), incidentCollection[0].GetTypedColumnValue<string>("Number"));

Но скрипт выполняется сразу после открытия окна инцидента, а т.к. запись еще не внесена он не видит эту строку, соответственно ничего не добавляет. Как подождать закрытия окна инцидента?

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

Нужно ждать сигнал, в данном случае IncidentEditPageClosed. Посмотрите, в блоге данный промежуточный обрабатывающий сигнал присутствует.

Да. вы права, человеческий фактор невнимательность, вместо обрабатывающего сигнала поставил генерирующий.

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

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

1. Создайте в карточке кнопку, создайте обработчик события кнопки, как показано на рисунке:

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

В скрипт следует добавить такой программный код:

var defValuesId = Guid.NewGuid();
var defValues = new Dictionary string, object>();
defValues.Add("Contact", Page.ContactEdit.Value.ToString());
defValues.Add("Account", Page.AccountEdit.Value.ToString());
defValues.Add("Title", Page.TitleEdit.Value.ToString());
var IncidentId = Guid.NewGuid();

UserConnection.UserContext.Add(defValuesId.ToString(), defValues);
var parameters =  
      new Dictionarystring, string> {
            {"createWithUId", IncidentId.ToString()},
                        {"entitySchemaUId", "C449D832-A4CC-4B01-B9D5-8A12C42A9F89"},
                        {"defValuesId", defValuesId.ToString()}
      };
OpenTaskEditPage.OpenerInstanceId = InstanceUId;
OpenTaskEditPage.UseCurrentActivePage = true;
OpenTaskEditPage.PageUId = new Guid("F2BF397B-8FA3-48BA-B691-57360871967A");
OpenTaskEditPage.PageParameters = parameters ;
OpenTaskEditPage.CloseMessage = "IncidentEditPageClosed";

После действия БП открытие окна добавить ожидание сигнала IncidentEditPageClosed

В скрипте после сигнала добавьте следующий программный код:


var entitySchemaManager = Page.Schema.SchemaManagerProvider.GetManager("EntitySchemaManager") as EntitySchemaManager;
var TargetScemaQuery = new EntitySchemaQuery(entitySchemaManager, "ServiceRequest");
var primaryColumn = TargetScemaQuery.AddColumn(TargetScemaQuery.RootSchema.GetPrimaryColumnName());
TargetScemaQuery.AddColumn("Number");
TargetScemaQuery.Filters.Add(TargetScemaQuery.CreateFilterWithParameters(
        FilterComparisonType.Equal,
        "Id",
        IncidentId));
var incidentCollection = TargetScemaQuery.GetEntityCollection(UserConnection);
if(incidentCollection.Count != 0){
        Page.IncidentEdit.SuspendAjaxEvents();
        Page.IncidentEdit.SetValueAndText(incidentCollection[0].GetTypedColumnValueGuid>(primaryColumn.Name), incidentCollection[0].GetTypedColumnValuestring>("Number"));
        Page.IncidentEdit.ResumeAjaxEvents();
}

2. Затем добавьте действие бизнес-процесса, и поместите его на тот же событийный подпроцесс, на котором уже лежат стартовое событие и скрипт. При необходимости событийный подпроцесс можно растянуть. Это важно для корректной работы процесса.

Проведите стрелку из скрипта в действие.

3. Переключитесь в расширенный режим и укажите в настройках действия бизнес-процесса следующее:

4. Сохраните и опубликуйте схему.

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

Нравится

Поделиться

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

спасибо!

Добрый день.

Помогите реализовать следующее:
1. Нужно сделать в Активности кнопку "Таймер" у нового поля "Счетчик времени"
2. При нажатии на эту кнопку нужно, чтобы включится счетчик времени.
3. При отжатии этой кнопки, счетчик времени останавливался.
4. При повторном нажатии на эту кнопку и при условии, что состояниеактивности не конечное (т.е. не Завершена и не Отменена), счетчик продолжал отсчитывать время.

Заранее спасибо

Татьяна, для версии 5.Х встроенной функциональности для создания таймера нет, еобходимо сложное программирование с использованием сторонних библиотек либо веб-сервисов.Что касается версий 7.X и позднее, точно сказать не могу. Пожалуйста, обратитесь в техническую поддержку по адресу support@terrasoft.ru

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

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

Нравится

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

Можно поступить так: добавить в запрос инцидентов (sq_Incident) группу фильтров вида:

WHERE( ...
 
([tbl_Incident].[ConfigurationItemID] = :ConfigurationItemID OR
	EXISTS 
	(SELECT
		[tbl_ConfigItemComplectation].[ID] AS [ID]
	FROM
		[dbo].[tbl_ConfigItemComplectation] AS [tbl_ConfigItemComplectation]
	WHERE([tbl_ConfigItemComplectation].[ConfigurationItemID] = :ConfigurationItemID AND
		[tbl_Incident].[ConfigurationItemID] = [tbl_ConfigItemComplectation].[ChildConfigurationItemID])))
)

Далее создать в реестре инцидентов (wnd_IncidentsGridArea) новое представление, в свойстве FilterSetCode которого указать код этой группы фильтров.

Необходимо также учесть, что данное представление должно быть видимым только тогда, когда реестр инцидентов используется в детали раздела "Конфигурационные единицы" - для этого соответственным образом отредактировать обработчик события OnPrepare реестра. Также при активации представления необходимо отключать фильтр по ConfigurationItemID, который включается при обновлении детали. Для этого необходимо создать обработчик события OnActiveViewChanged объекта grdData в окне реестра инцидентов, и добавить в него примерно такой текст:

function grdDataOnActiveViewChanged(DataGrid, OldDataGridView) {
	scr_BaseGridArea.grdDataOnActiveViewChanged(DataGrid, OldDataGridView);
	var ActiveView = DataGrid.ActiveView;
	if (ActiveView.Name == <Название представления>) {
		EnableDatasetFilters(dlData.Dataset, false, 'ConfigurationItemID');
		RefreshDataset(dlData.Dataset);
	}
}

"Лабьяк Олег Игоревич" написал: для этого соответственным образом отредактировать обработчик события OnPrepare реестра.

Этот момент не очень понятен, что именно дописывать в обработчик onPrepare?

Если там ничего не дописывать, и запустить представление видимым всегда, то при переключении на него в детали Инциденты Конфигурационного элемента выскакивает отладчик скриптов и ругается на следующее:
function RefreshDataset(Dataset) {
CheckAssigned(Dataset, 'Dataset');
Dataset.Close();
Dataset.Open();
}

В обработчике OnPrepare реестра инцидентов (скрипт wnd_IncidentsGridAreaScript) вызывается функция Initialize, в которой есть блок:

if (!IsEmptyValue(ParentItemFieldName)) {
	...
}

Таким образом осуществляется проверка, является ли реестр инцидентов деталью. В конце этого блока можно дописать:

if (!IsEmptyValue(ParentItemFieldName)) {
	...
	<Название представления>.IsVisible = ParentItemFieldName == 'ConfigurationItemID';
}

"shymaza" написал:Если там ничего не дописывать, и запустить представление видимым всегда, то при переключении на него в детали Инциденты Конфигурационного элемента выскакивает отладчик скриптов и ругается на следующее

Проверьте с помощью профайлера, какой запрос отправляется на сервер. Возможно, Вы не совсем верно сформировали фильтр для представления.

Доброе утро, Олег, спасибо за подробные комментарии.
Я исправил немного фильтр, ошибки выходить перестали, но требуемой информации тоже нет. Инциденты вижу только свои, без инцидентов вложенных КЕ. Дело в том, что у нас база Оракл, и я не знаю есть ли там аналог профайлера.

Можете выложить следующие сервисы из Вашей конфигурации: sq_Incident, wnd_IncidentsGridArea, wnd_IncidentsGridAreaScript?

Прикрепил

Не срабатывает из-за некорректного условия в функции grdDataOnActiveViewChanged: Вы сравниваете название представления с его заголовком (Caption). Замените текст обработчика на такой:

function grdDataOnActiveViewChanged(DataGrid, OldDataGridView) {
        scr_BaseGridArea.grdDataOnActiveViewChanged(DataGrid, OldDataGridView);
        var ActiveView = DataGrid.ActiveView;
        if (ActiveView.Name == 'dgvCIwithChild') {
                EnableDatasetFilters(dlData.Dataset, false, 'ConfigurationItemID');
                //EnableDatasetFilters(dlData.Dataset, true, 'CIID'); 
                RefreshDataset(dlData.Dataset);
        }
}

Включение фильтра CIID не обязательно, так как он включится автоматически при переходе на нужное представление, поскольку Вы его указали в свойстве FilterSetCode представления. Поэтому здесь я его закомментировал.

Единственное, что можно ещё подправить - убрать строку

dgvCIwithChild.IsVisible = ParentItemFieldName == 'ConfigurationItemID';

из блока

if (!IsEmptyValue(ParentItemFieldName)) {
        ...
}

и вставить её перед этим блоком. Это обеспечит скрытие представления в реестре раздела "Инциденты".

Спасибо, все было бы замечательно, только теперь эта группа фильтров не выключается автоматом, при переходе на представление "Все". Т.е. когда я открываю деталь инциденты интересующей меня КЕ, в представлении "Все" я вижу только те инциденты, которые зарегистрированы непосредственно на эту КЕ, перехожу на представление "byCI", вижу как и хотел инциденты по КЕ и вложенным КЕ, возвращаюсь на представление "Все" а картинка как будто на "byCI". Добавил else в обработчик ChangeView не помогло, как выключить этот фильтр, когда он не нужен?

function grdDataOnActiveViewChanged(DataGrid, OldDataGridView) {
        scr_BaseGridArea.grdDataOnActiveViewChanged(DataGrid, OldDataGridView);
        var ActiveView = DataGrid.ActiveView;
        if (ActiveView.Name == 'dgvCIwithChild') {
                EnableDatasetFilters(dlData.Dataset, false, 'ConfigurationItemID');
              //  EnableDatasetFilters(dlData.Dataset, true, 'CIID'); 
                RefreshDataset(dlData.Dataset);
        }  else  {
                EnableDatasetFilters(dlData.Dataset, false, 'CIID'); 
                RefreshDataset(dlData.Dataset); 
                }
}

Думаю, фильтр выключается, но необходимо обратно включить фильтр по ConfigurationItemID. Попробуйте так:

function grdDataOnActiveViewChanged(DataGrid, OldDataGridView) {
        scr_BaseGridArea.grdDataOnActiveViewChanged(DataGrid, OldDataGridView);
        var ActiveView = DataGrid.ActiveView;
        if (ActiveView.Name == 'dgvCIwithChild') {
                EnableDatasetFilters(dlData.Dataset, false, 'ConfigurationItemID');
                RefreshDataset(dlData.Dataset);
        }  else  {
                EnableDatasetFilters(dlData.Dataset, true, 'ConfigurationItemID');
                RefreshDataset(dlData.Dataset); 
        }
}

Спасибо, Олег. Еще один вопрос, как можно добавить это условие

var ActiveView = DataGrid.ActiveView;
if (ActiveView.Name == 'dgvCIwithChild') {
                EnableDatasetFilters(dlData.Dataset, false, 'ConfigurationItemID');
                RefreshDataset(dlData.Dataset);
        }  else  {
                EnableDatasetFilters(dlData.Dataset, true, 'ConfigurationItemID');
                RefreshDataset(dlData.Dataset); 
        }

в обработчик onPrepare. Иначе при переходе между КЕ при выбранном представлении byCI фильтр не включается, поскольку не срабатывает обработчик onChangeView.
При простом копировании невозможно задать ActiveView, из-за другого контекста.

Нужно добавлять не в обработчик OnPrepare реестра инцидентов, а в функцию обновления деталей (RefreshDetails) скрипта wnd_ConfigurationItemWorkspaceScript. Для быстрого решения можно изменить блок, который относится к обновлению детали "Инциденты", примерно так:

	...
	if (pcDetails.ActivePage.Name == pgIncidentsDetail.Name) {
		RefreshCommonDetail(BaseWorkspace, wndIncidentsDetail, 
			'ConfigurationItemID', 'ConfigurationItemID');
		if (wndIncidentsDetail.Window.ComponentsByName('grdData').ActiveView.Name == 'dgvCIwithChild') {
			var DetailDataset = wndIncidentsDetail.Window.ComponentsByName('dlData').Dataset;
			EnableDatasetFilters(DetailDataset, false, 'ConfigurationItemID');
			RefreshDataset(DetailDataset);
		}		
	} else
	...

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

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

Добрый день!

При сохранении инцидента пользователю предлагается отправить письмо-уведомление.
Если пользователь не админ, тогда ему выдается сообщение с ошибкой Template.Query.Error
Права на шаблон у общей группы пользователей есть, настроены так же как на шаблон для задачи.
В чем может быть причина такой ошибки?

Нравится

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

"Kat" написал:Права на шаблон у общей группы пользователей есть, настроены так же как на шаблон для задачи.

А на запрос из раздела запросы, который тянет туда информацию, тоже доступ есть?

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

Нередко при работе с Terrasoft CRM возникает необходимость отправить своему сотруднику оповещение о задаче (или инциденте). В стандартном приложении Terrasoft CRM подобный функционал реализован при помощи предварительного создания шаблона e-mail по задаче.
Но не менее удобный функционал также может пригодиться: отправка оповещения о задаче во вложении к письму с возможностью написания своему коллеге личное сообщение в самом теле письма.
Постараюсь как можно более подробно описать алгоритм реализации подобного функционала.

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

1. Создать файл MSWord с расширением dot. Особенность этого файла - его структура - это закладки (BookMarks), которые используются для автоматической замены значений, которые можно вытянуть из выборки данных необходимых для формирования шаблона. В прикрепленном файле высылаю пример - это таблица, в которую будут заноситься данные из конкретной выборки.

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

 var WordRecord = OpenWordTemplate('C:\\TestReport.dot');

Пример создания выборки данных:

var Datasets = GetNewDictionary();
var Dataset = Services.GetNewItemByUSI('ds_Contact');
//здесь можно наложить необходимые фильтры
Dataset.Open();
AddDatasetToDictionaryByUSI(Datasets, 'Contacts', Dataset);
return Datasets;

Для заполнения документа-шаблона можно использовать функцию ReplaceWordBookmarks(WordInfo, Datasets). Как параметры ей передаются файл и созданный датасет. Только в этой функции созданный документ с заполненными значениями открывается, а его можно сохранить во временных документах на диске, а после прикрепления к письму удалять.

Пример непосредственного использования функции CreateEmptyMessageWithAttachments:

var Attachments = System.CreateObject('TSObjectLibrary.StringsList');
Attachments.Add(// необходимо указать полный путь к сохраненному документу);
CreateEmptyMessageWithAttachments(MailAddress, MailSubject, BodyFormat, CodePage, Importance, Attachments);
DeleteFilesFromStringsList(Attachments);

Также можно использовать не только сохраненный на диске файл, но и загружать его из библиотеки, используя базовую функцию поиска файла по коду FindFileInLibraryByCode(Code)

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

С уважением,
Мельникова Екатерина

Нравится

Поделиться

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

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

Нравится

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

Добрый день, Сергей!

Для реализации необходимой Вам функцональности можем Вам посоветовать добавить в таблицу задач поле "Сопутствующая задача" (или подобрать название соответственно содержимому).
Далее при создании задач необходимо проставить им «пару» (создаём задачу1, получаем её ID1, создаём задачу2, проставляя ей ID1 в поле «Сопутсвующая задача», получаем ID2 и доп. запросом проставляем его в задаче1). При закрытии задачи, нужно проверять есть ли «сопутсвующая задача». Eсли нет – ничего не делать, если есть: проверять статус «сопутсвующей задачи», если она закрыта – надо закрыть связанный инцидент (у задач при создании он должен быть указан).

Желаем удачи!

Terrasoft Support Team

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