Добрый день!
На странице редактирования лида при вводе в поле контакт имени контакта система предлагает создать новый. И если нажать на "Создать", то сразу же создастся контакт с введенным именем в поле.
Стоит задача, что бы после нажатия "создать" открывалась страница контакта, и можно было дополнять данные уже на странице контакта.

Немогу словить в каком модуле реализовано создание контакта, да и в модуле ли вообще оно реализовано? ВОзможно кто-то решал подобное?
Есть конечно один вариант, при котором будет открываться страница контакта - это в обьекте Контакт сделать какое-то поле обьязательным на уровне приложения. Но это не выход...

Нравится

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

Здравствуйте!

Функциональность создания новой записи из справочника реализована в LookupQuickAddMixin.

В последней релизной версии уже встроена функциональность открытия карточки перед созданием объекта, в методах tryCreateEntityOrOpenCard->openPageForNewEntity.

За эту функциональность отвечает фича UseSilentCreation.

"Demchenko Olha" написал:

Здравствуйте!

Функциональность создания новой записи из справочника реализована в LookupQuickAddMixin.

В последней релизной версии уже встроена функциональность открытия карточки перед созданием объекта, в методах tryCreateEntityOrOpenCard->openPageForNewEntity.

За эту функциональность отвечает фича UseSilentCreation.

Ольга, спасибо!

Я, как раз перед Вашим ответом нашел нужный модуль LookupQuickAddMixin :smile:
Заместил его, и добавил необходимое условие, чтобы срабатывал метод openPageForNewEntity.
Все работает корректно!

Я попробовал в 7.16 менять эту фичу, но она ни на что не повлияла. Как была возможность создать новую запись прямо из поля, так и осталась

В 7.16 карточка лида выглядит уже не так, как в первом сообщении темы. Возможно, и логика менялась. Где именно вводите и создаётся запись?

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

В таблице SysAdminUnitType (Типы административных юнитов, на данный момент мне все понятно со всеми типами кроме одного - "Team" (3)

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

Нравится

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

Здравствуйте,
на карточке “группы” есть поле “гид” и деталь “контакты группы”. Необходимо при изменении поля “гид” пройтись по всем контактам в детали и используя значение поля “сделка” из каждого контакта, изменить поле “гид” для всех соответсвующих сделок этих контактов в разделе “сделки”.
Реализовали данную задача с помощью БП, но меняется только одна запись, подскажите пожалуйста, как можно читать всю выборку записей из детали и для множества соответствующих записей в другом разделе менять поле.

Нравится

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

Здравствуйте!

Судя по описанию необходимо работать одновременно с коллекцией данных, а не первой выбранной записью.
Пока с коллекцией может работать только элемент "Добавить данные" в режиме выборки.
Для решения вашей задачи можно использовать [Задание-сценарий], написать в нем интересующий запрос.
Или создать циклический процесс, который будет обрабатывать 1 контакт за 1 итерацию в цикле.

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

int count = Get<int>("count");
int count2 = count - 1;
Set<int>("count", count2);
return true;

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

Здравствуйте!

Алгоритм следующий:
А. Добавить в объект Контакты служебное поле с типом Логическое, например ForCycle.
Б. Первый процесс запускается по нужному событию и устанавливает для всех контактов ForCycle = true.
В. Второй процесс циклический:
1) читать кол-во контактов, у которых ForCycle = true
2) поток по умолчанию ведет к завершению процесса
3) условный поток: [Читать кол-во записей] > 0 - ведет к элементу Читать первый контакт, где ForCycle = true. На этом этапе мы получим Id первого интересующего контакта - > обрабатываем контакт - > устанавливаем ForCycle = false -> в конце поток перехода ведет к Пункт 1

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

Спасибо, получилось.

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

Если Вам необходимо управлять доступностью (enabled) или видимостью (visible) вы биндите их на атрибут (который предварительно создаете) и в последующем меняя значение атрибута - управляете соответствующим значением.
И все таки... (ради спортивного интереса, и для более углубленно понимания системы и применяемых в ней механизмов)
Каким образом в коде можно подступиться до элемента/поля и воздействовать на его свойства ?
(Это же компоненты Ext.JS насколько я понимаю)
Ну естественно я говорю не о том чтобы получить DOM-объект по имени компонента или по CSS-селектору (Ext.get() / Ext.getCmp()) и применить к нему стили инлайново.
Я про то как доступиться до конфига, изменить и инициализировать изменения ?

PS: Есть где ни будь кстати материал по разработке и внедрению кастомных элементов ? (быстрое гугление по форуму не помогло, хотя возможно я некорректно формулировал поисковый запрос :))

Нравится

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

Здравствуйте, Илья.

Помимо ExtJs в bpm'online также используется библиотека BackboneJs. Если на момент построение модели в ViewModel генераторе не были определены биндинги - изменить значение какого-либо свойства в рантайме Вы уже не сможете. Разве что писать свой пользовательский генератор.

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

Вопрос:
Как создать свой контрол?
Ответ:

контрол:

Ext.ns("Terrasoft");
/**
 * @class Terrasoft.controls.UsrMyControl
 * Класс элемента управления для отображения
 */
Ext.define("Terrasoft.controls.UsrMyControl", {
	extend: "Terrasoft.Component",
	alternateClassName: "Terrasoft.UsrMyControl",
	mixins: {
	},
	myParam: "",
	/**
	 * @inheritdoc Terrasoft.Component#tpl
	 * @protected
	 * @overridden
	 */
	tpl: [
		/*jshint white:false */
		"<div id='{id}-usr-my-control' class='{wrapClass}'>",
		"{myParam} test-test-test",
		"</div>"
		/*jshint white:true */
	],
	getTplData: function() {
		var tplData = this.callParent(arguments);
		tplData.myParam = this.myParam;
		this.updateSelectors(tplData);
		return tplData;
	},
	/**
	 * @inheritDoc Terrasoft.Component#init
	 * @protected
	 * @overridden
	 */
	init: function() {
		this.callParent(arguments);
		this.addEvents(
			"myMethod"
		);
	},
	/**
	 * @inheritDoc Terrasoft.Component#constructor
	 * @overridden
	 */
	constructor: function() {
		this.callParent(arguments);
	},
 
	updateSelectors: function(tplData) {
		var id = tplData.id;
		var baseIdSuffix = "-usr-my-control";
		var selectors = this.selectors = {};
		selectors.wrapEl = "#" + id + baseIdSuffix;
		return selectors;
	},
	/**
	 * Подписка на события элементов элемента управления
	 */
	initDomEvents: function() {
		this.callParent(arguments);
		var wrapEl = this.getWrapEl();
		if (wrapEl) {
			wrapEl.on("click", this.onClick, this);
		}
	},
	/**
	 * Обработчик события click на основном элементе
	 * @param event
	 */
	onClick: function(event) {
		event.stopEvent();
		this.fireEvent("myMethod", this);
	},
	/**
	 * Возвращает конфигурацию привязки к модели. Реализует интерфейс миксина {@link Terrasoft.Bindable}.
	 * @overridden
	 */
	getBindConfig: function() {
		var parentBindConfig = this.callParent(arguments);
		var bindConfig = {
			myParam: {
				changeMethod: "setMyParam"
			}
		};
		Ext.apply(bindConfig, parentBindConfig);
		return bindConfig;
	},
 
	setMyParam: function(val) {
		this.myParam = val || this.myParam;
		if (this.allowRerender()) {
			this.reRender();
		}
	},
	/**
	 * @overridden
	 * @inheritdoc Terrasoft.Component#destroy
	 */
	onDestroy: function() {
		var wrapEl = this.getWrapEl();
		if (wrapEl) {
			wrapEl.un("myMethod", this.onClick, this);
		}
		this.callParent(arguments);
	},
	/**
	 * Устанавливает значение флага только для чтения
	 * @param {Boolean} readonly Значение флага только для чтения. Если true - элемент управления устанавливается в
	 * режим только для чтения, false - рабочий режим элемента управления
	 */
	setReadonly: function(readonly) {
		if (this.readonly === readonly) {
			return;
		}
		this.readonly = readonly;
		if (this.allowRerender()) {
			this.reRender();
		}
	}
});

генератор:

 define("UsrMyControlGenerator", ["UsrMyControlGeneratorV2Resources", "terrasoft", "ext-base", "UsrMyControl"],
		function(resources) {
	var UsrMyControlGenerator = Ext.define("Terrasoft.configuration.UsrMyControlGenerator", {
		extend: "Terrasoft.ViewGenerator",
		alternateClassName: "Terrasoft.UsrMyControlGenerator",
		generateUsrMyControl: function(config) {
			var usrMyControl = {
				className: "Terrasoft.UsrMyControl",
				id: config.name + "UsrMyControl",
				selectors: {wrapEl: "#" + config.name + "UsrMyControl"},
				myParam: {bindTo: config.getMyParam},
				myMethod: {bindTo: config.myMethod}
			};
			if (!Ext.isEmpty(config.wrapClassName)) {
				usrMyControl.classes = {
					wrapClassName: config.wrapClassName
				};
			}
			return usrMyControl;
		}
	});
	return Ext.create(UsrMyControlGenerator);
});

схема:

 define("CasePage", ["CasePageResources", "terrasoft", "UsrMyControlGeneratorV2", "UsrMyControl"],
	function(resources, Terrasoft) {
	return {
		entitySchemaName: "Case",
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		attributes: {
			"Test": {
				"dataValueType": Terrasoft.DataValueType.TEXT,
				"type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
				"value": "123"
			}
		},
		diff: /**SCHEMA_DIFF*/[
			{
				"operation": "insert",
				"parentName": "SolutionTab_gridLayout",
				"name": "UsrTest",
				"propertyName": "items",
				"values": {
					"className": "Terrasoft.UsrMyControl",
					"layout": { "colSpan": 24, "rowSpan": 1, "column": 0, "row": 4 },
					"generator": "UsrMyControlGenerator.generateUsrMyControl",
					"visible": true,
					"getMyParam": "getMyParam",
					"myMethod": "myMethod"
				}
			}
		]/**SCHEMA_DIFF*/,
		methods: {
			onEntityInitialized: function() {
				this.callParent(arguments);
				// just for debug:
				document.scope = this;
			},
			getMyParam: function() {
				return this.get("Test");
			},
			myMethod: function() {
				var oldTest = this.get("Test");
				this.set("Test", oldTest + "!");
			}
		},
		rules: {}
	};
});
Показать все комментарии

Добрый день!
Возник такой вопрос - после нажатия на кнопку Печать в Счете система просит открыть или сохранить печатную форму Счет на оплату, имея определенное название (смотреть вложенный файл). Как можно изменить название печатной формы при сохранении документа?

Нравится

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

Здравствуйте, Александр!

Если в настройках браузера активна функция "Спрашивать куда сохранять файл перед скачиванием", то при скачивании ПФ у Вас будет появлятся окно, где Вы указываете путь сохранения файла и можете изменить его название (см. скриншоты). Также, Вы можете изменить название ПФ в справочнике "Печатные формы".

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

Нужно разработать логику которая бы копировала и данные детали раздела.
Есть такой код isCopyMode

onEntityInitialized: function() {
                                if (this.isAddMode() || this.isCopyMode()) {
//код
}
}

Но как узнать данные источника откуда копируется раздел ?

Нравится

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

Если это isCopyMode то id от кого скопировались узнать можно так:
this.get("SourceEntityPrimaryColumnValue")
Если isAddMode, то понятно, что нет никакого источника.

Максим спасибо.
Буду делать копирование так

onEntityInitialized: function() {
				if (this.isCopyMode()) {
 
this.get("SourceEntityPrimaryColumnValue"); //id источника
//код копирования
 
				}

вопрос...как я понимаю пока еще нет же созданной записи в БД?

"Юсупов Марат" написал:как я понимаю пока еще нет же созданной записи в БД?

Все верно, данной записи еще нет в бд, но вы можете вызвать:
this.save({silent:true}) что сохранит данную запись в бд, не закрывая карточку. После чего можно будет добавлять в неё и записи деталей.

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

В системе введено 2 сущности: элементы организационной структуры "Орг.юниты" и "Функциональные роли", в каждую из них по отдельности можно включать пользователей, и друг друга.
Причем включение одной сущности в другую создает реверс-тождественную связь (Добавление Функ.роли в Орг.Юнит равнозначно добавлению Орг.Юнита в Функ.Роль, эти связи вне зависимости от направления даже описываются в одной таблице).
Так вот, логично было бы предположить что данное разделение для обеспечения многопользовательского режима в приложении имеет смысл на уровне предоставления прав доступа, но эксперимент показывает весьма непредсказуемое поведение:
Пользователь А включен в Функц.Роль B которая в свою очередь включена в Орг.Юнит C.
В настройках прав доступа, например на объект "Контакты", установлено запрещающее правило на "Чтение", что фактически скрывает раздел для Орг.Юнита C.
Логическая цепочка: Пользователю A, раздел контакты станет недоступен, т.к. он входит в Функ.Роль B которая в свою очередь входит в Орг.юнит С, которому запрещено чтение по объекту "Контакты".
Фактически: Пользователю A - остается доступен раздел "Контакты", и ограничение он получает только если будет непосредственно включен в Орг.Юнит C
т.е. ограничения распостраняются только на пользователей непосредственно включенных в соответствующую Функ.роль или Орг.Юнит, через включение их друг в друга пользователям не наследуются никакие разрешения целевой группы, так же как и пользователям целевой что либо от включаемой.

А теперь самый главный вопрос: Зачем ?!
Какой смысл в 2-х сущностях, и в возможности их связи друг с другом, если это ни коим образом не связано с наследованием предоставленных разрешений ?!
И зачем вообще тогда нужны Функциональные роли...
В чем вообще "соль" обособленности Орг.Юнитов и Функ.Ролей ?

Нравится

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

Здравствуйте, Илья.

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

Ответы на ваши вопросы вы можете найти в статьях на Академии: https://academy.terrasoft.ru/documents/sales-team/7-9/razdel-upravleniy…
https://academy.terrasoft.ru/documents/sales-team/7-9/stranica-organiza…
https://academy.terrasoft.ru/documents/sales-team/7-9/stranica-funkcion…

Также, рекомендуем вам просмотреть видео:
https://www.youtube.com/watch?v=mSWfz61NKFI
https://www.youtube.com/watch?v=x5C6VcOhKj4

В них поясняются все особенности работы с Организационными и Функциональными ролями и описаны примеры настройки прав доступа для Функциональных и Организационных ролей.

Ну с членством в другой функ.роли которой явно разрешено и разрешающее правило расположено выше - понятно.

а вот тут, прошу пару слов пояснения:

"Мария Ватулина" написал:входить в дргую функциональную роль, которой доступ к данному разделу не запрещен

Т.е. включая пользователя в Орг.юнит или Функ.роль которой явно что-то не запрещено, все связанные явно заданные запрещающие правила всех других включений для пользователя - не применяются ?

Илья,

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

Рекомендуем ознакомиться с видео (ссылки указаны выше), там озвучены все нюансы настройки прав доступа для Организационных и Функциональных ролей.

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

Здравствуйте, подскажите, пожалуйста, реально ли реализовать автонумерацию строк в детали, к примеру “контакт в группе” с учетом удаленных записей и т.д.?

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

Нравится

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

Вряд ли это надо делать с системной настройкой, так как с деталью обычно в одно время работает 1 пользователь. Достаточно перед сохранением записи брать максимальное значение в группе.
А при удалении - пересчитывать номер у всех последующих записей.
Ну, и дать возможность пользователю менять очередность записей, если необходимо.

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

Добрый день, Мария!

Подскажите, по статье ниже добавляли автонумерацию?
https://academy.terrasoft.ua/documents/technic-sdk/7-9/primer-ispolzova…

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

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

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

Это не самая корректная реализация. Правильнее присваивать номер счета после сохранения записи

спасибо :smile:

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

Коллеги, приветствую! Дурацкий вопрос. Вот кусок кода:

statusChanged: function() {
        if (this.get("Status").value === '394d4b84-58e6-df11-971b-001d60e938c6') {
        var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {rootSchemaName: "VwAvailablePlaces"});
        esq.addColumn("Classroom", "Classroom");
        var freePlacesColumn = esq.addColumn("FreePlaces", "FreePlaces");
        freePlacesColumn.orderDirection = Terrasoft.OrderDirection.ASC;
        freePlacesColumn.orderPosition = 0;
        esq.filters.addItem(
                this.Terrasoft.createColumnFilterWithParameter(
                        this.Terrasoft.ComparisonType.EQUAL, "Branch", this.get("Branch").value));
        esq.getEntityCollection(function(response) {
           if (response.success && response.collection.getCount() > 0) {
                var classRoomValue = response.collection.getByIndex(0).get("Classroom").value;
                var classRoomDisplayValue = response.collection.getByIndex(0).get("Classroom").displayValue;
                 this.set("Classroom", {value: classRoomValue, displayValue: classRoomDisplayValue});
            }
          }, this);
     }                         

Так вот сортировка игнорируется(что ASC, что DESC - один фиг). Вот это место:

        freePlacesColumn.orderDirection = Terrasoft.OrderDirection.ASC;
        freePlacesColumn.orderPosition = 0;

А ну да, самое прикольное, что на сервак-то это передается. В консоли:

orderDirection: 1 (ну или 2 для DESC)
orderPosition: 0

Вообще смахивает на системную ошибку это все. Или нет? Код - точная копия того, как делают ОНИ. В чем проблема? Это 7.9, если что.

Нравится

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

Добрый день, Дмитрий!

Только что проверил на своей 7.9.2 - направление сортировки работает. Попробуйте в это же представление в базе выполнить запрос с ASC и DESC, возможно, там всего одна запись, потому и есть ощущение, что сортировка не отрабатывает.

Илья, спасибо за ответ. Ну да, вьюха показала небольшой недочет. Спс.

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

Подскажите, возможно ли вытягивать поле "Примечания" (на вкладках "Файлы и примечания") автоматически по высоте текста?

Нравится

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

Добрый день!

Конечно можно. Для решения Вашего кейса нужно применить собственные css-стили. Как это можно сделать в приложении:
http://www.community.terrasoft.ru/forum/topic/16386

Подробное руководство по css:
https://developer.mozilla.org/en-US/docs/Web/CSS

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