Вопрос

Как добавить поле типа web (как в детали средства связи), чтобы при клике в другой вкладке открывалась эта ссылка.

Ответ

Для превращения обычного текстового поля в ссылку, необходимо доработать diff этого поля следующим кодом:

{
    "operation": "insert",
    "name": "AdditionalExpenses",
    "values": {
 
        // -->
        "showValueAsLink": true,
        "controlConfig": {
            "enabled": true,
            "href": {
                "bindTo": "getAdditionalExpensesLink"
            },
            "linkclick": {
                "bindTo": "onExternalLinkClick"
            }
        },
        // <--
 
        "layout": {
            "colSpan": 12,
            "rowSpan": 1,
            "column": 0,
            "row": 3,
            "layoutName": "Tab07720f4eTabLabelGridLayout691629ea"
        },
        "labelConfig": {},
        "enabled": true,
        "bindTo": "AdditionalExpenses"
    },
    "parentName": "Tab07720f4eTabLabelGridLayout691629ea",
    "propertyName": "items",
    "index": 5
},

А в секцию methods страницы добавить:

methods: {
 
    getAdditionalExpensesLink: function() {
        return this.getLink(this.get("AdditionalExpenses"));
    },
    onExternalLinkClick: function() {
        return;
    },
    getLink: function(value) {
        if (Terrasoft.isUrl(value)) {
            return {
                url: value,
                caption: value
            };
        }
    }
},

После этого поле будет со ссылкой, если указать её в правильном ссылочном формате, или скопировать из браузера. Клик из контекстного меню "Открыть в новой вкладке" откроет ссылку в новом окне, по клику первой кнопкой мыши, откроет в текущем:

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

Чтоб ссылка открывалась в новом окне, нужно дополнить метод onExternalLinkClick():

onExternalLinkClick: function() {
    var value = this.get("AdditionalExpenses");
    if (!Ext.isEmpty(value)) {
        window.open(value, "_blank");
    }
    return false;
},

 

Нравится

Поделиться

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

Кейс

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

Цель

Корректная работа кнопки "Закрыть" окно.

Необходимые условия

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

Выполнение

Данное поведения наблюдается в случае, если пользователь нажимает на стрелочку, справа от поля с выпадающим списком. Если открывать окно “Раздел”, нажимая на центр поля, кнопка “Закрыть” работает корректно. 

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

Нравится

Поделиться

0 комментариев
Показать все комментарии
Публикация

Задача

Необходимо создать свой виджет

Решение

В системе виджеты используются для вывода данных из встроенных в систему модулей.

Данная опция позволяет выводить данные в нестандартном виде.

Пример реализации виджета:

define('SxClientUnitTable', ['ext-base', 'terrasoft', 'sandbox', "BaseFiltersGenerateModule", 'SxClientUnitTableResources', "ChartModuleHelper", "css!SxClientUnitTableCss", "ServiceHelper"],
	function(Ext, Terrasoft, sandbox, BaseFiltersGenerateModule, resources, ChartModuleHelper, css, ServiceHelper, ConfigurationConstants) {
 
		function getViewModel() {
					return Ext.create('Terrasoft.BaseViewModel', {
				//todo SysModuleAnalyticsChart deleted
						entitySchema: 'Order',
						methods: {
							getChart: function(key) {
							debugger
								sandbox.publish('GenerateChart', key);
								var filters = this.getReportFilters();
							},
							load: function() {
 
							}
						}
					});
				};
		var result = "";
		function generateMainView(renderTo) {
 
			var resultConfig = Ext.create('Terrasoft.Container', {
				id: 'tableOtchetMetkiParamContainer',
				selectors: {
					wrapEl: '#tableOtchetMetkiParamContainer'
				},
				renderTo: renderTo
			});
			return resultConfig;
		}
		function getInfoMetki(parameters) {
 
			var serviceData = {
				site: parameters[0].toString(),
				ownerId: parameters[1].toString(),
				startDate: parameters[2].toString(),
				dueDate: parameters[3].toString(),
				countryId: parameters[4].toString()
			};
 
			ServiceHelper.callService("UsrOtchetMetkiGetCollectionConfigurationService", "GetCollectionOtchetMetki",
				function (response) {
				debugger
					if (response.status == 404) {
						this.showInformationDialog("\t Error: \n" + response.message);
					}
					else if(response.GetCollectionOtchetMetkiResult.length > 0){
						result = response.GetCollectionOtchetMetkiResult;
						var arrR = result.split(":");
						var htmlAdded = "<table><tr><th>Метка</th><th>Количество заказов</th><th>На уточнении</th><th>% уточн.</th><th>Подтвержденные</th><th>% Подтв.</th><th>Завершенные</th><th>% Заверш.</th><th>Отмены</th><th>% Отм.</th></tr>";
						for(var i = 0; i < arrR.length - 1; i++){
							//get items
							var strArr = arrR[i].split(" ");
							var metkaOne = strArr[0].replace("{","");
							var metka = metkaOne.replace("}","");
 
							htmlAdded += "<tr><td>" + metka + "</td><td>" + strArr[1].toString() + "</td><td>"  + strArr[2].toString() + "</td><td>" + strArr[3].toString() + "</td><td>" + strArr[4].toString() + "</td><td>" + strArr[5].toString() + "</td><td>" + strArr[6].toString() + "</td><td>" + strArr[7].toString() + "</td><td>"  + strArr[8].toString() + "</td><td>" + strArr[9].toString() + "</td></tr>";
						}
						//end table
						htmlAdded += "</table>";
						var el = Ext.get('tableOtchetMetkiParamContainer').setHTML(htmlAdded);
						var elemV = Ext.get('Module1');
						elemV.toggleCls('my-table');
					}
					else
						result = null;
				}, serviceData, this);
		}
		function getReplaceString(str, separator){
		debugger
		var arSt = str.split(separator);
			var res = arSt[2] + separator + arSt[1] + separator + arSt[0];
			while(res.indexOf(separator) > 0){
				res = res.replace(separator,"-");
			}
			return res;
		}
 
		var render = function(renderTo) {
 
 
			debugger
			var viewConfig = generateMainView(renderTo);
			var viewModel = getViewModel();
			var getStartD = Ext.get("fixedFilterDateView-wrap").dom.innerText;
			var getEndD = Ext.get("fixedFilterDateDueView-wrap").dom.innerText;
 
			var startD = getStartD.indexOf("Начало") > 0 ? "2016-05-01" : getReplaceString(getStartD, ".");
			var endD = getEndD.indexOf("Завершение") > 0 ? "2016-07-04" : getReplaceString(getEndD, ".");
			var ar = [];
			ar.push("all");
			ar.push("all");
			ar.push(startD);
			ar.push(endD);
			ar.push("a570b005-e8bb-df11-b00f-001d60e938c6");
			getInfoMetki(ar);
 
 
			viewConfig.bind(viewModel);
			//viewConfig.render(renderTo);
		};
		return {
			schema: 'Order',
			methods: {
				onLookupChange: function(newValue, columnName) {
 
                                this.callParent(arguments);
                                if (columnName === "UsrCityFilter") {
                                        this.set("UsrCityFilter", newValue);
                                        this.onUsrCityFilterChanged();
                                }
                        },
 
                        onUsrCityFilterChanged: function() {
                                this.reloadGridData();
                        },
				getOrdersForFilter: function(){
					var items = [];
					var selOrders = Ext.create("Terrasoft.EntitySchemaQuery", {
						rootSchemaName: "Order"
					});
					selOrders.addColumn("SxMark.Name");
					selOrders.addColumn("SxCountry.Name");
					var filterByUser = Terrasoft.createColumnFilterWithParameter(
					Terrasoft.ComparisonType.EQUAL, "SxCountry.Name", "Россия");
					selOrders.filters.addItem(filterByUser);
					selOrders.getEntityCollection(function (response) {
					debugger
						if (response.collection.collection.length > 0)
							items = result.collection.getItems();
						else
							items = null;
					}, this);
					return items;
				}
 
			},
			userCode: function() {
 
			},
			init: function() {
 
			},
			filterChanged: function( filter, eOpts ){
				debugger
				var c = [];
			},
			render: render
		};
	}
)
;

 

Нравится

Поделиться

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

Симптомы

'Bpm'online mobile bug report Тип: Terrasoft.ODataItemNotFoundException Сообщение: Элемент Contact не найден 

Тип: Terrasoft.ODataItemNotFoundException 

Сообщение: Элемент Contact не найден 

Дополнительная информация: 

{"error":{"code":"1","message":{"lang":"","value":"Элемент Contact не найден"},"innererror":{"message":"Элемент Contact не найден","type":"Terrasoft.Common.ItemNotFoundException","stacktrace":"   at Terrasoft.Core.Entities.Services.EntityLazyProxy.<>c__DisplayClass7.b__6()\r\n   at Terrasoft.Core.Entities.Services.EntityLazyProxy.<>c__DisplayClass2.<.ctor>b__0()\r\n   at System.Lazy`1.CreateValue()\r\n   at System.Lazy`1.LazyInitValue()\r\n   at Terrasoft.Core.Entities.Services.EntityLazyProxy.SaveChanges()\r\n   at Terrasoft.Core.Entities.Services.ServiceContext.SaveChanges()\r\n   at System.Data.Services.DataService`1.HandleNonBatchRequest(RequestDescription description)\r\n   at System.Data.Services.DataService`1.HandleRequest()"}}}

Причина

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

Решение

Для решения вопроса с ошибкой синхронизации мобильного приложения нужно:

1. Синхронизироваться, убедится, что в настройках стоит корректная дата и время последней синхронизации (соответствует текущему).

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

Ниже прикреплен скриншот для наглядности:

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

 

3. Повторно синхронизироваться.

Также проверьте, что розданы соответствующие права.

Т.е. необходимо очистить кэш и заново провести синхронизацию.

Нравится

Поделиться

0 комментариев
Показать все комментарии
Публикация

Краткая инструкция по фичам с датами.

Пример кода условного потока, который проверяет, что «дата изменения средства связи» меньше или равно 90 дн.

(DateTime.Now.Subtract([#Читать средство связи Физ.лица.Первый элемент результирующей коллекции.Дата изменения#])).TotalDays <= 90 

DateTime.Now = это эквивалент [#Системная переменная.Текущее значение даты и времени#]

То есть, запись может быть и:

(([#Системная переменная.Текущее значение даты и времени#]).Subtract([#Читать средство связи Физ.лица.Первый элемент результирующей коллекции.Дата изменения#])).TotalDays <= 90

Пояснения

Метод Subtract работает так: ДатаВремя1. Subtract(ДатаВремя2) возвращает временной интервал равный ДатаВремя1 - ДатаВремя2.

Временной интервал содержит следующие свойства, доступные через точку ((предположим интервал составляет 1 день 12 часов 15 минут 20 секунд)):

2.1.

ДатаВремя1. Subtract(ДатаВремя2).Days – целое количество дней = 1

2.2.

ДатаВремя1. Subtract(ДатаВремя2).TotalDays – дробное количество дней ~ 1.5…

2.3.

ДатаВремя1. Subtract(ДатаВремя2).Hours – количество часов = 12

2.4.

ДатаВремя1. Subtract(ДатаВремя2).TotalHours – общее дробное количество часов ~ 36.25…

2.5.

ДатаВремя1. Subtract(ДатаВремя2).Minutes – количество минут = 15

2.6.

ДатаВремя1. Subtract(ДатаВремя2).TotalMinutes – общее дробное количество минут ~ 2160.33

2.7.

Аналогично для Seconds и Milliseconds

Проверка дня недели:

ДатаВремя1.DayOfWeek.

Например: проверим четверг сегодня или нет:

[#Системная переменная.Текущее значение даты#]).DayOfWeek == DayOfWeek.Thursday 

Добавить (вычесть) время

ДатаВремя1.AddDays(2).

Возвращает ДатаВремя1 + 2 дня.

(Аналогично Add.Seconds(N)/Minutes(N)/Hours()/Months()/Years().

Если указать отрицательное число, то ДатаВремя1 уменьшится на заданное значение (ДатаВремя1.AddHours(-5) = отнять 5 часов).

Другие возможности типа данных DateTime:

https://msdn.microsoft.com/ru-ru/library/system.datetime(v=vs.110).aspx

Нравится

Поделиться

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

Вопрос

Как вывести на страницу виртуальное лукап поле, что бы корректно работало модальное окно выбора.

Ответ

define("ContactAddressPageV2", [],
    function() {
        return {
            attributes: {
                "UsrKladrStreet": {
                    dataValueType: Terrasoft.DataValueType.LOOKUP
                },
                "UsrVirtualKladrStreet": {
                    "dataValueType": Terrasoft.DataValueType.LOOKUP,
                    "type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
                    isLookup: true,
                    caption: "virtual street",
                    referenceSchemaName: "UsrKladrStreet"
                }
            },
            diff: /**SCHEMA_DIFF*/[
                {
                    "operation": "insert",
                    "parentName": "Header",
                    "propertyName": "items",
                    "name": "UsrKladrStreet",
                    "values": {
                        bindTo: "UsrKladrStreet",
                        "caption": {"bindTo": "Resources.Strings.UsrKladrStreetCaption"},
                        layout: {column: 0, row: 8, colSpan: 12}
                    }
                },
                {
                    "operation": "insert",
                    "parentName": "Header",
                    "propertyName": "items",
                    "name": "UsrVirtualKladrStreet",
                    "values": {
                        bindTo: "UsrVirtualKladrStreet",
                        "caption": "virtual street",
                        layout: {column: 0, row: 9, colSpan: 12}
                    }
                }
            ]/**SCHEMA_DIFF*/
        };
    }
);

 

Нравится

Поделиться

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

Симптомы

Не отправляется тестовое письмо.

Даже если лицензии marketing active contacts/ marketing campaigns выданы пользователю Supervisor.

При попытке отправить возникает ошибка "Method not allowed. Please see the service help page for constructing valid requests to the service":

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

Причина

Нет системной настройки 'UnsubscribeApplicationUrl'

Решение

Выполнить скрипт:

INSERT INTO SysSettingsValue (Id, SysSettingsId, SysAdminUnitId, IsDef)
SELECT N'6A96A15A-34BD-4932-A924-56CC013E3312', Id, N'A29A3BA5-4B0D-DE11-9A51-005056C00008', 1
    FROM SysSettings WHERE Code = 'UnsubscribeApplicationUrl'

 

Нравится

Поделиться

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

Вопрос

Подскажите как реализовать следующую задачу: есть преднастроенная страница с добавленной на нее деталью Контактов (Физ. лиц). Необходимо иметь возможность выделить конкретную запись грида детали и получить ее ID в основную страницу. Как это лучше реализовать?

Ответ

Для получения идентификатора текущей записи детали в родительской карточке редактирования используя механизм подписки/публикации сообщений между соответствующими модулями - алгоритм будет примерно следующий:



Для начала необходимо в коде карточки и детали добавить адресное сообщение с одним и тем же названием, но в карточке с типом "Подписка", а в детали - с типом "Публикация". Например, для карточки редактирования:

 

messages: {
    "GetContactDetailActiveRecordId": {
        mode: Terrasoft.MessageMode.PTP,
        direction: Terrasoft.MessageDirectionType.SUBSCRIBE
    }
}

Для детали точно так же, только в свойстве direction необходимо указать

Terrasoft.MessageDirectionType.PUBLISH

Далее при инициализации карточки редактирования (метод init) нужно подписаться на это сообщение, а в функции обратного вызова получить значение из детали и сохранить его в атрибут модели карточки:

 

init: function() {
    this.callParent(arguments);
    this.sandbox.subscribe("GetContactDetailActiveRecordId", function(recordId) {
        this.set("ContactDetailActiveRecordId", recordId);
    }, this, [contactDetailSandboxId]);
}

Значение contactDetailSandboxId Вам необходимо определить самостоятельно (оно формируется в зависимости от названия детали). В коде детали оно хранится в свойстве this.sandbox.id.



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

 

onButtonClick: function() {
    this.sandbox.publish("GetContactDetailActiveRecordId", this.get("ActiveRow"), [this.sandbox.id]);
}

 

Нравится

Поделиться

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

Вопрос

Как добавить кнопку в коммуникационную панель?

Ответ

  1. Заместить модуль «CommunicationPanel» в котором будет описана новая кнопка.
define("CommunicationPanel", ["terrasoft", "CommunicationPanelHelper"],
	function(Terrasoft, CommunicationPanelHelper) {
		return {
			messages: {
				"SelectCommunicationPanelItem": {
					"mode": Terrasoft.MessageMode.PTP,
					"direction": Terrasoft.MessageDirectionType.SUBSCRIBE
				}
			},
			attributes: {
				"UsrMyMenuActive": {
					"dataValueType": Terrasoft.DataValueType.BOOLEAN,
					"type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
					"value": false
				},
				"UsrMyMenuCounter": {
					"dataValueType": Terrasoft.DataValueType.TEXT,
					"type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
					"value": ""
				},
				"UsrMyMenuVisible": {
					"dataValueType": Terrasoft.DataValueType.BOOLEAN,
					"type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
					"value": true
				}
			},
			methods: {
				getPanelItemConfig: function(moduleName) {
					var config = this.callParent(arguments);
					if (moduleName !== "UsrMyMenuModule") {
						return config;
					}
					return Ext.apply(config, {
						keepAlive: false
					});
				},
				selectItem: function(config) {
					this.set("SelectedMenuItem", config.selectedItem);
				},
				getUsrMyMenuImageConfig: function(itemTag) {
					return this.get("Resources.Images.VisaMenuIcon");
				},
				getUsrMyMenuCaption: function(itemTag) {
					return "test";
				}
			},
			diff: [
				{
					"operation": "insert",
					"index": 0,
					"parentName": "communicationPanelContent",
					"propertyName": "items",
					"name": "usrMyMenu",
					"values": {
						"tag": "UsrMyMenu",
						"visible": {"bindTo": "UsrMyMenuVisible"},
						"imageConfig": {"bindTo": "getUsrMyMenuImageConfig"},
						"caption": {"bindTo": "getUsrMyMenuCaption"},
						"generator": "CommunicationPanelHelper.generateMenuItem"
					}
				}
			]
		};
	});
  1. Создать модуль панели, в модуле должны быть обязательные сообщения, как и в ESNFeedModule:

RerenderModule – подписка, адресное.

InitModuleViewModel – подписка, адресное.

define("UsrMyMenuModule", [], function() {
	Ext.define("Terrasoft.configuration.UsrMyMenuModule", {
 
		extend: "Terrasoft.BaseSchemaModule",
		alternateClassName: "Terrasoft.UsrMyMenuModule",
 
		generateViewContainerId: false,
 
		initSchemaName: function() {
			this.schemaName = "UsrMyMenu";
		},
 
		initHistoryState: Terrasoft.emptyFn,
 
		init: function() {
			this.callParent(arguments);
			this.initMessages();
		},
 
		initMessages: function() {
			this.sandbox.subscribe("RerenderModule", function(config) {
				if (this.viewModel) {
					this.render(this.Ext.get(config.renderTo));
					return true;
				}
			}, this, [this.sandbox.id]);
		},
 
		createViewModel: function() {
			var viewModel = this.callParent(arguments);
			return viewModel;
		}
 
	});
	return Terrasoft.UsrMyMenuModule;
});

3. Создать схему представления карточки

define("UsrMyMenu", [], function() {
		return {
			mixins: {
			},
			messages: {
			},
			attributes: {
			},
			methods: {
				init: function(callback, scope) {
					this.callParent([function() {
						callback.call(scope);
					}, this]);
				},
				onTestClick: function() {
					alert(1);
				}
			},
			diff: [
				//MyMenu
				{
					"operation": "insert",
					"name": "MyMenu",
					"propertyName": "items",
					"values": {
						"generateId": false,
						"itemType": Terrasoft.ViewItemType.CONTAINER,
						"items": []
					}
				},
				//ShowNewMessagesButton
				{
					"operation": "insert",
					"name": "ShowNewMessagesButton",
					"parentName": "MyMenu",
					"propertyName": "items",
					"values": {
						"generateId": false,
						"itemType": Terrasoft.ViewItemType.BUTTON,
						"caption": "Test!",
						"click": {bindTo: "onTestClick"}
					}
				}
			]
		};
	});

Результат:

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

Нравится

Поделиться

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

Вопрос

Как сделать, чтобы колонка была ссылочной? Например, колонка "Контрагент" в разделе "Контакт".

Ответ

По умолчанию пользовательские колонки (колонки через мастер мобильного приложения), которые имеют тип «Справочник» в мобильном приложении не являются ссылочным (нет возможности перейти на страницу редактирования). Например раздел «Обращения» в мобильном приложении не является базовым. Для реализации необходимо добавить в модуль мобильного приложения для раздела  ссылку на поля.

Пример реализации, можете посмотреть в MobileContactModuleConfig (раздел «Контакты»):

Terrasoft.sdk.RecordPage.configureColumn("Contact", "primaryColumnSet", "Account", {
    viewType: Terrasoft.ViewTypes.Preview
});
 

Нравится

Поделиться

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