Деталь с реестром в виде иерархии

Добрый день, коллеги!
Подскажите, в продукте версии 7.8 есть деталь "Структура" , в разделе "Проекты".
Хочу сделать со своей кастомной деталью тоже самое, иерархию.

Пока что добавил вот такой код, к старнице детали:

define("UsrReportingDetailV2", ["ConfigurationEnums", "css!ProjectCSSModule"], function(enums) {
        return {
                entitySchemaName: "UsrReporting",
                attributes: {
                        /**
                         * Отвечает за загруженные уровни иерархии
                         */

                        "expandedElements": {
                                dataValueType: Terrasoft.DataValueType.CUSTOM_OBJECT,
                                type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN
                        },
                        /**
                         * Отвечает за список развернутых элементов,
                         * хранит массив уникальных идетификаторов записей
                         */

                        "expandHierarchyLevels": {
                                dataValueType: Terrasoft.DataValueType.CUSTOM_OBJECT,
                                type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN
                        },

                        /**
                         * Отвечает за хранение разворачиваемого элемента иерархии
                         */

                        "ExpandItemId": {
                                dataValueType: Terrasoft.DataValueType.GUID,
                                type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN
                        }
                },
                methods: {
                        /**
                         * @overridden
                         */

                        disableGridSorting: Ext.emptyFn,

                        /**
                         * @overridden
                         */

                        sortColumn: Ext.emptyFn,

                        /**
                         * Обнавляет страницы редактирования проекта
                         * @overridden
                         * @private
                         */

                        initEditPages: function() {
                                this.callParent(arguments);
                                debugger;
                                var editPages = this.get("EditPages");
                                var workPage = editPages.collection.items[0];
                                workPage.set("Click", {"bindTo": "addToRootRecord"});
                                workPage.set("Caption", "Super");
                                workPage.set("Visible", true);
                                var childItemId = Terrasoft.utils.generateGUID();
                                var config = {
                                        "Id": childItemId,
                                        "Caption": "Sub",
                                        "Click": {"bindTo": "addToChildRecord"},
                                        "Enabled": {"bindTo": "isSingleSelected"},
                                        "Tag": childItemId,
                                        "SchemaName": "UsrReportingPageV2"
                                };
                                var addToChildMenuItem = this.getActionsMenuItem(config);
                                editPages.add(childItemId, addToChildMenuItem);
                        },
                        /**
                         * Открывает страницу Работы для добавления элемента дочернего элемента выбранной записи
                         * @protected
                         * @virtual
                         */

                        addToChildRecord: function() {
                                var scope = this;
                                var selectedItems = this.getSelectedItems();
                                this.set("ExpandItemId", selectedItems[0]);
                                this.replaceDefaultValue("UsrParent", selectedItems[0]);
                                var masterRecordId = this.get("MasterRecordId");
                                this.addRecord();
                        },
                        /**
                         * Устанавливает значение по умолчанию для параметра страницы редактировани новой записи
                         * @protected
                         * @virtual
                         * @param {String} key Имя параметра
                         * @param {*} value значение параметра
                         */

                        replaceDefaultValue: function(key, value) {
                                var defaultValues = this.get("DefaultValues");
                                var oldValue = defaultValues.filter(function(item) {
                                        return item.name === key;
                                });
                                if (Ext.isEmpty(oldValue)) {
                                        defaultValues.push({
                                                name: key,
                                                value: value
                                        });
                                } else {
                                        Terrasoft.each(oldValue, function(item) {
                                                item.value = value;
                                        });
                                }
                        },
                        /**
                         * Открывает страницу Проекта или Работы для добавления элемента в текущий уровань иерархии
                         * @protected
                         * @virtual
                         * @param {String} typeUId Уникальный идентификатор типа записи проекта
                         */

                        addToRootRecord: function(typeUId) {
                                var masterRecordId = this.Terrasoft.GUID_EMPTY;
                                this.replaceDefaultValue("UsrParent", masterRecordId);
                                this.addRecord();
                        },
                        /**
                         * Обработчик загрузки дочерних элементов. Осуществляет проверку загружености
                         * дочерних элеметов выбранной записи. Запускает загрузку нового уровня.
                         * @protected
                         * @virtual
                         * @param {String} primaryColumnValue Уникальный идентификатор записи
                         * @param {Boolean} isExpanded Признак того, разворачивает или сворачивает пользователь дочерние элеметы
                         * true - если разворачивает, false в обратном случае
                         */

                        onExpandHierarchyLevels: function(primaryColumnValue, isExpanded) {
                                if (!isExpanded || this.isItemExpanded(primaryColumnValue)) {
                                        return;
                                }
                                this.setExpandedItem(primaryColumnValue);
                                this.set("ExpandItemId", primaryColumnValue);
                                this.loadGridData();
                        },

                        /**
                         * Удаляет логику постраничности если загружаются дочерние объекты
                         * @protected
                         * @overridden
                         */

                        initQueryOptions: function() {
                                var parentItem = this.get("ExpandItemId");
                                if (!parentItem) {
                                        var isClearGridData = this.get("IsClearGridData");
                                        if (isClearGridData) {
                                                this.clearExpandHierarchyLevels();
                                        }
                                        this.callParent(arguments);
                                }
                        },
                        /**
                         * Удаляет логику постраничности если загружаются дочерние объекты
                         * @protected
                         * @overridden
                         */

                        initCanLoadMoreData: function() {
                                var parentItem = this.get("ExpandItemId");
                                if (!parentItem) {
                                        this.callParent(arguments);
                                }

                                this.get("expandedElements")["EB64FD9A-DF42-4275-8F1B-0A32ABB68AD5"] = {
                                        page: 0
                                };
                        },
                        /**
                         * Инициализирует параметры детали
                         * @protected
                         * @overridden
                         */

                        initData: function() {
                                this.callParent(arguments);
                                this.set("expandedElements", {});
                                this.set("expandHierarchyLevels", []);
                        },
                },
                diff: /**SCHEMA_DIFF*/[
                        {
                                "operation": "merge",
                                "name": "DataGrid",
                                "values": {
                                        "type": "listed",
                                        "hierarchical": true,
                                        "sortColumnDirection": {"bindTo": "disableGridSorting"},
                                        "hierarchicalColumnName": "ParentId",
                                        "updateExpandHierarchyLevels": {
                                                "bindTo": "onExpandHierarchyLevels"
                                        },
                                        expandHierarchyLevels: {
                                                "bindTo": "expandHierarchyLevels"
                                        },
                                        "listedConfig": {
                                                "name": "DataGridListedConfig",
                                                "items": [
                                                        {
                                                                "name": "UsrFileNameListedGridColumn",
                                                                "bindTo": "UsrFileName",
                                                                "position": {
                                                                        "column": 1,
                                                                        "colSpan": 12
                                                                }
                                                        },
                                                        {
                                                                "name": "UsrDisplayPortalListedGridColumn",
                                                                "bindTo": "UsrDisplayPortal",
                                                                "position": {
                                                                        "column": 21,
                                                                        "colSpan": 4
                                                                }
                                                        }
                                                ]
                                        },
                                        "tiledConfig": {
                                                "name": "DataGridTiledConfig",
                                                "grid": {
                                                        "columns": 24,
                                                        "rows": 3
                                                },
                                                "items": [
                                                        {
                                                                "name": "UsrFileNameTiledGridColumn",
                                                                "bindTo": "UsrFileName",
                                                                "position": {
                                                                        "row": 1,
                                                                        "column": 1,
                                                                        "colSpan": 12
                                                                }
                                                        },
                                                        {
                                                                "name": "UsrDisplayPortalTiledGridColumn",
                                                                "bindTo": "UsrDisplayPortal",
                                                                "position": {
                                                                        "row": 1,
                                                                        "column": 21,
                                                                        "colSpan": 4
                                                                }
                                                        }
                                                ]
                                        }
                                }
                        },
                        {
                                "operation": "insert",
                                "name": "UpButton",
                                "parentName": "Detail",
                                "propertyName": "tools",
                                "index": 0,
                                "values": {
                                        "itemType": Terrasoft.ViewItemType.BUTTON,
                                        "tag": -1,
                                        "imageConfig": {
                                                "bindTo": "Resources.Images.UpImage"
                                        },
                                        "click": {"bindTo": "changePosition"},
                                        "visible": {"bindTo": "getToolsVisible"},
                                        "enabled": {"bindTo": "canChangeSelectedItemPosition"},
                                        "hint": {"bindTo": "Resources.Strings.ChangePositionUpButtonCaption"}
                                }
                        },
                        {
                                "operation": "insert",
                                "name": "DownButton",
                                "parentName": "Detail",
                                "propertyName": "tools",
                                "index": 1,
                                "values": {
                                        "itemType": Terrasoft.ViewItemType.BUTTON,
                                        "tag": 1,
                                        "imageConfig": {
                                                "bindTo": "Resources.Images.DownImage"
                                        },
                                        "click": {"bindTo": "changePosition"},
                                        "visible": {"bindTo": "getToolsVisible"},
                                        "enabled": {"bindTo": "canChangeSelectedItemPosition"},
                                        "hint": {"bindTo": "Resources.Strings.ChangePositionDownButtonCaption"}
                                }
                        },
                        {
                                "operation": "remove",
                                "name": "ViewSortMenu",
                                "parentName": "ViewButton",
                                "propertyName": "menu"
                        }
                ]/**SCHEMA_DIFF*/
        };
});

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

Нравится

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

Может кто то делал подобное

Или как и когда , правильно добавлять в эти объекты ('expandHierarchyLevels', 'expandedElements') Id дочерних записей ?

Роман, все вычисляется напрямую запросом к колонкам UsrPosition и UsrParent.

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

define("UsrSchema2Detail", ["XRMConstants", "terrasoft", "css!ProjectCSSModule"], function(XRMConstants, Terrasoft) {
	return {
		entitySchemaName: "UsrTestTest",
		messages: {
			"CardModuleEntityInfo": {
				"mode": Terrasoft.MessageMode.PTP,
				"direction": Terrasoft.MessageDirectionType.SUBSCRIBE
			}
		},
		attributes: {
			"expandedElements": {
				dataValueType: Terrasoft.DataValueType.CUSTOM_OBJECT,
				type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN
			},
			"expandHierarchyLevels": {
				dataValueType: Terrasoft.DataValueType.CUSTOM_OBJECT,
				type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN
			},
			"ExpandItemId": {
				dataValueType: Terrasoft.DataValueType.GUID,
				type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN
			}
		},
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[
				{
					"operation": "merge",
					"name": "DataGrid",
					"values": {
						"type": "listed",
						"hierarchical": true,
						"sortColumnDirection": {"bindTo": "disableGridSorting"},
						"hierarchicalColumnName": "ParentId",
						"updateExpandHierarchyLevels": {
							"bindTo": "onExpandHierarchyLevels"
						},
						expandHierarchyLevels: {
							"bindTo": "expandHierarchyLevels"
						}
					}
				},
				{
					"operation": "insert",
					"name": "UpButton",
					"parentName": "Detail",
					"propertyName": "tools",
					"index": 0,
					"values": {
						"itemType": Terrasoft.ViewItemType.BUTTON,
						"tag": -1,
						"imageConfig": {
							"bindTo": "Resources.Images.UpImage"
						},
						"click": {"bindTo": "changePosition"},
						"visible": {"bindTo": "getToolsVisible"},
						"enabled": {"bindTo": "canChangeSelectedItemPosition"},
						"hint": {"bindTo": "Resources.Strings.ChangePositionUpButtonCaption"}
					}
				},
				{
					"operation": "insert",
					"name": "DownButton",
					"parentName": "Detail",
					"propertyName": "tools",
					"index": 1,
					"values": {
						"itemType": Terrasoft.ViewItemType.BUTTON,
						"tag": 1,
						"imageConfig": {
							"bindTo": "Resources.Images.DownImage"
						},
						"click": {"bindTo": "changePosition"},
						"visible": {"bindTo": "getToolsVisible"},
						"enabled": {"bindTo": "canChangeSelectedItemPosition"},
						"hint": {"bindTo": "Resources.Strings.ChangePositionDownButtonCaption"}
					}
				},
				{
					"operation": "remove",
					"name": "ViewSortMenu",
					"parentName": "ViewButton",
					"propertyName": "menu"
				}
		]/**SCHEMA_DIFF*/,
		methods: {
				disableGridSorting: Ext.emptyFn,
				sortColumn: Ext.emptyFn,
				init: function() {
					this.callParent(arguments);
				},
				initData: function() {
					this.callParent(arguments);
					this.set("expandedElements", {});
					this.set("expandHierarchyLevels", []);
				},
				onDeleted: function(result) {
					this.callParent(arguments);
					if (result.Success) {
						this.reloadGridData();
					}
				},
				clearExpandHierarchyLevels: function() {
					this.set("expandedElements", {});
					this.set("expandHierarchyLevels", []);
					var grid = Ext.getCmp("ProjectDetailV2DataGridGrid");
					if (grid) {
						grid.expandHierarchyLevels = [];
					}
				},
				removeExpandHierarchyLevel: function(itemId) {
					var expandHierarchyLevels = this.get("expandHierarchyLevels");
					this.set("expandHierarchyLevels", Terrasoft.without(expandHierarchyLevels, itemId));
					var grid = Ext.getCmp("ProjectDetailV2DataGridGrid");
					if (grid) {
						grid.expandHierarchyLevels = Terrasoft.without(grid.expandHierarchyLevels, itemId);
					}
				},
				addGridDataColumns: function(esq) {
					this.callParent(arguments);
					this.putParentColumn(esq);
					this.putNestingColumn(esq);
					this.putPositionColumn(esq);
				},
				putParentColumn: function(esq) {
					var parentItem = this.get("ExpandItemId");
					if (parentItem && !esq.columns.contains("ParentId")) {
						esq.addColumn("UsrParent.Id", "ParentId");
					}
				},
				putPositionColumn: function(esq) {
					if (!esq.columns.contains("UsrPosition")) {
						esq.addColumn("UsrPosition");
					}
				},
				putNestingColumn: function(esq) {
					var aggregationColumn = this.Ext.create("Terrasoft.AggregationQueryColumn", {
						aggregationType: Terrasoft.AggregationType.COUNT,
						columnPath: "[UsrTestTest:UsrParent].Id"
					});
					if (!esq.columns.contains("HasNesting")) {
						esq.addColumn(aggregationColumn, "HasNesting");
					}
				},
				getParents: function(primaryValues) {
					var parentPrimaryValues = [];
					var gridData = this.getGridData();
					if (Ext.isEmpty(primaryValues)) {
						return parentPrimaryValues;
					}
					primaryValues.forEach(function(primaryColumnValue) {
						var project = gridData.get(primaryColumnValue);
						var parentPrimaryColumnValue = project.get("ParentId");
						if (parentPrimaryColumnValue) {
							parentPrimaryValues.push(parentPrimaryColumnValue);
						}
					});
					return parentPrimaryValues;
				},
				removeGridRecords: function(primaryColumnValues) {
					var updateNestingCollection = this.getParents(primaryColumnValues);
					this.callParent(arguments);
					var gridData = this.getGridData();
					Terrasoft.each(updateNestingCollection, function(projectId) {
						var count = gridData.filterByFn(function(item) {
							return item.get("ParentId") === projectId;
						}, this).getCount();
						if (gridData.contains(projectId)) {
							this.removeExpandHierarchyLevel(projectId);
							var parent = gridData.get(projectId);
							parent.set("HasNesting", count);
						}
					}, this);
				},
				getExpandedItems: function() {
					return this.get("expandedElements");
				},
				setExpandedItem: function(primaryColumnValue) {
					(this.getExpandedItems()[primaryColumnValue]) = {"page": 0};
				},
				isItemExpanded: function(primaryColumnValue) {
					return Boolean(this.getExpandedItems()[primaryColumnValue]);
				},
				addItemsToGridData: function(dataCollection, options) {
					if (dataCollection.isEmpty()) {
						return;
					}
					var firstItem = dataCollection.getByIndex(0);
					var parentId = firstItem.get("ParentId");
					if (parentId) {
						options = {
							mode: "child",
							target: parentId
						};
						var gridData = this.getGridData();
						var parentObj = gridData.get(parentId);
						if (parentObj) {
							parentObj.set("HasNesting", 1);
						}
						if (!this.isItemExpanded(parentId)) {
							return;
						}
					} else {
						this.set("LastRecord", dataCollection.getByIndex(dataCollection.getCount() - 1));
					}
					this.callParent([dataCollection, options]);
					if (options && (options.mode === "child" || options.mode === "top")) {
						var gridDataChild = this.getGridData();
						var tempCollection = this.Ext.create("Terrasoft.Collection");
						tempCollection.loadAll(gridDataChild);
						this.sortCollection(tempCollection);
						gridDataChild.clear();
						gridDataChild.loadAll(tempCollection);
					}
					this.set("ExpandItemId", null);
				},
				onExpandHierarchyLevels: function(primaryColumnValue, isExpanded) {
					if (!isExpanded || this.isItemExpanded(primaryColumnValue)) {
						return;
					}
					this.setExpandedItem(primaryColumnValue);
					this.set("ExpandItemId", primaryColumnValue);
					this.loadGridData();
				},
				initQueryOptions: function() {
					var parentItem = this.get("ExpandItemId");
					if (!parentItem) {
						var isClearGridData = this.get("IsClearGridData");
						if (isClearGridData) {
							this.clearExpandHierarchyLevels();
						}
						this.callParent(arguments);
					}
				},
				initCanLoadMoreData: function() {
					var parentItem = this.get("ExpandItemId");
					if (!parentItem) {
						this.callParent(arguments);
					}
				},
				changeSorting: function() {
					this.clearExpandHierarchyLevels();
					this.callParent(arguments);
				},
				initQuerySorting: function(esq) {
					var sortedColumn = esq.columns.get("UsrPosition");
					sortedColumn.orderPosition = 0;
					sortedColumn.orderDirection = Terrasoft.OrderDirection.ASC;
				},
				getFilters: function() {
					var parentItem = this.get("ExpandItemId");
					if (parentItem) {
						var parentProjectFilterGroup = this.Terrasoft.createFilterGroup();
						parentProjectFilterGroup.addItem(this.Terrasoft.createColumnFilterWithParameter(
							this.Terrasoft.ComparisonType.EQUAL,
							"UsrParent",
							parentItem,
							Terrasoft.DataValueType.GUID
						));
						return parentProjectFilterGroup;
					} else {
						var filters = this.callParent(arguments);
						if (this.get("DetailColumnName") !== "UsrParent") {
							var group = this.Terrasoft.createFilterGroup();
							group.addItem(filters);
							group.addItem(
								Terrasoft.createColumnIsNullFilter(this.entitySchema.hierarchicalColumnName)
							);
							filters = group;
						}
						return filters;
					}
				},
				isProjectsInRoot: function() {
					return (this.get("DetailColumnName") !== "UsrParent");
				},
				isWorkInRoot: function() {
					return !this.isProjectsInRoot();
				},
				initEditPages: function() {
					this.callParent(arguments);
					var editPages = this.get("EditPages");
					var workPage = editPages.collection.items[0];
					workPage.set("Click", {"bindTo": "addToRootRecord"});
					workPage.set("Caption", "Add");
					workPage.set("Visible", true);
					var childItemId = editPages.collection.items[0].imstanceId;
					var config = {
						"Id": childItemId,
						"Caption": "New",
						"Click": {"bindTo": "addToChildRecord"},
						"Enabled": {"bindTo": "isSingleSelected"},
						"Tag": childItemId,
						"SchemaName": "UsrTestTest1Page"
					};
					var addToChildMenuItem = this.getActionsMenuItem(config);
					editPages.add(childItemId, addToChildMenuItem);
				},
				replaceDefaultValue: function(key, value) {
					var defaultValues = this.get("DefaultValues");
					var oldValue = defaultValues.filter(function(item) {
						return item.name === key;
					});
					if (Ext.isEmpty(oldValue)) {
						defaultValues.push({
							name: key,
							value: value
						});
					} else {
						Terrasoft.each(oldValue, function(item) {
							item.value = value;
						});
					}
				},
				getDetailInfo: function() {
					var detailInfo = this.sandbox.publish("GetDetailInfo", null, [this.sandbox.id]) || {};
					var defaultValues = this.get("DefaultValues");
					detailInfo.defaultValues = defaultValues;
					return detailInfo;
				},
				addToRootRecord: function(typeUId) {
					var masterRecordId = (typeUId === XRMConstants.Project.EntryType.Project)
						? this.Terrasoft.GUID_EMPTY
						: this.get("MasterRecordId");
					this.replaceDefaultValue("UsrParent", masterRecordId);
					this.addRecord(typeUId);
				},
				addToChildRecord: function() {
					var scope = this;
					var selectedItems = this.getSelectedItems();
					this.set("ExpandItemId", selectedItems[0]);
					this.replaceDefaultValue("UsrParent", selectedItems[0]);
					var masterRecordId = this.get("MasterRecordId");
					if (masterRecordId !== null) {
						var esq = Ext.create("Terrasoft.EntitySchemaQuery", {rootSchemaName: "UsrTestTest"});
						var filter = Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL,
							"Id", masterRecordId, Terrasoft.DataValueType.GUID);
						esq.filters.add("filter", filter);
						esq.getEntityCollection(function(response) {
							if (!response || !response.success) {
								return;
							}
							var result = response.collection;
							result.each(function(resultItem) {
								scope.addRecord('c485c4e3-c0a1-4008-a8fe-054f167d5068');
							});
						}, this);
					} else {
						this.addRecord('c485c4e3-c0a1-4008-a8fe-054f167d5068');
					}
				},
				canChangeSelectedItemPosition: function() {
					var selectedItems = this.getSelectedItems();
					return (!Ext.isEmpty(selectedItems) && (selectedItems.length <= 1));
				},
				changePosition: function() {
					var shift = arguments[3];
					var activeRow = this.getActiveRow();
					var projectId = activeRow.get("Id");
					this.updateProjectPosition(projectId, shift, this.onPositionChanged, this);
				},
				onPositionChanged: function(result) {
					if (!result) {
						return;
					}
					var activeRow = this.getActiveRow();
					var parentId = activeRow.get("ParentId");
					if (!parentId) {
						parentId = this.get("MasterRecordId");
					}
					var esq = Ext.create("Terrasoft.EntitySchemaQuery", {rootSchema: this.entitySchema});
					esq.addColumn("UsrPosition");
					var filter = Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL,
						"UsrParent", parentId, Terrasoft.DataValueType.GUID);
					esq.filters.add("filter", filter);
					esq.getEntityCollection(function(response) {
						if (!response || !response.success) {
							return;
						}
						var gridData = this.getGridData();
						var result = response.collection;
						result.each(function(resultItem) {
							var resultItemId = resultItem.get("Id");
							var newPosition = resultItem.get("UsrPosition");
							var gridRecord = gridData.get(resultItemId);
							gridRecord.set("UsrPosition", newPosition);
						});
						var tempCollection = this.Ext.create("Terrasoft.Collection");
						tempCollection.loadAll(gridData);
						this.sortCollection(tempCollection);
						gridData.clear();
						gridData.loadAll(tempCollection);
					}, this);
				},
				sortCollection: function(collection) {
					collection.sortByFn(function(a, b) {
						var positionA = a.get("UsrPosition");
						var positionB = b.get("UsrPosition");
						var q = positionA - positionB;
						return q === 0 ? 0 : (q) / Math.abs(q);
					});
				},
		}
	};
});

Доброго времени суток.
Недавно столкнулся с такой же задачкой. Вот примерный алгоритм:
1) Отрубить в системных настройках ненавистный префикс "Usr" (необяз. пункт).
2) Создать Объект (род. объект - Базовый объект), заполнить его нужными полями, сохранить, опубликовать.
3) Еще раз открыть объект на редактирование и создать там справочник Parent, род. объект - ставим ссылку на только что созданный объект. В свойствах объекта (справа кнопка списка-свойства-все) в графе "Родитель в иерархии" ставим поле Parent. Сохраняем, Публикуем.
4) Когда заполнять таблицу - решаете сами. Либо в схеме (через InsertQuery), либо через хранимку, либо через С# код. Без разницы (в крайнем случае - this.reloadGridData()). Главное у нужных записей заполнять поле Parent айдишником записи-родителя (тем самым формируя иерархию)
5) Теперь сама схема. Создаем схему. Род. объект - Базовая схема детали с реестром. Код приложу. Там надо поменять название схемы в define, ссылку на entitySchemaName (объект, который мы создали ранее) и ссылку на колонку объекта в putNestingColumn.
6) Все. Теперь добавляем деталь на страницу (из кода. в дизайнере не отобразится, т.к. не зарегистрирована). Если все сделано правильно и карточка загрузилась без ошибок, последний шаг - настроить вывод информации в гриде. Это можно сделать через ToolButton прямо в карточке.

[quote="Мотков Илья"]

Роман, все вычисляется напрямую запросом к колонкам UsrPosition и UsrParent.

Спасибо вам большое, вы мне очень помогли. Но теперь такая проблема, во первых не сохраняется ID записи, родителя, если это нормально, то я переделаю просто save на карточке и все. Во-вторых не открывается одновременно две записи с дочерними записями, пишет в консоли

 message: Uncaught Terrasoft.ItemNotFoundException: Item with key f5695207-a45d-4975-a484-92384cb438d1 Does not exist 

Виртуальный обьект и коллекцию, проверил, все корректно добавляет и удаляет. Уже несколько раз сравниваю с разделом "Проекты" , не могу найти разницу ...
Код схемы:

define("UsrReportingDetailV2", ["XRMConstants","ConfigurationGrid", "ConfigurationGridGenerator", "ProcessModuleUtilities", "ConfigurationGridUtilities", "ConfigurationEnums"], 
	function(XRMConstants, ProcessModuleUtilities, ConfigurationGridUtilities, enums) {
	return {
		entitySchemaName: "UsrReporting",
		messages: {
			"CardModuleEntityInfo": {
				"mode": Terrasoft.MessageMode.PTP,
				"direction": Terrasoft.MessageDirectionType.SUBSCRIBE
			}
		},
		attributes: {
			"isPortal": {
				"dataValueType": Terrasoft.DataValueType.BOOLEAN,
				"value":  false
			},
			"isCRM": {
				"dataValueType": Terrasoft.DataValueType.BOOLEAN,
				"value":  true
			},
			/**
			 * Отвечает за загруженные уровни иерархии
			 */
			"expandedElements": {
				dataValueType: Terrasoft.DataValueType.CUSTOM_OBJECT,
				type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN
			},
			/**
			 * Отвечает за список развернутых элементов,
			 * хранит массив уникальных идетификаторов записей
			 */
			"expandHierarchyLevels": {
				dataValueType: Terrasoft.DataValueType.CUSTOM_OBJECT,
				type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN
			},
			/**
			 * Отвечает за хранение разворачиваемого элемента иерархии
			 */
			"ExpandItemId": {
				dataValueType: Terrasoft.DataValueType.GUID,
				type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN
			}
		},
		mixins: {
			ConfigurationGridUtilites: "Terrasoft.ConfigurationGridUtilities"
		},
		methods: {
			disableGridSorting: Ext.emptyFn,
			sortColumn: Ext.emptyFn,
			init: function() {
				this.callParent(arguments);
			},
			initData: function() {
				this.callParent(arguments);
				this.set("expandedElements", {});
				this.set("expandHierarchyLevels", []);
			},
			onDeleted: function(result) {
				this.callParent(arguments);
				if (result.Success) {
					this.reloadGridData();
				}
			},
			clearExpandHierarchyLevels: function() {
				this.set("expandedElements", {});
				this.set("expandHierarchyLevels", []);
				var grid = Ext.getCmp("ProjectDetailV2DataGridGrid");
				if (grid) {
					grid.expandHierarchyLevels = [];
				}
			},
			removeExpandHierarchyLevel: function(itemId) {
				var expandHierarchyLevels = this.get("expandHierarchyLevels");
				this.set("expandHierarchyLevels", Terrasoft.without(expandHierarchyLevels, itemId));
				var grid = Ext.getCmp("ProjectDetailV2DataGridGrid");
				if (grid) {
					grid.expandHierarchyLevels = Terrasoft.without(grid.expandHierarchyLevels, itemId);
				}
			},
			addGridDataColumns: function(esq) {
				this.callParent(arguments);
				this.putParentColumn(esq);
				this.putNestingColumn(esq);
				this.putPositionColumn(esq);
			},
			putParentColumn: function(esq) {
				var parentItem = this.get("ExpandItemId");
				if (parentItem && !esq.columns.contains("ParentId")) {
				esq.addColumn("UsrParent.Id", "ParentId");
				}
			},
			putPositionColumn: function(esq) {
				if (!esq.columns.contains("UsrPosition")) {
					esq.addColumn("UsrPosition");
				}
			},
			putNestingColumn: function(esq) {
				var aggregationColumn = this.Ext.create("Terrasoft.AggregationQueryColumn", {
				aggregationType: Terrasoft.AggregationType.COUNT,
					columnPath: "[UsrReporting:UsrParent].Id"
				});
				if (!esq.columns.contains("HasNesting")) {
					esq.addColumn(aggregationColumn, "HasNesting");
				}
			},
			getParents: function(primaryValues) {
				var parentPrimaryValues = [];
				var gridData = this.getGridData();
				if (Ext.isEmpty(primaryValues)) {
					return parentPrimaryValues;
				}
				primaryValues.forEach(function(primaryColumnValue) {
					var project = gridData.get(primaryColumnValue);
					var parentPrimaryColumnValue = project.get("ParentId");
					if (parentPrimaryColumnValue) {
						parentPrimaryValues.push(parentPrimaryColumnValue);
					}
				});
				return parentPrimaryValues;
			},
			removeGridRecords: function(primaryColumnValues) {
				var updateNestingCollection = this.getParents(primaryColumnValues);
				this.callParent(arguments);
				var gridData = this.getGridData();
				Terrasoft.each(updateNestingCollection, function(projectId) {
				var count = gridData.filterByFn(function(item) {
				return item.get("ParentId") === projectId;
				}, this).getCount();
				if (gridData.contains(projectId)) {
				this.removeExpandHierarchyLevel(projectId);
				var parent = gridData.get(projectId);
				parent.set("HasNesting", count);
				}
				}, this);
			},
			getExpandedItems: function() {
				return this.get("expandedElements");
			},
			setExpandedItem: function(primaryColumnValue) {
				(this.getExpandedItems()[primaryColumnValue]) = {"page": 0};
			},
			isItemExpanded: function(primaryColumnValue) {
				return Boolean(this.getExpandedItems()[primaryColumnValue]);
			},
			addItemsToGridData: function(dataCollection, options) {
				if (dataCollection.isEmpty()) {
					return;
				}
				var firstItem = dataCollection.getByIndex(0);
				var parentId = firstItem.get("ParentId");
				if (parentId) {
					options = {
						mode: "child",
						target: parentId
					};
					var gridData = this.getGridData();
					var parentObj = gridData.get(parentId);
					if (parentObj) {
						parentObj.set("HasNesting", 1);
					}
					if (!this.isItemExpanded(parentId)) {
						return;
					}
				} else {
					this.set("LastRecord", dataCollection.getByIndex(dataCollection.getCount() - 1));
				}
				this.callParent([dataCollection, options]);
				if (options && (options.mode === "child" || options.mode === "top")) {
					var gridDataChild = this.getGridData();
					var tempCollection = this.Ext.create("Terrasoft.Collection");
					tempCollection.loadAll(gridDataChild);
					this.sortCollection(tempCollection);
					gridDataChild.clear();
					gridDataChild.loadAll(tempCollection);
				}
				this.set("ExpandItemId", null);
			},
			onExpandHierarchyLevels: function(primaryColumnValue, isExpanded) {
				if (!isExpanded || this.isItemExpanded(primaryColumnValue)) {
					return;
				}
				this.setExpandedItem(primaryColumnValue);
				this.set("ExpandItemId", primaryColumnValue);
				this.loadGridData();
			},
			initQueryOptions: function() {
				var parentItem = this.get("ExpandItemId");
				if (!parentItem) {
					var isClearGridData = this.get("IsClearGridData");
					if (isClearGridData) {
						this.clearExpandHierarchyLevels();
					}
					this.callParent(arguments);
				}
			},
			initCanLoadMoreData: function() {
				var parentItem = this.get("ExpandItemId");
				if (!parentItem) {
					this.callParent(arguments);
				}
			},
			changeSorting: function() {
				this.clearExpandHierarchyLevels();
				this.callParent(arguments);
			},
			initQuerySorting: function(esq) {
				var sortedColumn = esq.columns.get("UsrPosition");
				sortedColumn.orderPosition = 0;
				sortedColumn.orderDirection = Terrasoft.OrderDirection.ASC;
			},
			getFilters: function() {
				var parentItem = this.get("ExpandItemId");
				if (parentItem) {
					var parentProjectFilterGroup = this.Terrasoft.createFilterGroup();
					parentProjectFilterGroup.addItem(this.Terrasoft.createColumnFilterWithParameter(
						this.Terrasoft.ComparisonType.EQUAL, "UsrParent", parentItem, Terrasoft.DataValueType.GUID));
					return parentProjectFilterGroup;
				} else {
					var filters = this.callParent(arguments);
					if (this.get("DetailColumnName") !== "UsrParent") {
						var group = this.Terrasoft.createFilterGroup();
						group.addItem(filters);
						group.addItem(Terrasoft.createColumnIsNullFilter(this.entitySchema.hierarchicalColumnName));
						filters = group;
					}
					return filters;
				}
			},
			isProjectsInRoot: function() {
				return (this.get("DetailColumnName") !== "UsrParent");
			},
			isWorkInRoot: function() {
				return !this.isProjectsInRoot();
			},
			initEditPages: function() {
				this.callParent(arguments);
				var editPages = this.get("EditPages");
				var workPage = editPages.collection.items[0];
				workPage.set("Click", {"bindTo": "addToRootRecord"});
				workPage.set("Caption", "Add");
				workPage.set("Visible", true);
				var childItemId = editPages.collection.items[0].instanceId;
				var config = {
					"Id": childItemId,
					"Caption": "New",
					"Click": {"bindTo": "addToChildRecord"},
					"Enabled": {"bindTo": "isSingleSelected"},
					"Tag": childItemId,
					"SchemaName": "UsrReportingPageV2"
				};
				var addToChildMenuItem = this.getActionsMenuItem(config);
				editPages.add(childItemId, addToChildMenuItem);
			},
			replaceDefaultValue: function(key, value) {
				var defaultValues = this.get("DefaultValues");
				var oldValue = defaultValues.filter(function(item) {
					return item.name === key;
				});
				if (Ext.isEmpty(oldValue)) {
					defaultValues.push({
						name: key,
						value: value
					});
				} else {
					Terrasoft.each(oldValue, function(item) {
						item.value = value;
					});
				}
			},
			getDetailInfo: function() {
				var detailInfo = this.sandbox.publish("GetDetailInfo", null, [this.sandbox.id]) || {};
				var defaultValues = this.get("DefaultValues");
				detailInfo.defaultValues = defaultValues;
				return detailInfo;
			},
			addToRootRecord: function(typeUId) {
				var masterRecordId = (typeUId === XRMConstants.Project.EntryType.Project)
					? this.Terrasoft.GUID_EMPTY
					: this.get("MasterRecordId");
				this.replaceDefaultValue("UsrParent", masterRecordId);
				this.addRecord(typeUId);
			},
			addToChildRecord: function() {
				var scope = this;
				var selectedItems = this.getSelectedItems();
				this.set("ExpandItemId", selectedItems[0]);
				this.replaceDefaultValue("UsrParent", selectedItems[0]);
				var masterRecordId = this.get("MasterRecordId");
				if (masterRecordId !== null) {
					var esq = Ext.create("Terrasoft.EntitySchemaQuery", {rootSchemaName: "UsrReporting"});
					var filter = Terrasoft.createColumnFilterWithParameter(
						Terrasoft.ComparisonType.EQUAL, "UsrAccount", masterRecordId, Terrasoft.DataValueType.GUID);
					esq.filters.add("filter", filter);
					esq.getEntityCollection(function(response) {
						if (!response || !response.success) {
							return;
						}
						var result = response.collection;
						result.each(function(resultItem) {
							scope.addRecord('c485c4e3-c0a1-4008-a8fe-054f167d5068');
						});
					}, this);
				} else {
					this.addRecord('c485c4e3-c0a1-4008-a8fe-054f167d5068');
				}
			},
			canChangeSelectedItemPosition: function() {
				var selectedItems = this.getSelectedItems();
				return (!Ext.isEmpty(selectedItems) && (selectedItems.length <= 1));
			},
			changePosition: function() {
				var shift = arguments[3];
				var activeRow = this.getActiveRow();
				var projectId = activeRow.get("Id");
				this.updateProjectPosition(projectId, shift, this.onPositionChanged, this);
			},
			onPositionChanged: function(result) {
				if (!result) {
					return;
				}
				var activeRow = this.getActiveRow();
				var parentId = activeRow.get("ParentId");
				if (!parentId) {
					parentId = this.get("MasterRecordId");
				}
				var esq = Ext.create("Terrasoft.EntitySchemaQuery", {rootSchema: this.entitySchema});
				esq.addColumn("UsrPosition");
				var filter = Terrasoft.createColumnFilterWithParameter(
					Terrasoft.ComparisonType.EQUAL, "UsrParent", parentId, Terrasoft.DataValueType.GUID);
				esq.filters.add("filter", filter);
				esq.getEntityCollection(function(response) {
					if (!response || !response.success) {
						return;
					}
					var gridData = this.getGridData();
					var result = response.collection;
					result.each(function(resultItem) {
						var resultItemId = resultItem.get("Id");
						var newPosition = resultItem.get("UsrPosition");
						var gridRecord = gridData.get(resultItemId);
							gridRecord.set("UsrPosition", newPosition);
						});
						var tempCollection = this.Ext.create("Terrasoft.Collection");
						tempCollection.loadAll(gridData);
						this.sortCollection(tempCollection);
						gridData.clear();
						gridData.loadAll(tempCollection);
				}, this);
			},
			sortCollection: function(collection) {
				collection.sortByFn(function(a, b) {
					var positionA = a.get("UsrPosition");
					var positionB = b.get("UsrPosition");
					var q = positionA - positionB;
					return q === 0 ? 0 : (q) / Math.abs(q);
				});
			},
		},
		diff: /**SCHEMA_DIFF*/[
			{
				"operation": "merge",
				"name": "DataGrid",
				"values": {
					"type": "listed",
					"hierarchical": true,
					"sortColumnDirection": {"bindTo": "disableGridSorting"},
					"hierarchicalColumnName": "ParentId",
					"updateExpandHierarchyLevels": {
						"bindTo": "onExpandHierarchyLevels"
					},
					expandHierarchyLevels: {
						"bindTo": "expandHierarchyLevels"
					}
				}
			},
			{
				"operation": "insert",
				"name": "OpenReportButton",
				"parentName": "Detail",
				"propertyName": "tools",
				"values": {
					"itemType": Terrasoft.ViewItemType.BUTTON,
					"click": {"bindTo": "openReport"},
					"visible": {"bindTo": "isPortal"},
					"enabled": {"bindTo": "isPortal"},
					"caption": "Open"
				},
				"index": 0
			},
			{
				"operation": "remove",
				"name": "AddRecordButton",
				"values": {
					"visible": {"bindTo": "isCRM"}
				}
			},
			{
				"operation": "insert",
				"name": "UpButton",
				"parentName": "Detail",
				"propertyName": "tools",
				"index": 0,
				"values": {
					"itemType": Terrasoft.ViewItemType.BUTTON,
					"tag": -1,
					"imageConfig": {
						"bindTo": "Resources.Images.UpImage"
					},
					"click": {"bindTo": "changePosition"},
					"visible": {"bindTo": "getToolsVisible"},
					"enabled": {"bindTo": "canChangeSelectedItemPosition"},
					"hint": {"bindTo": "Resources.Strings.ChangePositionUpButtonCaption"}
				}
			},
			{
				"operation": "insert",
				"name": "DownButton",
				"parentName": "Detail",
				"propertyName": "tools",
				"index": 1,
				"values": {
					"itemType": Terrasoft.ViewItemType.BUTTON,
					"tag": 1,
					"imageConfig": {
						"bindTo": "Resources.Images.DownImage"
					},
					"click": {"bindTo": "changePosition"},
					"visible": {"bindTo": "getToolsVisible"},
					"enabled": {"bindTo": "canChangeSelectedItemPosition"},
					"hint": {"bindTo": "Resources.Strings.ChangePositionDownButtonCaption"}
				}
			}
		]/**SCHEMA_DIFF*/
	};
});

Ошибка Uncaught Terrasoft.ItemNotFoundException: Item with key f5695207-a45d-4975-a484-92384cb438d1 Does not exist говорит, что не был найден какой-то элемент.

Уточните, пожалуйста, Вы не удаляли колонки из объекта?

Нет конечно, в обьект вообще не заходил после того как добавил колонки.
Ну и плюс смотрите, если я зайду на страницу, и открою первую запись, её дерево откроется, а у второй не открывается тогда дерерво. Но если обновить страницу, и открыть сначала вторую запись, то у неё дерево откроется, но тогда уже у первой не откроется. Ошибка та же самая в консоли ... То есть могу сейчас открыть только одну иерархию, и после обновления страницы другую, но только одну !

Попробуйте заместить метод addItemToGridData следующим образом:

	addItemsToGridData: function(dataCollection, options) {
					if (dataCollection.isEmpty()) {
						return;
					}
					var firstItem = dataCollection.getByIndex(0);
					var parentId = firstItem.get("ParentId");
					if (parentId) {
						options = {
							mode: "child",
							target: parentId
						};
						var gridData = this.getGridData();
						var parentObj = gridData.contains(parentId) ? gridData.get(parentId) : undefined;
						if (parentObj) {
							parentObj.set("HasNesting", 1);
						}
						if (!this.isItemExpanded(parentId)) {
							return;
						}
					} else {
						this.set("LastRecord", dataCollection.getByIndex(dataCollection.getCount() - 1));
					}
					this.callParent([dataCollection, options]);
					if (options && (options.mode === "child" || options.mode === "top")) {
						var gridDataChild = this.getGridData();
						var tempCollection = this.Ext.create("Terrasoft.Collection");
						tempCollection.loadAll(gridDataChild);
						this.sortCollection(tempCollection);
						gridDataChild.clear();
						gridDataChild.loadAll(tempCollection);
					}
					this.set("ExpandItemId", null);
				},

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

define("UsrReportingPageV2", [],
	function() {
		return {
			entitySchemaName: "UsrReporting",
			messages: {},
			attributes: {
				"enabledForPortal": {
					"dataValueType": Terrasoft.DataValueType.BOOLEAN,
					"value":  true
				},
				"visibleEnablePortalBool": {
					"dataValueType": Terrasoft.DataValueType.BOOLEAN,
					"value":  true
				}
			},
			details: /**SCHEMA_DETAILS*/{
				Files: {
					schemaName: "FileDetailV2",
					entitySchemaName: "UsrReportingFile",
					filter: {
						masterColumn: "Id",
						detailColumn: "UsrReporting"
					}
				}
			}/**SCHEMA_DETAILS*/,
 
			mixins: {},
			methods: {
				init: function () {
					var esq = Ext.create("Terrasoft.EntitySchemaQuery", {
						"rootSchemaName": "SysAdminUnit"
					});
					esq.addColumn("Id");
					esq.filters.addItem(Terrasoft.createColumnFilterWithParameter(
						Terrasoft.ComparisonType.EQUAL, "Id", Terrasoft.SysValue.CURRENT_USER.value));
					esq.filters.addItem(Terrasoft.createColumnFilterWithParameter(
						Terrasoft.ComparisonType.EQUAL, "[SysAdminUnitInRole:SysAdminUnit:Id].SysAdminUnitRoleId",
						"720B771C-E7A7-4F31-9CFB-52CD21C3739F"));
					esq.getEntityCollection(function(result) {
						var isPortalUser = result.success && (result.collection.getCount() === 1);
						this.set("enabledForPortal", !isPortalUser);
						this.set("visibleEnablePortalBool", !isPortalUser);
						if (isPortalUser) this.checkEnabled();
					}, this);
					this.callParent(arguments);
				},
				checkEnabled: function () {
					if (this.get("UsrEnablePortal")) {
 
					}
				},
				onSaved: function() {
					if (Terrasoft.configuration.Storage.hasOwnProperty("newChildReport")) {
						var accountID, parentID;
						var defValues = this.get("DefaultValues");
						for (var i = 0; i < defValues.length; i++) {
							switch (defValues[i].name) {
								case "UsrAccount":
									accountID = defValues[i].value;
									break;
								case "UsrParent": 
									parentID = defValues[i].value;
									break;
							}
						}
						var update = Ext.create("Terrasoft.UpdateQuery", {
							rootSchemaName: "UsrReporting"
						});
						update.setParameterValue("UsrAccount", accountID, Terrasoft.DataValueType.GUID);
						update.setParameterValue("UsrParent", parentID, Terrasoft.DataValueType.GUID);
						var filter = update.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL, "Id" , this.get("Id"));
						update.filters.add("FilterId", filter);
 
						update.execute(function(result) {
 
						});
						delete Terrasoft.configuration.Storage.newChildReport;
					}
					this.callParent(arguments);
				}
			},
			diff: /**SCHEMA_DIFF*/[
	{
		"operation": "insert",
		"name": "UsrDisplayPortal",
		"values": {
			"layout": {
				"colSpan": 12,
				"rowSpan": 1,
				"column": 12,
				"row": 1,
				"layoutName": "Header"
			},
			"enabled": {"bindTo": "enabledForPortal"},
			"visible": {"bindTo": "visibleEnablePortalBool"},
			"bindTo": "UsrDisplayPortal"
		},
		"parentName": "Header",
		"propertyName": "items",
		"index": 0
	},
	{
		"operation": "insert",
		"name": "UsrFileName",
		"values": {
			"layout": {
				"colSpan": 12,
				"rowSpan": 1,
				"column": 0,
				"row": 0,
				"layoutName": "Header"
			},
			"bindTo": "UsrFileName",
			"enabled": {"bindTo": "enabledForPortal"},
			"visible": true
		},
		"parentName": "Header",
		"propertyName": "items",
		"index": 1
	},
	{
		"operation": "insert",
		"name": "HistoryTabContainer",
		"values": {
			"caption": {
				"bindTo": "Resources.Strings.HistoryTabCaption"
			},
			"items": []
		},
		"parentName": "Tabs",
		"propertyName": "tabs",
		"index": 0
	},
	{
		"operation": "insert",
		"name": "Files",
		"values": {
			"itemType": 2
		},
		"parentName": "HistoryTabContainer",
		"propertyName": "items",
		"index": 0
	},
	{
		"operation": "remove",
		"name": "ESNTab"
	},
	{
		"operation": "remove",
		"name": "ESNFeedContainer"
	},
	{
		"operation": "remove",
		"name": "ESNFeed"
	}
]/**SCHEMA_DIFF*/
	};
});
Показать все комментарии