Вопрос:

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

Ответ:

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

Нравится

Поделиться

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

Чтобы получить список элементов именно диаграммы БП, а не экземпляра (т.е. не содержимое tbl_WorkflowItem, а содержимое сервиса диаграммы БП):

надо:

1. добавить в окно LookupControl с DatasetLink'ом на датасет mds_WorkflowDiagramAction
2. для этого контрола добавить обработчик OnPrepareSelectWindow:

function edtWFItemNameLUOnPrepareSelectWindow(LookupControl, SelectWindow) {
        var Dataset = LookupControl.LookupDatasetLink.Dataset;
        var DiagramUSI = GetAttribute(Self, 'DiagramUSI');
        SetAttribute(Dataset, 'DiagramUSI', DiagramUSI);
}

где DiagramUSI - USI диаграммы БП (я брал из атрибутов)

3. В скрипте mds_WorkflowDiagramActionScript изменить:

function FillDatasetFromDiagramDesigner(Dataset) {
        var Designer = Dataset.Attributes('Designer');
        var Diagram = Designer.ServiceCopy;
        var DiagramItem;

на
function FillDatasetFromDiagramDesigner(Dataset) {
        var Designer = Dataset.Attributes('Designer');
        if (!Assigned(Designer)) {
                DiagramUSI = Dataset.Attributes('DiagramUSI');
                if (!IsEmptyValue(DiagramUSI)) {
                        var Diagram = Services.GetNewItemByUSI(DiagramUSI);
                } else return;
        } else {
                var Diagram = Designer.ServiceCopy;
        }
        var DiagramItem;

Нравится

Поделиться

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

кстати, это я сделал для функционала перезапуска привязанных бизнес-процессов для записей:

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

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

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

В разделе записи создаются исключительно по БП.
Но БП переделывается, глючит и т.д. В таких ситуациях возникает необходимость, не удаляя запись, перезапустить БП.
Я дал себе такую возможность :biggrin:. А приведенный функционал использую для выбора элемента (шага) БП, с которого надо начать заново. Т.е. я выбираю ActionItem, который станет активным после перезапуска.

"Андросов Дмитрий" написал:

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

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

На гриде раздела лидов, которые создаются только по БП, есть кнопка:

на нее повешена следующая логика:

function amiRestartSelectedLeadsOnExecute(ActionMenuItem, Sender) { // перезапускает БП для выбранных записей
	var SelectedIDs = ListToArray(grdData.SelectedIDs);
	if (SelectedIDs.length) {RestartLeadsBP(SelectedIDs);}
}
 
function amiRestartAllLeadsOnExecute(ActionMenuItem, Sender) {// перезапускает БП для всех отфильтрованных записей
	var Dataset = dlData.Dataset;
	var SelectedIDs = []
	Dataset.GoToFirst();
	while(!Dataset.IsEOF) {
		SelectedIDs.push(Dataset('ID'));
		Dataset.GoToNext();
	}
	if (SelectedIDs.length) {RestartLeadsBP(SelectedIDs);}
}
 
function RestartLeadsBP(SelectedIDs){ // SelectedIDs - массив
	var WorkflowItemNameWindow = Services.GetNewItemByUSI('wnd_EnterRestartWorkflowItemName');
	SetAttribute(WorkflowItemNameWindow, 'NotifyObject', Self);	
	SetAttribute(WorkflowItemNameWindow, 'SelectedIDs', SelectedIDs);
 
// получаем USI диаграммы, к которой принадлжит привязанный к записи элемент БП
	var DiagramUSI = GetDatasetFieldValueByID('ds_WorkflowItem', 
											GetDatasetFieldValueByID('ds_Leads', SelectedIDs[0], 'WorkflowItemID'),
											'DiagramUSI');
	SetAttribute(WorkflowItemNameWindow, 'DiagramUSI', DiagramUSI);
 
открываем окно выбора параметров перезапуска
	WorkflowItemNameWindow.Show();
}

окно выглядит так:

обратите внимание: для выбора элемена БП используется базовый mds_WorkflowDiagramAction, в котором (как указано в шапке) надо изменить:

function FillDatasetFromDiagramDesigner(Dataset) {
	var Designer = Dataset.Attributes('Designer');
	if (!Assigned(Designer)) {  		                                        // это
		DiagramUSI = Dataset.Attributes('DiagramUSI');                          // это
		if (!IsEmptyValue(DiagramUSI)) {                             			// это
			var Diagram = Services.GetNewItemByUSI(DiagramUSI);                 // это
		} else return;                                                          // это
	} else {                                                                    // это
		var Diagram = Designer.ServiceCopy;
	}                                                                           // это
	var DiagramItem;

скрипт для окна:

//-----------------------------------------------------------------------------
// wnd_EnterRestartWorkflowItemNameScript
//-----------------------------------------------------------------------------
var Data = {} ;
 
function wnd_EnterRestartWorkflowItemNameOnClose(Window) {
	btnCancelOnClick();
}
 
function btnOKOnClick(Control) {
	Data.MppContactID = edtOwner.Value;
 
	SendNotify(Self, MSG_OK, Data); // посылаем Notify обратно на окно грида (где и отрабатывается логика рестарта)
	Self.Close();
}
 
function btnCancelOnClick(Control) {
	SendNotify(Self, MSG_CANCEL); // посылаем Notify обратно на окно грида (где и отрабатывается логика рестарта)
	Self.Close();
}
 
function edtWFItemNameLUOnPrepareSelectWindow(LookupControl, SelectWindow) {
	var Dataset = LookupControl.LookupDatasetLink.Dataset;
	SetAttribute(Dataset, 'DiagramUSI', GetAttribute(Self, 'DiagramUSI'));
}
 
function edtWFItemNameLUOnChange(LookupControl) {
	var Dataset = LookupControl.LookupDatasetLink.Dataset;
	Data.WFItemName = Dataset('Name'); // сохраняем имя выбранного элемента БП
}
 
function wnd_EnterRestartWorkflowItemNameOnPrepare(Window) {
	edtWFItemNameLU.Value = null;
	edtWFItemNameLU.Text = null;
}

получив оповещение на гриде отрабатывает:

function wnd_LeadsGridAreaOnNotify(ScriptableService, Sender, Message, Data) {
	var Dataset = dlData.Dataset;
 
	if (Message == MSG_OK) {
		if (Sender.Caption == 'wnd_EnterRestartWorkflowItemName') {
			var SelectedIDs = GetAttribute(Sender, 'SelectedIDs');//Data.SelectedIDs;	
			if (!!SelectedIDs.length) { // здесь немного логике по обработке выбранного нового ответственного (для записи лида)
				var WorkflowItemName = Data.WFItemName;
				var MppContactID = Data.MppContactID;
				var MppContactIDs = [];
				var UpdateOwner = true;
				if (!IsEmptyGUID(MppContactID)) {
					for (var i = 0; i < SelectedIDs.length; i++) {
						MppContactIDs[i] = MppContactID;
					}
				} else {
					for (var i = 0; i < SelectedIDs.length; i++) {
						MppContactIDs[i] = GetDatasetFieldValueByID('ds_Leads', SelectedIDs[i], 'OwnerID'); // если не выбрано - берется из текущего датасета
						UpdateOwner = false;
					}
				}
				ProcessRestartLeadsBP(SelectedIDs, WorkflowItemName, MppContactIDs, UpdateOwner); // сама функция
			}
 
			var RecordID = '';
			for (var i = 0; i < SelectedIDs.length; i++) {
				RecordID = SelectedIDs[i];
				RecreateDefaultRights(['tbl_Leads'], 'OwnerID', null, true, RecordID); // немаловажно, если поменяли ответственного - обновляем права на запись (функционал см здесь - _http://www.community.terrasoft.ru/blogs/9611#comment-40937)
 
				Dataset.RefreshRecord(RecordID, true);
			}
			return;
		}
	}
	wnd_BaseGridAreaOnNotify(ScriptableService, Sender, Message, Data);
}
function ProcessRestartLeadsBP(SelectedIDs, WorkflowItemName, MppContactIDs, UpdateOwner){ // функция-обертка
	RestartWorkflowBySelectedRecordsInTableAndWorkflowItemName('tbl_Leads', SelectedIDs
																, WorkflowItemName, MppContactIDs, UpdateOwner);
}
 
function RestartWorkflowBySelectedRecordsInTableAndWorkflowItemName(TableName // название таблицы записей, на основе которых делается перезапуск
														, SelectedIDs // массив ИД записей
														, WorkflowItemName // название элемента, на который перезапустится БП
														, MppContactIDs // массив новых ответственных для записей (согласованный с массивом SelectedIDs )
														, UpdateOwner) { // признак, что надо обновлять ответственного
	var CQ = GetSingleItemByCode('cq_ClearBaseOfWorkflowItemIDs'); // см ниже - много sql
// по сути: зачищаем все ссылки на конкретный экземпляр БП (связанный с конкретной записью) во всех (опционально) таблицах и в этой самой записи
	CQ.Parameters.ItemsByName('BaseTableName').Value = TableName;
	CQ.Parameters.ItemsByName('IncludedString').Value = SelectedIDs.join();
	CQ.Parameters.ItemsByName('Delimeter').Value = ',';
	CQ.Parameters.ItemsByName('AllTables').Value = true;
	CQ.Execute();
 
 
	var WorkflowEngine = GetWorkflowEngine();
	var Now = new Date(System.Now()).getVarDate();
	var Dataset = GetSingleItemByCode('ds_Leads', 'RestartWF');
 
	if (Assigned(WorkflowEngine) && !IsEmptyGUID(WorkflowItemName)) { // не знаю почему IsEmptyGUID, наверно я имел ввиду IsEmptyValue ))
		for (var i = 0; i < SelectedIDs.length; i++) {
			// запускаем новый экземпляр БП, с параметром IsFake = true (который провоцирует остановку в самом начале - см. элемент "Задержка" (см. скрин ниже)
			var WF_ID = WFStartByUSI('wd_LeadsMain' 
										,['LeadsID', 'MppContactID', 'IsFake']
										,[SelectedIDs[i], MppContactIDs[i], true]);
// принудительно запускаем БП с нужного нам элемента (с нужными нам параметрами)
			var WorkflowItemWF_ID = StartWorkflowItem(WF_ID, WorkflowItemName
											,['LeadsID', 'MppContactID', 'IsFake']
											,[SelectedIDs[i], MppContactIDs[i], false]);
 
			// и обновляем WorkflowItemID для выбранной записи
			ApplyDatasetFilter(Dataset, 'ID', SelectedIDs[i], true);
			Dataset.Open();
			Dataset.Edit();
			if (!!UpdateOwner) {
				Dataset('OwnerID') = MppContactIDs[i];
			}
			Dataset('WorkflowItemID') = !IsEmptyGUID(WorkflowItemWF_ID) ? WorkflowItemWF_ID : null;
			Dataset.Post();
			Dataset.Close();
		}
	}
}

function WFStartByUSI(WorkflowUSI, ParamNames, ParamValues) { 
	var WorkflowEngine = GetWorkflowEngine();
	var Now = new Date(System.Now()).getVarDate();
	var Params = WFArrayToParams(ParamNames, ParamValues);
	var ID;
	/*if (!!NoReturn) {
		if (Assigned(Params)) {
			WorkflowEngine.StartWorkflow(WorkflowUSI, Now, Params);
		} else {
			WorkflowEngine.StartWorkflow(WorkflowUSI, Now);
		}
		return;
	}*/
	if (Assigned(Params)) {
		ID = WorkflowEngine.StartWorkflow(WorkflowUSI, Now, Params);
	} else {
		ID = WorkflowEngine.StartWorkflow(WorkflowUSI, Now);
	}
	return ID;
}
 
function StartWorkflowItem(WorkflowID, ItemName, ParamNames, ParamValues) {
	var WorkflowEngine = GetWorkflowEngine();
	var Now = new Date(System.Now()).getVarDate();
	var Params = WFArrayToParams(ParamNames, ParamValues);
	var ID;
	if (Assigned(Params)) {
		ID = WorkflowEngine.StartWorkflowItem(WorkflowID, ItemName, Now, Params);
	} else {
		ID = WorkflowEngine.StartWorkflowItem(WorkflowID, ItemName, Now);
	}
	return ID;
}

cq_ClearBaseOfWorkflowItemIDs

declare @BaseTableName varchar(250) = :BaseTableName
declare @TableName varchar(250) = :TableName
declare @IncludedString nvarchar(4000) = :IncludedString
declare @delimeter1 nvarchar(4000) = :Delimeter
 
declare @AllTables int = :AllTables -- опция для обработки всех таблиц с полем WorkflowItemID
 
if (@AllTables = 1)
begin -- получаем список всех таблиц с полем WorkflowItemID
     declare tc cursor local fast_forward for
     	     select name from SYSOBJECTS where 
	            	ID in (SELECT id FROM SYSCOLUMNS WHERE NAME='WorkflowItemID')
              		and xtype = 'U'
                	and name != @BaseTableName
 
     open tc
end
else 
begin -- либо берем из параметра
     declare tc cursor local fast_forward for
     	     select name from (values (@TableName)) as X(name)
 
     open tc
end
 
while (1=1)
begin
  fetch next from tc into @TableName
  if @@fetch_status = -1 break
  if @@fetch_status = -2 continue
 
print @IncludedString
print @TableName
exec(N'
-- для парсинга IncludedTablesString start  
 
-- см _http://www.community.terrasoft.ru/blogs/10130
declare @input_str nvarchar(4000) = ''' + @IncludedString + '''
declare @IncludedTablesStringTable table (Code uniqueidentifier)
declare @IncludedTablesStringCode nvarchar(4000)
declare @delimeter nvarchar(4000) = '''+@delimeter1+'''
declare @pos int = 0
declare @len int = LEN(@input_str + cast(2 as varchar)) - 1
declare @lend int = LEN(@delimeter + cast(2 as varchar)) - 1
declare @oldpos int = 0
declare @substr nvarchar(4000)
 
while (@pos <= @len)
begin
	set @substr = SUBSTRING(@input_str, @pos, @lend)
	if (@substr = @delimeter or @pos = @len)
	begin
		set @substr = SUBSTRING(@input_str, @oldpos, 
			@pos - @oldpos + case when (@pos = @len) then 1 else 0 end
		)
		insert into @IncludedTablesStringTable (Code) values (@substr)
		set @pos = @pos + @lend 
		set @oldpos = @pos
		continue
	end
	set @pos = @pos + 1
end
-- для парсинга IncludedTablesString end
 
-- собственно логика зачистки ссылок на БП
update ' + @TableName + ' set WorkflowItemID = null
where WorkflowItemID in (select ID from tbl_WorkflowItem where WorkflowID in 
							(select WorkflowID from tbl_WorkflowItem where ID in 
								(select WorkflowItemID from '+ @BaseTableName + ' where ID in 
									(select Code from @IncludedTablesStringTable)
								)
							)
						)
update ' + @BaseTableName + ' set WorkflowItemID = null 
       where ID in 
       (select Code from @IncludedTablesStringTable)
 
')
end
close tc
deallocate tc

не претендую на изящность, делалось по пути и кусками))

в любом случае оно проще, чем кажется

Показать все комментарии
Идея
Реализована
Предлагаю ввести новые элементы - карточка поиска (тут можно обойтись созданием нового типа схемы представления) и карточку вида. Второй вариант - карточка с настраиваемыми в БП полями, для заполнения данными разных объектов, то есть можно будет создать карточку, не привязанную ни к одному объекту, но данными которой можно будет заполнить сразу несколько не связанных объектов.
0 комментариев
Показать все комментарии
Идея
Реализована
Предлагаю ввести новые элементы - карточка поиска (тут можно обойтись созданием нового типа схемы представления) и карточку вида. Второй вариант - карточка с настраиваемыми в БП полями, для заполнения данными разных объектов, то есть можно будет создать карточку, не привязанную ни к одному объекту, но данными которой можно будет заполнить сразу несколько не связанных объектов.
1 комментарий

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

В 7.0.1 есть действия «Автогенерируемая страница» и «Преднастроенная страница». Первая — список полей настраивается в свойствах действия. Вторая — страница создаётся обычным образом, но не привязанная к объекту в БД, данные передаются через параметры.

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