Вопрос

Добрый день!

Подскажите, пожалуйста, как решить данный кейс?

1. По процессу необходимо отправить клиенту письмо по шаблону, котором должна отображаться продуктовая корзина заказа. 

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

 

2. Как в HTML код шаблона вставить динамические поля? возможно ли это?

 

Спасибо

 

Сейчас версия 7.13, на проде будет 7.14

У меня такой же вопрос

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

1. Полностью решить такую задачу с помощью "родных" элементов не получится. Вам понадобится пользовательское действие, которое формирует html-таблицу из выборки по объекту. Не нашел его на маркетплейсе, но я точно его скачивал где-то на ресурсах Террасофта, возможно в том же маркетплейсе, просто искал плохо. Называется "Таблица детали для email". С помощью него Вы можете в процессе читать ваш шаблон, затем с помощью пользовательского действия формировать таблицу. После этого в элемент отправки письма вставляете html-текст шаблона + таблица. Должно сработать :)

2. Расскажите подробнее что Вы хотите отобразить в динамических полях. В шаблонах доступны макросы, которые подставляют имя, фамилию адресата и данные объекта, из которого создается письмо. Возможно это Вам и нужно.

Ксензов Егор пишет:

1. Полностью решить такую задачу с помощью "родных" элементов не получится. Вам понадобится пользовательское действие, которое формирует html-таблицу из выборки по объекту. Не нашел его на маркетплейсе, но я точно его скачивал где-то на ресурсах Террасофта, возможно в том же маркетплейсе, просто искал плохо. Называется "Таблица детали для email". С помощью него Вы можете в процессе читать ваш шаблон, затем с помощью пользовательского действия формировать таблицу. После этого в элемент отправки письма вставляете html-текст шаблона + таблица. Должно сработать :)

Егор, тысяча благодарностей, будем пробовать.

 

Ксензов Егор пишет:

2. Расскажите подробнее что Вы хотите отобразить в динамических полях. В шаблонах доступны макросы, которые подставляют имя, фамилию адресата и данные объекта, из которого создается письмо. Возможно это Вам и нужно.

Егор, предположим  у нас есть html-код, который нам предоставил клиент. Это код из сайта продуктовой корзины(например). В данном коде нам необходимо отобразить какие-то поля из системы. Или надо отдельными блоками добавляться блок с динамиечскими полями из пользовательского макроса, отдельно блок html?

Также см. обсуждение.

И в маркете нашлось очень по описанию похожее на Вашу потребность решение: Работа с поставщиками по товарам «под заказ»:

  • Отправка e-mail уведомлений со списком продуктов покупателям и поставщикам (в зависимости от типа заказа).

Виктория Мун,

Если не подойдут готовые решения, на которые указал Александр, то у Вас есть два пути:
1. Как вы и указали, делать блоками: часть письма будет шаблоном из справочника, часть - html'ем клиента. В бизнес-процессе уже будете склеивать шаблон письма из справочника + таблица + код клиента. Это подойдет если поля из макроса нужно указывать только в определенном блоке письма, например, в приветствии (т.е. заменить часть кода клиента на шаблон из справочника, в который подставляются макросы).
2. Обрабатывать весь html клиента в скрипт-таске.

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

Добрый день!

Тема уже поднималась, но не до конца понял.

Сделал замещающий клиентский модуль , добавил в diff поле UsrCardNumber (оно Integer). Но не отображает значение.

И тут же задам вопрос - есть возможность поиска по числовым полям? Я указал в свойствах поля индексировать, но видимо это другое. Или поиск возможен только текстовым полям? Очень скудная документация по настройке поиска(

 

define("ContactSearchRowSchema", [], function() {
	return {
 
		diff: /**SCHEMA_DIFF*/[
			{
				"operation": "remove",
				"name": "EntitySchemaCaption"
			},
			{
				"operation": "merge",
				"name": "FoundColumnsContainerList",
				"values": {
					"layout": {
						"column": 0,
						"row": 2,
						"colSpan": 12
					}
				}
			},
			{
				"operation": "insert",
				"parentName": "DataContainer",
				"propertyName": "items",
				"name": "Job",
				"values": {
					"layout": {
						"column": 12,
						"row": 0,
						"colSpan": 6
					}
				}
			},
			{
				"operation": "insert",
				"parentName": "DataContainer",
				"propertyName": "items",
				"name": "UsrCardNumber",
				"values": {
                        "enabled": true,
                        "visible": true,
					"layout": {
						"column": 18,
						"row": 0,
						"colSpan": 6
					}
				}
			},
			{
				"operation": "insert",
				"parentName": "DataContainer",
				"propertyName": "items",
				"name": "Account",
				"values": {
					"layout": {
						"column": 0,
						"row": 1,
						"colSpan": 12
					}
				}
			},
			{
				"operation": "insert",
				"parentName": "DataContainer",
				"propertyName": "items",
				"name": "Email",
				"values": {
					"className": "Terrasoft.Hyperlink",
					"href": {bindTo: "getEmailUrl"},
					"click": {bindTo: "onEmailUrlClick"},
					"layout": {
						"column": 18,
						"row": 1,
						"colSpan": 6
					}
				}
			},
			{
				"operation": "insert",
				"parentName": "DataContainer",
				"propertyName": "items",
				"name": "MobilePhone",
				"values": {
					"layout": {
						"column": 12,
						"row": 1,
						"colSpan": 6
					}
				}
			}
 
 
		]/**SCHEMA_DIFF*/
	};
});

 

У меня такой же вопрос

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

Поиск идёт только по тексту, используется движок для полнотекстового поиска Elastic SearchВ ConfItemSearchRowSchema номера InventoryNumber и SerialNumber сделаны текстовыми, по ним ищет. То есть лучше и новое поле UsrCardNumber делать текстовым, либо дублировать в текстовое поле значение из числового. 

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

Где найти справочник способов оплаты?

У меня такой же вопрос

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

Добрый день!
Может тип оплаты?

Добрый день!
Может тип оплаты?

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

Получаю по oData данные счета и клиента, необходимо понять является ли клиент контрагентом или контактом. Как это сделать? 

У меня такой же вопрос

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

В счетах поле 'Клиент' составное и состоит оно из полей 'Контрагент' и 'Контакт'.

Таким образом, Вам нужно проанализировать, если поле 'Контакт' пусто, значит клиент является юр. лицом (контрагентом), если заполнено - физ.

В счетах поле 'Клиент' составное и состоит оно из полей 'Контрагент' и 'Контакт'.

Таким образом, Вам нужно проанализировать, если поле 'Контакт' пусто, значит клиент является юр. лицом (контрагентом), если заполнено - физ.

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

В общем задача на первый взгляд простая.

Нужно в одной из страниц переписать метод кнопки Закрыть, чтобы в одном из случаев перехода (в случае если адрес предыдущей страницы был XXX (нужен URL c параметрами, просто document.referrer не подойдет) , то поменять адрес возврата.

Метод onCloseCardButtonClick переопределил, но не могу понять, как мне URL получить предыдущей страницы.

Уже все перерыл.

var router = Terrasoft.router.Router;

hash = router.getHash(); - тут только текущий адрес карточки

 

 

У меня такой же вопрос

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

Буквально утром отвечал на похожий вопрос, посмотрите эту ветку

Буквально утром отвечал на похожий вопрос, посмотрите эту ветку

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

По какому адресу нужно обратится чтобы получить данную информацию?

https://<domen>/0/ServiceModel/EntityDataService.svc/?????

У меня такой же вопрос

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

Добрый день!
InvoicePaymentStatusCollection

Добрый день!
InvoicePaymentStatusCollection

Сидоров Александр В.,

Большое спасибо!

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

Вопрос такой - через oData создаю объект, например, контакт, но БП настроенный на добавление объекта не запускается. Куда копать?

У меня такой же вопрос

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

SERGEY PIMINOV,

Под внутренними бизнес-процессами подразумеваются событийные процессы объекта. Сделайте автонумерацию по аналогии с этим примером.

Добрый день!
Действительно почему то не работают сигнала по созданию объектов через OData.
Но работают сигналы во внутреннем бизнез процессе, например ContactInserted

Сидоров Александр В.,

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

SERGEY PIMINOV,

Под внутренними бизнес-процессами подразумеваются событийные процессы объекта. Сделайте автонумерацию по аналогии с этим примером.

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

В общем задам вопрос отдельно.

В  ActivitySectionV2 я добавил свой атрибут

 "activeLeadId":{
           	DataValueType: Terrasoft.DataValueType.STRING
           }

В методе загрузки представления я устанавливаю его значение через  this.set  -  все замечательно, вывожу сообщение - там то, что мне нужно.

А вот затем я формирую состав Url для iframe и мне нужно получить его значение. Не пойму как. Через  this.get не работает.

 diff: /**SCHEMA_DIFF*/[
        	 {
                "operation": "insert",
                "name": "iframe",
                "parentName": "DataViewsContainer",
                "propertyName": "items",
                "values": {
                    "id": "iframe",
                    "selectors": {"wrapEl": "#iframe"},
                    "itemType": Terrasoft.ViewItemType.CONTAINER,
					"layout": { "colSpan": 24, "rowSpan": 1, "column": 0, "row": 4 },
					"html": '<iframe name="iframe" id="iframe" width="100%" height="700px"' +
			  		'src="https://domain.com/base.php'+
					'?owner='+
					Terrasoft.SysValue.CURRENT_USER.value + 
					'&leadid='+ 
 
	//Вот сюда надо получить значение атрибута
 
			   		'" frameborder="0"></iframe>',
                    "visible": {"bindTo": "isTimelineDataView"}
             	}

 

У меня такой же вопрос

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

Создайте атрибут с нужной вам ссылкой (пecnfyjdbnm его например в init)

забиньте  html на ваш атрибут с сылкой

Пример можно посмотреть в методе getRecommendationConfig схемы GlobalSearchResultPage

 

Григорий Чех,

Я вроде пробую забиндить, только не получается, те пишу в Init пишу

this.set("iframeUrl", '<iframe name="iframe" id="iframe" width="100%" height="700px"' +
					'src="https://domain.com'+
					'" frameborder="0"></iframe>');

Затем в Diff

diff: /**SCHEMA_DIFF*/[
        	 {
                "operation": "insert",
                "name": "iframe",
                "parentName": "DataViewsContainer",
                "propertyName": "items",
                "values": {
                    "id": "iframe",
                    "selectors": {"wrapEl": "#iframe"},
 
                    "itemType": Terrasoft.ViewItemType.CONTAINER,
					"layout": { "colSpan": 24, "rowSpan": 1, "column": 0, "row": 4 },
				    "html":{"bindTo": "iframeUrl"},
                    "visible": {"bindTo": "isTimelineDataView"}
             	}
             },

И ничего не выводит, только заголовок секции - дальше пусто. Если в "html" прописываю код напрямую, то все работает.  

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

Те через переменную сформировать нужный html получается, но выводится старое значение, через атрибут не получается получить значение(( 

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

 

 В итоге, вместо атрибута я смог вставлять туда функцию обычную, но вот проблема - она не вызывается, если не делать рефреш страницы. Те неважно, какой там код внутри DIFF, он просто не вызывается. Те получается, неважно как я получаю ID, будь то URL или любой другой способ, чтобы вставить его в iframe - нужно как-то его обновить принудительно.

Вот вопрос - как это побороть?

Вот полный исходный код на текущий момент. Куда копать в части принудительного обновления и/или сброса  кэша?

 

define("ActivitySectionV2", [], function() {
 
	var currentLocation = '';
	var leadGuid = '';
 
	 function getFrame()
        {
        	currentLocation = window.location+'';
        	leadGuid = getAllUrlParams(currentLocation).guid;
        	if (leadGuid != null) 
        	{
        	var strFrame = '<iframe name="iframe" id="iframe" width="100%" height="700px"' +
					'src="https://domain.com'+
					'/'+
		             Terrasoft.SysValue.CURRENT_USER.value + 
					'/'+ 
			 		leadGuid +
					'" frameborder="0"></iframe>';
 
        	}
			else
			{
				var strFrame = '<iframe name="iframe" id="iframe" width="100%" height="700px"' +
					'src="https://domain.com'+
					'/'+
		             Terrasoft.SysValue.CURRENT_USER.value + 
					'" frameborder="0"></iframe>';
 
			}	
			alert(strFrame);
        	return strFrame;
        }
 
	//Функция получения параметра из businessRules
	  function getAllUrlParams(url) {
            //код функции .....
      return obj;
      }
 
    return {
        entitySchemaName: "Activity",
        mixins: {},
        attributes: {
            "isTimelineVisible": {
                dataValueType: Terrasoft.DataValueType.BOOLEAN
            },
 
           // "activeLeadId":{
           //	DataValueType: Terrasoft.DataValueType.STRING
           //},
           // "iframeUrl":{
           //	DataValueType: Terrasoft.DataValueType.TEXT
           // }
 
        },
        messages: {},
        methods: {
 
        	getDefaultDataViews: function(){
        		var baseDataViews = this.callParent();
        		baseDataViews.TimelineDataView = {
        			index: 3,
                    name: "TimelineDataView",
                    caption: "Расписание",
                    hint: "Таймлайн",
                    icon: this.get("Resources.Images.SchedulerDataViewIcon")
        		};
        		return baseDataViews;
        	},
 
        	loadTimelineDataView: function(loadData) {
        		this.set("IsActionButtonsContainerVisible", true);
        		this.set("isTimelineVisible", true);
                this.set("IsAnalyticsActionButtonsContainerVisible", false);
                if (loadData === false) {
                        return;
                }
        	},
        	isTimelineDataView: function() {
                 return (this.get("ActiveViewName") === "TimelineDataView");
        	},
        	isNotTimelineDataView: function() {
                 return !this.isTimelineDataView();
        	}
        },
        rules: {},
        businessRules: /**SCHEMA_BUSINESS_RULES*/{}/**SCHEMA_BUSINESS_RULES*/,
        modules: /**SCHEMA_MODULES*/{}/**SCHEMA_MODULES*/,
        diff: /**SCHEMA_DIFF*/[
        	 {
                "operation": "insert",
                "name": "iframe",
                "parentName": "DataViewsContainer",
                "propertyName": "items",
                "values": {
                    "id": "iframe",
                    "selectors": {"wrapEl": "#iframe"},
                    "itemType": Terrasoft.ViewItemType.CONTAINER,
					"layout": { "colSpan": 24, "rowSpan": 1, "column": 0, "row": 4 },
		   			"html":getFrame(),
                    "visible": {"bindTo": "isTimelineDataView"}
             	}
             },
             {
             	"operation": "merge",
                "name": "GridUtilsContainer",
                "propertyName": "items",
                "values": {
                	'visible': {"bindTo": "isNotTimelineDataView"}
                }
             }
        ]/**SCHEMA_DIFF*/
    };
});

 

Для обновления страницы используют this.reloadEntity(); 

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

 А тут страница раздела, не срабатывает(( Пишет this.reloadEntity() is not a function. Я уже все перепробовал - не совсем пойму, вот у меня в DIFF формируется html и там есть вызов функции (getIframe). Так вот она отрабатывает только если F5 сделать, и все. Такое ощущение, что один раз этот код отработал и все, держит в кэше где-то.

Потом ходишь по меню - никаких изменений. Пробовал на Onrender повесить изменение DOM - тоже не работает, вроде как DOM уже есть, меняю src и ничего, все перезаписывается, в общем везде затык. А после onrender методов и нет никаких.

 

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

При этом методы Init и OnRender отрабатывают при каждом переходе.

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

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

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

Вопрос такой - в разделе Активности сделал дополнительное представление. А как его жестко сделать по умолчанию, когда по ссылке в данный раздел перехожу?

И как можно ему принудильно рефреш делать, тк я передаю туда гуид в параметрах URL и затем получаю в переменной, но код в define берет старые значения, нужно принудительно  refresh делать.

Либо может можно как-то более красиво передавать параметр (мне по сути нужно передать в раздел id Лида, из раздела которого я по кнопке в раздел Активности перехожу:

define("LeadSectionV2", [], function() {
	return {
		entitySchemaName: "Lead",
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[
			{
                                        "operation": "insert",
                                        "name": "CalculateButton",
                                        "values": {
                                        "itemType": Terrasoft.ViewItemType.BUTTON,
                                        "caption": "Бронировать",
                                        "style": Terrasoft.controls.ButtonEnums.style.BLUE,
                                        "visible": true,
                                        "click": {bindTo: "onOpenPrimaryContactClick"},
                                },
                                        "parentName": "CombinedModeActionButtonsCardLeftContainer",//"CombinedModeActionButtonsSectionContainer",
                                        "propertyName": "items",
               }
 
			]/**SCHEMA_DIFF*/,
		methods: {
 
			//
				onOpenPrimaryContactClick: function() {
                // Определение активной записи 
                var LeadId = this.get("ActiveRow");
   						var primaryId = "?guid="+LeadId;
                        // Формирование строки адреса.
                        var requestUrl = "SectionModuleV2/ActivitySectionV2/" + primaryId;
                        this.sandbox.publish("PushHistoryState", {
                            hash: requestUrl
                        });
 
            },
 
 
			//
 
		}
	};
});

 

У меня такой же вопрос

2 комментария
define("ActivitySectionV2", [], function() {
 
	var currentLocation = '';
	var leadGuid = '';
    currentLocation = window.location+'';

А тут в разделе я получаю URL, но он берет старый из кэша, новый только при рефреше

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

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

Проблема в блокировке колонок редактируемого реестра. Почти все колонки завязаны на правилах, а требуется заблокировать всю страницу. Использую:

attributes: {

   "IsModelItemsEnabled": {
      dataValueType: Terrasoft.DataValueType.BOOLEAN,
      value: true,
      dependencies: [{
         columns: ["NavOrderStatus"],
         methodName: "changeNavOrderStatus"
      }]
   }

}

А в методе изменения:

this.set("IsModelItemsEnabled", false);

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

getDefaultCellControlsConfig: function(columnName, params) {
   var config = {
      itemType: Terrasoft.ViewItemType.MODEL_ITEM,
      name: columnName,
      labelConfig: {visible: false},
   };

   if(columnName = "Product" && ...){
      config.enabled = false;
   }


   return Ext.apply(config, params);
}

Что тоже не помогает.. Подскажите, пожалуйста решение данной проблемы.

У меня такой же вопрос

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

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

generateActiveRowControlsConfig: function (id, columnsConfig, rowConfig) {
                    this.columnsConfig = columnsConfig;
                    var gridData = this.getGridData();
                    var activeRow = gridData.get(id);
                    var isEditableColumn;
                    if (activeRow.values.IDSBParent === "") {
                        isEditableColumn = this.isEditableParentColumn;
                    }
                    else {
                        isEditableColumn = this.isEditableColumn;
                    }
                    var gridLayoutItems = [];
                    var currentColumnIndex = 0;
                    Terrasoft.each(columnsConfig, function (columnConfig) {
                        var cellConfig = this.getActiveRowCellConfig(columnConfig, currentColumnIndex);
                        cellConfig.enabled = isEditableColumn(cellConfig.name);
                        if (!cellConfig.hasOwnProperty("isNotFound")) {
                            gridLayoutItems.push(cellConfig);
                        }
                        currentColumnIndex += cellConfig.layout.colSpan;
                    }, this);
                    this.applyBusinessRulesForActiveRow(id, gridLayoutItems);
                    var viewGenerator = Ext.create(this.getRowViewGeneratorClassName());
                    viewGenerator.viewModelClass = this;
                    var gridLayoutConfig = viewGenerator.generateGridLayout({
                        name: this.name,
                        items: gridLayoutItems
                    });
                    rowConfig.push(gridLayoutConfig);
                }

 

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

generateActiveRowControlsConfig: function (id, columnsConfig, rowConfig) {
                    this.columnsConfig = columnsConfig;
                    var gridData = this.getGridData();
                    var activeRow = gridData.get(id);
                    var isEditableColumn;
                    if (activeRow.values.IDSBParent === "") {
                        isEditableColumn = this.isEditableParentColumn;
                    }
                    else {
                        isEditableColumn = this.isEditableColumn;
                    }
                    var gridLayoutItems = [];
                    var currentColumnIndex = 0;
                    Terrasoft.each(columnsConfig, function (columnConfig) {
                        var cellConfig = this.getActiveRowCellConfig(columnConfig, currentColumnIndex);
                        cellConfig.enabled = isEditableColumn(cellConfig.name);
                        if (!cellConfig.hasOwnProperty("isNotFound")) {
                            gridLayoutItems.push(cellConfig);
                        }
                        currentColumnIndex += cellConfig.layout.colSpan;
                    }, this);
                    this.applyBusinessRulesForActiveRow(id, gridLayoutItems);
                    var viewGenerator = Ext.create(this.getRowViewGeneratorClassName());
                    viewGenerator.viewModelClass = this;
                    var gridLayoutConfig = viewGenerator.generateGridLayout({
                        name: this.name,
                        items: gridLayoutItems
                    });
                    rowConfig.push(gridLayoutConfig);
                }

 

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

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

 

Просто забрать у пользователя права на запись на объект детали.

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

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

Права определяются содержимым таблиц, для изменения прав нужно добавлять/изменять/удалять записи в них. Например, для прав доступа на записи в разделе контактов это SysContactRight. А если это делать в БП, есть специальный элемент изменения прав.

Дмитрий А.,

Проблема в том, что поля детали не блокируются из-за правил, а делал я именно по этим ссылкам

Если отобрать права доступа, правила будут безразличны.

Войдите или зарегистрируйтесь, чтобы комментировать