Доброго времени суток, коллеги!

 

Возникла необходимость собирать несколько записей из справочников и затем все эти данные отправлять одним письмом в виде таблицы.

Какие есть варианты решения данного вопроса и можно ли это сделать стандартными средствами предоставляемые Studio?

 

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

Нравится

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

Mykhailo Kozlenko,

Добрый день.

Можно в элементе 'Задание-сценарий' сформировать таблицу, которую нужно отправлять, и записать её в параметр бизнес-процесса. Например, я делала вот таким образом:

const string quote = "\"";
const string siteRef = "http://site_name";
 
var userConnection = Get<UserConnection>("UserConnection");
var softwareInventoryTableHTML = String.Empty;
 
var esqSoftwareInventory = new EntitySchemaQuery(userConnection.EntitySchemaManager, "SoftwareInventory");
esqSoftwareInventory.AddColumn("Id");
esqSoftwareInventory.AddColumn("Number");
esqSoftwareInventory.AddColumn("Software.Name");
esqSoftwareInventory.AddColumn("LicenseKey.Name");
var expirationDateColumn = esqSoftwareInventory.AddColumn("LicenseExpirationDate");
expirationDateColumn.OrderDirection = OrderDirection.Ascending;
expirationDateColumn.OrderPosition = 1;
esqSoftwareInventory.AddColumn("Equipment.Name");
esqSoftwareInventory.AddColumn("Equipment.Id");
esqSoftwareInventory.Filters.Add(esqSoftwareInventory.CreateFilterWithParameters(FilterComparisonType.Equal,
	"LicenseStatus", new Guid("CDD71003-D5BB-43E7-8E06-280FE40BEBF8")));
DateTime dueDate = DateTime.Today.AddMonths(2);
esqSoftwareInventory.Filters.Add(esqSoftwareInventory.CreateFilterWithParameters(FilterComparisonType.Less,
	"LicenseExpirationDate", dueDate));
var softwareInventories = esqSoftwareInventory.GetEntityCollection(userConnection);
if (softwareInventories.Count > 0)
{
	var htmlTableBuilder = new StringBuilder();
	htmlTableBuilder.AppendLine("<table border='1' width='100%' cellpadding='5'>");
	var headerSB = new StringBuilder();
	headerSB.Append("<tr>");
	string[] columnNames = { "Номер", "Программное обеспечение", "Лицензионный ключ", "Дата окончания лицензии",
		"Оборудование" };
	foreach (string columnName in columnNames)
	{
		headerSB.Append("<td>" + columnName + "</td>");
	}
    headerSB.Append("</tr>");
    htmlTableBuilder.AppendLine(headerSB.ToString());
	foreach (var softwareInventory in softwareInventories)
	{
		var rowSB = new StringBuilder();
		rowSB.Append("<tr>");
		string Number = softwareInventory.GetTypedColumnValue<string>("Number");
		Guid recordId = softwareInventory.GetTypedColumnValue<Guid>("Id1");
		string softwareInventoryRef = siteRef + "/0/Nui/ViewModule.aspx#CardModuleV2/SoftwareInventory1Page/edit/" +
			recordId.ToString();
		string inventoryNumberRef = "<a href=" + quote + softwareInventoryRef + quote +">" + Number + "</a>";
		rowSB.Append("<td width=\"44\">" + inventoryNumberRef + "</td>");
		rowSB.Append("<td width=\"120\">" + softwareInventory.GetTypedColumnValue<string>("Software_Name") + "</td>");
		string licenseKey = softwareInventory.GetTypedColumnValue<string>("LicenseKey_Name");
		int licenseKeyLength = licenseKey.Length;
		int indexStr = 8;
		if (licenseKeyLength <= 8)
		{
			indexStr = licenseKeyLength;
		}
		rowSB.Append("<td width=\"110\">" + licenseKey.Substring(0, indexStr) + "</td>");
		DateTime licenseExpirationDate = softwareInventory.GetTypedColumnValue<DateTime>(expirationDateColumn.Name);
		string licenseExpirationDateStr = "";
		if (licenseExpirationDate != DateTime.MinValue) {
			licenseExpirationDateStr = licenseExpirationDate.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture);
		}
		rowSB.Append("<td width=\"83\">" + licenseExpirationDateStr + "</td>");
		rowSB.Append("<td width=\"106\">" +	softwareInventory.GetTypedColumnValue<string>("Equipment_Name") + "</td>");
		rowSB.Append("</tr>");
 
        htmlTableBuilder.AppendLine(rowSB.ToString());
	}
	htmlTableBuilder.AppendLine("</table>");	
	softwareInventoryTableHTML = htmlTableBuilder.ToString();
}
Set<string>("SoftwareInventoryTableHTML", softwareInventoryTableHTML);
return true;

А потом этот параметр бизнес-процесса использовать в элементе 'Отправить email' в качестве макроса:

Обратите внимание, по данному программному обеспечению истек срок действия лицензии либо истечет в ближайшее время.
Пожалуйста, актуализируйте в срм информацию по этому ПО:
[#Software inventory table#]
 
С уважением,
Системный администратор crm

 

P.S. В скрипте, приведенном выше, ещё и гиперссылки на карточки сущностей в срм формируются.

 

 

 

 

Добрый день.

 

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

Добрый! Кроме данного дополнения больше никаких вариантов нет? Ибо при попытке работать с данным расширением оказалось, что оно не совсем рабочее. 

Mykhailo Kozlenko,

Добрый день.

Можно в элементе 'Задание-сценарий' сформировать таблицу, которую нужно отправлять, и записать её в параметр бизнес-процесса. Например, я делала вот таким образом:

const string quote = "\"";
const string siteRef = "http://site_name";
 
var userConnection = Get<UserConnection>("UserConnection");
var softwareInventoryTableHTML = String.Empty;
 
var esqSoftwareInventory = new EntitySchemaQuery(userConnection.EntitySchemaManager, "SoftwareInventory");
esqSoftwareInventory.AddColumn("Id");
esqSoftwareInventory.AddColumn("Number");
esqSoftwareInventory.AddColumn("Software.Name");
esqSoftwareInventory.AddColumn("LicenseKey.Name");
var expirationDateColumn = esqSoftwareInventory.AddColumn("LicenseExpirationDate");
expirationDateColumn.OrderDirection = OrderDirection.Ascending;
expirationDateColumn.OrderPosition = 1;
esqSoftwareInventory.AddColumn("Equipment.Name");
esqSoftwareInventory.AddColumn("Equipment.Id");
esqSoftwareInventory.Filters.Add(esqSoftwareInventory.CreateFilterWithParameters(FilterComparisonType.Equal,
	"LicenseStatus", new Guid("CDD71003-D5BB-43E7-8E06-280FE40BEBF8")));
DateTime dueDate = DateTime.Today.AddMonths(2);
esqSoftwareInventory.Filters.Add(esqSoftwareInventory.CreateFilterWithParameters(FilterComparisonType.Less,
	"LicenseExpirationDate", dueDate));
var softwareInventories = esqSoftwareInventory.GetEntityCollection(userConnection);
if (softwareInventories.Count > 0)
{
	var htmlTableBuilder = new StringBuilder();
	htmlTableBuilder.AppendLine("<table border='1' width='100%' cellpadding='5'>");
	var headerSB = new StringBuilder();
	headerSB.Append("<tr>");
	string[] columnNames = { "Номер", "Программное обеспечение", "Лицензионный ключ", "Дата окончания лицензии",
		"Оборудование" };
	foreach (string columnName in columnNames)
	{
		headerSB.Append("<td>" + columnName + "</td>");
	}
    headerSB.Append("</tr>");
    htmlTableBuilder.AppendLine(headerSB.ToString());
	foreach (var softwareInventory in softwareInventories)
	{
		var rowSB = new StringBuilder();
		rowSB.Append("<tr>");
		string Number = softwareInventory.GetTypedColumnValue<string>("Number");
		Guid recordId = softwareInventory.GetTypedColumnValue<Guid>("Id1");
		string softwareInventoryRef = siteRef + "/0/Nui/ViewModule.aspx#CardModuleV2/SoftwareInventory1Page/edit/" +
			recordId.ToString();
		string inventoryNumberRef = "<a href=" + quote + softwareInventoryRef + quote +">" + Number + "</a>";
		rowSB.Append("<td width=\"44\">" + inventoryNumberRef + "</td>");
		rowSB.Append("<td width=\"120\">" + softwareInventory.GetTypedColumnValue<string>("Software_Name") + "</td>");
		string licenseKey = softwareInventory.GetTypedColumnValue<string>("LicenseKey_Name");
		int licenseKeyLength = licenseKey.Length;
		int indexStr = 8;
		if (licenseKeyLength <= 8)
		{
			indexStr = licenseKeyLength;
		}
		rowSB.Append("<td width=\"110\">" + licenseKey.Substring(0, indexStr) + "</td>");
		DateTime licenseExpirationDate = softwareInventory.GetTypedColumnValue<DateTime>(expirationDateColumn.Name);
		string licenseExpirationDateStr = "";
		if (licenseExpirationDate != DateTime.MinValue) {
			licenseExpirationDateStr = licenseExpirationDate.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture);
		}
		rowSB.Append("<td width=\"83\">" + licenseExpirationDateStr + "</td>");
		rowSB.Append("<td width=\"106\">" +	softwareInventory.GetTypedColumnValue<string>("Equipment_Name") + "</td>");
		rowSB.Append("</tr>");
 
        htmlTableBuilder.AppendLine(rowSB.ToString());
	}
	htmlTableBuilder.AppendLine("</table>");	
	softwareInventoryTableHTML = htmlTableBuilder.ToString();
}
Set<string>("SoftwareInventoryTableHTML", softwareInventoryTableHTML);
return true;

А потом этот параметр бизнес-процесса использовать в элементе 'Отправить email' в качестве макроса:

Обратите внимание, по данному программному обеспечению истек срок действия лицензии либо истечет в ближайшее время.
Пожалуйста, актуализируйте в срм информацию по этому ПО:
[#Software inventory table#]
 
С уважением,
Системный администратор crm

 

P.S. В скрипте, приведенном выше, ещё и гиперссылки на карточки сущностей в срм формируются.

 

 

 

 

Я однажды делал подобное следующим образом:
в БП в элементе "Задание-сценарий" вычитывал в коллекцию какие-то значения.
После кодом формировал html-код в виде:
<table>
<tbody>
  <tr>
    <td >Номер</td>
    <td >Колонка 1</td>
    <td >Колонка 2</td>
  </tr>
  <tr>
    <td >1</td>
    <td >Значение 1</td>
    <td >Значение 2</td>
  </tr>
</tbody>
</table>

где Значение 1 и Значение 2 значения из коллекции.
После использовал элемент БП "Отправка письма", где в тело подсовывал html-код в виде строки.
После отправки получатель увидит заполненную html-таблицу

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

bpm 5.4 on-site

не выполнятся цикл по реестру
имею следующий код, но внутри цикла ничто не выполняется (цикл совершает 0 шагов).
Подскажите, в чём может быть дело.

Page.TreeGrid.DataSource.PageRowsCount = -1;
Page.TreeGrid.DataSource.LoadRows();
foreach (var row in Page.TreeGrid.DataSource.Rows)
{
var name = row.GetTypedColumnValue("email");
try {
                       
var NewRecordId = Guid.NewGuid();
var insert = new Insert(UserConnection).Into("Emailing")
        .Set("Id", Column.Parameter(NewRecordId))
                .Set("EmailAddress", Column.Parameter(name))
                .Set("type", Column.Parameter(1));
insert.Execute();
   
} catch (Exception e) {System.IO.File.AppendAllText("C:\\logs\\email_debug.txt", e.Message);}
}

Нравится

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

1) Посмотрите в отладчике, что возвращает это значение:

Page.TreeGrid.DataSource.Rows

2) В файл лога что-нибудь выводится?

поставил код:

Page.TreeGrid.DataSource.PageRowsCount = -1;
Page.TreeGrid.DataSource.LoadRows();
foreach (var row in Page.TreeGrid.DataSource.Rows)
{
 
	var NewRecordId3 = Guid.NewGuid();
var insert3 = new Insert(UserConnection).Into("Emailing")
        .Set("Id", Column.Parameter(NewRecordId3))
		.Set("email", Column.Parameter("укапу3пуп"))
		.Set("type", Column.Parameter(1));
insert3.Execute();
 
var name = row.GetTypedColumnValue("email");
try {
 
var NewRecordId = Guid.NewGuid();
var insert = new Insert(UserConnection).Into("Emailing")
        .Set("Id", Column.Parameter(NewRecordId))
		.Set("email", Column.Parameter(name))
		.Set("type", Column.Parameter(1));
insert.Execute();
 
} catch (Exception e) {System.IO.File.AppendAllText("C:\\logs\\email_debug.txt", e.Message);}
}

причем код

var NewRecordId3 = Guid.NewGuid();
var insert3 = new Insert(UserConnection).Into("Emailing")
        .Set("Id", Column.Parameter(NewRecordId3))
		.Set("email", Column.Parameter("укапу3пуп"))
		.Set("type", Column.Parameter(1));
insert3.Execute();

если поставить до цикла, то он выполняется, значит в цикл не происходит вход

отладчиком не умею пользоваться (поставил visual studio, что надо нажать в ней, чтобы отладить bpm?)

в файл ничего не выводится

Илья, инструкция по отладке описана здесь:
http://www.community.terrasoft.ru/blogs/8747

После выполнения всех действий включите возможность отлавливать в студии все ошибки, как говорил Максим Кривонос:
http://www.community.terrasoft.ru/blogs/8747#comment-36912

нашел в списке процессов на отладку только http://joxi.ru/6Qj8U4wyTJDAAsoj74o
такой процесс как в инструкции я не нашел

нашлась ошибка, как с этим бороться?
скрин: http://joxi.ru/Ag38U_3JTJBqeXPt1wk

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

Да, 287000 записей. Т.е. мне желательно расширить оперативную память, чтобы загрузить все записи?

А какая бизнес задача? Почему нельзя сделать запросом из MSSQL Management Studio ?

Или так:

var insert = new Insert(userConnection).Into("City")
        .Set("CreatedById",
             new Select(userConnection).Top(1)
                 .Column("Id")
                 .From("Contact")
                 .Where("Name").IsEqual(Column.Parameter("Supervisor")))
        .Set("ModifiedById",
             new Select(userConnection).Top(1)
                 .Column("Id")
                 .From("Contact")
                 .Where("Name").IsEqual(Column.Parameter("User1")));

Илья,

для того, чтобы проходится не по всему множеству записей, можно установить PageRowsCount, например, в 1000. Тогда будут браться первые 1000 записей реестра (по аналогии с SQL - Top 1000).

Page.TreeGrid.DataSource.PageRowsCount = 1000;

"Безродный Андрей" написал:

Илья,

для того, чтобы проходится не по всему множеству записей, можно установить PageRowsCount, например, в 1000. Тогда будут браться первые 1000 записей реестра (по аналогии с SQL - Top 1000).

Page.TreeGrid.DataSource.PageRowsCount = 1000;

можно ли сначала 1000, потом еще 1000 ?

"Соколов Илья Андреевич" написал:А какая бизнес задача?

"Олейник Дмитрий" написал:

задача - в реестре контрагентов отфильтровать контрагентов, например по городу и исключая конкурентов, после чего нажать на кнопку "Отправить КП" и в таблицу отправки КП заносятся все записи являющиеся результатом выборки

например, в городе Н 10000 компаний, всем надо выбрать в реестре и нажать кнопку отправить всем КП

Илья,

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

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

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

if (schema.Parameters.ExistsByName("TreeGridSelectedRowsIds")) {
if (schema.Name == 'название Вашего процесса' {
     // здесь Ваш запрос на выборку необходимых контрагентов с помощью EntitySchemaQuery
     // .......
     propValue[i] = idsCollection[i];
     moduleProcess.SetPropertyValue("TreeGridSelectedRowsIds", propValue);
}
else {
		var idsCollection = dataSource.SelectedItemPrimaryColumnValues;
		var selectedRowsCount = idsCollection.Count;
		var propValue = new Guid[selectedRowsCount];
		for(var i = 0; i < selectedRowsCount; i++) 
			propValue[i] = idsCollection[i];
		moduleProcess.SetPropertyValue("TreeGridSelectedRowsIds", propValue);
	}
}
Показать все комментарии

БП продажи
Задача: Создать N-счетов, для каждого созданного счета создать задачу, переводить продажу в состояние "Завершена" только после завершения всех задач, если хотя бы одна задача с отрицательным результатом - отменять продажу.

Решил обернуть это все в подпроцесс, которому на вход передаю количество счетов(вводится пользователем), а результат будет храниться в параметре подпроцесса. В элементе "script" создаю нужное кол-во сущностей. Вопрос - как теперь заставить БП ожидать все созданные задачи?

Нравится

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

Вам нужен элемент "Слияние" (треугольник) в редакторе БП.

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

Добрый день!

Занимался когда-то похожей задачей.
нашел сервисы процесса (во вложении).
Суть в том, чтобы проверять количество завершенных и не завершенных подпроцессов.
services.rar
сервисы для версии 3.3.1.148 (MSSQL)

Спасибо за ответы, уже натолкнули коллеги на решение - В элементе "Скрипт" создать в цикле все задачи и перейти в следующий элемент "Скрипт", где запросом проверять статус задач и по результатам выставлять текущему WorkflowItem(наш элемент "Скрипт") параметр IsCompleted в true или false. Запускать этот элемент каждый раз когда изменяем задачу данного БП.

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

Добрый день!

Есть кусок кода:

var Done = false;
        while (!Done) {
                var Done = Cycle(FilePath, User, DateStr, TimeStr, PhonePart); 
        }

function Cycle(FilePath, User, DateStr, TimeStr, PhonePart) {
        var fso2 = new ActiveXObject('Scripting.FileSystemObject');
    var f2 = fso2.GetFolder('\\\\Pc230712\\sms');
        var fc = new Enumerator(f2.Files);
        for (; !fc.atEnd(); fc.moveNext())
        {
                var fl = fc.item();
                if (fso2.GetBaseName(fl.Path)) {
                                        var name = fso2.GetBaseName(fl.Path);
                                }
                if (name == 'SEND' + User + DateStr + TimeStr + 'x' + PhonePart + 'done'){
                                        Log.Write(1, 'TRUE!!!!!!');
                                    return true;
                                }                                
    }
    System.Sleep(10000);
        return false;
}

Функция сканирует папку, и пока нужный файл не появится, скрипт выполняется, но когда условие совпадает и функция должна возвратить true, Террасофт зависает прямо перед строчкой return true; Подскажите, что можно сделать в данной ситуации?

Нравится

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

Здравствуйте!
Пробовали ли Вы отладиться? На какой именно строке кода Террасофт зависает? Log.Write(1, 'TRUE!!!!!''); ?
И удалите "var" в цикле while:

var Done = false; 
        while (!Done) {
                Done = Cycle(FilePath, User, DateStr, TimeStr, PhonePart); 
        }

А лучше вообще одной строкой

        while (!Cycle(FilePath, User, DateStr, TimeStr, PhonePart)) {}

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

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