Добрый день!



Интересует вопрос:

1) как достать тело письма и "засунуть" его в текстовое поле (как это делает базовые процессы), немного не понимаю процесс (в теле активности текст в html коде)

2) как забирать из письма вложения и добавлять их к вложениям другого объекта?

Нравится

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

Логика создания инцидента по письму реализована в БП IncidentRegistrationFromEmailProcess, его функции RegisterIncidentFromEmail. Там обращаются к вспомогательной схеме IncidentRegistrationFromEmaillHelper. В ней и сделано: функция CreateNewCaseByEmail создаёт запись и наполняет поля, функция ClearHtmlText превращает содержимое тела письма из HTML в текст.

Зверев Александр,

А базовой логики переноса файлов не реализовано?

См. UploadAttachmentsData в ExchangeUtility.

Зверев Александр,

Стартовым сигналом в  БП IncidentRegistrationFromEmailProcess является FinishedSyncSession и если я меняю его на простую активность(письмо) то все рушится. Как в этом плане лучше реализовать регистрацию обращения по письму, не изобретая велосипед. Ведь есть уже практически готовый код, но не могу понять как его переделать.

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

Не могу пронять логику:

- Поступает письмо по имеющемуся обращению

- В активности прикрепляется обращение

Но, если я делаю стартовый сигнал "Новая активность" (входящее письмо) с заполненным обращением - ничего не выходит. А если и запускается процесс (не указываю, что "обращение" заполнено), то в ходе процесса выясняю, что "обращение" не заполнено. 



При этом если я вручную меняю обращение и БП запускается после изменения - все работает как положено.



Вопрос - какой тогда использовать входной сигнал для обработки только что входящей почты с привязанным обращением?

Стандартный БП IncidentRegistrationFromEmailProcess запускается по сигналу «Добавление записи в FinishedSyncSession». Затем Id записей активностей получают из Id сессии при помощи функции GetActivityRecordsId, которая реализована в схеме этого же БП.

Зверев Александр,

Проблема не в том, что я не могу найти активность, а в том что в этой активности БП не видит привязанное обращение. Внешне я вижу, что он определил правильно все. Я могу зайти посмотреть письмо и что оно привязано к нужному обращению. Но сам БП активно игнорирует этот факт.

Значит, на момент срабатывания оно ещё не привязано, а привязка происходит позже, чем отрабатывает Ваша логика.

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

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

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

Пробовал разными способами.

this.showBodyMask();

также пробовал в зависимости карточки добавлять MaskHelper, и далее в коде вызывать MaskHelper.ShowBodyMask() - не срабатывает

 

так же пробовал следующим образом  - var maskId = this.Terrasoft.controls.Mask.show(this);

 так же не работает.

 страница наследуется от Базовая схема карточки ( NUI ).

Ниже приведен код карточки:

define("AbReportSettingsPageV2", ["AbReportSettingsPageV2Resources", "BaseFiltersGenerateModule", "ServiceHelper"], 
	function(Resources, BaseFiltersGenerateModule, ServiceHelper) {
	return {
		entitySchemaName: "AbCustomReport",
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		modules: /**SCHEMA_MODULES*/{}/**SCHEMA_MODULES*/,
		dataModels: /**SCHEMA_DATA_MODELS*/{}/**SCHEMA_DATA_MODELS*/,
		attributes: {
			"AbDateFrom":{
				"dataValueType": Terrasoft.DataValueType.DATE,
				"type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
				"isRequired": true
			},
			"AbDateBy":{
				"dataValueType": Terrasoft.DataValueType.DATE,
				"type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
				"isRequired": true
			},
			"AbCustomReport": {
				"dataValueType": Terrasoft.DataValueType.LOOKUP,
				"type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
				"isLookup": true,
				"referenceSchemaName":"AbCustomReport",
				"isRequired": true
			},
			"City": {
				"dataValueType": Terrasoft.DataValueType.LOOKUP,
				"type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
				"isLookup": true,
				"referenceSchemaName":"City",
			},
			"Country": {
				"dataValueType": Terrasoft.DataValueType.LOOKUP,
				"type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
				"isLookup": true,
				"referenceSchemaName":"Country",
			},
			"Service": {
				"dataValueType": Terrasoft.DataValueType.LOOKUP,
				"type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
				"isLookup": true,
				"referenceSchemaName":"Product",
			}
		},
		diff: /**SCHEMA_DIFF*/[
			{
				"operation": "merge",
				"name": "SaveButton",
				"values": {
					"caption": "$Resources.Strings.AbLoadReportCaption",
					"visible": true
				}
			},
			{
				"operation": "merge",
				"name": "BackButton",
				"values": {
					"visible": true
				}
			},
			{
				"operation": "remove",
				"name": "actions"
			},
			{
				"operation": "remove",
				"name": "Tabs"
			},
			{
				"operation": "remove",
				"name": "PrintButton"
			},
			{
				"operation": "remove",
				"name": "OptionsButtonButton"
			},
			{
				"operation": "insert",
				"name": "AbReportSettingsContainer",
				"parentName": "CardContentContainer",
				"propertyName": "items",
				"values": {
					"itemType": Terrasoft.ViewItemType.CONTAINER,
					"items": [],
					"markerValue": "AbReportSettingsContainer"
				}
			},
			{
				"operation": "insert",
				"name": "AbReportSettingsGridLayoutContainer",
				"parentName": "AbReportSettingsContainer",
				"propertyName": "items",
				"values": {
					"itemType": Terrasoft.ViewItemType.GRID_LAYOUT,
					"items": []
				}
			},
			{
				"operation": "insert",
				"name": "AbDateFrom",
				"values": {
					"layout": {
						"colSpan": 8,
						"rowSpan": 1,
						"column": 0,
						"row": 0,
						"layoutName": "AbReportSettingsGridLayoutContainer"
					},
					"caption": "$Resources.Strings.AbDateFromCaption",
					"bindTo": "AbDateFrom",
					"enabled": true
				},
				"parentName": "AbReportSettingsGridLayoutContainer",
				"propertyName": "items",
				"index": 0
			},
			{
				"operation": "insert",
				"name": "AbDateBy",
				"values": {
					"layout": {
						"colSpan": 8,
						"rowSpan": 1,
						"column": 0,
						"row": 2,
						"layoutName": "AbReportSettingsGridLayoutContainer"
					},
					"caption": "$Resources.Strings.AbDateByCaption",
					"bindTo": "AbDateBy",
					"enabled": true
				},
				"parentName": "AbReportSettingsGridLayoutContainer",
				"propertyName": "items",
				"index": 1
			},
			{
				"operation": "insert",
				"name": "AbCustomReport",
				"values": {
					"layout": {
						"colSpan": 8,
						"rowSpan": 1,
						"column": 0,
						"row": 4,
						"layoutName": "AbReportSettingsGridLayoutContainer"
					},
					"caption": "$Resources.Strings.AbReportCaption",
					"bindTo": "AbCustomReport",
					"enabled": true,
					"contentType": 3
				},
				"parentName": "AbReportSettingsGridLayoutContainer",
				"propertyName": "items",
				"index": 2
			},
			{
				"operation": "insert",
				"name": "City",
				"values": {
					"layout": {
						"colSpan": 8,
						"rowSpan": 1,
						"column": 8,
						"row": 0,
						"layoutName": "AbReportSettingsGridLayoutContainer"
					},
					"caption": "$Resources.Strings.AbCityCaption",
					"bindTo": "City",
					"enabled": true,
					"contentType": 5
				},
				"parentName": "AbReportSettingsGridLayoutContainer",
				"propertyName": "items",
				"index": 3
			},
			{
				"operation": "insert",
				"name": "Country",
				"values": {
					"layout": {
						"colSpan": 8,
						"rowSpan": 1,
						"column": 8,
						"row": 2,
						"layoutName": "AbReportSettingsGridLayoutContainer"
					},
					"caption": "$Resources.Strings.AbCountryCaption",
					"bindTo": "Country",
					"enabled": true,
					"contentType": 5
				},
				"parentName": "AbReportSettingsGridLayoutContainer",
				"propertyName": "items",
				"index": 4
			},
			{
				"operation": "insert",
				"name": "Service",
				"values": {
					"layout": {
						"colSpan": 8,
						"rowSpan": 1,
						"column": 8,
						"row": 4,
						"layoutName": "AbReportSettingsGridLayoutContainer"
					},
					"caption": "$Resources.Strings.AbServiceCaption",
					"bindTo": "Service",
					"enabled": false,
					"contentType": 5
				},
				"parentName": "AbReportSettingsGridLayoutContainer",
				"propertyName": "items",
				"index": 5
			}
		]/**SCHEMA_DIFF*/,
		methods: {
			onEntityInitialized: function () {
				this.callParent(arguments);
				this.set("Name", this.getHeader());
				this.getCurrentContactCountry();
 
			},
			save: function() {
				this.runReportForming();
			},
			getCurrentContactCountry: function () {
				var currentContactId = Terrasoft.SysValue.CURRENT_USER_CONTACT.value;
				var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
					rootSchemaName: "Contact"
				});
				esq.addColumn("Country", "CountryId");
				esq.getEntity(currentContactId, function(result) {
					if (!result.success) {
						this.showInformationDialog(Resources.localizableStrings.AbCurrentUserCountryError);
						return;
					}
					var country = result.entity.get("CountryId");
					if(country!== null || country!== undefined) {
						this.set("Country", country);
					}
				},this)
			},
			runReportForming: function () {
				var startDate = this.get("AbDateFrom");
				var stopDate = this.get("AbDateBy");
				var report = this.get("AbCustomReport");
				var country = this.get("Country");
				var city = this.get("City");
				var service = this.get("Service");
				if(startDate === null || startDate === undefined) {
					this.showInformationDialog(Resources.localizableStrings.AbFillDateStartCaption);
					return;
				}
				if(stopDate === null || stopDate === undefined) {
					this.showInformationDialog(Resources.localizableStrings.AbFillDateStartCaption);
					return;
				}
				if(startDate > stopDate){
					this.showInformationDialog(Resources.localizableStrings.AbIntervalDateWarningCaption);
					return;
				}
				if(report === null || report === undefined) {
					this.showInformationDialog(Resources.localizableStrings.AbFillReportCaption);
					return;
				}
				if(report.value == "c77af1b5-7118-40c7-8636-870edb85d45f" && (city === null || city === undefined)) {
					this.showInformationDialog(Resources.localizableStrings.AbFiillCityWarningCaption);
					return;
				}
				var reportId = report.value;
				countryId = (country === null || country === undefined) ? null : country.value;
				cityId = (city === null || city === undefined) ? null : city.value;
				serviceId = (service === null || service === undefined) ? null : service.value;
 
				var dateStart = Ext.Date.format(startDate, 'Y-m-d');
				var dateStop = Ext.Date.format(stopDate, 'Y-m-d');
				var dataSend = {
					startDate: dateStart,
					stopDate: dateStop,
					reportId: reportId,
					countryId: countryId,
					cityId: cityId,
					serviceId: serviceId
				};
				this.getReportServices(dataSend);
			},
			getReportServices: function(dataSend) {
				var reportId = dataSend.reportId;
				var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
					rootSchemaName: "AbCustomReport"
				});
				esq.addColumn("Name", "ReportName");
				esq.addColumn("AbSourceCodeServiceName", "SourceCodeServiceName");
				esq.getEntity(reportId, function(result) {
					if (!result.success) {
						this.showInformationDialog("Ошибка запроса данных");
						return;
					}
					var reportName =  result.entity.get("ReportName");
					var sourceCodeName =  result.entity.get("SourceCodeServiceName");
					this.downloadReport(sourceCodeName, reportName, dataSend);
				}, this);
			},
			downloadReport: function(sourceCodeName, reportName, dataSend) {
				debugger;
				this.showBodyMask();
				Terrasoft.AjaxProvider.request({
					url: "../rest/" + sourceCodeName + "/GetReportUrl",
					headers: {
						"Accept": "application/json",
						"Content-Type": "application/json"
					},
					method: "POST",
					jsonData: dataSend,
					callback: function (request, success, response) {
						var responseObject = {};
						if (success) {
							responseObject = Terrasoft.decode(response.responseText);
							var key = "/" + responseObject.GetReportUrlResult;
							var reportCaption = reportName + ".xlsx";
							var report = document.createElement("a");
							report.href = "../rest/" + sourceCodeName + "/GenerateSalaryReport" + key;
							report.download = reportCaption;
							document.body.appendChild(report);
							report.click();
							document.body.removeChild(report);
						}
						this.hideBodyMask();
					}, scope: this
				});
			},
			getHeader: function() {
				return Resources.localizableStrings.AbPageSettingsCaption;
			}
		},
		rules: {},
		businessRules: /**SCHEMA_BUSINESS_RULES*/{}/**SCHEMA_BUSINESS_RULES*/
	};
});

 

Нравится

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

Добрый день.

Для вызова маски из this необходимо проверить, не потерялся ли контекст где-то по стэку вызова.  При правильном this метод showBodyMask(); должен работать.

Для вызова из MaskHelper необходимо модуль подключить в define.

define("AbReportSettingsPageV2", ["AbReportSettingsPageV2Resources", "BaseFiltersGenerateModule", "ServiceHelper", "MaskHelper"], 
	function(Resources, BaseFiltersGenerateModule, ServiceHelper, MaskHelper) 

В консоли ошибки есть?

Пащенко Александр Сергеевич,

В зависимости MaskHelper добавлял. Во всех трех случаях которые я описал ошибок в консоли нет.

Нигрескул Алексей,

попробуйте так:

 

var maskId = Terrasoft.MaskHelper.showBodyMask();

Terrasoft.MaskHelper.hideBodyMask(maskId);

 

Нигрескул Алексей,

Страница настроек случайно не в SystemDesigner вставлена?

Варфоломеев Данила,

Да, туда.

Есть какие то особенности в данной реализации?

Нигрескул Алексей,

Ну как сказать. Я уперся в те же самые проблемы, перепробовал все способы, маска так и не заработала. Убил часа 3, потом просто плюнул на это дело. Только в дизайнере такой бред происходит.

Принял, благодарю. А ТП ничего не говорит по данному поводу?

Cм. пример использования showBodyMask и hideBodyMask. 

Зверев Александр,

Все перепробовал - не работает.

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

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

Добрый день!

 

Версия 7.12.3.

Столкнулся с проблемой, одному из пользователей (upd: на самом деле не одному, проблемы точно нет у cупервизора) система не позволяет менять/удалять инфу на детали Средства связи в Контакте (добавлять разрешает). Происходит это не у всех контактов, только на некоторых, системы еще не обнаружил (upd: обнаружил - сбой на контактах, заведенных после 16 августа сего года включительно).

BatchQuery возвращает сообщение об ошибке из темы, сама CRM показывает:

Изображение удалено.

Права на объект вроде по умолчанию.

Изображение удалено.

Что это за глюк?

Нравится

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

Попробуйте перераздать права на через решение Access rights setup wizard for bpm'online

А объект "Контакт" администрируется?

Судя по настройкам, нет.

Хотя на вкладке "Доступ к записям по умолчанию:" есть записи.

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

Не, сообщение приходит в ответ на 0/DataService/json/SyncReply/BatchQuery.

"Трасформируется" (неправильно) для вывода на экран в методе onSaved от BaseCommunicationDetail.

 

Борис Леонов,

Добрый день! В объекте детали ContactCommunication в свойстве "Доступ" у вас стоят где нибудь выбранные пункты?("По операциям, по колонкам и др."). Для полного доступа галочки напротив данных полей должны отсутствовать.

 

engineer7,

нет, галочки не установлены

Борис Леонов,

возможно данная статья сможет Вам помочь: https://academy.terrasoft.ru/documents/sales-enterprise/7-10/detal-dostup-k-obektu-razdela-dostup-k-obektam

engineer7,

на детали, указанной в статье, пусто, нет ни одной записи

Опыты показали, проблема не у одного пользователя, и затрагивает контакты, заведенные в систему после 16 августа сего года включительно...

А нет другой логики на сигналы с детали (в том числе на уровне объектных бп)?

Если есть доступ в базу, можно посмотреть скрытые в интерфейсе таблицы прав SysContactRight и SysContactCommunicationRight, если они есть. Отфильтровать по Id контактов и записей на детали средств связи, взяв примеры, созданные до и после 16. Может, права почему-то всё же выдались. Например, если тогда кто-то менял настройки прав для объекта и вернул как было.

Также можно попробовать включить по объектам Contact и/или ContactCommunication права по записям, а потом обратно выключить. Возможно, при прошлом выключении был сбой и не выключилось полностью. Лучше перед этим сделать бекап или проверять на копии базы.

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

 

 

Попробуйте перераздать права на через решение Access rights setup wizard for bpm'online

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

Коллеги доброго времени суток!

Реализовал деталь с редактируемым реестром и карточкой редактирования записи код ниже:

define("AbAccountProductDetail",
	["ConfigurationEnums", "ConfigurationGrid", "ConfigurationGridGenerator", "ConfigurationGridUtilities"],
	function (enums) {
		return {
			entitySchemaName: "AbAccountProduct",
			attributes: {
				"IsEditable": {
					dataValueType: Terrasoft.DataValueType.BOOLEAN,
					type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
					value: true
				}
			},
			mixins: {
				ConfigurationGridUtilities: "Terrasoft.ConfigurationGridUtilities"
			},
			methods:{
				init: function() {
					this.callParent(arguments);
					this.sandbox.subscribe("[AbAccountProductEntityEventListner]:[RequestToPlatform]:[Exception]", this.accountProductEntityListnerMsgHandler, this);
				},
 
				/*Overrided "BaseGridDetailV2"*/
				editRecord: function(record) {
					var activeRow = record || this.getActiveRow();
					if (!activeRow) {
						return;
					}
					if (!this.getIsCardValid()) {
						return;
					}
					var isCardChanged = this.getIsCardChanged();
					var primaryColumnValue = typeof activeRow === "object" ? activeRow.get(activeRow.primaryColumnName) : activeRow;
					var typeColumnValue = this.getTypeColumnValue(activeRow);
					this.setLastActiveRow(primaryColumnValue);
					if (isCardChanged) {
						this.set("CardState", enums.CardStateV2.EDIT);
						this.set("EditPageUId", typeColumnValue);
						this.set("PrimaryValueUId", primaryColumnValue);
						var args = {
							isSilent: true,
							messageTags: [this.sandbox.id]
						};
						this.sandbox.publish("SaveRecord", args, [this.sandbox.id]);
					} else {
						this.openCard(enums.CardStateV2.EDIT, typeColumnValue, primaryColumnValue);
					}
				},
 
				accountProductEntityListnerMsgHandler: function() {
					Terrasoft.ServerChannel.on(Terrasoft.EventName.ON_MESSAGE,
					function(scope, message){
							if (!message || message.Header.Sender !== "[AbAccountProductEntityEventListner]:[RequestToPlatform]:[Exception]") {
							return;
						}
						var message2 = message.Body;
						if (!this.Ext.isEmpty(message2)) {
							window.console.info(message2);
							this.Terrasoft.showInformation(message2);
						}
					}, this);
				}
			},
			diff: [
				{
					"operation": "merge",
					"name": "DataGrid",
					"values": {
						"className": "Terrasoft.ConfigurationGrid",
						"generator": "ConfigurationGridGenerator.generatePartial",
						"generateControlsConfig": { "bindTo": "generateActiveRowControlsConfig" },
						"changeRow": { "bindTo": "changeRow" },
						"unSelectRow": { "bindTo": "unSelectRow" },
						"onGridClick": { "bindTo": "onGridClick" },
						"activeRowActions": [
							{
								"className": "Terrasoft.Button",
								"style": this.Terrasoft.controls.ButtonEnums.style.TRANSPARENT,
								"tag": "save",
								"markerValue": "save",
								"imageConfig": { "bindTo": "Resources.Images.SaveIcon" }
							},
							{
								"className": "Terrasoft.Button",
								"style": this.Terrasoft.controls.ButtonEnums.style.TRANSPARENT,
								"tag": "card",
								"markerValue": "card",
								"imageConfig": { "bindTo": "Resources.Images.CardIcon" }
							},
							{
								"className": "Terrasoft.Button",
								"style": this.Terrasoft.controls.ButtonEnums.style.TRANSPARENT,
								"tag": "cancel",
								"markerValue": "cancel",
								"imageConfig": { "bindTo": "Resources.Images.CancelIcon" }
							},
							{
								"className": "Terrasoft.Button",
								"style": this.Terrasoft.controls.ButtonEnums.style.TRANSPARENT,
								"tag": "remove",
								"markerValue": "remove",
								"imageConfig": { "bindTo": "Resources.Images.RemoveIcon" }
							}
						],
						"initActiveRowKeyMap": { "bindTo": "initActiveRowKeyMap" },
						"activeRowAction": { "bindTo": "onActiveRowAction" },
						"multiSelect": false
					}
				}
			]
		};
	});

Пришлось переопределить editRecord():

 

var primaryColumnValue = typeof activeRow === "object" ? activeRow.get(activeRow.primaryColumnName) : activeRow;

т.к. дальше не срабатывало.

сейчас работает - http://prntscr.com/lfe61n

но при переходе на страницу редактирования поле для связи не заполняется - http://prntscr.com/lfe6k4

Подскажите в чем может быть дело?

Заранее благодарен.

Нравится

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

Попробуйте например вызывать openCardInChain и передавать туда значения по умолчанию

 

addChildRecord: function(typeColumnValue, date) {
var selectedItem = this.get("ActiveRow");
var defaultValues = [];
defaultValues.push({
	name: "ParentId",
	value: typeColumnValue
});
defaultValues.push({
	name: "UsrDate",
	value: date
});
var config = {
	sandbox: this.sandbox,
	schemaName: this.getEditPageSchemaName(typeColumnValue),
	operation: ConfigurationEnums.CardStateV2.ADD,
	moduleId: this.getChainCardModuleSandboxId(typeColumnValue),
	defaultValues: defaultValues
};
this.openCardInChain(config);
},

 

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

В разделе справочников есть с редактируемым реестром и карточкой, там всё работает; нужно посмотреть, как там сделали.

Попробуйте например вызывать openCardInChain и передавать туда значения по умолчанию

 

addChildRecord: function(typeColumnValue, date) {
var selectedItem = this.get("ActiveRow");
var defaultValues = [];
defaultValues.push({
	name: "ParentId",
	value: typeColumnValue
});
defaultValues.push({
	name: "UsrDate",
	value: date
});
var config = {
	sandbox: this.sandbox,
	schemaName: this.getEditPageSchemaName(typeColumnValue),
	operation: ConfigurationEnums.CardStateV2.ADD,
	moduleId: this.getChainCardModuleSandboxId(typeColumnValue),
	defaultValues: defaultValues
};
this.openCardInChain(config);
},

 

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

Всем доброго времени суток!

Коллеги нужна Ваша помощь. Необходимо в С# коде получить идентификатор SystemUser.

http://prntscr.com/lczocy

Хардкодом как то не очень хочется забивать.

Данный идентификатор необходим для работы в событийном слое Entity.

Заранее благодарен.

Нравится

3 комментария
Лучший ответ
//получить значение системной настройки
var mySystemUser=Terrasoft.Core.Configuration.SysSettings.GetValue(UserConnection, "SystemUser");
//получить значение системной настройки
var mySystemUser=Terrasoft.Core.Configuration.SysSettings.GetValue(UserConnection, "SystemUser");

Ну или, учитывая что система по-умолчанию работает от Supervisor-a:

UserConnection.AppConnection.SystemUserConnection.CurrentUser.Id

Не уверен, что сработает, но вроде должно)

Вставлю свои 5 копеек

в Terrasoft.Configuration определена константа id супервизора 

BaseConsts.SystemUserId 

 

 

 

 

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

Коллеги всем доброго времени суток. Подскажите, кто нибудь работал с событийным слоем Entity.

Написал класс обработчик в качестве примера для обучения скомпилировал приложение упал сайт.

Вот пример класса обработчика:

 

using System;
using System.Web;
using Terrasoft.Core;
using Terrasoft.Core.Entities;
using Terrasoft.Core.Entities.Events;
 
 
namespace Terrasoft.Configuration
{
    [EntityEventListener(SchemaName = "Order")]
    public class OrderEntityEventListener : BaseEntityEventListener
    {
 
        private UserConnection _userConnection;
 
        private LogService _logService;
        public UserConnection UserConnection
        {
            get
            {
                if (_userConnection != null)
                {
                    return _userConnection;
                }
                _userConnection = HttpContext.Current.Session["UserConnection"] as UserConnection;
                if (_userConnection != null)
                {
                    return _userConnection;
                }
                return _userConnection;
            }
 
            set { _userConnection = value; }
        }        
 
        public LogService LogService { get => _logService; set => _logService = value; }
 
        public OrderEntityEventListener()
        {
            LogService = new LogService(UserConnection);
        }
 
        public override void OnInserting(object sender, EntityBeforeEventArgs e)
        {
            try
            {
                base.OnInserting(sender, e);
 
                var systemUserName = UserConnection.CurrentUser.ContactName;
                var systemUserId = UserConnection.CurrentUser.ContactId;
                LogService.RecInfo($"LOG:[OnInserting]:systemUserId: {systemUserId}; systemUserName:{systemUserName}");
            }
            catch (Exception exception)
            {
                LogService.RecInfo($"LOG:[OnInserting]:Exception: {exception.Message}; {exception.InnerException}");
            }
 
        }
    }
}

Подскажите, что тут не так? Если кто работал, скинте пожалуйста примеры.

Заранее благодарен

Нравится

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

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

UserConnection необходимо получать из sender

Пример:

public override void OnSaved(object sender, EntityAfterEventArgs e) {

            base.OnSaved(sender, e);

            var entity = (Entity) sender;

            var userConnection = entity.UserConnection;

        }

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

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

UserConnection необходимо получать из sender

Пример:

public override void OnSaved(object sender, EntityAfterEventArgs e) {

            base.OnSaved(sender, e);

            var entity = (Entity) sender;

            var userConnection = entity.UserConnection;

        }

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

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

Добрый день, коллеги!



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

  1. настроить необходимое в разделе [Лиды] в CRM
  2. сформировать ссылку в письме с нужными параметрами
  3. на сайте проверять параметры в ссылке

  4. при необходимости ставить куку - флаг первого посещения
  5. если посещение первое, то динамически создавать и программно сабмитить форму

Если кто то уже решал такую задачу более просто, опишите процесс концептуально (или подтвердите верность моего видения :), расскажите, какие подводные камни могут быть?

Нравится

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

Нужно разбирать и переделывать стандартный код, который сгенерирован при создании нового лендинга и предназначен для вставки на сайт. Он работает с веб-сервисом, находящимся по адресу:

http://адрес_сайта.com/0/ServiceModel/GeneratedObjectWebFormService.svc/SaveWebFormObjectData

Клиентская логика — в общедоступных скриптах:

https://webtracking-v01.bpmonline.com/JS/track-cookies.js
https://webtracking-v01.bpmonline.com/JS/create-object.js

Серверная — в схеме GeneratedObjectWebFormService.

Другой вариант — писать полностью свой сервис с нужной функциональностью, доступный извне без авторизации.

Спрашивали - отвечаем.

Если в ссылке будет передан параметр bpmContactEmail="email@email.email", этот код зарегистрирует лида и привяжет к контакту с почтовым ящиком email@email.email. Ловушка для лида должна быть настроена в разделе [Лендинги].

/*Форма авторегистрации по ссылке из Email*/
					var configAutoReg = {
						fields: {
							"Email": ".bpm-contact-email" // Email посетителя
						},
					    landingId: "00000000-0000-0000-0000-000000000000", // ID лендинга, смотреть в шаге 2 настройки лендинга
					    serviceUrl: "http://CRM_SITE_DNS_NAME/0/ServiceModel/GeneratedWebFormService.svc/SaveWebFormLeadData",
						redirectUrl: ""
					};
					function createLeadFromAutoReg() {
						landing.createObjectFromLanding(configAutoReg)
					};
 
					//функция позволяет выполнять действие (регистрацию лида) один раз
					//взята тут https://developer.mozilla.org/ru/docs/Web/API/Document/cookie#Example_5_Do_something_only_once_%E2%80%93_a_general_library
					function executeOnce () {
						var argc = arguments.length, bImplGlob = typeof arguments[argc - 1] === "string";
						if (bImplGlob) { argc++; }
						if (argc < 3) { throw new TypeError("executeOnce - not enough arguments"); }
						var fExec = arguments[0], sKey = arguments[argc - 2];
						if (typeof fExec !== "function") { throw new TypeError("executeOnce - first argument must be a function"); }
						if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { throw new TypeError("executeOnce - invalid identifier"); }
						if (decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) === "1") { return false; }
						fExec.apply(argc > 3 ? arguments[1] : null, argc > 4 ? Array.prototype.slice.call(arguments, 2, argc - 2) : []);
						document.cookie = encodeURIComponent(sKey) + "=1; expires=Fri, 31 Dec 9999 23:59:59 GMT" + (bImplGlob || !arguments[argc - 1] ? "; path=/" : "");
						return true;
					};
 
					// функция возвращает значение указанного парметра из ссылки
					function getSearchParams(k){
						var p={};
						location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi,function(s,k,v){p[k]=v})
						return k?p[k]:p;
					};
 
					jQuery(document).ready(function() {
						var bpmContactEmail = getSearchParams("bpmContactEmail");
						if (bpmContactEmail && bpmContactEmail !== "[") {
 
							executeOnce(function() {
								landing.initLanding(configAutoReg);								
								var $form = $("<form />", { style: "display: none;", onSubmit: "createLeadFromAutoReg(); return false" });
								$form.append($("<input />", { class: "bpm-contact-email" , name: "bpmContactEmail", value: bpmContactEmail}));
								$form.appendTo("body").submit().remove();
							},
							"bpmLeadFromEmail");
 
						}
					});

 

Зверев Александр,

да не, все проще оказалось, в bpm все настройки только стандартные, а на сайте легкая доработка :)

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

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

Добрый день. Подскажите пожалуйста, по какой причине может не быть прав доступа на редактирование итогов для роли администраторов, в частности под пользователем Supervisor. Доступ на операцию Настройка аналитики проверял, роль администраторы там есть. Supervisor в роль Администраторы добавлен. Куда копать не понятно...

Изображение удалено.

Нравится

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

Для решения проблемы попробуйте актуализировать роли.

Если актуализация ролей не решит проблему, попробуйте ещё очистить Redis.

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

Ефанов Иван Александрович пишет:

Скажите, а дело может быть в лицензиях?

Думаю, что дело не в лицензиях.

Проверьте ещё настройку прав доступа к объекту SysDashboard на локальной версии и на тестовом сервере.

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

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

Так и сделал сразу, не помогло к сожалению(

А если открыть пункт «Настроить права доступа», который на Вашем скриншоте не заблокирован, и настроить там?

Ефанов Иван Александрович,

Попробуйте скомпилировать все в конфигурации.

Алла Савельева пишет:

Ефанов Иван Александрович,

Попробуйте скомпилировать все в конфигурации.

Тоже не повлияло никак. Пункт Настроить права доступа вызывает настройки прав к конкретному графику, там все ок.

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

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

Кстати, о лицензиях. Если на базе кончилась полноценная лицензия и включилась read only, то действительно кнопки добавления и изменения залочатся. Но сразу везде, и в реестрах разделов тоже.

 

Мы такое решали с поддержкой. Решили исправительным запросом на ТОЙ стороне вроде.

Дмитрий Степанов пишет:

Мы такое решали с поддержкой. Решили исправительным запросом на ТОЙ стороне вроде.

Дмитрий, а у вас не сохранился код запроса? Кстати о правах, еще есть тестовая среда, там настройка прав не выполнялась, но были загружены лицензии клиента и проблема тоже присутствует. 

Если лицензии read only, все кнопки заблокируются.

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

Колодяжный Владислав Эдуардович пишет:

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

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

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

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

Добрый день!

Столкнулся с проблемой, поле справочника Состояние на детали графика оплат и поставок на вкладке Паспорт в заказе, в справочнике два значения (Выполнер/Не выполнен) и более не предвидится.

Хотелось бы переделать в выпадающий список, но у поля State замещающего объекта SupplyPaymentElement в пакете с моими доработками галка "Cписок" снята и задизейблена.

Как теперь это обойти?

Нравится

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

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

+ MetaData.Schema.D2.["0a3324bb-55c8-4791-b51f-409cfedc6fe2"].E20 true

это добавит признак список к колонке UID которой указан в квадратных скобках

Добавьте нужные Вам значения в справочник 'Состояния элемента графика поставок и оплат'.

Алла Савельева,

нет, мне нужно, что бы это поле на детали отображалось выпадающим списком. Значения в этот справочник добавлять не надо.

Борис Леонов,

Вы можете реализовать эту функциональность на уровне карточки редактирования:

                {

                    "operation": "insert",

                    "parentName": "Header",

                    "propertyName": "items",

                    "name": "State",

                    "values": {

                        "bindTo": "State",

                        "contentType": Terrasoft.ContentType.ENUM,

                        "layout": {"column": 12, "row": 3, "colSpan": 12}

                    }

                },

Или же создать не замещающий объект, а унаследованный от SupplyPaymentElement, тогда признак 'Список' у поля 'State' будет доступен.

Алла Савельева,

У вас первый вариант прям получилось осуществить? Я сразу так сделал, только "operation" не "insert", а "merge", не работает на детали, действует только на карточку.

А второй как провернуть, что бы остальную логику не переделывать или обойтись минимальными доработками?

Борис Леонов,

Верно, первый вариант работает только для карточки редактирования - я так и написала.

А для отображения в виде списка в редактируемом реестре подойдет только второй вариант.

Как добавить унаследованный объект описано в статье, смотрите 'Создание пользовательской схемы объекта' и 'Указание родительского объекта'.

Алла Савельева,

завтра попробую

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

+ MetaData.Schema.D2.["0a3324bb-55c8-4791-b51f-409cfedc6fe2"].E20 true

это добавит признак список к колонке UID которой указан в квадратных скобках

Колодяжный Владислав Эдуардович,

добавление метаданных работает, спасибо!

Интересно, а в методе getCellControlsConfig это можно как то провернуть?

Зверев Александр,

Благодарю!

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

Подскажите, как создать бизнес процесс, который бы мог добавить в шаблон письма несколько строк из раздела CRM?

Например, необходимо из реестра Контрагенты получить всех Контрагентов, которые не имеют заполненного поля: Средства связи-web и отправить письмом каждому ответственному списком, указав название Контрагента.

Нравится

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

Игорь Г. пишет:

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

Можно настроить элемент "Чтение данных" на получение нужного количества записей.

Для этого в параметрах БП объявляете параметр с типом 'Коллекция объектов' и в значения этого параметра добавляете коллекцию из нужного элемента "Чтение данных": https://prnt.sc/l7j5xs

Нужное количество вычитываемых данных указываете в расширенных параметрах элемента "Чтение данных": https://prnt.sc/l7j69n

А потом в элементе "Задание-сценарий" обращаетесь к параметру процесса и вычитываете из него коллекцию:

var userConnection = UserConnection;

for(int i = 0; i < PCBCollection.Count; i++)

{

        EntitySchema schema2 = userConnection.EntitySchemaManager.GetInstanceByName("BTPatienCardBlock");

        Entity entity2 = schema2.CreateEntity(userConnection);

        entity2.SetDefColumnValues();

        entity2.SetColumnValue("Id", Guid.NewGuid());

        entity2.SetColumnValue("Name",                                       PCBCollection[i].GetTypedColumnValue<string>("Name"));

        entity2.Save();

}

return true;

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

Здравствуйте! Я вижу тут 2 варианта.

1) Писать свой макрос для шаблона. Описание как делать свой макрос описана здесь - https://academy.terrasoft.ru/documents/technic-sdk/7-13/dobavlenie-obra…

2) Формировать тело письма непосредственно в БП заносить его в переменную процесса, а в элементе отправки письма подставлять параметр - http://prntscr.com/l7fj1f&nbsp;

http://prntscr.com/l7fkqf и так можно наполнить тело письма разными параметрами.

Игорь Г. пишет:

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

Можно настроить элемент "Чтение данных" на получение нужного количества записей.

Для этого в параметрах БП объявляете параметр с типом 'Коллекция объектов' и в значения этого параметра добавляете коллекцию из нужного элемента "Чтение данных": https://prnt.sc/l7j5xs

Нужное количество вычитываемых данных указываете в расширенных параметрах элемента "Чтение данных": https://prnt.sc/l7j69n

А потом в элементе "Задание-сценарий" обращаетесь к параметру процесса и вычитываете из него коллекцию:

var userConnection = UserConnection;

for(int i = 0; i < PCBCollection.Count; i++)

{

        EntitySchema schema2 = userConnection.EntitySchemaManager.GetInstanceByName("BTPatienCardBlock");

        Entity entity2 = schema2.CreateEntity(userConnection);

        entity2.SetDefColumnValues();

        entity2.SetColumnValue("Id", Guid.NewGuid());

        entity2.SetColumnValue("Name",                                       PCBCollection[i].GetTypedColumnValue<string>("Name"));

        entity2.Save();

}

return true;

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