Статья посвящена отдельным проблемам управления процессом.
Тезис: Управление процессом возможно при осуществлении связи контроля и коррекции.
Проблема: Технологичный контроль (и как следствие коррекция) в некоторых случаях затруднителен.
Решение:

  1. Перенос функций контроля на участников процесса (взаимный контроль)
  2. Нормализуется не задача, а результат, к которому она приводит.
  3. Мотивация привязвается к результатам работы

Регуляция процесса и задачи контроля

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

Проще всего контролировать сущности, которые сами по себе являются индикаторами:
• Итоговые результаты работы (объем продаж, трудозатраты),
• Показатели хода процесса (количество встреч, звонков)
• Факты наступления событий (поступление платежа, выполнение задачи).

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

Ограничение механизмов контроля.

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

Проблема уникальности задачи.

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

Проблема изменчивости критериев.

Во-вторых, некоторые критерии неоднозначны в своей динамике, иными словами, меняются в ходе выполнения задачи. Самый простой пример это критерий своевременности. Мы можем ввести регламентный срок выполнения, но в силу вышеописанных причин норматив, может быть, не применим. Дело даже не, столько в том, что могут измениться фактические трудозатраты, сколько в том, что своевременность, часто зависит от факторов, на которые мы повлиять не можем. У каждого продавца, наверное, были случаи, когда презентация коммерческого предложения планировалась через неделю, а клиент сообщил, что ее необходимо провести через три дня. Таким образом, своевременность определяется не нормативом или плановым сроком, а наступлением некоторого события.
Если взять пример с расчетом, то требования к его содержанию также, могут меняться в процессе подготовки (например, требования к уровню детализации задач).

Описанные затруднения, наглядно иллюстрируют причины, по которым многие компании не могут реализовать технологичность процесса. Отсутствие механизмов контроля и регуляции вынуждает менеджера вникать в детали процесса или даже отдельной задачи, оценивать качество решения и регулировать дальнейшую работу. Этому собственно и посвящены многие человеко-часы планерок и совещаний. Несмотря на то, что такие затраты уменьшают количество времени затрачиваемого на выполнение производственных задач, отказаться от них, в подобной ситуации, нельзя, поскольку возникает угроза сбоя всего процесса.
Самое удивительное в этой задаче то, что отдельные элементы решения успешно применяются в практике каждой компании. Вы, наверное, обратили внимание, что для одной из ролей в организации свойственны именно такие уникальные и изменчивые задачи. Да, это роль руководителя (менеджера). Поэтому предлагаю рассматривать работу сотрудников решающих подобные задачи как работу руководителей.

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

  1. Заинтересованность в результате
  2. Необъективность контроля

Проблема заинтересованности

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

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

В тоже время, сотрудники, ориентированные на высокий результат, как правило, ожидают и требуют, чтобы и их коллеги также разделили подобное стремление, что является мощным двигателем развития команды. В противном случае возможна ситуация, когда один сотрудник «болеет» за результат, но не встречает поддержки (а причины всегда есть) у своих коллег. Тогда кроме возникновения рисков процесса, существенно снижается мотивация этого сотрудника, что в конечном итоге не может не привести к снижению качества его работы и, как следствие, к упущенным возможностям.

Проблема контроля и ответственности

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

Для этого для каждого "узкого" шага бизнес процесса участники вырабатывают критерии взаимного контроля и после утверждения у руководителя используют их в практике (естественно количество контролируемых шагов ограничивается целесообразностью). Такой контроль обязательно должен быть двухсторонним, каждый оценивает работу друг друга по важным для себя критериям. Тот, кто принимает задачу в работу, оценивает постановщика задачи по следующим, например, критериям: полнота передаваемой информации, своевременность, точность постановки задачи. А тот, кто принимает результаты, в свою очередь, оценивает исполнителя, по своим критериям: уровень детализации, глубина проработки. Роль руководителя в контроле и визировании этих оценок.

Разумеется, что эти критерии представляют собой некие приближения. Объективность достигается за счет того, что одного сотрудника оценивает не один человек, а все кто с ним работает, использует результаты его труда. Кроме этого если раньше исполнитель был ответственен перед руководителем, то теперь он отвечает перед теми, кто кровно заинтересован в его результатах. Привязка значимой части бонусирования к такой оценке резко повышает качество взаимной работы.

Риски и решения:

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

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

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

Нравится

Поделиться

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

Саша!
Мне кажется, нужно больше структурировать материал. Может даже стоит разбить его на несколько глав.
Мне, например, твой текст трудно читать, а из-за этого возникают трудности с пониманием.
Я думаю, что ты согласишься, что хорошая подача материала - залог успеха. Поработай над формой:)

Согласен с Инной. Не хватает тезисов и разбивки статьи на части. Статья интересная, но "ниасилил".

Спасибо. Я пока только разбираюсь с тем как писать посты. То что вы видите - черновик. Обязательно подправлю.

Хочу сделать дополнение к теме индикаторов контроля.

Часто все KPI и критерии оценки разрабатываются "внутри" отдела. Мне кажется правильнее если внутри отдела будут формироваться только критерии эффективности (производительности), а критерии оценки качества будут разрабатываться исключительно "потребителями" работы отдела.

О каком уровне разработки критериев оценки идет речь, Александр? Ведь разработка потребителями критериев качества будет приводить к лоббированию интересов потребителя без понимания процессов рамок, сроков, ресурсов исполнителя. Или ты имел ввиду что-то другое?

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

Потребитель дает исполнителю цель его работы и критерии оценки качества. Исполнитель строит свои процессы исходя из этих требований и разрабатывает критерии эффективности (но не качества).

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

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

Добрый день!
Есть бизнес-процесс, который стартует в разделе "Проекты" по кнопке "Запустить процесс" следующим образом:

var Dataset = dlData.Dataset;
var ParamNames = new Array();
ParamNames[0] = 'OpportunityID';
var ParamValues = new Array();
ParamValues[0] = Dataset.Values('ID');
WFStartByID("{96A6BC55-B2F9-499C-A2E0-66FD2DF13B32}", ParamNames, ParamValues);

Запускается задача ( 1 шаг бизнес-процесса). Необходимо, чтобы в эту задачу ( только в эту) в качестве даты начала и даты завершения брались значения из специальных полей в проекте.
Подскажите, пожалуйста как можно это сделать ( пробовали передавать эти значения аналогично ParamNames[0] = 'OpportunityID' - не получилось)
Версия Террасофта 3.0.4.118 Х25

Нравится

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

Дарья, попробуйте передавать эти значения в параметры, как и OpportunityID (например, с названиями StartDate и DueDate), а при обработке события OnExecute элемента wa_TaskAction проверять название элемента и в зависимости от него устанавливать даты начала и завершения задач:

function wa_TaskActionOnExecute(WorkflowAction, ActionItem, ItemDataset,
		IsComplete, ResultLinkCodes) {
	.........
	var DefaultValues = GetNewDictionary();
	DefaultValues.Add('Title', ActionItem.Caption);
	DefaultValues.Add('WorkflowItemID', ItemID);
	if (ActionItem.Name == '<Название задачи - первого элемента БП>') {
		var Diagram = GetDiagramByItem(ActionItem);
		var ExecuteDate = WFGetParamValue(Diagram, 'StartDate');
		var DueDate = WFGetParamValue(Diagram, 'DueDate');
		DefaultValues.Add('StartDate', ExecuteDate);
		DefaultValues.Add('DueDate', DueDate);
	} else {
		var ExecuteDate = ItemDataset.ValAsDateTime('ExecuteDate');
		var ExecuteAfterTimeTypeID = 
			WFGetParamsMapItemValue(ActionItem, 'ExecuteAfterTimeTypeID');
		var ExecuteAfterTimeValue = 
			parseInt(WFGetParamsMapItemValue(ActionItem, 'ExecuteAfterTimeValue'));
		ExecuteDate = WFCalcExecuteTime(ExecuteDate, ExecuteAfterTimeTypeID, 
			ExecuteAfterTimeValue);
		ExecuteDate = ExecuteDate.getVarDate();
		DefaultValues.Add('StartDate', ExecuteDate);
		var ExecuteTimeTypeID = 
			WFGetParamsMapItemValue(ActionItem, 'ExecuteTimeTypeID');
		var ExecuteTimeValue = 
			parseInt(WFGetParamsMapItemValue(ActionItem, 'ExecuteTimeValue'));
		var DueDate = WFCalcExecuteTime(ExecuteDate, ExecuteTimeTypeID, 
			ExecuteTimeValue);
		DefaultValues.Add('DueDate', DueDate.getVarDate());
	}
	..........
}

Спасибо.
Скажите, пожалуйста, надо ли подключать дополнительные скрипты?
Сейчас возникает ошибка
на строчке var Diagram = GetDiagramByItem(ActionItem);
"Предполагается наличие объекта"

Должен быть подключен скрипт scr_WorkflowUtils.

Возможно, в версии 3.0.4 ещё не было этой функции. В этом случае Вы можете её добавить самостоятельно в скрипт scr_WorkflowUtils. Её текст такой:

function GetDiagramByItem(DiagramItem) {
	return DiagramItem.ParentItems.ParentDiagram;
}

Либо вместо вызова var Diagram = GetDiagramByItem(ActionItem); вставьте такую строчку:

var Diagram = ActionItem.ParentItems.ParentDiagram;
Показать все комментарии

Добрый день.

Я создала новое поле в сервисе sq_Task 'Колонка с типом SQL'. Там написала запрос:

case when isnull((SELECT acc.MyColumn FROM tbl_Account acc
 WHERE tbl_Task.AccountID = acc.ID), '') = '' then ''
else
isnull((SELECT StringValue FROM tbl_SystemSetting WHERE Code = 'MyCode'), '')
 + isnull((SELECT cast(acc.MyColumn AS nvarchar(max)) FROM tbl_Account acc
 WHERE tbl_Task.AccountID = acc.ID ), '')
 end

в датасете добавила поле типа справочник и указала там эту колонку, оставив поле "источник" пустым.
Теперь при открытии карточки задачи пользователем бз админских прав у меня вылетает ошибка
"The Select permission was denied on the object 'tbl_Account', database 'CRM', achema 'dbo'"

Что-то с правами не так?

Нравится

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

Kat, да проблема с правами. Если говорить, не вдаваясь сильно глубоко, то Вы должны заменять названия таблиц tbl_Account, tbl_Task на vw_Account, vw_Task если конфигурация работает под пользователем, а не администратором.

Александр, спасибо!
поменяла в своем запросе tbl_Account на vw_Account

Kat, Вы решили проблему частично :)
Под пользователем с правами администратора могут быть нюансы, поэтому Вам необходимо добавить логику перед открытием DataSet - заменять или нет таблицы на вью в БД.

Все работает и под админом корректно сейчас.
На будущее: нужно прямо текст запроса менять перед открытием? Или есть способы попроще?

"Kat" написал:Все работает и под админом корректно сейчас.

Это Вам кажется :)
Во вью стоит фильтр, который отбирает записи на которые пользователь имеет доступ. Администратор должен "видеть", все записи, но если на какие-то записи нет доступа или есть, но группам, куда администратор не входит, то Администратор не "увидит" некоторые записи, что противоречит абсолютным правам.

"Kat" написал:На будущее: нужно прямо текст запроса менять перед открытием? Или есть способы попроще?

По как что способа проще нет. Да и куда уж проще:)

Есть другая проблема. При открытии карточки (только при создании записи не админом), которая на датасет ссылается, где я запрос написала, вылетает ошибка
"Ошибка выполнения метода 'wnd_MyWindowEditOnPrepare'. OLE error 80020102"
Когда отключаю поле с запросом в сервисе, тогда ошибки нет.

Kat, Вы не проверяли с помощью профайлера, какой запрос посылается на сервер при открытии карточки пользователем? Проверьте, пожалуйста, и попробуйте выполнить этот запрос в SQL Management Studio (или Query Analyzer).

Да, проверила. запроса вообще нет при открытии карточки.
При этом заметила следующее: если зайти в раздел и сразу нажать Добавить, тогда ошибки нет. Затем, если открою и закрою какую-нибудь задачу, а после этого пытаюсь добавить новую, тогда тоже ошибки нет.

Так сообщение возникает при открытии карточки, или при попытке сохранения? Отладчик запускается, или просто возникает окно лога?

Отладчик появляется и потом лог. Это при попытке открыть карточку новой задачи.

Можете сказать, в каком скрипте и каком месте останавливается отладчик?

Да.вот:

function ProcessBaseDBEditOnPrepare(Window, BaseDBEdit){
	try {
		var Datalink = Window.ComponentsByName('dlData');
		InitializeDBEdit(Datalink, BaseDBEdit, Window);
		if (BaseDBEdit.RecordID == GUID_NULL) {
		    if (BaseDBEdit.IsCopy){
		        CopyRecord(BaseDBEdit, Window);
			} else {
			    AppendRecord(BaseDBEdit, Window);
			}
		} else {
			if (!EditRecord(BaseDBEdit, Window)) {
				return;
			}
		}
		var btnOK = Window.ComponentsByName('btnOK');
		EnableOKButtonByRights(btnOK, BaseDBEdit);
	} finally {/// дебагер кидает сюда
		Window.Attributes('WindowWasPrepared') = true;
	}
}

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

function EditRecord(BaseDBEdit, Window) {
	var Dataset = BaseDBEdit.Dataset;
	OpenDatasetWithRecordID(Dataset, BaseDBEdit.RecordID);
	if (IsDatasetEmpty(Dataset)) {
	    var Message = "Запись удалена";
	    if (!Connector.CurrentUser.IsAdmin) {
	        Message += ' ' + "или" + ' ' +
				"текущий пользователь не имеет достаточно прав для чтения записи";
		}
		ShowWarningDialog(Message);
		var btnOK = Window.ComponentsByName('btnOK');
		EnableControl(btnOK, false);
		return false;//проваливаемся до сюда
	}	
	Dataset.Edit();
	SetEditWindowCaption(BaseDBEdit, Window);
	return true;
}

Попадаем сюда, потому что BaseDBEdit.RecordID не нулевой, хотя задачу только создаем.

Kat, попробуйте добавить строчку debugger; сразу после try {. Потом перезапустите Terrasoft и добавьте новую запись. После того, как отладчик остановится на строке c debugger, пройдитесь дальше в ручном режиме и определите, где на самом деле происходит сбой.

Проваливаюсь в:

function AppendRecord(BaseDBEdit, Window) {
	var Dataset = BaseDBEdit.Dataset;
	var Attributes = Window.Attributes;
	var DoDisableEvents = !Attributes('DoNotDisableEvents');
	BaseDBEdit.RecordID = Connector.GenGUID();
	Dataset.DisableGettingDisplayValues();
	try {
	Dataset.Append();
	if (DoDisableEvents) {
		Dataset.DisableEvents();
	}
	try {
		Dataset.ValAsGUID('ID') = BaseDBEdit.RecordID;
		Window.Attributes('RecordID') = BaseDBEdit.RecordID;
		if ((!IsEmptyValue(BaseDBEdit.ParentItemID)) &&
			(!IsEmptyValue(BaseDBEdit.ParentItemFieldName))) {
			var DataField = Dataset.DataFields(BaseDBEdit.ParentItemFieldName);
			if (Assigned(DataField)) {
				DataField.Value = BaseDBEdit.ParentItemID;
			}
		}
		SetDefaultValues(BaseDBEdit);
	} finally {
		if (DoDisableEvents) {
			Dataset.EnableEvents();
		}
	}
	} finally {
		Dataset.EnableGettingDisplayValues();//падает здесь
	}
	SetEditWindowCaption(BaseDBEdit, Window);
}

Думаю, дело в

"Kat" написал:в датасете добавила поле типа справочник и указала там эту колонку, оставив поле "источник" пустым.

Ядро пытается определить колонку для отображения для этого поля, но у него ничего не получается, так как датасет не указан.

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

гениально) спасибо огромное за потраченное время!!!

Добрый день у меня та же ошибка. В SelectQuery есть колонка подзапроса, в которой есть колонка с текстом SQL (если ее отключить все работает). Ошибка вылетает только при открытии существующих записей и только если пользователь не администратор. Принажатии на кнопку "добавить" все работает.Вот скриншот. Как это можно обойти.

Михаил, думаю, Вы можете (поскольку всё-равно используете CustomSQL-колонку) внести все фильтры и используемые таблицы внутрь SQL-текста, не используя подзапрос (то есть, использовать CustomSQL-колонку непосредственно в основном запросе). А дальше - как уже было описано ранее: либо

"Kat" написал:поменяла в своем запросе

все tbl_ на vw_, либо

"Осауленко Александр" написал:Вам необходимо добавить логику перед открытием DataSet - заменять или нет таблицы на вью в БД

Лучше, конечно, второй вариант.

Спасибо за совет. Так и сделаю!

Я сделал, как Вы говорили, но под не-админом все равно эта колонка не работает (та же ошибка). На событии BeforeOpen заменяю значение свойства колонки SQLText из

(SELECT	SUM([Invoice].[Amount]) - SUM([Invoice].[PaymentAmount]) AS [DebtorAmount]
	FROM
		[dbo].[tbl_IssueInApplication] AS [IssueInApplication]
	LEFT OUTER JOIN
		[dbo].[tbl_Issue] AS [Issue] ON [Issue].[ID] = [IssueInApplication].[IssueID]
	LEFT OUTER JOIN
		[dbo].[tbl_OfferingInApplication] AS [OfferingInApplication] ON [OfferingInApplication].[ID] = [IssueInApplication].[OfferingInApplicationID]
	LEFT OUTER JOIN
		[dbo].[tbl_Application] AS [Application] ON [Application].[ID] = [OfferingInApplication].[ApplicationID]
	LEFT OUTER JOIN
		[dbo].[tbl_Invoice] AS [Invoice] ON [Invoice].[ApplicationID] = [Application].[ID]
	WHERE([IssueInApplication].[IsPlaced] = :IsPlaced AND
		[Application].[CustomerID] = [tbl_Account].[ID] AND
		[Issue].[StatusID] = :IssueIsClosed AND
		([Invoice].[BillStatusID] = :InvoiceWaiting OR
		[Invoice].[BillStatusID] = :InvoicePartPayed)))

на

(SELECT	SUM([Invoice].[Amount]) - SUM([Invoice].[PaymentAmount]) AS [DebtorAmount]
	FROM
		[dbo].[tbl_IssueInApplication] AS [IssueInApplication]
	LEFT OUTER JOIN
		[dbo].[vw_Issue] AS [Issue] ON [Issue].[ID] = [IssueInApplication].[IssueID]
	LEFT OUTER JOIN
		[dbo].[tbl_OfferingInApplication] AS [OfferingInApplication] ON [OfferingInApplication].[ID] = [IssueInApplication].[OfferingInApplicationID]
	LEFT OUTER JOIN
		[dbo].[vw_Application] AS [Application] ON [Application].[ID] = [OfferingInApplication].[ApplicationID]
	LEFT OUTER JOIN
		[dbo].[vw_Invoice] AS [Invoice] ON [Invoice].[ApplicationID] = [Application].[ID]
	WHERE([IssueInApplication].[IsPlaced] = :IsPlaced AND
		[Application].[CustomerID] = [tbl_Account].[ID] AND
		[Issue].[StatusID] = :IssueIsClosed AND
		([Invoice].[BillStatusID] = :InvoiceWaiting OR
		[Invoice].[BillStatusID] = :InvoicePartPayed)))

в строке

[Application].[CustomerID] = [tbl_Account].[ID] AND

[tbl_Account].[ID] берется из основного селекта.

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

Михаил, у Вас в запросе используются как минимум две таблицы (tbl_IssueInApplication и tbl_OfferingInApplication). Они администрируются по записям? Вы смотрели профайлером, какой запрос посылается на сервер при открытии датасета? Предоставьте, пожалуйста, текст запроса и полный текст сообщения об ошибке.

По первой ошибке (открытие карточки редактирования контрагента не админом):
Текст запроса с профайлера:

exec sp_executesql N'SELECT
	[tbl_Account].[ID] AS [ID],
	[tbl_Account].[Name] AS [Name],
	[tbl_Account].[OfficialAccountName] AS [OfficialAccountName],
	[tbl_Account].[AnnualRevenue] AS [AnnualRevenue],
	[tbl_Account].[EmployeesNumber] AS [EmployeesNumber],
	[tbl_Account].[Address] AS [Address],
	[tbl_Account].[AddressTypeID] AS [AddressTypeID],
	[tbl_Account].[Communication1] AS [Communication1],
	[tbl_Account].[Communication1TypeID] AS [Communication1TypeID],
	[tbl_Account].[Communication2] AS [Communication2],
	[tbl_Account].[Communication2TypeID] AS [Communication2TypeID],
	[tbl_Account].[Communication3] AS [Communication3],
	[tbl_Account].[Communication3TypeID] AS [Communication3TypeID],
	[tbl_Account].[Communication4] AS [Communication4],
	[tbl_Account].[Communication4TypeID] AS [Communication4TypeID],
	[tbl_Account].[Communication5] AS [Communication5],
	[tbl_Account].[Communication5TypeID] AS [Communication5TypeID],
	[tbl_City].[Name] AS [CityName],
	[tbl_Account].[CityID] AS [CityID],
	[tbl_Account].[ZIP] AS [ZIP],
	[tbl_Contact].[Name] AS [PrimaryContactName],
	[tbl_Account].[PrimaryContactID] AS [PrimaryContactID],
	[tbl_Country].[Name] AS [CountryName],
	[tbl_Account].[CountryID] AS [CountryID],
	[tbl_State].[Name] AS [StateName],
	[tbl_Account].[StateID] AS [StateID],
	[tbl_Territory].[Name] AS [TerritoryName],
	[tbl_Account].[TerritoryID] AS [TerritoryID],
	[Owner].[Name] AS [OwnerName],
	[tbl_Account].[OwnerID] AS [OwnerID],
	[tbl_Account].[ActivityID] AS [ActivityID],
	[tbl_Activity].[Name] AS [ActivityName],
	[tbl_Account].[FieldID] AS [FieldID],
	[tbl_Field].[Name] AS [FieldName],
	[tbl_Account].[AccountTypeID] AS [AccountTypeID],
	[tbl_AccountType].[Name] AS [AccountTypeName],
	[tbl_AddressType].[Name] AS [AddressTypeName],
	[CommunicationType1].[Name] AS [Communication1TypeName],
	[CommunicationType2].[Name] AS [Communication2TypeName],
	[CommunicationType3].[Name] AS [Communication3TypeName],
	[CommunicationType4].[Name] AS [Communication4TypeName],
	[CommunicationType5].[Name] AS [Communication5TypeName],
	[tbl_Account].[Code] AS [Code],
	[tbl_Account].[TaxRegistrationCode] AS [TaxRegistrationCode],
	[tbl_Account].[CreatedOn] AS [CreatedOn],
	[tbl_Account].[CreatedByID] AS [CreatedByID],
	[CreatedBy].[Name] AS [CreatedByName],
	[tbl_Account].[ModifiedOn] AS [ModifiedOn],
	[tbl_Account].[ModifiedByID] AS [ModifiedByID],
	[ModifiedBy].[Name] AS [ModifiedByName],
	[tbl_Job].[NameOf] AS [JobNameOf],
	(SELECT SUM([Invoice].[Amount]) - SUM([Invoice].[PaymentAmount]) AS [DebtorAmount]	FROM [dbo].[tbl_IssueInApplication] AS [IssueInApplication]	LEFT OUTER JOIN [dbo].[vw_Issue] AS [Issue] ON [Issue].[ID] = [IssueInApplication].[IssueID] LEFT OUTER JOIN [dbo].[tbl_OfferingInApplication] AS [OfferingInApplication] ON [OfferingInApplication].[ID] = [IssueInApplication].[OfferingInApplicationID] LEFT OUTER JOIN [dbo].[vw_Application] AS [Application] ON [Application].[ID] = [OfferingInApplication].[ApplicationID] LEFT OUTER JOIN [dbo].[vw_Invoice] AS [Invoice] ON [Invoice].[ApplicationID] = [Application].[ID] WHERE([IssueInApplication].[IsPlaced] = @P1 AND [Application].[CustomerID] = [tbl_Account].[ID] AND [Issue].[StatusID] = @P2 AND ([Invoice].[BillStatusID] = @P3 OR [Invoice].[BillStatusID] = @P4))) AS [DebtorAmount]
FROM
	[dbo].[vw_Account] AS [tbl_Account]
LEFT OUTER JOIN
	[dbo].[vw_Contact] AS [tbl_Contact] ON [tbl_Contact].[ID] = [tbl_Account].[PrimaryContactID]
LEFT OUTER JOIN
	[dbo].[tbl_Territory] AS [tbl_Territory] ON [tbl_Territory].[ID] = [tbl_Account].[TerritoryID]
LEFT OUTER JOIN
	[dbo].[vw_Contact] AS [Owner] ON [Owner].[ID] = [tbl_Account].[OwnerID]
LEFT OUTER JOIN
	[dbo].[tbl_City] AS [tbl_City] ON [tbl_City].[ID] = [tbl_Account].[CityID]
LEFT OUTER JOIN
	[dbo].[tbl_State] AS [tbl_State] ON [tbl_State].[ID] = [tbl_Account].[StateID]
LEFT OUTER JOIN
	[dbo].[tbl_Country] AS [tbl_Country] ON [tbl_Country].[ID] = [tbl_Account].[CountryID]
LEFT OUTER JOIN
	[dbo].[tbl_Activity] AS [tbl_Activity] ON [tbl_Activity].[ID] = [tbl_Account].[ActivityID]
LEFT OUTER JOIN
	[dbo].[tbl_Field] AS [tbl_Field] ON [tbl_Field].[ID] = [tbl_Account].[FieldID]
LEFT OUTER JOIN
	[dbo].[tbl_AccountType] AS [tbl_AccountType] ON [tbl_AccountType].[ID] = [tbl_Account].[AccountTypeID]
LEFT OUTER JOIN
	[dbo].[vw_Contact] AS [CreatedBy] ON [CreatedBy].[ID] = [tbl_Account].[CreatedByID]
LEFT OUTER JOIN
	[dbo].[vw_Contact] AS [ModifiedBy] ON [ModifiedBy].[ID] = [tbl_Account].[ModifiedByID]
LEFT OUTER JOIN
	[dbo].[tbl_AddressType] AS [tbl_AddressType] ON [tbl_AddressType].[ID] = [tbl_Account].[AddressTypeID]
LEFT OUTER JOIN
	[dbo].[tbl_CommunicationType] AS [CommunicationType1] ON [CommunicationType1].[ID] = [tbl_Account].[Communication1TypeID]
LEFT OUTER JOIN
	[dbo].[tbl_CommunicationType] AS [CommunicationType2] ON [CommunicationType2].[ID] = [tbl_Account].[Communication2TypeID]
LEFT OUTER JOIN
	[dbo].[tbl_CommunicationType] AS [CommunicationType3] ON [CommunicationType3].[ID] = [tbl_Account].[Communication3TypeID]
LEFT OUTER JOIN
	[dbo].[tbl_CommunicationType] AS [CommunicationType4] ON [CommunicationType4].[ID] = [tbl_Account].[Communication4TypeID]
LEFT OUTER JOIN
	[dbo].[tbl_CommunicationType] AS [CommunicationType5] ON [CommunicationType5].[ID] = [tbl_Account].[Communication5TypeID]
LEFT OUTER JOIN
	[dbo].[tbl_Job] AS [tbl_Job] ON [tbl_Job].[ID] = [tbl_Contact].[JobID]
WHERE([tbl_Account].[ID] = @P5)',N'@P1 int,@P2 varchar(8000),@P3 varchar(8000),@P4 varchar(8000),@P5 varchar(8000)',1,'{E78E618C-956B-4D7E-9F6B-53955E31ADB5}','{89E2E62C-E2B0-47E6-A183-B774ED20CDE8}','{E64C1967-77EE-46DD-97D5-C0101067F6FA}','{A0EAB21C-2BB6-4B50-B4D0-90091A091736}'

Таблицы tbl_IssueInApplication и tbl_OfferingInApplication не администрируются по записям.
Скриншот ошибки в файле "Ошибка 1".

Другая ошибка - при открытии окна отображения связанных записей.Воспроизводится не только в контрагентах, а и в других разделах. Я сначала подумал, что это из за 3 пробелов в названии базы данных, но сделал копию без пробелов и ошибка не пропала(. Скриншот в файле "Ошибка 2".

Михаил, а сам запрос средствами SQL выполняется нормально? Посмотрите, пожалуйста, какое значение содержится в CustomSQL-колонке. Может, не совпадают типы контрола окна и возвращаемого значения?

Уже все нормально :smile:. Клиент просто в правах доступа к полям таблиц поставил уровень "Запрет" на поля, которые вытягивались в CustomSQL. Спасибо за помощь.
А ошибка при удалении связанных записей куда то сама исчезла :smile:

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

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

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

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

Версия 3.2.0.45.

Нравится

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

На сервере, в триггере.

Спасибо. Это даже лучше и проще :)

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

Нередко для пользователей было бы удобно отобразить в поле карточки задачи наименование процесса, по которому эта задача формируется.
Для реализации подобного функционала необходимо выполнить следующие действия:
1. Добавить новое поле для отображения процесса в следующие сервисы
 - tbl_Task
 - sq_Task
 - ds_Task
 - wnd_TaskEdit
2. Затем перейти в скрипт действия бизнес-процесса задач wa_TaskActionScript и найти обработчик события ws_TaskActionOnExecute. В этом обработчике события Вы увидите реализацию заполнения строк по умолчанию. Эти строки начинаются с DefaultValues.Add....
В этой части скрипта нужно добавить еще одну строку типа:

DefaultValues.Add('WorkflowName', ActionItem.ParentItems.ParentDiagram.Caption);

3. Сохраните внесенные изменения. Перезапустите рабочее приложение Terrasoft CRM и протестируйте работоспособность функционала.

Желаю удачи!

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

Нравится

Поделиться

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

а можно просто в sq_Task вытянуть из таблицы tbl_Workflow, которая присутствует уже в запросе, поле Name и не нужно добавлять новое поле в таблицу tbl_Task

Может таки лучше вытаскивать название сервиса, по которому стартовали БП, во время выполнения запроса, а не хранить его в задаче?
Upd: За Раловец Ольгой не поспеешь :) Но ее вариант возможно даже лучше.

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

"Раловец Ольга" написал:есть отличие

Ну я как-бы догадался :)
Я имел ввиду, что Вы предложили вытаскивать из tbl_Workflow, а я из tbl_Service. И, возможно, Ваш вариант лучше.

я просто уточнила на всякий случай )

"Раловец Ольга" написал:можно просто в sq_Task вытянуть из таблицы tbl_Workflow

Это будет работать только для уже созданных задач БП. При первом открытии карточки (когда кнопка "Отмена" выключена) в этом поле будет пусто. Ведь запись в таблице фактически ещё не создана, не к чему делать join. В тот момент только из wa_TaskActionScript известно точно, что за процесс запустил эту задачу.

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

Нередко при работе с 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 комментариев
Показать все комментарии

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

var DataFields = Dataset.DataFields;
var StartDateDataField = DataFields.ItemsByName('StartDate');
var DueDateDataField = DataFields.ItemsByName('DueDate');
var OwnerIDDataField = DataFields.ItemsByName('OwnerID');
Dataset.Locate('ID', Event.ID);
var PriorStartDate = StartDateDataField.ValAsDateTime;
var PriorDueDate = DueDateDataField.ValAsDateTime;
if (!CheckTaskCovering(Dataset, OwnerIDDataField, Event.Start,
Event.Finish)) {
CancelChangeEvent(Event, PriorStartDate, PriorDueDate);
return;
}
Dataset.Edit();
strong>var RemindingToOwnerID = Dataset.Values('RemindingToOwnerID');
if (!IsEmptyValue(RemindingToOwnerID)) {
var uq_Remindings = GetSingleItemByCode('uq_Remindings', 'uq_RemindSingle');
var Parameters = uq_Remindings.Parameters;
var ColumnsValues = uq_Remindings.ColumnsValues;
SetParameterValue(Parameters, 'ID', RemindingToOwnerID);
ColumnsValues.ItemsByName('RemindTime').Value = Event.Start;
uq_Remindings.Execute();
}strong>
//Dataset.ValAsStr('Title') = Event.Caption;
StartDateDataField.ValAsDateTime = Event.Start;
DueDateDataField.ValAsDateTime = Event.Finish;
UpdateDuration(Dataset);
Dataset.Post();
UpdateEventColorsByDatasetRecord(Event, Dataset);

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

Желаю удачи!

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

Нравится

Поделиться

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

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

"Мамажанов Марат Кадралдыкович" написал:но ничего не получилось, выходят ошибки

Опишите, пожалуйста, ошибки которые у Вас возникают.
Заметил что в коде остались лишние теги (не удалось выделить текст), должно быть так:

var DataFields = Dataset.DataFields;
var StartDateDataField = DataFields.ItemsByName('StartDate');
var DueDateDataField = DataFields.ItemsByName('DueDate');
var OwnerIDDataField = DataFields.ItemsByName('OwnerID');
Dataset.Locate('ID', Event.ID);
var PriorStartDate = StartDateDataField.ValAsDateTime;
var PriorDueDate = DueDateDataField.ValAsDateTime;
if (!CheckTaskCovering(Dataset,OwnerIDDataField,Event.Start,Event.Finish)) {
    CancelChangeEvent(Event, PriorStartDate, PriorDueDate);
    return;
}
Dataset.Edit();
var RemindingToOwnerID = Dataset.Values('RemindingToOwnerID');
if (!IsEmptyValue(RemindingToOwnerID)) {
    var uq_Remindings = GetSingleItemByCode('uq_Remindings', 'uq_RemindSingle');
    var Parameters = uq_Remindings.Parameters;
    var ColumnsValues = uq_Remindings.ColumnsValues;
    SetParameterValue(Parameters, 'ID', RemindingToOwnerID);
    ColumnsValues.ItemsByName('RemindTime').Value = Event.Start;
    uq_Remindings.Execute();
}
StartDateDataField.ValAsDateTime = Event.Start;
DueDateDataField.ValAsDateTime = Event.Finish;
UpdateDuration(Dataset);
Dataset.Post();
UpdateEventColorsByDatasetRecord(Event, Dataset);

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

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

ColumnsValues.ItemsByName('RemindTime').Value = Event.Start;

на следующий:
- найти разницу между StartDateDataField.ValAsDateTime и Event.Start;
- найти запись из таблицы tbl_Remanding, значение поля RemindTime;
- добавить к нему разницу м/д StartDateDataField.ValAsDateTime и Event.Start;
- обновить запись в таблице tbl_Remanding.

Здравствуйте!
Подскажите пожалуйста как фильтровать, у меня есть 2 датасета (задачи и напоминания) и мне нужно, по мере изменения расписания задачи изменять и время напоминания, так чтобы время напоминания изменялось на столько же насколько изменяется расписание,
я было написал определенный скрипт, но когда я пытаюсь записать данные в таблицу напоминаний, у меня выходит ошибка, что поле "описание" не заполнено, и значение "нуль" в нем, не допустимо, вот пример:

function UpdateDatasetRecordByEvent(Dataset, Event) {

var DataFields = Dataset.DataFields;
var StartDateDataField = DataFields.ItemsByName('StartDate');
var DueDateDataField = DataFields.ItemsByName('DueDate');
var OwnerIDDataField = DataFields.ItemsByName('OwnerID');
Dataset.Locate('ID', Event.ID);
var PriorStartDate = StartDateDataField.ValAsDateTime;
var PriorDueDate = DueDateDataField.ValAsDateTime;
if (!CheckTaskCovering(Dataset,OwnerIDDataField,Event.Start,Event.Finish)) {
CancelChangeEvent(Event, PriorStartDate, PriorDueDate);
return;
}

Dataset.Edit();
var RemindingToOwnerID = Dataset.Values('RemindingToOwnerID');
var uq_Remindings = GetSingleItemByCode('uq_Remindings', 'uq_RemindSingle');
var ds_Remindings = dl_remLNK.dataset;
var Parameters = uq_Remindings.Parameters;
var ColumnsValues = uq_Remindings.ColumnsValues;
var ds_rem = ds_Remindings.DataFields.ItemsByName('RemindTime');
SetParameterValue(Parameters, 'ID', RemindingToOwnerID);
Event.Start;
uq_Remindings.Execute();

ds_Remindings.Close();
ApplyDatasetFilter(ds_Remindings, ID',RemindingToOwnerID, true);
ds_Remindings.Open();
ds_Remindings.edit();
ds_rem.ValAsDateTime = Event.Start;
ds_Remindings.post();

StartDateDataField.ValAsDateTime = Event.Start;
DueDateDataField.ValAsDateTime = Event.Finish;
UpdateDuration(Dataset);
Dataset.Post();
UpdateEventColorsByDatasetRecord(Event, Dataset);
}
я хотел фильтровать ds_Remindings.id по полю RemindingToOwnerID из первого датасета (ds_Task.RemindingToOwnerID), чтобы изменть напоминание связанное с текущим ответственным.
буду рад любым ответам

чем отличаются ds_Reminding и ds_Remindings какова роль каждого?

"Мамажанов Марат Кадралдыкович" написал:чем отличаются ds_Reminding и ds_Remindings какова роль каждого?

ds_Remindings соответствует всплывающему окну напоминаний. Вы можете посмотреть в соответствующем запросе на выборку sq_Remindings связи с таблицами, по записям которых появляются напоминания в этом окне.
ds_Reminding соответствует закладке "Напоминания" менеджера деталей.

Что касается Вашего кода, то проблема в не совсем корректном обновлении записей в таблице напоминаний. Например, нигде не указаны параметры для UpdateQuery.
Для решения проблемы рекомендую сделать следующим образом.
Сначала отфильтровать датасет напоминаний по нужной записи:

ds_Remindings.Close();
ApplyDatasetIDFilter(ds_Remindings, RemindingToOwnerID, true);
ds_Remindings.Open();

Затем записать в переменную описание напоминания (т.к. оно обязательно должно быть в UpdateQuery):

var RemindingDescription = ds_Remindings.Description;
if (IsEmptyValue(RemindingDescription)) {
                RemindingDescription = ‘Напоминание по задаче ’ + Dataset.Values(‘Title’);
}

Затем инициализация колонок UpdateQuery:

ColumnsValues.ItemsByName(‘Description’).Value = RemindingDescription;
ColumnsValues.ItemsByName(‘RemindTime’).Value = Event.Start;

Все это можно вставить между строками:

var ds_rem = ds_Remindings.DataFields.ItemsByName('RemindTime'); и SetParameterValue(Parameters, 'ID', RemindingToOwnerID);

Что касается блока:

ds_Remindings.Close();
ApplyDatasetFilter(ds_Remindings, ID',RemindingToOwnerID, true);
ds_Remindings.Open();
ds_Remindings.edit();
ds_rem.ValAsDateTime = Event.Start;
ds_Remindings.post();

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

Желаю удачи!

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

Инструмент импорт в интеграции. Не понятен один момент при импорте из экселя. Хочу импортировать таблицу задач с напоминаниями ответственному о выполнении и вижу: все даты как даты, т.е. с типом дата, а напоминания почему-то сделаны строковым типом.
В результате задачи без даты напоминания отлично импортируются, а задачи с проставленным полем напоминания не импортируются вовсе.

Нравится

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

Здравствуйте, Андрей!

Андрей, дело в том, что данные поля - ссылки на таблицу tbl_Reminding. Для того, чтобы они корректно отображались при настройке импорта нужно в таблицу tbl_Task добавить две связи (relation):

1. tbl_Task.RemindingToAuthorID -> tbl_Reminding.ID
2. tbl_Task.RemindingToOwnerID -> tbl_Reminding.ID

В результате Вы сможете настраивать импорт на деталь [Напоминания].

Terrasoft Support Team

Спасибо!

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

Иногда при постановке задачи возникает необходимость добавить к этой задаче еще десяток пользователей системы. Добавление вручную каждого пользователя на закладку "Контакты" занимает значительную часть времени пользователя. Полезным функционалом было бы создание новой кнопки "Добавить группу контактов".
Предлагаю вариант решения:
1. Вам необходимо в грид детали "Контакты" (менеджера деталей раздела "Задачи") добавить новую кнопку. Назвать ее, например, "Добавить группу пользователей" и присвоить Caption "AddUsersGroup".
2. Далее Вам необходимо реализовать две функции, которые потом будут вызываться в обработчике события нажатия на созданную кнопку.
2.1. Одна из функций должна реализовывать появление окна выбора существующих групп пользователей. Необходимая функция будет выглядеть примерно следующим образом:

function AddUsersGroup() {
var Dataset = GetSingleItemByCode('ds_Group', 'ContactInTaskGridArea');
var SearchFieldNames = 'Name';
var DisplayFieldNames = 'Name';
var KeyFieldName = 'ID';
var KeyValue = '';
var SearchFieldName = 'Name';
var SearchValue = '';
var NotifyObject = Self;
var Tag = 'SelectUsersGroupWindow';
var IsReadOnly = true;
ShowSelectDataWindow(Dataset, SearchFieldNames, DisplayFieldNames,
KeyFieldName, KeyValue, SearchFieldName, SearchValue, NotifyObject,
Tag, IsReadOnly);
}

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

function ProcessAddUsersGroup(Sender) {
var GroupID = Sender.Attributes('KeyValue');
if (!GroupID) {
return;
}
var UserSelectQuery = GetSingleItemByCode('sq_User',
'ContactInTaskGridArea');
var ContactInTaskDataset = GetSingleItemByCode('ds_ContactInTask',
'ContactInTaskGridArea');
ApplySelectQueryFilter(UserSelectQuery, 'GroupID', GroupID, true);
var UserDataset = UserSelectQuery.Open();
var TaskID = Self.Attributes('ParentItemID');
while (!UserDataset.IsEOF) {
ContactInTaskDataset.Append();
ContactInTaskDataset.Values('ID') = Connector.GenGUID();
ContactInTaskDataset.Values('TaskID') = TaskID;
ContactInTaskDataset.Values('ContactID') =
UserDataset.Values('UserContactID');
ContactInTaskDataset.Post();
UserDataset.GotoNext();
}
ContactInTaskDataset.Close();
UserDataset.Close();
}

3. Далее нужно в обработчике события OnClick созданной кнопки прописать вызов процедуры AddUsersGroup (вызова окна выбора групп).
function amiAddUsersGroupOnExecute(ActionMenuItem) {
AddUsersGroup();
}

4. Затем в обработчике события OnNotify грида менеджера деталей "Контакты" раздела "Задачи", прописать вызов фукнции ProcessAddUsersInGroup с необходимыми условиями.

function wnd_ContactInTaskGridAreaOnNotify(ScriptableService, Sender, Message,
Data) {
if (Message == MSG_OK) {
if (Sender.Tag == 'SelectUsersGroupWindow') {
ProcessAddUsersGroup(Sender);
}
RefreshDataset(BaseGridArea.GridDataset);
} else {
scr_BaseGridArea.wnd_BaseGridAreaOnNotify(ScriptableService, Sender,
Message, Data);
}
}

В прикрепленном файле высылаю пример текста доработанного сервиса.

Желаю удачи!

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

Нравится

Поделиться

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

У многих пользователей системы Terrasoft CRM в разделе "Задачи" создано множество задач с различными типами. И вопрос зависимости
цвета задачи от ее типа является более нужным и востребованым, чем зависимость цвета задачи от состояния (как это реализовано в стандартной версии). Например, задачи с типом "Звонок" необходимо выделять синим цветом среди сотни задач сегодняшнего расписания, а задачу "Втреча" красным (для того, чтобы она не осталась незамеченной).

Предлагаю заменить функционал зависимости цвета от состояния на зависимость цвета от типа задачи следующим образом:
1. В таблицу tbl_TaskType довавляем поле Color с типом "Целое число".
2. Добавляем поле цвета в сервис sq_TaskType.
3. Добавляем целочисленное поле (IntegerDataField) в датасет ds_TaskType.
4. В карточку редактирования wnd_TaskTypeEdit добавляем контрол ColorBoxDataControl. Устанавливаем необходимые значения свойствам DatasetLink и DataFieldName.
5. Открываем запрос на выборку sq_Task. В этом запросе нужно изменить уже существующее поле StatusColor. А именно, сделать выборку поля Color из таблицы tbl_TaskType.
После сохранения всех внесенных изменений и перезапуска рабочего приложения, при выборе цвета в справочнике "Типы задач", созданные задачи будут подсвечиваться этим цветом.

Желаю удачи!

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

Нравится

Поделиться

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

Отлично! Я первый пользователь данной функциональности!
Ещё бы задачи с приоритетом "Срочно" или "Критично" всегда были наверху, в независимости от сортировки (опцию можно сделать отключаемой)

Вопрос.
Как прикрутить данную функциональность к разделу Контрагенты?
Проделал всё вышеописанное для собственного справочника, как теперь прикрутить цвета к гриду?

Пытаюсь сделать тоже самое для договоров по статусу...
"Подписан. Оплачен.", "Не подписан. Не оплачен."...
Проделал вышеизложенный алгоритм со справочником "Состояние договора" и с sq_Contract, ds_Contract.
Создал в scr_ContractsGridArea для grdData

function GetContractColorByDatasetRecord(Dataset) {
	var TextColor = Dataset.ValAsInt('Color');
	return TextColor;
}
 
// --------------
// Event handlers
// --------------
 
function grdDataOnGetRowDrawInfo(DataGrid, Color, TextColor, ImageName, Font) 
{
TextColor.Value = GetContractColorByDatasetRecord(BaseGridArea.GridDataset);
}

по аналогии с Задачами...
Но всё равно раскраска не происходит...

---
Конфигурация:
TSCRM 3.3.1.36
Firebird 2.0 (v.2.0.3.12981)

Александр, а HasCustomDraw в реестре включили?

уже включил...))
спасибо за подсказку, всё работает)

---
Конфигурация:
TSCRM 3.3.1.36
Firebird 2.0 (v.2.0.3.12981)

Отличная возможность + описание:twisted:
Счета, Документы, Договора - всё расскрасил!:wink:
Вот только при добавлении ColorBoxDataControl'а появляется сообщение "Разрушительный сбой!"...Никаких проблем после не возникало. Надеюсь так и останется просто плохим воспоминанием))

Я, например, в своих проектах стараюсь избегать раскраски реестра - вместо нее использую различные иконки в реестре (в зависимости от типа и состояния).

Ну, раскраска - это не мой креатив, а привычка тех, кто много лет работал еще с 2.8
Да и вообще, как говорят: "на вкус и цвет фломастеров нет":lol:
А вот с ошибкой непонятно:confused:

В родительских/подчиненных документах цветом не выделяется.
Внес нужные изменения в wmd_DocumentInDocumentGridAreaScript,
и не могу подобрать нужное значение в ф-ю GetContractColorByDatasetRecord() в обработчике grdDataOnGetRowDrawInfo.

Для реализации необходимого Вам функционала нужно выполнить следующие действия:
1. Сперва добавить поле Color в каждый из UNION sq_ChildDocument (из таблиц DocumentStatus, ContractStaus и BillStatus). Безусловно, предварительно нужно будет создать целочисленное поле Color в таблице, запросе, датасете и окне справочников "Состояния счета" и "Состояния договора".
2. Добавить целочисленное поле Color в ds_ChildDocument.
3. Добавить поле Color в каждый из UNION sq_ParentDocument.
4. Добавить целочисленное поле Color в ds_ParentDocument.
5. Установить для grdData в wnd_DocumentInDocumentGridArea свойство HasCustomDraw в true.
6. Прописать функцию и обработчик таким образом:

function GetDocumentColorByDatasetRecord(Dataset) {
        var TextColor = DocumentInDocument.Dataset.ValAsInt('Color');
        return TextColor;
}
 
// ----------------------------------------------------------------------------
// Event handlers
// ----------------------------------------------------------------------------
function grdDataOnGetRowDrawInfo(DataGrid, Color, TextColor, ImageName, Font) {
TextColor.Value = GetDocumentColorByDatasetRecord(DocumentInDocument.Dataset);	
}

7. Сохраните внесенные изменения. Перезапустите рабочее приложение Terrasoft CRM и протестируйте работоспособность функционала. В результате при указании документа, договора или счета - они будут подсвечиваться цветами, указанными в справочниках состояний.

Желаю удачи!

Так ведь и делал, только вот в вместо DocumentInDocument.Dataset писал DocumentInDocument.
А, и "Всегда выбирать в запросе":smile:
Спасибо!

"Мельникова Екатерина" написал:

"Швец Александр" написал:

я все проделал как описано - строки грида или текст окрашиваются нормально, но только если я вывожу колонку Color в гриде (в представлении gvMain грида определяю колонки и делаю colStatusColorgvMain видимой и доступной), но если сделать колонку невидимой для каждой строки грида выдается ошибка "Ошибка выполнения метода 'grdDataOnGetRowDrawInfo'. Поле 'Цвет' не активно «Call Stack»" и строки не окрашиваются.

Вопрос: Как сделать так чтоб колонка "Цвет" не выводилась и при этом корректно осуществлялась возможность раскрашивать строки грида?

Для колонки цвет сделайте "всегда выбирать в запросе" .

поменяйте функцию

function GetDocumentColorByDatasetRecord(Dataset) {
        var TextColor = DocumentInDocument.Dataset.ValAsInt('Color');
        return TextColor;
}

на такой вид

function GetDocumentColorByDatasetRecord(Dataset) {
        var TextColor = GetFieldValueFromDisabledField(Dataset, 'Color');
        return TextColor;
}

Или просто поставьте для поля Color галочку "Всегда выбирать в запросе" в сервисе запроса

Оба варианта ([1]Сделать колонку цвет "всегда выбирать в запросе" и [2]воспользоваться функцией GetFieldValueFromDisabledField()) работают - спасибо

Здравствуйте!
Помогите решить проблемы (Terrasoft 3.3.1.65).
1. Я раскрасил реестр (главные записи дерева), все работает.
Мне нужно забрать раскраску выделенной записи, то есть при изменение фокуса в реестре (при переходе на другую запись), данная запись выделяется стандартным оранжевым цветом. Думаю с рисунков будет понятнее:

2. Есть ли возможность при выборе ячейки (фокус или клик) вывести Caption?

Когда-то для реестра я включил прозрачность выделения (альфа = 128). Для дерева пока осталось по старому. Сегодня-завтра обязательно исправлю это недоразумение. Если не будет никаких нюансов -- то отпишусь какую версию бинарных файлов нужно взять.

Спасибо! Будем ждать! :smile:
Может заодно подправить DataGrid.SelectedColumn (всегда возвращает null)?

"genekogo" написал:Может заодно подправить DataGrid.SelectedColumn (всегда возвращает null)?

Исправить не обещаю -- но посмотрю обязательно.

"Александр Кравчук" написал:Когда-то для реестра я включил прозрачность выделения (альфа = 128). Для дерева пока осталось по старому. Сегодня-завтра обязательно исправлю это недоразумение. Если не будет никаких нюансов -- то отпишусь какую версию бинарных файлов нужно взять.

Поправил в 3.3.1.89 и 3.3.2.26:

"genekogo" написал:Может заодно подправить DataGrid.SelectedColumn (всегда возвращает null)?

Поправил также в 3.3.1.89 и 3.3.2.26.
Вот теперь я усну... :wink:

Саша! Низкий поклон и сладких снов!!!
:twisted:

--
www.it-sfera.com.ua

"genekogo" написал:Есть ли возможность при выборе ячейки (фокус или клик) вывести Caption?

Уточните, пожалуйста, какой Caption Вы имеете ввиду. Объясните, пожалуйста, на конкретном примере, поскольку не совсем очевидно, какой именно функционал Вам нужно реализовать.
Заранее спасибо за помощь.
Ожидаю ответа.

Мельникова Екатерина

Когда текст не вмещается в поле реестра появляется надпись (на рисунке поле «Заголовок»). Например, вместо надписи с текстом «Контроль оплаты счета (t)» я хотел бы отобразить надпись с текстом «Тестовая надпись» (и неважно вмещается текст в поле реестра или нет).
Есть ли возможность при выборе ячейки (фокус или клик) вывести свой Caption?

"genekogo" написал: Например, вместо надписи с текстом «Контроль оплаты счета (t)» я хотел бы отобразить надпись с текстом «Тестовая надпись».

Т.е. Вы хотите изменить хинт ячейки?

Да. :smile:

Никогда ни от кого не слышал такого пожелания. Для чего это может понадобится?

"Александр Кравчук" написал:Никогда ни от кого не слышал такого пожелания. Для чего это может понадобится?

А вот это я давно хотел увидеть в контролах - скажем подсказка-описание что за страшная информация скрывается в данном поле и каково ее назначение.

"Александр Кудряшов" написал:А вот это я давно хотел увидеть в контролах - скажем подсказка-описание что за страшная информация скрывается в данном поле и каково ее назначение.

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

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

Пример (из данной ветки форума): выводим в реестр информацию о продажах по дням

Когда наводим на ячейку, я хочу, что бы клиент увидел какую-то детализацию. Например, процентное наполнение дня... и т.д.

--
www.it-sfera.com.ua

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

+1

--
www.it-sfera.com.ua

Ну что ж, заводите идею.

Вопрос такой, Подскажите пожалуйста как мне реализовать цветность в проектах?

1. В сервисе wnd_ProjectsGridArea в свойстве grdData необходимо найти свойство HasCustomDraw и присвоить ему значение true
2. В сервисе sq_Project для поля TypeID и активировать опцию "Всегда выбирать в запросе"
3. В сервисе wnd_ProjectGridArea найдите grdData и на закладке "Свойства" найдите свойство OnGetRowDrawInfo. Дважды кликните на правой части этого поля. В результате Вы автоматически перейдете в скрипт, в обработчик данного события.
4. В обработчике данного события необходим прописать следующее
function grdDataOnGetRowDrawInfo(DataGrid, Color, TextColor, ImageName, Font) {
// DefineRowDrawInfo(Color, TextColor, ImageName, Font);
var Dataset = dlData.Dataset;
var TypeID = Dataset.Value('TypeID');
switch (TypeID) {
case Type1:
TextColor.Value = clGreen;
break;
case Type2:
TextColor.Value = clRed;
break;
default:
TextColor.Value = clBlack;
break;
}
В данном скрипте вместо Type1, Type2, Type3 необходимо ставить ID типов инцидентов, которые предварительно нужно посмотреть в таблице tbl_ProjectType в используемой Вами СУБД
Что касается цветов, то Вы можете просмотреть перечень всех возможных цветов в сервисе scr_Const

А как раскрасить расписание? Не текст а event. подобие задач не предлагать - не получается

Здравствуйте! Посмотрите реализацию изменения цвета фона задач в расписании, в зависимости от состояния во вложенном файле.

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