Вопрос

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

Пример:

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

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

Ответ

В реестре может отображаться не только колонка для отображения, но можно выводить и значения, сформированные на основании значений нескольких колонок. Для такого сложного форматирования, или отображения разных значений в зависимости от условий, значения колонок можно задавать в виде функций (используя методы Terrasoft.sdk.GridPage.setPrimaryColumn() и Terrasoft.sdk.GridPage.setSecondaryColumn()):

Terrasoft.sdk.GridPage.setPrimaryColumn('Account', {
	columns: ['Name', 'PrimaryContact'],
	convertFunction: function(values) {
		if (!Ext.isEmpty(values.PrimaryContact)) {
			return values.Name + ' (' + values.PrimaryContact + ')';
		} else {
			return values.Name;
		}
	}
});

Кроме того, можно указывать дополнительные колонки и при выборе значения поля-справочника. Делается это по аналогии с гридом, но только используется класс Terrasoft.sdk.LookupGridPage:

Terrasoft.sdk.LookupGridPage.setSecondaryColumn("Account", "PrimaryContact");

А вот альтернативный вариант расширения возможностей грида - изменение шаблона элементов грида:

Terrasoft.util.writeStyles(
	".div-table {",
		"display:table;",
		"width:100%;",
	"}",
	".div-table-row {",
		"display:table-row;",
		"width:100%;",
		"clear:both;",
	"}",
	".div-table-col {",
		"float:left;",
		"display:table-column;",
		"min-width:50%;",
	"}",
	".div-table-col-button {",
		"float:right;",
		"display:table-column;",
	"}"
);
Ext.define("MyCustomList", {
	override: "Ext.Terrasoft.List",
 
	initializeItemTpl: function() {
		this.callParent(arguments);
		var store = this.getStore();
		var model = store.getModel();
		var modelName = model.getName();
		if (modelName === "Account") {
			var tpl = this.getItemTpl();
			tpl.html =
			"<div class=\"x-list-item-tpl div-table\">" +
				"<div class=\"div-table-row\">" +
					"<div class=\"div-table-col\">{[this.applyPrimaryColumn(values)]}</div>" +
					"<div class=\"div-table-col-button\">{Phone}</div>" +
				"</div>" +
				"<div class=\"div-table-row\">" +
					"<div class=\"div-table-col\">{[this.applySecondaryColumn(values)]}</div>" +
					"<div class=\"div-table-col-button\">{Web}</div>" +
				"</div>" +
			"</div>";
		}
	}
 
});

 

Нравится

Поделиться

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

Задача

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

Решение

Поиск можно доработать в «строке поиска по базе знаний» страницы портального пользователя. Это отдельный элемент, похожий на командную строку, использующий ее вид, но реализация его логики содержится в схеме KnowledgeBaseSearchModule.

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

Эту страницу можно отладить и под обычным пользователем, в обычном интерфейсе, открыв ее как модуль данной ссылкой:

http://ваш-сайт/0/Nui/ViewModule.aspx#PortalMainPageModule/

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

/*sessionFilters.CustomFilters = {
    value: value,
    displayValue: value,
    primaryDisplayColumn: true
};*/
 
var filters = Ext.create("Terrasoft.FilterGroup");
filters.addItem(Terrasoft.createColumnFilterWithParameter(
    Terrasoft.ComparisonType.CONTAIN, "Notes", value));
filters.addItem(Terrasoft.createColumnFilterWithParameter(
    Terrasoft.ComparisonType.CONTAIN, "Name", value));
filters.addItem(Terrasoft.createColumnFilterWithParameter(
    Terrasoft.ComparisonType.CONTAIN, "Keywords", value));
filters.logicalOperation = Terrasoft.LogicalOperatorType.OR;
 
var serializationInfo = filters.getDefSerializationInfo();
serializationInfo.serializeFilterManagerInfo = true;
 
sessionFilters.CustomFilters = {
    "null": {
        "displayValue": value,
        "filter": filters.serialize(serializationInfo)
    }
};

 

Также для того, чтобы в колонку "Keywords" записывались публичные теги для последующего поиска по этой колонке, необходимо реализовать события изменения, добавления и удаления тегов базы знаний, путем создания их в замещающем объекте KnowledgeBaseInTagV2.

 

var userConnection = (UserConnection)HttpContext.Current.Session["UserConnection"];
Guid entityId = Entity.GetTypedColumnValue<Guid>("EntityId");
var esq = new EntitySchemaQuery(userConnection.EntitySchemaManager, "KnowledgeBaseInTagV2");
var publicTypeId = "D6FB4DE6-0809-41FE-A84F-6D245CBC5F32";
esq.AddColumn("Tag.Name");
var entityFilter = esq.CreateFilterWithParameters(FilterComparisonType.Equal,
    "Entity.Id", entityId);
var typeFilter = esq.CreateFilterWithParameters(FilterComparisonType.Equal,
    "Tag.Type", publicTypeId);
esq.Filters.Add(entityFilter);
esq.Filters.Add(typeFilter);
var entityCollection = esq.GetEntityCollection(userConnection);
string allTags = string.Empty;
foreach (var entity in entityCollection) {
    var tagName = entity.GetTypedColumnValue<string>("Tag_Name");
    allTags += tagName + ", ";
}
using (DBExecutor executor = userConnection.EnsureDBConnection()) {
    Update updateRelationshipQuery = new Update(userConnection, "KnowledgeBase");
    updateRelationshipQuery.Set("Keywords", Column.Parameter(allTags));
    updateRelationshipQuery.Where("Id").IsEqual(Column.Parameter(entityId));
    updateRelationshipQuery.Execute(executor);
}
return true;

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

UPDATE KnowledgeBase
SET Keywords = ISNULL(res.Tags, '')
FROM (SELECT a.Id, Tags = (SELECT stuff((
                select ', ' + Name from (SELECT t.Name FROM KnowledgeBaseTagV2 t WHERE
                                t.TypeId = 'D6FB4DE6-0809-41FE-A84F-6D245CBC5F32'
                                and t.Id
                                IN (
                                                SELECT e.TagId FROM KnowledgeBaseInTagV2 e WHERE e.EntityId = a.Id
                                )
) tb FOR XML PATH('')), 1, 2, ''))
FROM KnowledgeBase a
GROUP BY a.Id) res
WHERE res.ID = KnowledgeBase.Id

Где 'D6FB4DE6-0809-41FE-A84F-6D245CBC5F32' идентификатор типа "публичный тег".

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

Версия приложения со страницей портального пользователя и базовой схемой KnowledgeBaseSearchModule.

Нравится

Поделиться

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

Вопрос

У нас возникла задача программно запретить редактирование поля "Поставщика" на странице договора (ContractPage) при наступлении определенных условий. Все попытки сделать что-то вроде this.get или find('Supplier') и установить ему enabled или IsEnabled или всё что удалось нагуглить - не работает. 

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

Ответ

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

var supp = this.find('Supplier');
supp.customConfig = {
    enabled: { bindTo: 'methodName''}
}

или

var supp = this.find('Supplier');
supp.customConfig = {
    enabled: false
}

Следующий пример кода демонстрирует блокирование поля "Поставщик" если значение поля "Номер" не равняется 2:

var supp = this.find('Supplier');
supp.customConfig = {
    enabled: { bindTo: 'test'}
}
this.methods.test = function(){ return this.get('Number') == '2' };

 

Нравится

Поделиться

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

Вопрос

Как добавить или изменить CSS стиль

Ответ

Чтобы добавить любой необходимый стиль необходимо:

1) Создать новый модуль. Добавить css стиль на вкладку Less. Рекомендуется использовать следующую структуру:

body[OldUI=false][CustomUI="true"] {
    .control-width-15 .t-label {
        color: #7FFF00;
    }
}

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

2) Создайте необходимую замещающую страницу используя пункт "Замещающий клиентский модуль". Например, необходимо заместить модуль BasePageV2. Добавьте модуль из первого шага в define замещающей страницы.

Переопределите метод init() и вызовите метод setAttributeToBody() чтобы добавить ваш стиль к body.

define("BasePageV2", ["css!UsrBasePageV2CSS"],
    function() {
        return {
            messages: {},
            mixins: {},
            attributes: {},
            methods: {
                init: function() {
                    this.callParent(arguments);
                    this.initializeCustomCss();
                },
                initializeCustomCss: function() {
                    Terrasoft.utils.dom.setAttributeToBody("CustomUI", true);
                }
            },
            diff: /**SCHEMA_DIFF*/[]/**SCHEMA_DIFF*/
        };
});

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

3) Обновите страницу.

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

Нравится

Поделиться

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

Это применяется во всех разделах. Если это необходимо в одном разделе, этот вариант не работает

А если замещать не BasePageV2, а свою страницу?

В конфигурации достаточно схем с названиями, оканчивающимися на CSS, в которых вносятся изменения стилей для конкретных схем. Например, AccountRelationshipDetailCss добавляет стили, специфичные для этой детали:

.account-relationship.detail {
	.relationship-mode-button {
		border-top-right-radius: 1px;
		border-bottom-right-radius: 1px;
	}
	.datagrid-mode-button {
		border-top-left-radius: 1px;
		border-bottom-left-radius: 1px;
	}
	.relationship-mode-button, .datagrid-mode-button {
		padding-left: 0.308em;
		padding-right: 0.462em;
	}
	.mode-button-pressed {
		background: #e4eaf9;
	}
	.account-relationship-chart {
		height: 450px;
	}
}

Затем в основной схеме этой детали AccountRelationshipDetailV2 подключается эта схема со стилями:

define("AccountRelationshipDetailV2", ["AccountRelationshipDetailV2Resources", "MiniPageUtilities",
	"ConfigurationDiagramGenerator", "RelationshipDiagramViewModel", "RelationshipDiagram",
	"LookupQuickAddMixin", "RelationshipChartGenerator", "RelationshipChart",
	"css!AccountRelationshipDetailCss"
], function() {

И так для конкретного элемента, например, кнопки, использовать стили оттуда:

{
 "operation": "insert",
 "name": "RelationshipModeButton",
 "parentName": "Detail",
 "propertyName": "tools",
 "values": {
	"itemType": this.Terrasoft.ViewItemType.BUTTON,
	"hint": {"bindTo": "Resources.Strings.DiagramViewButtonHint"},
	"click": {
		"bindTo": "setRelationshipMode"
	},
	"visible": {
		"bindTo": "getModeButtonsVisible"
	},
	"pressed": {
		"bindTo": "getRelationshipButtonPressed"
	},
	"classes": {
		"wrapperClass": ["relationship-mode-button"],
		"pressedClass": ["mode-button-pressed"]
	},
	"controlConfig": {
		"imageConfig": {
 "bindTo": "Resources.Images.RelationshipViewIcon"
		}
	},
	"markerValue": relationshipMode.Relationship
 }
{

 

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

Вопрос

В разделе Активности, вкладке «Связи» необходимо убрать привязку к Документам и Лидам. 

Ответ

Запрошенные колонки можно удалить из детали «Связи» для Активностей, следующим запросом в базу данных:

DELETE FROM EntityConnection WHERE SysEntitySchemaUId = 'c449d832-a4cc-4b01-b9d5-8a12c42a9f89'
AND (ColumnUId = 'd6e94162-4354-413a-bc84-e118df5e852e'
OR ColumnUId = 'f6137557-741e-42f8-8bf6-69b2524a83f7')

 

Нравится

Поделиться

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

Дополню.

 

Id колонки смотреть в SysEntitySchemaReference.ColumnUId

например,

select distinct ColumnName , ColumnUId from SysEntitySchemaReference
where SysSchemaId in (select id from SysSchema where Name = 'Activity')
order by ColumnName

 

Id объекта будет в выборке

select [UId] from SysSchema where Name = 'Activity'

 

Доброго времени суток! А если скрыть, не удалять? 

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

 

Можете переопределить\доработать метод: loadColumnValues

Если из коллекции, после получения данных, но до её перебора “collection.each(function(item)…” удалить какой-то элемент, к примеру:

collection.collection.removeAt(1);

То этот элемент связи не будет обработан и показан на детали.

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

Вопрос

Как отключить сборку клиентских скриптов в all-combined.js

Ответ

Для решения данного вопроса необходимо в Web.config (...\Terrasoft.WebApp\) добавить ключ в блок .

Хотелось бы отметить, что данное изменение в приложение повлияет на производительность приложения в целом.

Для увеличения производительности как раз и был создан all-combined.js.

Нравится

Поделиться

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

Вопрос

Подскажите пожалуйста, как сделать поле-выпадающий список из поля-справочника в редактируемом реестре версии 7.7.0.2293 SalesEnterprise + Marketing + CustomerCenter? Я сделал редактируемый реестр активностей, в карточке активности поле "Состояние" - выпадающий список. Но в редактируемом реестре это же поле представляет собой классический лукап с открываюшимся лукапным окном.

P.S. Решение полезно если страницы редактирования нет, или их много, или они базовые.

Ответ

Можно переопределить метод generateActiveRowControlsConfig() в схеме детали, и в нужных случаях, передавать нужный дополнительный конфиг.

В вашем случае можно добавить:

if (columnName = "Status") {
 
   cellConfig.contentType = Terrasoft.ContentType.ENUM;
 
}

Сразу после создания в нем cellConfig.

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

Пример схемы детали, на которой все тестировалось, включая превращение грида в редактируемый и переопределение generateActiveRowControlsConfig:

define("UsrSchema1Detail", ["terrasoft", "BusinessRulesApplierV2", "ConfigurationEnums", "BusinessRuleModule",
    "GridUtilitiesV2", "ConfigurationGrid", "ConfigurationGridGenerator", "ConfigurationGridUtilities"], function(
        Terrasoft, BusinessRulesApplier, ConfigurationEnums, BusinessRuleModule) {
    return {
        entitySchemaName: "Activity",
        attributes: {
         "IsEditable": {
            dataValueType: Terrasoft.DataValueType.BOOLEAN,
            type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
            value: true
         }
        },
        mixins: {
         ConfigurationGridUtilites: "Terrasoft.ConfigurationGridUtilities"
        },
        details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
        diff: /**SCHEMA_DIFF*/[
            {
            "operation": "merge",
            "name": "DataGrid",
            "values": {
               "className": "Terrasoft.ConfigurationGrid",
               "generator": "ConfigurationGridGenerator.generatePartial",
               "generateControlsConfig": {"bindTo": "generatActiveRowControlsConfig"},
               "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": "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
            }
         }
        ]/**SCHEMA_DIFF*/,
        methods: {
            generateActiveRowControlsConfig: function(id, columnsConfig, rowConfig) {
                this.columnsConfig = columnsConfig;
                var gridLayoutItems = [];
                var currentColumnIndex = 0;
                this.Terrasoft.each(columnsConfig, function(columnConfig) {
                    var columnName = columnConfig.key[0].name.bindTo;
                    var column = this.getColumnByColumnName(columnName);
                    var cellConfig = this.getCellControlsConfig(column);
 
                    cellConfig = this.Ext.apply({
                        layout: {
                            colSpan: columnConfig.cols,
                            column: currentColumnIndex,
                            row: 0,
                            rowSpan: 1
                        }
                    }, cellConfig);
 
                    if (columnName = "Status") {
                        cellConfig.contentType = Terrasoft.ContentType.ENUM;
                    }
 
                    gridLayoutItems.push(cellConfig);
                    currentColumnIndex += columnConfig.cols;
                }, this);
                var gridData = this.getGridData();
                var activeRow = gridData.get(id);
                var rowClass = {prototype: activeRow};
                BusinessRulesApplier.applyRules(rowClass, gridLayoutItems);
                var viewGenerator = this.Ext.create("Terrasoft.ViewGenerator");
                viewGenerator.viewModelClass = {prototype: this};
                var gridLayoutConfig = viewGenerator.generateGridLayout({
                    name: this.name,
                    items: gridLayoutItems
                });
                rowConfig.push(gridLayoutConfig);
            },
        }
    };
});

 

Нравится

Поделиться

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

Симптомы

Bpm'online mobile bug report

Type: Terrasoft.SourceCodeException

Message: TypeError: undefined is not an object (evaluating 'columnConfig.columnType')

Причина

Ошибка возникает из-за того, что Вы не верно настроили параметры встроенной деталей, например, в разделе «Объекты».

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

Рис. 1

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

Рис. 2

Решение

Для решения необходимо корректно настроить все детали.

Например, как настроить деталь «Файлы и примечания» в разделе «Лиды»:

- Для отображения в мобильном приложении необходимо выбрать деталь «Файлы и ссылки Лида»

- В поле «Колонка детали» указать «Лид»

- В поле «Колонка объекта Лид» указать «Id»

Также как примеры можно посмотреть как реализованы детали в других разделах.

Следует обратить внимание, что в результате данная деталь будет работать в основном для отображения названия файлов и работы с веб-ссылками.

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

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

Рис. 3

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

Рис. 4

Нравится

Поделиться

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

Вопрос

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

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

Есть возможность поместить кнопку именно где-то посреди страницы? Действие для пользователя будет довольно неудобным. Или это принципиально невозможно?

Коммьюнити - https://community.terrasoft.ru/questions/dobavit-knopku-na-stranicu-redaktirovania-v-mobilnom-prilozenii

Ответ

Можете посмотреть алгоритм реализации в похожем топике: https://community.terrasoft.ru/questions/dobavlenie-kastomnyh-elementov-v-mobilnom-prilozenii

Например, в конфиге вью определяем нужный контрол + метод для его отображения, а в контроллере завязываем на него обработчик:

Ext.define("...вью...", {
    config: {
       refreshButton: {
            id: 'usr_order_refresh_btn',
            cls: "x-button-primary-blue",
            text: 'Обновить'
        }
    },
    showRefreshButton: function (isShow) {
        var navigationPanel = this.getNavigationPanel(); /*компонент в котором нужно отобразить контрол*/
        var refreshButton = this.getRefreshButton();
        if (isShow) {
            this._refreshButton = navigationPanel.addButton(refreshButton);
        } else {
            navigationPanel.removeButton(refreshButton);
        }
        return this._refreshButton;
    }
});
Ext.define("...контроллер...", {
    initializeView: function (view) {
        this.callParent(arguments);
        var btn = view.showRefreshButton(true);
        btn.on("tap", this.onRefreshButtonTap, this);
    },
    onRefreshButtonTap: function() {
        /* сабж */
    }
});

Если нужно открыть пикер для выбора значения, то посмотрите MobileActivityGridPageControllerV2. Там много пример открытия пикеров. Например, для выбора «Ответственного» (метод getEmployeePicker()

) или выбора режима расписания (метод getGridModePicker()).

Нравится

Поделиться

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

Вопрос

Кейс:

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

Входе анализа выяснилось, что основной тип файлов это изображения, которые присутствуют в подписи писем (имеют название «image»).

Можно ли настроить так фильтрацию, чтобы мобильное приложение не затягивало лишние файлы?

Ответ

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

Добавить в манифест мобильного приложения (MobileApplicationManifestDefaultWorkplace) следующий код:

{
    "SyncOptions": {
        "SysSettingsImportConfig": [],
        "ModelDataImportConfig": [
            {
                "Name": "ActivityFile",
                "SyncFilter": {
                    "property": null,
                    "valueIsMacros": false,
                    "value": null,
                    "isNot": true,
                    "type": "Terrasoft.FilterTypes.Group",
                    "logicalOperation": "Terrasoft.FilterLogicalOperations.Or",
                    "subfilters": [
                        {
                            "property": "Name",
                            "funcType": "Terrasoft.FilterFunctions.SubStringOf",
                            "funcArgs": ["image"] //по какому слову не будут попадать значения
                        },
                        {
                            "property": "Activity.Owner",
                            "isNot": true,
                            "valueIsMacros": true,
                            "value": "Terrasoft.ValueMacros.CurrentUserContact"
                        }
                    ]
                }
            }
        ]
    },
    "Modules": {},
    "Models": {}
}

Если это продукт FieldForce, то также необходимо добавить этот же код в манифест "MobileApplicationManifestFieldForceWorkplace".

Нравится

Поделиться

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