Добрый день.

У меня есть раздел "Материалов" в котором есть числовое поле "Количество пустых", его заполнение происходит через БП.
Иногда требуется обнуление поля "Количество пустых" для всех записей.
Я хочу добавить кнопку которая будет выполнять очистку данных, с подтверждением своего намерения очистить, дабы избежать случайного нажатия.
Подскажите, пожалуйста, как лучше реализовать эту задачу.

Нравится

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

Попыталась создать кнопку по статье http://www.community.terrasoft.ru/forum/topic/13848 , но у меня не получилось.
Делаю первый раз и потому не знаю как решить это, буду рада если подскажите как исправить и где можно еще почитать информацию.

define("UsrConsumablesSection1", ["GridUtilitiesV2"],
function() {
	return {
		entitySchemaName: "UsrConsumables",
		contextHelpId: "1001",
		diff: /**SCHEMA_DIFF*/[
			{
				"operation": "insert",
				"parentName": "UsrConsumable",
				"propertyName": "items",
				"name": "ResetSectionButton",
				"values": {
						itemType: Terrasoft.ViewItemType.BUTTON,
						caption: {bindTo: "Resources.Strings.ClearQuantityEmptyButtonCaption"},
						click: {bindTo: "onClearQuantityEmptyClick"},
						enabled: {bindTo: "isUsrConsumablesUsrQuantityEmpty"},
						"layout": {
							"column": 1,
							"row": 6,
							"colSpan": 1
						}
					}
				}
			]/**SCHEMA_DIFF*/,
			messages: {},
			methods: {
				onClearQuantityEmptyClick: function() {
					var activeRow = this.get("ActiveRow");
					if (activeRow) {
						var primaryID = this.get("GridData").get(activeRow).get("UsrConsumables").value;
					}
				},
				isUsrConsumablesUsrQuantityEmpty: function() {
					var activeRow = this.get("ActiveRow");
					if (activeRow) {
						var pc = this.get("GridDate").get(activeRow).get("UsrQuantityEmpty");
						return (pc || pc !== "") ? true : false;
					}
					return false,
					Page.UsrQuantityEmpty.Clear();
				}
			}
		};
});

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

Самый простой способ - через БП.
Реализация:
1) Начальный элемент - простой
2) Вопрос пользователю:
"Вы уверены, что хотите очистить значение поля для всех записей"
Ответ:
- Не очень. Отменить.
- Just do it!

Если выбран ответ "Не очень...", то идем на конец процесса.
Иначе:

3) Изменить данные в объекте UsrConsumable (указать надо его название) с фильтром Id заполнено (то есть всем записям).
Необходимо задать полю UsrQuantityEmpty значение default(int)

4) Конец процесса.

В свойствах процесса в блоке "Доступен в разделах" выберите нужный раздел.

ЗЫ. Page.UsrQuantityEmpty.Clear() не обращается к БД, поэтому очистка не происходит.

Добрый день.
Создала БП, в странице схемы прописала код, но возникла такая проблема, если я делаю замещающую сраницу с этим кодом (код ниже), страница раздела у меня не прорисовывается. Что мне нужно сделать, что бы исправить это?
Пример запуска БП брала из http://www.community.terrasoft.ru/forum/topic/10554#comment-47944 .

define("UsrConsumablesSection1", ["GridUtilitiesV2"],
function() {
	return {
		entitySchemaName: "UsrConsumables",
		contextHelpId: "1001",
		diff: /**SCHEMA_DIFF*/[
			{
				"operation": "insert",
				"parentName": "UsrConsumable",
				"propertyName": "items",
				"name": "ResetSectionButton",
				"values": {
						itemType: Terrasoft.ViewItemType.BUTTON,
						caption: {bindTo: "Resources.Strings.ClearQuantityEmptyButtonCaption"},
						click: {bindTo: "onClearQuantityEmptyClick"},
						enabled: {bindTo: "isUsrConsumablesUsrQuantityEmpty"},
						"layout": {
							"column": 1,
							"row": 6,
							"colSpan": 1
						}
					}
				}
			]/**SCHEMA_DIFF*/,
			messages: {},
			methods: {
				onClearQuantityEmptyClick: function() {
					var url = "https://" + document.domain + "/0/ServiceModel/ProcessEngineService.svc/UsrProcessEmpty";
					var request = null;
					request = new XMLHttpRequest();
					request.open("GET", url, true);
					request.send(null);
				}
			}
		};
});

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

Обратитесь к службе поддержки. Листинг кода корректный.

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

Есть такая задача:
У каждой стадии продажи есть максимальная длительность. Хочу на детали Стадии продажи видеть планируемую дату выхода из стадии на основании этой длительности + считать задержку относительно планируемой даты.
Может кто подскажите куда что надо прописать .поделится наработками.
Используем bpmonline 7-x.

Нравится

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

С планируемой датой всё просто - при смене стадии новая запись добавляется на деталь. Достаточно просто сделать бизнес-процесс, который срабатывает на добавление записи, прибавить к текущей стадии определенное количество дней (можно сделать поле в справочнике стадий) и записать планируемую дату в новое поле детали стадии продаж.

С задержкой чуть сложнее. Для отображения задержки в карточке можно сделать виртуальное поле (требуется программирование). Для использования в фильтрах необходимо уже поле в БД и надо пересчитывать его каждый день

Можно ли реализовать периодический запуск БП?

"Кулак Олег" написал:Можно ли реализовать периодический запуск БП?

Да. На форуме есть обсуждения. Например: http://www.community.terrasoft.ua/forum/topic/15055

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

Добрый день

Есть шаблон для отправки уведомлений (напр., о регистрации Инцидента). В этом шаблоне мне нужно сделать вывод данных не из %Symptoms% , а из %Notes%. Я внесла это изменение в шаблон - но в самом уведомлении подтянулось "Нет данных" (хотя в Инцидент.Заметки имеется текст!). Подскажите, в чем моя ошибка? Заранее спасибо

Нравится

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

Notes — не совсем обычное поле, в нём хранится форматированный текст. Можете показать, как в нему в шаблоне обращаетесь?

в шаблоне обращаюсь - %Notes%

- это как к колонке с объекте Инцидент и запрос на обслуживание

Татьяна, логика заполнения макросов значениями задана в схеме FillEmailTemplateUserTask в куске кода:

string dataEmpty = "Нет данных";
foreach (var columnNamePass in subjectMacros) {
	string columnName = string.Empty;
	string value = string.Empty;
	try {
		columnName = columnNames[columnNamePass];
		value = (entity.GetColumnValue(columnName) ?? dataEmpty).ToString();
	} catch {
		//value = "<Колонка [" + columnNamePass + "] не найдена>";
		value = dataEmpty;
	}
	Subject = Subject.Replace("%"+columnNamePass+"%",value);
}
foreach(var columnNamePass in bodyMacros) {
	string columnName = string.Empty;
	string value = string.Empty;
	try {
		columnName = columnNames[columnNamePass];
		value = (entity.GetColumnValue(columnName) ?? dataEmpty).ToString();
	} catch {
		//value = "<Колонка [" + columnNamePass + "] не найдена>";
		value = dataEmpty;
	}
	Body = Body.Replace("%"+columnNamePass+"%",value);
}

«Нет данных» выводится при любой исключительной ситуации во время попытки считать поле из Entity или при его пустом значении. Более подробно можно выяснить, проведя отладку этого места, используя Visual Studio.

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

Нашел несколько примеров по сообществу применения функционала сообщений через SandBox.

Вот здесь: http://www.academy.terrasoft.ru/documents/docs/technic/SDK/7.4.1/ModuleDevelopmentInBPMonline.html
Содержится обещание

Более подробно механизм обмена сообщениями между модулями системы будет рассмотрен в отдельной статье SDK.

Статья не обнаружена. Просьба ткнуть носом или сообщить, что искать бесполезно..

Нравится

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

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

Вот пример общения между деталями через SandBox

define("AccountBillingInfoDetailV2", [], function() {
    return {
        entitySchemaName: "AccountBillingInfo",
        details: /**SCHEMA_DETAILS*/{
        }/**SCHEMA_DETAILS*/,
        diff: /**SCHEMA_DIFF*/[
            {
                "operation": "merge",
                "name": "DataGrid",
                "values": {
                    "selectRow": {
                        "bindTo": "rowSelected"
                    }
                }
            }
        ]/**SCHEMA_DIFF*/,
        methods: {
            rowSelected: function() {
                this.sandbox.publish("RowSelectedInFirstDetail", { test: "param anything" }, [this.sandbox.id]);
                console.log("rowSelected in first detail...");
            }
        },
        messages: {
            "RowSelectedInFirstDetail": {
                mode: Terrasoft.MessageMode.PTP,
                direction: Terrasoft.MessageDirectionType.PUBLISH
            }
        }
    };
});
define("AccountAddressDetailV2", [], function() {
    return {
        entitySchemaName: "AccountAddress",
        details: /**SCHEMA_DETAILS*/{
        }/**SCHEMA_DETAILS*/,
        diff: /**SCHEMA_DIFF*/[]/**SCHEMA_DIFF*/,
        methods: {
            init: function() {
                this.sandbox.subscribe("RowSelectedInFirstDetail", function(arg) {
                    console.log("test " + arg.test);
                }, this, [this.getSenderSandboxId()]);
            },
            getSenderSandboxId: function() {
                return this.sandbox.id.replace("_AccountAddress", "_AccountBillingInfo");
            }
        },
        messages: {
            "RowSelectedInFirstDetail": {
                mode: Terrasoft.MessageMode.PTP,
                direction: Terrasoft.MessageDirectionType.SUBSCRIBE
            }
        }
    };
});

Так же, в конфигурации Вы можете найти дополнительные примеры.

То есть примерно так:
Заявляем в блоке messages {} сообщение с одним наименованием, но с разным параметром направления(MessageDirectionType).

Отправляем сообщение из нужного модуля так:
this.sandbox.publish (A1,B1,C1)
A1 - Наименование сообщения, зарегистрированного в messages
B1 - массив параметров
C1 - Id сообщения, обычно = this.sandbox.id

Ловим в нужном модуле сообщение так:
this.sandbox.subscribe(A2, B2, C2, D2);
A2 - Наименование сообщения, зарегистрированного в messages
B2 - Некая функция, принимающая массив параметров в "arg" и что-то делающая полезное
С2 - объект, содержащийся в модуле-получателе.. (в обычном случае this )
D2 - ссылка на метод, возвращающий чтото?

Вот насчет последнего - мне пока не понятно.. Можете дать пояснение?

Алексей, добрый день!

Отдельной статьи по сообщениям в SDK на данный момент нет.

Ок. А может, тогда подскажете иное решение, как передать Контрагента ( Account_id) В деталь Продукты в заказе?
Я пытаюсь сделать это через sandbox, правильно ли это и есть ли более правильные пути?

"Шестаков Алексей Владимирович" написал:

Ок. А может, тогда подскажете иное решение, как передать Контрагента ( Account_id) В деталь Продукты в заказе?

Я пытаюсь сделать это через sandbox, правильно ли это и есть ли более правильные пути?

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

"Мотков Илья" написал:D2 - ссылка на метод, возвращающий чтото?
Вот насчет последнего - мне пока не понятно.. Можете дать пояснение?

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

А поскольку "this.sandbox.id" у разных схем разный, то метод этот "getSenderSandboxId" призван сгенерировать такой же идентификатор, как и у схемы, которая публикует сообщение.

Что именно содержится в this.sandbox.id, для каждой их схем, вы можете посмотреть в режиме отладки (F12) в вашем браузере. И на основании увиденного, подумать, как привести Id песочницы слушателя, к Id песочницы схемы публикации.

"Щиголь Максим" написал:Если у вас возникнут трудности с этим, можем написать пример именно по общению через sandbox страницы детали и страницы карточки.

Надеюсь, справлюсь :)
Один только вопрос: сообщения хранятся в Sandbox пока его не прочитают, или только передается между модулями и они оба должны существовать в момент publish?

Спасибо, Максим..
Подскажите еще, пожалуйста, как правильно получить данные содержащиеся в this по пути в отладчике:

this.values.DefaultValues[0].value

this.get("Order"), а именно заказ я там вижу в данном случае, не помогает.. а указывать как выше сделано, явно некошерно.

"Шестаков Алексей Владимирович" написал:
Один только вопрос: сообщения хранятся в Sandbox пока его не прочитают, или только передается между модулями и они оба должны существовать в момент publish?

Оба должны быть живы (прошли свой init, и еще не выгружены из песочницы) в момент публикации сообщения.
Когда происходит publish сообщения, sandbox смотрит есть ли кто-то из ныне живых, слушающий тот же Id песочницы что и публикующий, и, если такого находит, дает ему сообщение.

Но в вашем случае, когда вы открываете страницу редактирования детали из страницы карточки, страница карточки не удаляется, так что страница карточки и детали, могут общаться через sandbox.

На ваш второй вопрос, отвечу комментарием ниже.

"Шестаков Алексей Владимирович" написал:Подскажите еще, пожалуйста, как правильно получить данные содержащиеся в this по пути в отладчике:

Не совсем понятно задан вопрос, но постараюсь предположить что вас интересует, как в карточке заказа, получить id контрагента, что бы его передавать дальше по sandbox.

Смотрите, первое, что бы в отладчике смотреть в this именно страницы заказа, вам нужно поставить точку остановки в любом методе страницы заказа. И остановится в ней, иначе, если вы просто будете писать в консоли, то this укажет на окно браузера.

Второе, это не должен быть метод init, в нем еще не заполнены поля. Можете для целей отладки использовать метод onEntityInitialized, или onSaved, что бы посмотреть что за поля есть у заказа.

Так же учтите что в 7.7. в заказе можно покупателем указать как контакт, так и аккаунт, в зависимости от этого и получать их необходимо будет по разному,
так: this.get("Account") для контрагента,
и так: this.get("Contact") для контакта.

Вот, думаю по скриншоту станет понятно:

В точку! Именно на Init я и застрял. :smile:

Понимание происходящего так и не пришло.. Прошу о помощи... что было сделано... В карточке заказа:

define("OrderPageV2", ["OrderPageV2Resources", "GeneralDetails", "OrderConfigurationConstants",
	"BusinessRuleModule", "MoneyModule", "VisaHelper",  "ProcessModuleUtilities", "MaskHelper"],
function(Resources, GeneralDetails, OrderConfigurationConstants, BusinessRuleModule, MoneyModule,
	VisaHelper, ProcessModuleUtilities, MaskHelper) {
return {
...
methods: {
	onEntityInitialized: function() {
		this.sandbox.publish("GetAccountInOrderId", { AccId: this.get("Account").value, AccDisp: this.get("Account").displayValue}, [this.sandbox.id]);
		this.callParent(arguments);
	},			
	...
messages: {
	"GetAccountInOrderId": {
	mode: Terrasoft.MessageMode.PTP,
	direction: Terrasoft.MessageDirectionType.PUBLISH
}

В карточке добавляемого в заказ продукта:

define("OrderProductPageV2", ["BaseFiltersGenerateModule", "OrderProductPageV2Resources", "GeneralDetails", "ConfigurationConstants"],
function (BaseFiltersGenerateModule, resources, GeneralDetails, ConfigurationConstants) {
return {
...
methods : {
	onEntityInitialized : function () {
		var ContrId;
		var ContrName;
		this.sandbox.subscribe("GetAccountInOrderId", function (acc) {
			ContrId = acc.AccId;
			ContrName = acc.AccName
		}, this, [this.getSenderSandboxId]);
		this.callParent(arguments);
	},
	getSenderSandboxId : function () {
		return this.sandbox.id.replace("_SectionModuleV2_OrderSectionV2_CardModuleV2_detail_ProductOrderProductOrderProductPageV200000000-0000-0000-0000-000000000000", "_SectionModuleV2_OrderSectionV2_CardModuleV2");
	},
	...
messages : {
	"GetAccountInOrderId" : {
		mode : Terrasoft.MessageMode.PTP,
		direction : Terrasoft.MessageDirectionType.SUBSCRIBE
	}

Переменные Contr - не заполняются.. :cry:
Смущает Guid.Empty в sandbox.id карточки редактирования записи детали. Но он там есть в отладчике..
И, результата все нет.. Может, неправильно выбраны методы?

Понятно, что в случае добавления новой записи Контрагент не заполнен на onEntityInitialized, тестирую на редактировании.

Guid.Empty это нормально для не сохраненного объекта, он не должен Вас смущать, более того, Вы очень грубо приводите id песочниц к единому виду. Уверен там разница минимальна, буквально в "OrderProductOrderProductPageV2", а заменять все, вместе с Guid-ом, тем более когда у карточки заказа в id песочницы явно все это остается, не стоит.

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

Строить Id песочницы вместе с Guid-ом открытой записи разумно для того, что бы, если у вас открыто несколько карточек, в нескольких закладках браузера, что бы на изменения одной, не реагировала деталь другой, и.т.д.

Но и это не главная ошибка.
Главная ошибка сейчас в том что вы делаете publish в методе onEntityInitialized карточки заказа, а в этот момент у вас еще не открыта даже деталь.

Еще одна ошибка, что Вы зачем-то вынесли значения переменных в начало метода onEntityInitialized карточки схемы. Они не заполнятся значениями никогда. Т.к. даже когда у вас сработает колбек месседжа подписки, то контекст его выполнения будет равен контексту объекта карточки, а не метода.

Заведите себе атрибуты в карточке детали, и заполняйте их в колбеке subscribe метода.
Либо, заполняйте сразу через this.set("имя поля") необходимые вам поля. Вы же наверно хотите заполнить какие-то поля детали автоматически? Так реализуйте вначале их. Если получаете Вы это значение не для поля, то тогда используйте атрибут.

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

1. В карточке заказа сделайте метод: ДетальПроситДанные() в теле которой публикуйте сообщение в песочницу с именем к примеру "ДанныеПоКонтрагентуДляДетали", и данными по контрагенту.

2. В карточке заказа сделайте подписку на сообщение, с именем к примеру "ДетальПроситДанные", в колбеке которой, дергайте свой метод ДетальПроситДанные()

3. В карточке детали в методе onEntityInitialized вначале подпишитесь на сообщение "ДанныеПоКонтрагентуДляДетали", в теле которой заполняйте свои атрибуты или поля. (собственно это результат всей нашей работы)

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

Как-то так.
Если возникнут трудности, пишите свой код сюда.
Текущий явно не может выполнить поставленную цель. Так же обратите особое внимание еще раз на то что бы Id песочниц был одинаковым.

Максим, спасибо за терпение!
Уже почти результат достигнут :) Сейчас в Заказе такие методы:

sendAccountToOrderDetail: function() {
   this.sandbox.publish("GiveAccountToOrderDetail", {
      AccId: this.get("Account").value,
      AccDisp: this.get("Account").displayValue
   }, [this.sandbox.id]);
},
onEntityInitialized : function() {
   this.callParent(arguments);	
   this.sandbox.subscribe("AskAccountFromOrderDetail", this.sendAccountToOrderDetail(), this, [this.getSenderSandboxId]);
},
getSenderSandboxId : function() {
   return "SectionModuleV2_OrderSectionV2_CardModuleV2_detail_ProductOrderProductOrderProductPageV2";
},

sandbox.id Уже вставляю константой, скопировав из отладчика. пробовал как с Guid.Empty в конце id так и без него
Судя по всему именно с этим у меня сейчас проблема.
В детали так:

onEntityInitialized : function () {
   this.callParent(arguments);
   this.sandbox.subscribe("GiveAccountToOrderDetail", function (acc) {
      this.set("UsrContragents", {
         value : acc.AccId,
         displayValue : acc.AccDisp
      });
   }, this, [this.getSenderSandboxId]);
   this.sandbox.publish("AskAccountFromOrderDetail", null, [this.sandbox.id]);
},
 
getSenderSandboxId : function () {
   return "SectionModuleV2_OrderSectionV2_CardModuleV2";
},

И никак. :(

Воссоздал пример на своей базе.
Передаю на карточку детали "Продукт в заказе", сообщение из "Заказа". С значением Id контрагента заполненного в заказе.
Прилагаю полный листинг схем как карточки, так и детали.

define("OrderPageV2", ["OrderPageV2Resources", "GeneralDetails"],
function(resources, GeneralDetails) {
	return {
		entitySchemaName: "Order",
		details: /**SCHEMA_DETAILS*/{
		}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[
		]/**SCHEMA_DIFF*/,
		attributes: {},
		methods: {
			onEntityInitialized: function() {
				this.callParent(arguments);
 
				this.sandbox.subscribe("OrderProductPageAsksForData", function(arg) {
					console.log("OrderProductPageV2 запрашивает данные прямо сейчас,");
					console.log("по Id песочницы: " + arg.sandboxId);
					// Высылаем данные.
					this.sendDataToOrderProductPage(arg.sandboxId);
				}, this, [this.sandbox.id]);
 
				console.log("Мы(OrderPageV2) подписались на сообщение: OrderProductPageAsksForData.");
				console.log("Id песочницы в этой карточке(OrderPageV2) следующий:");
				console.log(this.sandbox.id);
			},
			sendDataToOrderProductPage: function(sandboxId) {
				this.sandbox.publish("DataToOrderProductPage", { accountId: this.get("Account").value }, [sandboxId]);
				console.log("AccountId отправлен сообщением для OrderProductPageV2 по Id: " + sandboxId);
			}
		},
		rules: {},
		messages: {
			"DataToOrderProductPage": {
				mode: Terrasoft.MessageMode.PTP,
				direction: Terrasoft.MessageDirectionType.PUBLISH
			},
			"OrderProductPageAsksForData": {
				mode: Terrasoft.MessageMode.PTP,
				direction: Terrasoft.MessageDirectionType.SUBSCRIBE
			}
		},
		userCode: {}
	};
});

Схема детали:

define("OrderProductPageV2", ["BusinessRuleModule", "OrderUtilities"],
	function(BusinessRuleModule) {
		return {
			entitySchemaName: "OrderProduct",
			mixins: {},
			attributes: {},
			methods: {
				onEntityInitialized: function() {
					this.callParent(arguments);
 
					this.sandbox.subscribe("DataToOrderProductPage", function(arg) {
						console.log("OrderPageV2 передает нам данные!");
						alert("accountId: " + arg.accountId);
					}, this, [this.sandbox.id]);
 
					console.log("Мы(OrderProductPageV2) подписались на сообщение: DataToOrderProductPage.");
					console.log("По нашему(OrderProductPageV2) Id песочницы:");
					console.log(this.sandbox.id);
 
					this.sandbox.publish("OrderProductPageAsksForData", { 
						sandboxId: this.sandbox.id
					}, [this.getOrderPageSandboxId()]);
 
					console.log("Запросили данные у OrderPageV2, по её Id песочницы: " + this.getOrderPageSandboxId());
				},
				getOrderPageSandboxId: function() {
					var index = this.sandbox.id.indexOf("_detail_ProductInProducts");
					return this.sandbox.id.substring(0, index);
				}
			},
			messages: {
				"DataToOrderProductPage": {
					mode: Terrasoft.MessageMode.PTP,
					direction: Terrasoft.MessageDirectionType.SUBSCRIBE
				},
				"OrderProductPageAsksForData": {
					mode: Terrasoft.MessageMode.PTP,
					direction: Terrasoft.MessageDirectionType.PUBLISH
				}
			},
			diff: /**SCHEMA_DIFF*/[
			]/**SCHEMA_DIFF*/,
			rules: {
			}
		};
	}
);

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

Что является немного другой схемой, а именно схемой детали (OrderProductDetailV2), но, передача сообщений туда, по сути не сильно бы и отличалась. Главное понять саму механику работы с sandbox.

P.S. В результате работы вышеуказанного кода, в консоли браузера можно будет видеть:

Мы(OrderPageV2) подписались на сообщение: OrderProductPageAsksForData.
Id песочницы в этой карточке(OrderPageV2) следующий:
CardModuleV2_b8978c7f-a094-4943-bd13-2846b0ecbec9_OrderPageV2

Мы(OrderProductPageV2) подписались на сообщение: DataToOrderProductPage.
По нашему(OrderProductPageV2) Id песочницы:
CardModuleV2_b8978c7f-a094-4943-bd13-2846b0ecbec9_OrderPageV2_detail_ProductInProductsTabOrderProductOrderProductPageV200000000-0000-0000-0000-000000000000

OrderProductPageV2 запрашивает данные прямо сейчас,
по Id песочницы:
CardModuleV2_b8978c7f-a094-4943-bd13-2846b0ecbec9_OrderPageV2_detail_ProductInProductsTabOrderProductOrderProductPageV200000000-0000-0000-0000-000000000000
OrderPageV2 передает нам данные!

Ну и значение выведено алертом:

Вот это уже тянет на мануал.. Спасибо, Максим, обещаю вскоре отчитаться об успехе :)

Максим, я отразил весь ваш пример на своей базе и перепроверил и

Мы (OrderPageV2), Подписались на AskAccountFromOrderDetail, наш Sandbox.id: CardModuleV2_OrderPageV2
Мы(OrderProductPageV2) подписались на сообщение: GiveAccountToOrderDetail.
По нашему(OrderProductPageV2) Id песочницы: CardModuleV2_OrderPageV2_detail_ProductOrderProductOrderProductPageV200000000-0000-0000-0000-000000000000 
Запросили данные у OrderPageV2, по её Id песочницы: CardModuleV2_OrderPageV2

И ответ не приходит, заказ не получает сообщение и не отсылает в ответ контрагента.

В глаза бросается то, что sandbox.id в ваших логах содержит guid заказа, а в моем случае он передается и выглядит именно так, как написано в логах - без Guid. Как на Init, так и на другие события.

Версия BPM 7.5 - может это как-то влияет... следующим постом пришлю код..

Заказ:

		messages: {
			"GiveAccountToOrderDetail": {
				mode: Terrasoft.MessageMode.PTP,
				direction: Terrasoft.MessageDirectionType.PUBLISH
			},
			"AskAccountFromOrderDetail": {
				mode: Terrasoft.MessageMode.PTP,
				direction: Terrasoft.MessageDirectionType.SUBSCRIBE
			}
		},
...
onEntityInitialized : function() { // OrderPageV2
	this.callParent(arguments);
	this.sandbox.subscribe("AskAccountFromOrderDetail", function(arg) {
	    console.log("OrderProductPageV2 запрашивает данные прямо сейчас,");
  	    console.log("по Id песочницы: " + arg.sandboxId);
	this.sendAccountToOrderDetail(arg.sandboxId);
	}, this, [this.sandbox.id]);
	console.log("Мы (OrderPageV2), Подписались на AskAccountFromOrderDetail, наш Sandbox.id: " + this.sandbox.id);
},
sendAccountToOrderDetail: function(sandboxId) {
	this.sandbox.publish("GiveAccountToOrderDetail", {
		AccId: this.get("Account").value,
		AccDisp: this.get("Account").displayValue
	}, [sandboxId]);
	console.log("AccountId отправлен сообщением GiveAccountToOrderDetail для OrderProductPageV2 по Id: " + sandboxId);
},

Продукт:

		messages : {
			"GiveAccountToOrderDetail" : {
				mode : Terrasoft.MessageMode.PTP,
				direction : Terrasoft.MessageDirectionType.SUBSCRIBE
			},
			"AskAccountFromOrderDetail" : {
				mode : Terrasoft.MessageMode.PTP,
				direction : Terrasoft.MessageDirectionType.PUBLISH
			}
		},
...
onEntityInitialized: function() { // Карточка продукта в заказе
	this.callParent(arguments);
	this.sandbox.subscribe("GiveAccountToOrderDetail", function(arg) {
		this.set("UsrContragents", { value: arg.AccId, displayValue: arg.AccDisp }) 
		}, this, [this.sandbox.id]);
	console.log("Мы(OrderProductPageV2) подписались на сообщение: GiveAccountToOrderDetail.");
	console.log("По нашему(OrderProductPageV2) Id песочницы:" + this.sandbox.id);
	this.sandbox.publish("AskAccountFromOrderDetail", { sandboxId: this.sandbox.id }, [this.getOrderSandboxId]);
	console.log("Запросили данные у OrderPageV2, по её Id песочницы: " + this.getOrderSandboxId());
},
getOrderSandboxId : function() {
	var index = this.sandbox.id.indexOf("_detail_ProductOrder");
	return this.sandbox.id.substring(0, index); // обрезаем 
},

Уточнил версию :
Версия 7.5.0.1122

иногда sandbox.id в заказе бывает таким:
SectionModuleV2_OrderSectionV2_CardModuleV2
В зависимости от того, открыта ли слева в карточке заказа секция со списком заказов.
правда, на результат это не влияет.

На 7.5 проблема наблюдалась только в другом составлении ид песочницы для детали.
Получилось добиться получения ид контрагента изменением только метода getOrderSandboxId, ну и на всякий случай добавлением this. к типу и направлению сообщений.

define("OrderPageV2", ["OrderPageV2Resources", "GeneralDetails"],
function(resources, GeneralDetails) {
        return {
                entitySchemaName: "Order",
                details: /**SCHEMA_DETAILS*/{
                }/**SCHEMA_DETAILS*/,
                diff: /**SCHEMA_DIFF*/[
                ]/**SCHEMA_DIFF*/,
                attributes: {},
                methods: {
                        onEntityInitialized: function() {
                                this.callParent(arguments);
 
                                this.sandbox.subscribe("OrderProductPageAsksForData", function(arg) {
                                        console.log("OrderProductPageV2 запрашивает данные прямо сейчас,");
                                        console.log("по Id песочницы: " + arg.sandboxId);
                                        // Высылаем данные.
                                        this.sendDataToOrderProductPage(arg.sandboxId);
                                }, this, [this.sandbox.id]);
 
                                console.log("Мы(OrderPageV2) подписались на сообщение: OrderProductPageAsksForData.");
                                console.log("Id песочницы в этой карточке(OrderPageV2) следующий:");
                                console.log(this.sandbox.id);
                        },
                        sendDataToOrderProductPage: function(sandboxId) {
                                this.sandbox.publish("DataToOrderProductPage", { accountId: this.get("Account").value }, [sandboxId]);
                                console.log("AccountId отправлен сообщением для OrderProductPageV2 по Id: " + sandboxId);
                        }
                },
                rules: {},
                messages: {
                        "DataToOrderProductPage": {
                                mode: this.Terrasoft.MessageMode.PTP,
                                direction: this.Terrasoft.MessageDirectionType.PUBLISH
                        },
                        "OrderProductPageAsksForData": {
                                mode: this.Terrasoft.MessageMode.PTP,
                                direction: this.Terrasoft.MessageDirectionType.SUBSCRIBE
                        }
                },
                userCode: {}
        };
});
define("OrderProductPageV2", ["BusinessRuleModule", "OrderUtilities"],
        function(BusinessRuleModule) {
                return {
                        entitySchemaName: "OrderProduct",
                        mixins: {},
                        attributes: {},
                        methods: {
                                onEntityInitialized: function() {
                                        this.callParent(arguments);
 
                                        this.sandbox.subscribe("DataToOrderProductPage", function(arg) {
                                                console.log("OrderPageV2 передает нам данные!");
                                                alert("accountId: " + arg.accountId);
                                        }, this, [this.sandbox.id]);
 
                                        console.log("Мы(OrderProductPageV2) подписались на сообщение: DataToOrderProductPage.");
                                        console.log("По нашему(OrderProductPageV2) Id песочницы:");
                                        console.log(this.sandbox.id);
 
                                        this.sandbox.publish("OrderProductPageAsksForData", { 
                                                sandboxId: this.sandbox.id
                                        }, [this.getOrderPageSandboxId()]);
 
                                        console.log("Запросили данные у OrderPageV2, по её Id песочницы: " + this.getOrderPageSandboxId());
                                },
                                getOrderPageSandboxId: function() {
                                        var index = this.sandbox.id.indexOf("_detail");
                                        return this.sandbox.id.substring(0, index);
                                }
                        },
                        messages: {
                                "DataToOrderProductPage": {
                                        mode: this.Terrasoft.MessageMode.PTP,
                                        direction: this.Terrasoft.MessageDirectionType.SUBSCRIBE
                                },
                                "OrderProductPageAsksForData": {
                                        mode: this.Terrasoft.MessageMode.PTP,
                                        direction: this.Terrasoft.MessageDirectionType.PUBLISH
                                }
                        },
                        diff: /**SCHEMA_DIFF*/[
                        ]/**SCHEMA_DIFF*/,
                        rules: {
                        }
                };
        }
);

Если не получится и так, остается только обращаться в support@terrasoft.ru, с предоставлением учетных данных для доступа к Вашему сайту, где будет написано все вышесказанное в замещающих схемах, и останется только найти проблему, почему в конкретно вашем случае messages не отрабатывают. т.к. выше указанный код работоспособен.

Заработало! Допилил присвоение в карточке, все закрутилось-завертелось!
Большое спасибо!
Заодно получился ПочтиМануал :)

P.S. Методом массированного научного тыка обнаружилось, что в 7.7 можно получить однократно при сбросе кеша и полной перезагрузке в Chrome sandbox.id с Guid заказа.
Но при повторных обновлениях страницы или переходах по модулям Guid больше не показывается до следующего хард релоада.

Мы (OrderPageV2), Подписались на AskAccountFromOrderDetail, наш Sandbox.id: CardModuleV2_65a22318-a5ad-41e2-85a9-8c25393381e8_OrderPageV2

на 7.5 даже такого эффекта добиться не удалось :)

Максим, Добрый день! Пытаюсь в BPM 7.7 в карточке Документы сделать передачу Id контрагента в деталь с редактируемым реестром. Id аккаунта необходим для фильтрации контактов, которые выбираются на детали. Использую Ваш пример. Но в моем случае, все что находится в methods не отрабатывает. Для теста добавил функцию init, а ней debugger, функция не была вызвана.
Но правило из rules работает. Т.е. добавляю атрибут Account, значение пишу туда руками и контакты фильтруются по этому значению.
Код карточки Документы:

define('DocumentPageV2', ['DocumentPageV2Resources', 'GeneralDetails', 'BusinessRuleModule'],
function(resources, GeneralDetails, BusinessRuleModule) {
	return {
		entitySchemaName: 'Document',
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[
 
]/**SCHEMA_DIFF*/,
		attributes: {
			"UsrType": {
				dependencies : [{
					columns : ["UsrDocumentType"],
					methodName : "onDocumentTypeChanged"
				}]
			},
		},
		messages: {
			"UsrDocumentTypeColumnChanged": {
				mode: Terrasoft.MessageMode.PTP,
				direction: Terrasoft.MessageDirectionType.PUBLISH
			},
			"DataToContactInDocumentPage": {
				mode: this.Terrasoft.MessageMode.PTP,
				direction: this.Terrasoft.MessageDirectionType.PUBLISH
			},
			"ContactInDocumentPageAsksForData": {
				mode: this.Terrasoft.MessageMode.PTP,
				direction: this.Terrasoft.MessageDirectionType.SUBSCRIBE
			}
		},
		methods: {
			onEntityInitialized: function() {
				this.callParent(arguments);
				this.sandbox.subscribe("ContactInDocumentPageAsksForData", function(arg) {
					// Высылаем данные.
					this.sendDataToContactInDocumentPage(arg.sandboxId);
				}, this, [this.sandbox.id]);
			},
			sendDataToContactInDocumentPage: function(sandboxId) {
				this.sandbox.publish("DataToContactInDocumentPage", { accountId: this.get("Account").value }, [sandboxId]);
			}
		},
		rules: {
 
		}
	};
});

Код карточки детали:

define("ContactInDocumentPageV2", ["BusinessRuleModule"], function(BusinessRuleModule) {
	return {
		entitySchemaName: "ContactInDocument",
		attributes: {
			"Account": {
				dataValueType: Terrasoft.DataValueType.STRING,
				type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
				value: ""
			},
		},
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[]/**SCHEMA_DIFF*/,
		messages: {
			"DataToContactInDocumentPage": {
				mode: this.Terrasoft.MessageMode.PTP,
				direction: this.Terrasoft.MessageDirectionType.SUBSCRIBE
			},
			"ContactInDocumentPageAsksForData": {
				mode: this.Terrasoft.MessageMode.PTP,
				direction: this.Terrasoft.MessageDirectionType.PUBLISH
			}
		},
		methods: {
			onEntityInitialized: function() {
				this.callParent(arguments);
				this.sandbox.subscribe("DataToContactInDocumentPage", function(arg) {
					this.set("Account", arg.accountId);
				}, this, [this.sandbox.id]);
				this.sandbox.publish("ContactInDocumentPageAsksForData", { 
					sandboxId: this.sandbox.id
				}, [this.getDocumentPageSandboxId()]);
			},
			getDocumentPageSandboxId: function() {
				var index = this.sandbox.id.indexOf("_detail");
				return this.sandbox.id.substring(0, index);
			}
		},
		rules: {
			"UsrContact": {
				FiltrationUsrContactByAccount: {
					ruleType: BusinessRuleModule.enums.RuleType.FILTRATION,
					autocomplete: true,
					autoClean: true,
					baseAttributePatch: "Account",
					comparisonType: Terrasoft.ComparisonType.EQUAL,
					type: BusinessRuleModule.enums.ValueType.ATTRIBUTE,
					attribute: "Account"
				}
			}
		}
	};
});

Помогите, пожалуйста, разобраться.

"Эмин Юнусов" написал:Помогите, пожалуйста, разобраться.

Методы из пейджа действительно работать не будут. Но вы можете написать свою фильтрацию колонки, написав её в атрибуте пейджа, в методе фильтрации. Примерно так:
http://www.community.terrasoft.ua/forum/topic/14388
Этот метод вызывается из пейджа, при работе редактируемого реестра, в момент создания конфига текущей строки.

"Мотков Илья" написал:
Но вы можете написать свою фильтрацию колонки, написав её в атрибуте пейджа, в методе фильтрации

Не получается передать accountId в функцию метода фильтрации. Через сообщение не выходит, т.к. метод завершает свою работу и функция в subscribe

this.sandbox.subscribe("DataToContactInDocumentPage", function(arg) {
                                        this.set("Account", arg.accountId);
                                }, this, [this.sandbox.id]);

вообще не отрабатывает.

Попробуйте заставить метод подождать результата. Объявите какой-нибудь флаг, и цикл пока флаг «ложь», по возвращению результата песочницей, меняйте флаг, и только тогда метод фильтрации вернет результат, с учетом того что вернула песочница.

function(query) {
  var r;
  myApi.exec('SomeCommand', function(response) {
    r = response;
  });
  while (!r) {}
  return r;
}
Показать все комментарии

Доброго времени суток!
Стоит задача настраивать права доступа к записям раздела с помощью фильтров. Есть карточка со справочным полем "Раздел". Необходимо отобразить фильтры записей для данного раздела, чтобы в дальнейшем запускать БП, настраивающий права доступа к отфильтрованным записям выбранного раздела.
Вопроса два:
1. Как можно хранить значения фильтров в колонке, чтоб потом использовать в БП при раздаче прав?
2. Как отобразить фильтры на карточке? Я создаю контейнер по аналогии с BaseSection, и использую sandbox.loadModule. Ошибок не выдает, как и сами фильтры.

diff:

{
        "operation": "insert",
        "name": "ExtendedFiltersContainer",
        "parentName": "Header",
        "propertyName": "items",
        "values": {
                "id": "ExtendedFiltersContainer",
                "selectors": {"wrapEl": "#ExtendedFiltersContainer"},
                "itemType": Terrasoft.ViewItemType.CONTAINER,
                "controlConfig": {"visible": {"bindTo": "IsExtendedFiltersVisible"}},
                "wrapClass": ["extended-filters-container-wrapClass", "left-inner-el"],
                "items": []
        }
}

methods:
init: function() {
        this.callParent(arguments);
        var extendedFilterModuleId = this.getExtendedFilterEditModuleId();
        this.sandbox.loadModule("ExtendedFilterEditModuleV2", {
                renderTo: "ExtendedFiltersContainer",
                id: extendedFilterModuleId
        });
}

Нравится

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

Здравствуйте, на данный момент, в системе не предусмотрено возможности использовать контролл фильтрации, который вы видите на странице секции, на пользовательских страницах другого типа.
Но если Вам необходимо собирать информацию по примененным фильтрам в разделе, а потом их куда-нибудь сохранять, и использовать в тех или иных целях.
Вы можете добавить в действия секции кнопку, которую будет видеть, к примеру, только администратор системы. Перед нажатием применять к разделу фильтрацию. И использовать метод секции «getFilters()» который вернет вам объект, всех, примененных к разделу фильтров.
Как с ним поступать далее, решать вам.
Так же, объект фильтров поддерживает сериализацию и десереализацию.
Ниже прилагаю небольшой пример по получению и сериализации объекта всех примененных к разделу фильтров:

define("OrderSectionV2", [], function() {
	return {
		entitySchemaName: "Order",
		attributes: {
		},
		methods: {
			getSectionActions: function() {
				var actionMenuItems = this.callParent(arguments);
				actionMenuItems.addItem(this.getActionsMenuItem({
					"Type": "Terrasoft.MenuSeparator",
					"Caption": ""
				}));
				actionMenuItems.addItem(this.getActionsMenuItem({
					"Caption": "testFilters",
					"Click": { "bindTo": "testFilters" }
				}));
				return actionMenuItems;
			},
			testFilters: function() {
				var filters = this.getFilters();
 
				var serializationInfo = filters.getDefSerializationInfo();
				serializationInfo.serializeFilterManagerInfo = true;
 
				var filtersJson = filters.serialize(serializationInfo);
				this.Terrasoft.showInformation(filtersJson);
			}
		},
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[]/**SCHEMA_DIFF*/
	};
});

простой вариант:

сделайте справочное поле со ссылкой на объект нужных вам фильтров (Например ContactFolder/Группа контакта) и выбирайте нужную группу. В таблицах групп есть поле SearchData - это сохраненные фильтры (для клиентской стороны). В базе они выглядят так:

Перед использованием их значение надо десериализовать:

Terrasoft.deserialize(SearchData)

После этого можете их добавлять в качестве фильтров в запросы:

var searchData = Terrasoft.deserialize(contactGroup.getByIndex(0).SearchData);
config.filters.addItem(searchData);
...
var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
	rootSchemaName: "Contact"
});
esq.addColumn("Id");
esq.filters = config.filters;
esq.getEntityCollection(function(result) {
	if (result.success) {
		...
	}
}, this);

Тут есть две проблемы:
1) Группа уже должна быть создана и настроена (это не всегда плохо, но в половине случаев - точно)
2) Если вам надо выбирать из фильтров разных разделов, надо будет сделать переключение схем в зависимости от выбранного раздела ,само справочное поле групп - виртуальным, а фильтры сохранять в другом, скрытом

Сложный вариант:
1) Сделать модуль для настройки и фильтров через интерфейс. Тут логичнее использовать схемы тех разделов, которые будут фильтроваться - так пользователь сможет сразу увидеть результат фильтрации. Но их придется обернуть в отдельный модуль, который будет загружать нужный раздел поверх открытой карточки и иметь как минимум две кнопки - ОК/Отмена - а из схемы раздела надо будет убрать весь остальной функционал (фактически - создать дочернюю схему, например, от contactSectionV2, на которой скрыть все ненужные элементы). Загружать модуль тогда надо будет как-то так

this.sandbox.loadModule("ReportModule", {
	id: reportModuleId,
	renderTo: this.renderTo,
	keepAlive: true
});

2) наладить сообщение между модулями карточки и только что созданным для обмена фильтрами

Еще один простой вариант:
создавать вашу сущность с того раздела, который собираетесь фильтровать - открываете Контакты, настраиваете фильтрацию в расширенном режиме и выбрав действие "Создать [Что-то]" передаете туда сразу и раздел и фильтры (их надо будет сначала считать в схеме раздела).

А вот как потом использовать эти фильтры в БП - это вопрос)).
Возможно будет проще сохранять не фильтрацию, а набор ИД отфильтрованных записей. Это простая деталь и в БП проблем особо не будет.

Пример добавления записей по преднастроенному фильтру:

define("SfTestTeamDetail", ["terrasoft", "SfTestTeamDetailResources","ConfigurationConstants", "ConfigurationEnums", "SferaConstants", 'TestingConstants'],
	function(Terrasoft, resources, ConfigurationConstants, enums, SferaConstants, TestingConstants) {
		return {
            entitySchemaName: "SfTestTeam",
			attributes: {},
			messages: {
                "GetTargetGroup": {
                    mode: Terrasoft.MessageMode.PTP,
                    direction: Terrasoft.MessageDirectionType.PUBLISH
                }
            },
			methods: {
                initData: function() {
                    this.callParent(arguments);
                },
				onAddButtonClick: function() {
					var cardState = this.sandbox.publish("GetCardState", null, [this.sandbox.id]);
					var isNew = (cardState.state === enums.CardStateV2.ADD ||
					    cardState.state === enums.CardStateV2.COPY);
					if (isNew) {
						var args = {
							isSilent: true,
							messageTags: [this.sandbox.id]
						};
						this.sandbox.publish("SaveRecord", args, [this.sandbox.id]);
						return;
					}
                    var SfTargetGroup = this.sandbox.publish("GetTargetGroup", null, [this.sandbox.id]).targetGroup;
                    this.set("SfTargetGroup", SfTargetGroup);
                    this.addContactToTeam(arguments[3] === "byGroup");
				},
                addContactToTeam: function(byGroup) {
                    if(byGroup) {
                        this.getGroupList();
                    } else {
                        this.getUsersList();
                    }
                },
				getUsersList: function() {
					/*var scope = this;
					var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
						rootSchemaName: "SfTestTemplate"
					});
					esq.addColumn("SfTargetGroup");
					esq.filters.addItem(Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL, "Id", this.get("MasterRecordId")));
					esq.getEntityCollection(function(result) {
						if (result.success && result.collection.getItems().length>0) {
							scope.prepareLookupConfig(result.collection.getItems()[0].get("SfTargetGroup").value);
						}
					}, scope);*/
                    this.Terrasoft.chain(
                            function(next) {
                                this.getContactLookupConfig(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.openContactLookup(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.addContactCallback(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.reloadGridData();
                                next();
                            },
                            this);
            	},
                getGroupList: function() {
                    this.Terrasoft.chain(
                            function(next) {
                                this.getContactGroupLookupConfig(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.openContactGroupLookup(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.getContactLookupConfig(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.getSelectedGroupFilter(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.getFilteredContacts(function() {
                                    next();
                                }, this);
                            },function(next) {
                                this.addContactCallback(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.reloadGridData();
                                next();
                            },
                            this);
                },
                getContactLookupConfig: function(callback) {
                    var SfTargetGroup = this.get("SfTargetGroup");
                    var templateId = this.get("MasterRecordId");
                    var filters = this.Ext.create("Terrasoft.FilterGroup");
                    var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
                        rootSchemaName: "SfTestTeam",
                        isDistinct: true
                    });
                    var IdsExists = [];
                    esq.addColumn("SfContact");
                    esq.filters.addItem(Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL, "SfTestTemplate",
                            templateId));
                    esq.getEntityCollection(function(result) {
                        if (result.success) {
                            var items = result.collection.getItems();
                            IdsExists = [];
                            Terrasoft.each(items, function(item) {
                                var contact = item.get("SfContact");
                                if (contact && contact.value) {
                                    IdsExists.push(contact.value);
                                }
                            }, this);
                            var contactType = SferaConstants.ContactType.Employee.value;
                            if (SfTargetGroup && SfTargetGroup.value) {
                                if (SfTargetGroup.value == TestingConstants.TargetGroupType.Doctor.value) {
                                    contactType = SferaConstants.ContactType.Doctor.value;
                                }
                                filters.addItem(Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL, "Type",
                                        contactType));
                            }
                            filters.addItem(this.getNotExistsInTeamFilter(IdsExists));
                            var config = {
                                entitySchemaName: "Contact",
                                multiSelect: true,
                                columns: ["Name"],
                                hideActions: true
                            };
                            config.filters = filters;
                            this.set("ContactLookupConfig", config);
                            if (this.Ext.isFunction(callback)) {
                                callback.call(this);
                            }
                        }
                    }, this);
                },
                getContactGroupLookupConfig: function(callback) {
                    var config = {
                        entitySchemaName: "ContactFolder",
                        //multiSelect: true,
                        columns: ["Id", "Name", "SearchData"],
                        hideActions: true
                    };
                    var filters = this.Terrasoft.createFilterGroup();
 
                    //todo use configconstants
                    filters.addItem(Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL, "FolderType.Id",
                            "65ca0946-0084-4874-b117-c13199af3b95"));//dynamic
                    config.filters = filters;
                    this.set("ContactGroupLookupConfig", config);
                    if (this.Ext.isFunction(callback)) {
                        callback.call(this);
                    }
                },
                openContactGroupLookup: function(callback) {
                    var config = this.get("ContactGroupLookupConfig");
                    this.openLookup(config, function(args) {
                        this.set("SelectedContactGroup", args.selectedRows);
                        if (this.Ext.isFunction(callback)) {
                            callback.call(this);
                        }
                    }, this);
                },
                getSelectedGroupFilter: function(callback) {
                    // todo учеть возможность множественного выбора групп
                    var contactGroup = this.get("SelectedContactGroup");
                    var config = this.get("ContactLookupConfig");
                    if (contactGroup.getCount()) {
                        var searchData = Terrasoft.deserialize(contactGroup.getByIndex(0).SearchData);
                        config.filters.addItem(searchData);
                    }
                    this.set("ContactLookupConfig", config);
                    if (this.Ext.isFunction(callback)) {
                        callback.call(this);
                    }
                },
                openContactLookup: function(callback) {
                    var config = this.get("ContactLookupConfig");
                    this.openLookup(config, function(args) {
                        this.set("SelectedContacts", args.selectedRows);
                        if (this.Ext.isFunction(callback)) {
                            callback.call(this);
                        }
                    }, this);
				},
                getFilteredContacts: function(callback) {
                    var ContactLookupConfig = this.get("ContactLookupConfig");
                    var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
                        rootSchemaName: "Contact"
                    });
                    esq.addColumn("Id");
                    esq.filters = ContactLookupConfig.filters;
                    esq.getEntityCollection(function(result) {
                        if (result.success) {
                            //Ids = result.collection.getKeys();
 
                            this.set("SelectedContacts", result.collection);
                            if (this.Ext.isFunction(callback)) {
                                callback.call(this);
                            }
                        }
                    }, this);
                },
                addContactCallback: function(callback){
                    this.showBodyMask();
                    var Ids = this.get("SelectedContacts").getKeys();
                    var bq = this.Ext.create("Terrasoft.BatchQuery");
                    this.Terrasoft.each(Ids, function(item) {
                        var insert = this.Ext.create("Terrasoft.InsertQuery", {
                            rootSchemaName: "SfTestTeam"
                        });
                        var newGuid = Terrasoft.generateGUID();
                        var testTemplate = this.get("MasterRecordId");
                        insert.setParameterValue("Id", newGuid , Terrasoft.DataValueType.GUID);
                        insert.setParameterValue("SfContact", item, Terrasoft.DataValueType.GUID);
                        insert.setParameterValue("SfTestTemplate", testTemplate, Terrasoft.DataValueType.GUID);
                        bq.add(insert);
                    }, this);
                    bq.execute(function(result) {
                        this.hideBodyMask();
                        if (result.success) {
                            if (this.Ext.isFunction(callback)) {
                                callback.call(this);
                            }
                        } else console.log("error");
                    }, this);
                },
                getNotExistsInTeamFilter: function(IdsExists) {
                    var existsFilter = this.Terrasoft.createNotExistsFilter("[SfTestTeam:SfContact:Id].SfContact");
                    var subFilter = this.Terrasoft.createColumnInFilterWithParameters(
                            "SfContact", IdsExists);
                    existsFilter.subFilters.addItem(subFilter);
                    existsFilter.subFilters.addItem(Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL, "SfTestTemplate",
                            this.get("MasterRecordId")));
                    return existsFilter;
                }
            },
			diff: /**SCHEMA_DIFF*/[
 
				{
					"operation": "merge",
					"name": "AddRecordButton",
					"values": {
						"click": {bindTo: "onAddButtonClick"},
						"menu": [],
                        "tag": "single"
					}
				},
				{
					"operation": "remove",
					"name": "AddTypedRecordButton"
				},
				{
					"operation": "remove",
					"name": "EditRecordMenu"
				},
				{
					"operation": "remove",
					"name": "CopyRecordMenu"
				},
                {
                    "operation": "insert",
                    "name": "AddTargetGroupButton",
                    "parentName": "Detail",
                    "propertyName": "tools",
                    "values": {
                        "itemType": Terrasoft.ViewItemType.BUTTON,
                        "click": {"bindTo": "onAddButtonClick"},
                        "style": Terrasoft.controls.ButtonEnums.style.TRANSPARENT,
                        "caption": {"bindTo": "Resources.Strings.AddTargetGroupButtonCaption"},
                        "tag": "byGroup"
                    },
                    "index": 1
                }
			]/**SCHEMA_DIFF*/
		};
	}
);

https://youtu.be/WlaJY3duIJY

Спасибо огромное за идеи! Взяв понемногу из каждой у меня нарисовался алгоритм.
Остается только одна задача: я передаю в бп коллекцию записей, UId схемы раздела и объект администрирования. Не могу понять как правильно их передать в элемент "изменить права доступа". Подскажите, пожалуйста, как правильно реализовать данную задачу.

Уже не нужно, разобрался

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

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

Подскажите как организовать сортировку в EntitySchemaQuery по дате модификации записей. В SDK не нашел подходящего метода.

Нравится

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

Добрый день!

Если Ваш вопрос касается логики на клиенте, то сортировка устанавливается примерно так:

var entitySchemaQuery = Ext.create("Terrasoft.EntitySchemaQuery", {
	rootSchemaName: entitySchemaName
});
var modifiedOnColumn = entitySchemaQuery.addColumn("ModifiedOn", "ModifiedOn");
modifiedOnColumn.orderPosition = 0;
modifiedOnColumn.orderDirection = Terrasoft.OrderDirection.ASC;
...

Для серверной логики:

var query = new EntitySchemaQuery(entitySchema);
var modifiedOnColumn = query.AddColumn("ModifiedOn");
modifiedOnColumn.OrderByAsc(0);
...
Показать все комментарии

Есть вопрос, кто сталкивался с этой проблемой прошу помочь
Создал параметр в БП LeadID, дал значения новый идентификатор лида, при создании лида в БП присвоил полю Id значение LeadID, потом в другой карточке сделал изменения и записал в поле лид значение LeadID. Но туда ничего не записывается, в чем проблема?

Нравится

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

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

Не могли Вы поподробнее описать момент:

"Радчук Виталий Владимирович" написал:

Создал параметр в БП LeadID, дал значения новый идентификатор лида, при создании лида в БП присвоил полю Id значение LeadID

Уточните, пожалуйста, зачем такая реализация? При добавлении лида элементом "Добавить данные" Id генерируется автоматически. У элемента "Добавить данные" есть параметр "Id созданной записи" - в этом параметре хранится сгенерированный Id. Значение этого параметра Вы сможете потом записать в другие сущности.

Дело в том что нужно создать 3 разных лида(в зависимости от условий) а потом в независимости какой был создан вести этот лид дальше по процессу.

В таком случае сделайте наоборот:
1) Создайте параметр бизнес процесса (с типом уникальный идентификатор)
2) После каждого элемента "Добавить данные", который создает лид, используйте элемент "Формула", чтобы заполнить созданный параметр значением Id созданной записи.

В результате, у Вас в параметре будет хранится корректное значение Id лида.

что может значить эта ошибка?
process_obrabotki_zvonkov_error.txt

"Радчук Виталий Владимирович" написал:
The INSERT statement conflicted with the FOREIGN KEY constraint "FKkw7kQYErf0GdDIOp9FOjniv8". The conflict occurred in database "BPMOnline", table "dbo.SysProcessData", column 'Id'.

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

Вот скрин процесса

Пришлите, пожалуйста, настройки элемента "А) Добавить лид".

Хмм убрал условный поток и по умолчанию от начального элемента, а в замен поставил обычный - все начало работать нормально...

еще вопрос...
сделал как сказали:

первая и третья ветки нормально заполняют поле лид в звонок, а во второй ветке лид создается но не записывается в поле лид карточки звонка. В журнале показывает что элемент "Добавить звонок к созданному лиду" отрабатывает. В чем может быть причина?

Виталий,

Предлагаю убедится, какое именно значение передается в поле Лид карточки Звонка. Для этого добавьте во второй ветке автогенерируемую страницу. С помощью элемента Формула передавать на автогенерируемую страницу Id созданного лида.
Также после создания лида попробуйте добавить таймер с задержкой в 1 секунду, после чего передавать значение Id в карточку звонка.

"Зарицкий Олег Васильевич" написал:Виталий,

Предлагаю убедится, какое именно значение передается в поле Лид карточки Звонка. Для этого добавьте во второй ветке автогенерируемую страницу. С помощью элемента Формула передавать на автогенерируемую страницу Id созданного лида.
Также после создания лида попробуйте добавить таймер с задержкой в 1 секунду, после чего передавать значение Id в карточку звонка.


Сделал по другому(решил сделать подобно первой ветке), поменял местами запоминание лида со следующими тремя элементами. И о чудо она сработало так как нужно, в чем ошибка я так и не понял(

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

В тестовых целях хочется запустить бизнес-процесс, обрабатывающий большое количество записей.
Подскажите, пожалуйста, как ограничить условием вроде "and rownum = 50" с помощью средств ESQ?

EntitySchemaQuery esq = new EntitySchemaQuery(schema);

Нравится

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

Добрый день!

Как на 7.6 можно перенести настройки колонок реестров и деталей с одной базы на другую (с базы разработки на тестовую) без SVN? И при этом сделать их для всех пользователей

Спасибо!

Нравится

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

Посмотрите в сторону таблицы SysProfileData , именно там хранятся данные настроек реестра. Первый приоритет настройки пользователя, второй приоритет настройки администратора системы, третий приоритет с ContactId = NULL

SELECT * FROM SysProfileData WHERE [Key] Like '%ContractSection%'

"Щиголь Максим" написал:второй приоритет настройки администратора системы, третий приоритет с ContactId = NULL

"ContactId = NULL" вроде это же и есть "настройки администратора системы"

Протестировал поведение, да, действительно, последние два приоритета сливаются в один.
До каких либо изменений в таблице существует запись с ContactId = NULL, после сохранения "для всех пользователей", именно она и обновляется.
Выходит есть только два приоритета. Пользователь, и ContactId = NULL.

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

Добрый день.

Необходимо создать деталь для стандартного раздела. Но есть определенные требования к функционалу детали:

1. У детали нет страницы редактирования

2. Объект детали хранит следующие связи: Поле1 - связь с объектом стандартного раздела, Поле2 - связь с объектом из справочника "Продукты"

3. У объекта стандартного раздела есть ряд полей: Категория продукта, Тип продукта, Вид продукта.

4. При выборе действия "Добавить" у детали необходимо открыть страницу выбора продуктов, отфильтрованных в соответствии со значениями из п. 3 И после выбора некоего продукта добавить его в реестр детали.

Посдкажите, пожалуйста - в какую сторону смотреть.

Нравится

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

Первым приходит на ум такой алгоритм:

1. У детали таки есть страница редактирования, но она выполняет функцию "страницы выбора продуктов". С одним лукапом, и лейблом с подсказкой пользователю.

2. Необходимые для фильтрации данные из страницы, на деталь, для анализа и фильтрации лукапа, необходимо получать сообщениями(песочница, sandbox) при init детали, и записывать в невидимые поля детали, или атрибуты.

держите :wink:

define('AnPlaceInOpportunityDetail', ['terrasoft', 'AnPlaceInOpportunityDetailResources',"ConfigurationConstants", 'ConfigurationEnums', "RightUtilities"],
    function(Terrasoft, Resources, ConfigurationConstants, enums, RightUtilities) {
        return {
            //entitySchemaName: 'AnPlaceInOpportunity',
            mixins: {},
            attributes: {},
            diff: /**SCHEMA_DIFF*/[
                {
					//заменяем обработчик нажатия на +
                    "operation": "merge",
                    "name": "AddRecordButton",
                    "values": {
                        "click": {bindTo: "onAddRecordButtonClick"},
                        "menu": []
                    }
                }
            ]/**SCHEMA_DIFF*/,
            methods: {
                onAddRecordButtonClick: function() {
					//сначала сохраняем основную карточку, если это добавление/копирование
                    var cardState = this.sandbox.publish("GetCardState", null, [this.sandbox.id]);
                    var isNew = (cardState.state === enums.CardStateV2.ADD ||
                    cardState.state === enums.CardStateV2.COPY);
                    if (isNew) {
                        var args = {
                            isSilent: true,
                            messageTags: [this.sandbox.id]
                        };
                        this.sandbox.publish("SaveRecord", args, [this.sandbox.id]);
                        return;
                    }
                    this.prepareLookupConfig();
                },
                prepareLookupConfig: function() {
                    var filters = Ext.create("Terrasoft.FilterGroup");
                    var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
                        rootSchemaName: "AnPlace"
                    });
                    var IdsExists = [];
                    esq.addColumn("Id");
                    esq.addColumn("AnName");
                    esq.filters.addItem(Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL, "[AnPlaceInOpportunity:AnPlace:Id].AnOpportunity",this.get("MasterRecordId")));
                    esq.getEntityCollection(function(result) { //выбираем те записи, что уже были добавлены, что не показывать их в справочнике
                        if (result.success) {
                            var existFilter = Ext.create("Terrasoft.FilterGroup");
                            var notExistFilter = Ext.create("Terrasoft.FilterGroup");
                            if (result.collection.getItems().length > 0) {
								// перебираем уже добавленные записи и добавляем их в фильтр
								// на самом деле, вместо запроса и перебора надо просто придумать какой-то зубодробительный фильтр
                                    var IdsExists = result.collection.getItems();
                                    var placesId = [];
                                    Terrasoft.each(IdsExists, function(item) {
                                        if (item.get("Id")) {
                                            placesId.push(item.get("Id"));
                                        }
                                    }, this);
                                    existFilter.addItem(Terrasoft.createColumnInFilterWithParameters("Id", placesId));
                                    notExistFilter = Terrasoft.createNotExistsFilter("Id", existFilter);
                            }
                                    var config = { // конфиг для справочника
                                        entitySchemaName: "AnPlace", // объект справочника
                                        multiSelect: true,	// множественный выбор в справочнике
                                        columns: ["AnName"], // отображаемые колонки на странице справочника
                                        hideActions: true // скрываем действия Добавить, изменить и т.д. в окне справочника
                                    };
                                    config.filters = notExistFilter;
                                    this.openLookup(//открываем справочник
										config,
										this.addCallback, // функция-обработчик ответа от окна справочника
										this // контекст для функции-обработчика
									); 
                        }
                    }, this);
                },
                addCallback: function(args){
                    this.showBodyMask();
                    var Ids = args.selectedRows.getKeys(); // получаем массив ИД выбранных записей
                    //var startDate = '';
                    //this.sandbox.subscribe("StartDate", function() {
 
                    //},this,null);
                    var bq = Ext.create("Terrasoft.BatchQuery"); // пакетный запрос
                    this.Terrasoft.each(Ids, function(item) { // перебираем выбранные записи - для каждой создает Insert и добавляем в BatchQuery
                        var insert = Ext.create("Terrasoft.InsertQuery", {
                            rootSchemaName: "AnPlaceInOpportunity"
                        });
                        var newGuid = Terrasoft.generateGUID();
                        var opportunity = this.get("MasterRecordId");
                        insert.setParameterValue("Id", newGuid , Terrasoft.DataValueType.GUID);
                        insert.setParameterValue("AnOpportunity", opportunity, Terrasoft.DataValueType.GUID);// значения по-умолчанию для добавляемых записей
                        insert.setParameterValue("AnPlace", item, Terrasoft.DataValueType.GUID);
                        bq.add(insert); 
                    }, this);
					// выполняем запрос
                    bq.execute(function(result) { // обработчик
                        if (result.success) {
                            this.hideBodyMask(); // скрыть иконку загрузки
                            this.reloadGridData(); // обнвоить деталь, чтобы добавленные записи отобразились
                        } else console.log("error");
                    }, this);
                },
                addRecordOperationsMenuItems: function(toolsButtonMenu) { // убираем действия Копировать и Изменить, оставляем Удалить
                    /*var copyRecordMenuItem = this.getCopyRecordMenuItem();
                    if (copyRecordMenuItem) {
                        toolsButtonMenu.addItem(copyRecordMenuItem);
                    }
                    var editRecordMenuItem = this.getEditRecordMenuItem();
                    if (editRecordMenuItem) {
                        toolsButtonMenu.addItem(editRecordMenuItem);
                    }*/
                    var deleteRecordMenuItem = this.getDeleteRecordMenuItem();
                    if (deleteRecordMenuItem) {
                        toolsButtonMenu.addItem(deleteRecordMenuItem);
                    }
                }
            },
            messages: {}
        };
    });

Спасибо огромное!

Прощу прощения, на радостях не обратил внимания, что приведенный код для версии 7.6

У нас версия 7.2 и данный код не работает. И вообще возникла проблема с элементарным обменом сообщениями между деталью и основной карточкой.

Простая задача: в зависимости от чекбокса на основной карточке разрешить или запретить добавление элементов в деталь.

1. В коде основной карточки:

добавим чекбокс на страницу

{
					type: Terrasoft.core.enums.ViewModelSchemaItem.ATTRIBUTE,
					name: 'IsProductChoose',
					columnPath: 'IsProductChoose',
					dataValueType: Terrasoft.DataValueType.BOOLEAN,
					visible: true,
					customConfig: {
						click: {
							bindTo: "IsProductChooseClicked"
						}
					}
				}

обработчик изменения чекбокса

this.methods.IsProductChooseClicked = function() {
 
			var args = {
				param: this.get("IsProductChoose")
			};
			sandbox.publish(
				"IsProductChooseChanged",
				args,
				[sandbox.id]
			);
		};

2. В коде детали:

изменим конфиг выпадающего меню

this.modifyUtilsButton = function(utilsButton) {
			var utilsMenuItems = utilsButton.menu.items;
 
			utilsMenuItems[1].caption = "Выбрать продукты";
			utilsMenuItems[1].enabled = {bindTo: "isProductChooseForSelection"};
			return utilsButton;
		};

обработчик "включить/выключить пункт меню"

this.methods.isProductChooseForSelection = function() {			
			this.sandbox.subscribe("IsProductChooseChanged", function(arg) {
				console.log(arg);
			}, this, [this.getSenderSandboxId()]);
		};
 
		this.methods.getSenderSandboxId = function() {
			return this.sandbox.id.replace('_detail_SynchronizedProduct', '');
		};

Однако при открытии карточки получаю ошибку в консоли браузера:

Uncaught Terrasoft.UnsupportedTypeException: Message IsProductChooseChanged is not defined in undefined module

К сожалению в документации не описано как работать с событиями через sandbox.

Буду благодарен за консультацию.

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